From 9d1d4a376333260b0e1735d658b91331e4478fba Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Mon, 5 Jan 2026 17:58:48 +0800 Subject: [PATCH] feat: add missing primops --- nix-js/runtime-ts/build.mjs | 8 + nix-js/runtime-ts/package.json | 2 +- nix-js/runtime-ts/src/builtins/attrs.ts | 17 +- nix-js/runtime-ts/src/builtins/derivation.ts | 9 + nix-js/runtime-ts/src/builtins/index.ts | 220 ++++++++++--------- nix-js/runtime-ts/src/builtins/io.ts | 8 + nix-js/runtime-ts/src/builtins/misc.ts | 16 ++ nix-js/runtime-ts/src/helpers.ts | 1 - nix-js/runtime-ts/src/type-assert.ts | 36 ++- nix-js/runtime-ts/src/types/global.d.ts | 2 + 10 files changed, 201 insertions(+), 118 deletions(-) create mode 100644 nix-js/runtime-ts/build.mjs create mode 100644 nix-js/runtime-ts/src/builtins/derivation.ts diff --git a/nix-js/runtime-ts/build.mjs b/nix-js/runtime-ts/build.mjs new file mode 100644 index 0000000..52eec08 --- /dev/null +++ b/nix-js/runtime-ts/build.mjs @@ -0,0 +1,8 @@ +import * as esbuild from "esbuild"; + +await esbuild.build({ + entryPoints: ["src/index.ts"], + outfile: "dist/runtime.js", + bundle: true, + minify: true, +}); diff --git a/nix-js/runtime-ts/package.json b/nix-js/runtime-ts/package.json index 76190fe..40d00de 100644 --- a/nix-js/runtime-ts/package.json +++ b/nix-js/runtime-ts/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "typecheck": "tsc --noEmit", - "build": "esbuild src/index.ts --bundle --target=es2020 --format=iife --outfile=dist/runtime.js", + "build": "node build.mjs", "dev": "npm run typecheck && npm run build" }, "devDependencies": { diff --git a/nix-js/runtime-ts/src/builtins/attrs.ts b/nix-js/runtime-ts/src/builtins/attrs.ts index fdc81cb..1eb1f95 100644 --- a/nix-js/runtime-ts/src/builtins/attrs.ts +++ b/nix-js/runtime-ts/src/builtins/attrs.ts @@ -5,7 +5,7 @@ import type { NixValue, NixAttrs, NixList } from "../types"; import { force_attrs, force_string, force_function, force_list } from "../type-assert"; -export const attrNames = (set: NixValue): string[] => Object.keys(force_attrs(set)); +export const attrNames = (set: NixValue): string[] => Object.keys(force_attrs(set)).sort(); export const attrValues = (set: NixValue): NixValue[] => Object.values(force_attrs(set)); @@ -31,6 +31,21 @@ export const mapAttrs = return new_attrs; }; +export const removeAttrs = + (attrs: NixValue) => + (list: NixValue): NixAttrs => { + const new_attrs: NixAttrs = {}; + const forced_attrs = force_attrs(attrs); + const forced_list = force_list(list); + + for (const key in forced_attrs) { + if (!(key in forced_list)) { + new_attrs[key] = forced_attrs[key]; + } + } + return new_attrs; + }; + export const listToAttrs = (e: NixValue): NixAttrs => { const attrs: NixAttrs = {}; const forced_e = [...force_list(e)].reverse(); diff --git a/nix-js/runtime-ts/src/builtins/derivation.ts b/nix-js/runtime-ts/src/builtins/derivation.ts new file mode 100644 index 0000000..34fc186 --- /dev/null +++ b/nix-js/runtime-ts/src/builtins/derivation.ts @@ -0,0 +1,9 @@ +import type { NixValue } from "../types"; + +export const derivation = (args: NixValue) => { + throw "Not implemented: derivation"; +}; + +export const derivationStrict = (args: NixValue) => { + throw "Not implemented: derivationStrict"; +}; diff --git a/nix-js/runtime-ts/src/builtins/index.ts b/nix-js/runtime-ts/src/builtins/index.ts index a12dc2c..9ffe6dd 100644 --- a/nix-js/runtime-ts/src/builtins/index.ts +++ b/nix-js/runtime-ts/src/builtins/index.ts @@ -33,7 +33,7 @@ export interface PrimopMetadata { * @param applied - Number of arguments already applied (default: 0) * @returns The marked function */ -export const markPrimop = ( +export const mkPrimop = ( func: T, name: string, arity: number, @@ -53,7 +53,7 @@ export const markPrimop = ( const result = func(...args); // If result is a function, mark it as the next layer if (typeof result === "function") { - return markPrimop(result, name, arity, applied + args.length); + return mkPrimop(result, name, arity, applied + args.length); } return result; }) as any; @@ -108,6 +108,7 @@ import * as functional from "./functional"; import * as io from "./io"; import * as conversion from "./conversion"; import * as misc from "./misc"; +import * as derivation from "./derivation"; /** * The global builtins object @@ -120,122 +121,131 @@ import * as misc from "./misc"; * All primop functions are marked with PRIMOP_METADATA symbol for runtime introspection */ export const builtins: any = { - add: markPrimop(arithmetic.add, "add", 2), - sub: markPrimop(arithmetic.sub, "sub", 2), - mul: markPrimop(arithmetic.mul, "mul", 2), - div: markPrimop(arithmetic.div, "div", 2), - bitAnd: markPrimop(arithmetic.bitAnd, "bitAnd", 2), - bitOr: markPrimop(arithmetic.bitOr, "bitOr", 2), - bitXor: markPrimop(arithmetic.bitXor, "bitXor", 2), - lessThan: markPrimop(arithmetic.lessThan, "lessThan", 2), + add: mkPrimop(arithmetic.add, "add", 2), + sub: mkPrimop(arithmetic.sub, "sub", 2), + mul: mkPrimop(arithmetic.mul, "mul", 2), + div: mkPrimop(arithmetic.div, "div", 2), + bitAnd: mkPrimop(arithmetic.bitAnd, "bitAnd", 2), + bitOr: mkPrimop(arithmetic.bitOr, "bitOr", 2), + bitXor: mkPrimop(arithmetic.bitXor, "bitXor", 2), + lessThan: mkPrimop(arithmetic.lessThan, "lessThan", 2), - ceil: markPrimop(math.ceil, "ceil", 1), - floor: markPrimop(math.floor, "floor", 1), + ceil: mkPrimop(math.ceil, "ceil", 1), + floor: mkPrimop(math.floor, "floor", 1), - isAttrs: markPrimop(typeCheck.isAttrs, "isAttrs", 1), - isBool: markPrimop(typeCheck.isBool, "isBool", 1), - isFloat: markPrimop(typeCheck.isFloat, "isFloat", 1), - isFunction: markPrimop(typeCheck.isFunction, "isFunction", 1), - isInt: markPrimop(typeCheck.isInt, "isInt", 1), - isList: markPrimop(typeCheck.isList, "isList", 1), - isNull: markPrimop(typeCheck.isNull, "isNull", 1), - isPath: markPrimop(typeCheck.isPath, "isPath", 1), - isString: markPrimop(typeCheck.isString, "isString", 1), - typeOf: markPrimop(typeCheck.typeOf, "typeOf", 1), + isAttrs: mkPrimop(typeCheck.isAttrs, "isAttrs", 1), + isBool: mkPrimop(typeCheck.isBool, "isBool", 1), + isFloat: mkPrimop(typeCheck.isFloat, "isFloat", 1), + isFunction: mkPrimop(typeCheck.isFunction, "isFunction", 1), + isInt: mkPrimop(typeCheck.isInt, "isInt", 1), + isList: mkPrimop(typeCheck.isList, "isList", 1), + isNull: mkPrimop(typeCheck.isNull, "isNull", 1), + isPath: mkPrimop(typeCheck.isPath, "isPath", 1), + isString: mkPrimop(typeCheck.isString, "isString", 1), + typeOf: mkPrimop(typeCheck.typeOf, "typeOf", 1), - map: markPrimop(list.map, "map", 2), - filter: markPrimop(list.filter, "filter", 2), - length: markPrimop(list.length, "length", 1), - head: markPrimop(list.head, "head", 1), - tail: markPrimop(list.tail, "tail", 1), - elem: markPrimop(list.elem, "elem", 2), - elemAt: markPrimop(list.elemAt, "elemAt", 2), - concatLists: markPrimop(list.concatLists, "concatLists", 1), - concatMap: markPrimop(list.concatMap, "concatMap", 2), - "foldl'": markPrimop(list.foldlPrime, "foldl'", 3), - sort: markPrimop(list.sort, "sort", 2), - partition: markPrimop(list.partition, "partition", 2), - genList: markPrimop(list.genList, "genList", 2), - all: markPrimop(list.all, "all", 2), - any: markPrimop(list.any, "any", 2), + map: mkPrimop(list.map, "map", 2), + filter: mkPrimop(list.filter, "filter", 2), + length: mkPrimop(list.length, "length", 1), + head: mkPrimop(list.head, "head", 1), + tail: mkPrimop(list.tail, "tail", 1), + elem: mkPrimop(list.elem, "elem", 2), + elemAt: mkPrimop(list.elemAt, "elemAt", 2), + concatLists: mkPrimop(list.concatLists, "concatLists", 1), + concatMap: mkPrimop(list.concatMap, "concatMap", 2), + "foldl'": mkPrimop(list.foldlPrime, "foldl'", 3), + sort: mkPrimop(list.sort, "sort", 2), + partition: mkPrimop(list.partition, "partition", 2), + genList: mkPrimop(list.genList, "genList", 2), + all: mkPrimop(list.all, "all", 2), + any: mkPrimop(list.any, "any", 2), - attrNames: markPrimop(attrs.attrNames, "attrNames", 1), - attrValues: markPrimop(attrs.attrValues, "attrValues", 1), - getAttr: markPrimop(attrs.getAttr, "getAttr", 2), - hasAttr: markPrimop(attrs.hasAttr, "hasAttr", 2), - mapAttrs: markPrimop(attrs.mapAttrs, "mapAttrs", 2), - listToAttrs: markPrimop(attrs.listToAttrs, "listToAttrs", 1), - intersectAttrs: markPrimop(attrs.intersectAttrs, "intersectAttrs", 2), - catAttrs: markPrimop(attrs.catAttrs, "catAttrs", 2), - groupBy: markPrimop(attrs.groupBy, "groupBy", 2), + attrNames: mkPrimop(attrs.attrNames, "attrNames", 1), + attrValues: mkPrimop(attrs.attrValues, "attrValues", 1), + getAttr: mkPrimop(attrs.getAttr, "getAttr", 2), + hasAttr: mkPrimop(attrs.hasAttr, "hasAttr", 2), + mapAttrs: mkPrimop(attrs.mapAttrs, "mapAttrs", 2), + removeAttrs: mkPrimop(attrs.removeAttrs, "removeAttrs", 2), + listToAttrs: mkPrimop(attrs.listToAttrs, "listToAttrs", 1), + intersectAttrs: mkPrimop(attrs.intersectAttrs, "intersectAttrs", 2), + catAttrs: mkPrimop(attrs.catAttrs, "catAttrs", 2), + groupBy: mkPrimop(attrs.groupBy, "groupBy", 2), - stringLength: markPrimop(string.stringLength, "stringLength", 1), - substring: markPrimop(string.substring, "substring", 3), - concatStringsSep: markPrimop(string.concatStringsSep, "concatStringsSep", 2), - baseNameOf: markPrimop(string.baseNameOf, "baseNameOf", 1), + stringLength: mkPrimop(string.stringLength, "stringLength", 1), + substring: mkPrimop(string.substring, "substring", 3), + concatStringsSep: mkPrimop(string.concatStringsSep, "concatStringsSep", 2), + baseNameOf: mkPrimop(string.baseNameOf, "baseNameOf", 1), - seq: markPrimop(functional.seq, "seq", 2), - deepSeq: markPrimop(functional.deepSeq, "deepSeq", 2), - abort: markPrimop(functional.abort, "abort", 1), - throw: markPrimop(functional.throwFunc, "throw", 1), - trace: markPrimop(functional.trace, "trace", 2), - warn: markPrimop(functional.warn, "warn", 2), - break: markPrimop(functional.breakFunc, "break", 1), + seq: mkPrimop(functional.seq, "seq", 2), + deepSeq: mkPrimop(functional.deepSeq, "deepSeq", 2), + abort: mkPrimop(functional.abort, "abort", 1), + throw: mkPrimop(functional.throwFunc, "throw", 1), + trace: mkPrimop(functional.trace, "trace", 2), + warn: mkPrimop(functional.warn, "warn", 2), + break: mkPrimop(functional.breakFunc, "break", 1), - import: markPrimop(io.importFunc, "import", 1), - scopedImport: markPrimop(io.scopedImport, "scopedImport", 2), - fetchClosure: markPrimop(io.fetchClosure, "fetchClosure", 1), - fetchGit: markPrimop(io.fetchGit, "fetchGit", 1), - fetchTarball: markPrimop(io.fetchTarball, "fetchTarball", 1), - fetchTree: markPrimop(io.fetchTree, "fetchTree", 1), - fetchurl: markPrimop(io.fetchurl, "fetchurl", 1), - readDir: markPrimop(io.readDir, "readDir", 1), - readFile: markPrimop(io.readFile, "readFile", 1), - readFileType: markPrimop(io.readFileType, "readFileType", 1), - pathExists: markPrimop(io.pathExists, "pathExists", 1), - path: markPrimop(io.path, "path", 1), - toFile: markPrimop(io.toFile, "toFile", 2), - toPath: markPrimop(io.toPath, "toPath", 1), - filterSource: markPrimop(io.filterSource, "filterSource", 2), - findFile: markPrimop(io.findFile, "findFile", 2), - getEnv: markPrimop(io.getEnv, "getEnv", 1), + derivation: mkPrimop(derivation.derivation, "derivation", 1), + derivationStrict: mkPrimop(derivation.derivationStrict, "derivationStrict", 1), - fromJSON: markPrimop(conversion.fromJSON, "fromJSON", 1), - fromTOML: markPrimop(conversion.fromTOML, "fromTOML", 1), - toJSON: markPrimop(conversion.toJSON, "toJSON", 1), - toXML: markPrimop(conversion.toXML, "toXML", 1), - toString: markPrimop(conversion.toString, "toString", 1), + import: mkPrimop(io.importFunc, "import", 1), + scopedImport: mkPrimop(io.scopedImport, "scopedImport", 2), + storePath: mkPrimop(io.storePath, "storePath", 1), + fetchClosure: mkPrimop(io.fetchClosure, "fetchClosure", 1), + fetchMercurial: mkPrimop(io.fetchMercurial, "fetchMercurial", 1), + fetchGit: mkPrimop(io.fetchGit, "fetchGit", 1), + fetchTarball: mkPrimop(io.fetchTarball, "fetchTarball", 1), + fetchTree: mkPrimop(io.fetchTree, "fetchTree", 1), + fetchurl: mkPrimop(io.fetchurl, "fetchurl", 1), + readDir: mkPrimop(io.readDir, "readDir", 1), + readFile: mkPrimop(io.readFile, "readFile", 1), + readFileType: mkPrimop(io.readFileType, "readFileType", 1), + pathExists: mkPrimop(io.pathExists, "pathExists", 1), + path: mkPrimop(io.path, "path", 1), + toFile: mkPrimop(io.toFile, "toFile", 2), + toPath: mkPrimop(io.toPath, "toPath", 1), + filterSource: mkPrimop(io.filterSource, "filterSource", 2), + findFile: mkPrimop(io.findFile, "findFile", 2), + getEnv: mkPrimop(io.getEnv, "getEnv", 1), - getContext: markPrimop(misc.getContext, "getContext", 1), - hasContext: markPrimop(misc.hasContext, "hasContext", 1), - hashFile: markPrimop(misc.hashFile, "hashFile", 2), - hashString: markPrimop(misc.hashString, "hashString", 2), - convertHash: markPrimop(misc.convertHash, "convertHash", 2), - unsafeDiscardOutputDependency: markPrimop( + fromJSON: mkPrimop(conversion.fromJSON, "fromJSON", 1), + fromTOML: mkPrimop(conversion.fromTOML, "fromTOML", 1), + toJSON: mkPrimop(conversion.toJSON, "toJSON", 1), + toXML: mkPrimop(conversion.toXML, "toXML", 1), + toString: mkPrimop(conversion.toString, "toString", 1), + + addErrorContext: mkPrimop(misc.addErrorContext, "addErrorContext", 1), + appendContext: mkPrimop(misc.appendContext, "appendContext", 1), + getContext: mkPrimop(misc.getContext, "getContext", 1), + hasContext: mkPrimop(misc.hasContext, "hasContext", 1), + hashFile: mkPrimop(misc.hashFile, "hashFile", 2), + hashString: mkPrimop(misc.hashString, "hashString", 2), + convertHash: mkPrimop(misc.convertHash, "convertHash", 2), + unsafeDiscardOutputDependency: mkPrimop( misc.unsafeDiscardOutputDependency, "unsafeDiscardOutputDependency", 1, ), - unsafeDiscardStringContext: markPrimop(misc.unsafeDiscardStringContext, "unsafeDiscardStringContext", 1), - unsafeGetAttrPos: markPrimop(misc.unsafeGetAttrPos, "unsafeGetAttrPos", 2), - addDrvOutputDependencies: markPrimop(misc.addDrvOutputDependencies, "addDrvOutputDependencies", 2), - compareVersions: markPrimop(misc.compareVersions, "compareVersions", 2), - dirOf: markPrimop(misc.dirOf, "dirOf", 1), - flakeRefToString: markPrimop(misc.flakeRefToString, "flakeRefToString", 1), - functionArgs: markPrimop(misc.functionArgs, "functionArgs", 1), - genericClosure: markPrimop(misc.genericClosure, "genericClosure", 1), - getFlake: markPrimop(misc.getFlake, "getFlake", 1), - match: markPrimop(misc.match, "match", 2), - outputOf: markPrimop(misc.outputOf, "outputOf", 2), - parseDrvName: markPrimop(misc.parseDrvName, "parseDrvName", 1), - parseFlakeName: markPrimop(misc.parseFlakeName, "parseFlakeName", 1), - placeholder: markPrimop(misc.placeholder, "placeholder", 1), - replaceStrings: markPrimop(misc.replaceStrings, "replaceStrings", 3), - split: markPrimop(misc.split, "split", 2), - splitVersion: markPrimop(misc.splitVersion, "splitVersion", 1), - traceVerbose: markPrimop(misc.traceVerbose, "traceVerbose", 2), - tryEval: markPrimop(misc.tryEval, "tryEval", 1), - zipAttrsWith: markPrimop(misc.zipAttrsWith, "zipAttrsWith", 2), + unsafeDiscardStringContext: mkPrimop(misc.unsafeDiscardStringContext, "unsafeDiscardStringContext", 1), + unsafeGetAttrPos: mkPrimop(misc.unsafeGetAttrPos, "unsafeGetAttrPos", 2), + addDrvOutputDependencies: mkPrimop(misc.addDrvOutputDependencies, "addDrvOutputDependencies", 2), + compareVersions: mkPrimop(misc.compareVersions, "compareVersions", 2), + dirOf: mkPrimop(misc.dirOf, "dirOf", 1), + flakeRefToString: mkPrimop(misc.flakeRefToString, "flakeRefToString", 1), + functionArgs: mkPrimop(misc.functionArgs, "functionArgs", 1), + genericClosure: mkPrimop(misc.genericClosure, "genericClosure", 1), + getFlake: mkPrimop(misc.getFlake, "getFlake", 1), + match: mkPrimop(misc.match, "match", 2), + outputOf: mkPrimop(misc.outputOf, "outputOf", 2), + parseDrvName: mkPrimop(misc.parseDrvName, "parseDrvName", 1), + parseFlakeName: mkPrimop(misc.parseFlakeName, "parseFlakeName", 1), + parseFlakeRef: mkPrimop(misc.parseFlakeRef, "parseFlakeRef", 1), + placeholder: mkPrimop(misc.placeholder, "placeholder", 1), + replaceStrings: mkPrimop(misc.replaceStrings, "replaceStrings", 3), + split: mkPrimop(misc.split, "split", 2), + splitVersion: mkPrimop(misc.splitVersion, "splitVersion", 1), + traceVerbose: mkPrimop(misc.traceVerbose, "traceVerbose", 2), + tryEval: mkPrimop(misc.tryEval, "tryEval", 1), + zipAttrsWith: mkPrimop(misc.zipAttrsWith, "zipAttrsWith", 2), builtins: create_thunk(() => builtins), currentSystem: create_thunk(() => { diff --git a/nix-js/runtime-ts/src/builtins/io.ts b/nix-js/runtime-ts/src/builtins/io.ts index 2baa9c4..983ca4a 100644 --- a/nix-js/runtime-ts/src/builtins/io.ts +++ b/nix-js/runtime-ts/src/builtins/io.ts @@ -28,10 +28,18 @@ export const scopedImport = throw "Not implemented: scopedImport"; }; +export const storePath = (args: NixValue): never => { + throw "Not implemented: storePath"; +}; + export const fetchClosure = (args: NixValue): never => { throw "Not implemented: fetchClosure"; }; +export const fetchMercurial = (args: NixValue): never => { + throw "Not implemented: fetchMercurial"; +}; + export const fetchGit = (args: NixValue): never => { throw "Not implemented: fetchGit"; }; diff --git a/nix-js/runtime-ts/src/builtins/misc.ts b/nix-js/runtime-ts/src/builtins/misc.ts index 70bd76d..18d752d 100644 --- a/nix-js/runtime-ts/src/builtins/misc.ts +++ b/nix-js/runtime-ts/src/builtins/misc.ts @@ -4,6 +4,18 @@ import type { NixValue } from "../types"; +export const addErrorContext = + (e1: NixValue) => + (e2: NixValue): never => { + throw "Not implemented: addErrorContext"; + }; + +export const appendContext = + (e1: NixValue) => + (e2: NixValue): never => { + throw "Not implemented: appendContext"; + }; + export const getContext = (s: NixValue): never => { throw "Not implemented: getContext"; }; @@ -90,6 +102,10 @@ export const parseFlakeName = (s: NixValue): never => { throw "Not implemented: parseFlakeName"; }; +export const parseFlakeRef = (s: NixValue): never => { + throw "Not implemented: parseFlakeRef"; +}; + export const placeholder = (output: NixValue): never => { throw "Not implemented: placeholder"; }; diff --git a/nix-js/runtime-ts/src/helpers.ts b/nix-js/runtime-ts/src/helpers.ts index 98c0d9b..8a47456 100644 --- a/nix-js/runtime-ts/src/helpers.ts +++ b/nix-js/runtime-ts/src/helpers.ts @@ -1,6 +1,5 @@ /** * Helper functions for nix-js runtime - * Implements attribute selection, parameter validation, and lazy sets */ import type { NixValue, NixAttrs } from "./types"; diff --git a/nix-js/runtime-ts/src/type-assert.ts b/nix-js/runtime-ts/src/type-assert.ts index c9680bb..2a2782e 100644 --- a/nix-js/runtime-ts/src/type-assert.ts +++ b/nix-js/runtime-ts/src/type-assert.ts @@ -5,6 +5,22 @@ import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber } from "./types"; import { force } from "./thunk"; +import { isAttrs } from "./builtins/type-check"; + +const typeName = (value: NixValue): string => { + const val = force(value); + + if (typeof val === "bigint") return "int"; + if (typeof val === "number") return "float"; + if (typeof val === "boolean") return "boolean"; + if (typeof val === "string") return "string"; + if (val === null) return "null"; + if (Array.isArray(val)) return "list"; + if (typeof val === "function") return "lambda"; + if (typeof val === "object") return "attribute set"; + + throw new TypeError(`Unknown Nix type: ${typeof val}`); +} /** * Force a value and assert it's a list @@ -13,7 +29,7 @@ import { force } from "./thunk"; export const force_list = (value: NixValue): NixList => { const forced = force(value); if (!Array.isArray(forced)) { - throw new TypeError(`Expected list, got ${typeof forced}`); + throw new TypeError(`Expected list, got ${typeName(forced)}`); } return forced; }; @@ -25,7 +41,7 @@ export const force_list = (value: NixValue): NixList => { export const force_function = (value: NixValue): NixFunction => { const forced = force(value); if (typeof forced !== "function") { - throw new TypeError(`Expected function, got ${typeof forced}`); + throw new TypeError(`Expected function, got ${typeName(forced)}`); } return forced; }; @@ -36,10 +52,10 @@ export const force_function = (value: NixValue): NixFunction => { */ export const force_attrs = (value: NixValue): NixAttrs => { const forced = force(value); - if (typeof forced !== "object" || forced === null || Array.isArray(forced)) { - throw new TypeError(`Expected attribute set, got ${typeof forced}`); + if (!isAttrs(forced)) { + throw new TypeError(`Expected attribute set, got ${typeName(forced)}`); } - return forced as NixAttrs; + return forced; }; /** @@ -49,7 +65,7 @@ export const force_attrs = (value: NixValue): NixAttrs => { export const force_string = (value: NixValue): string => { const forced = force(value); if (typeof forced !== "string") { - throw new TypeError(`Expected string, got ${typeof forced}`); + throw new TypeError(`Expected string, got ${typeName(forced)}`); } return forced; }; @@ -61,7 +77,7 @@ export const force_string = (value: NixValue): string => { export const force_bool = (value: NixValue): boolean => { const forced = force(value); if (typeof forced !== "boolean") { - throw new TypeError(`Expected boolean, got ${typeof forced}`); + throw new TypeError(`Expected boolean, got ${typeName(forced)}`); } return forced; }; @@ -75,7 +91,7 @@ export const force_int = (value: NixValue): NixInt => { if (typeof forced === "bigint") { return forced; } - throw new TypeError(`Expected int, got ${typeof forced}`); + throw new TypeError(`Expected int, got ${typeName(forced)}`); }; /** @@ -87,7 +103,7 @@ export const force_float = (value: NixValue): NixFloat => { if (typeof forced === "number") { return forced; } - throw new TypeError(`Expected float, got ${typeof forced}`); + throw new TypeError(`Expected float, got ${typeName(forced)}`); }; /** @@ -99,7 +115,7 @@ export const force_numeric = (value: NixValue): NixNumber => { if (typeof forced === "bigint" || typeof forced === "number") { return forced; } - throw new TypeError(`Expected numeric type, got ${typeof forced}`); + throw new TypeError(`Expected numeric type, got ${typeName(forced)}`); }; /** diff --git a/nix-js/runtime-ts/src/types/global.d.ts b/nix-js/runtime-ts/src/types/global.d.ts index dca85d8..354754e 100644 --- a/nix-js/runtime-ts/src/types/global.d.ts +++ b/nix-js/runtime-ts/src/types/global.d.ts @@ -1,3 +1,5 @@ +import type { NixRuntime } from ".."; + export {}; declare global {