bonsai-js 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"define.mjs","names":[],"sources":["../../src/functions/define.ts"],"sourcesContent":["import { BLOCKED_NAMES } from '../syntax/lexer.js'\nimport type { Span } from '../errors.js'\nimport type { State } from '../runtime/state.js'\nimport {\n Duration,\n isMap,\n isTimestamp,\n isValidDuration,\n keyListCost,\n shown,\n} from '../runtime/values.js'\nimport { formatType, typeText, type FunctionType, type Type, type TypeVar } from '../types.js'\n\n/** A lambda as seen by a built-in: called with the item and its index. */\nexport type Lambda = (item: unknown, index: number) => unknown\n\n/** Call-site information handed to a built-in implementation. */\nexport interface CallSite {\n readonly state: State\n readonly span: Span\n}\n\n/**\n * What async evaluation resolves to: a value in a box. JavaScript adopts (runs\n * `then` on) any thenable an async function returns or awaits, and data from\n * the host is opaque, never awaited, so values cross an `await` boxed.\n */\nexport class Out {\n readonly v: unknown\n constructor(v: unknown) {\n this.v = v\n }\n}\n\nexport interface Overload {\n /** Parameter types. Function-typed parameters take lambdas. */\n readonly params: readonly Type[]\n /** How many leading parameters are required (default: all). */\n readonly required?: number\n /** Type of any further arguments (variadic). */\n readonly rest?: Type\n /** Result type; may reference type variables bound by the parameters. */\n readonly result: Type\n readonly run: (args: unknown[], site: CallSite) => unknown\n /**\n * Variant used when a lambda argument is asynchronous. Its lambdas resolve\n * to an {@link Out}, and so does it.\n */\n readonly runAsync?: ((args: unknown[], site: CallSite) => Promise<Out>) | undefined\n /** Type variables that must bind to one orderable kind (number, string, timestamp, duration). */\n readonly ordered?: readonly string[]\n /**\n * Static validation of literal arguments (undefined where an argument is\n * not a literal). Returns an error message, so mistakes in constant\n * patterns are reported when the expression is checked.\n */\n readonly literals?: ((values: readonly unknown[]) => string | undefined) | undefined\n}\n\nexport interface FunctionDef {\n readonly name: string\n readonly description: string\n readonly overloads: readonly Overload[]\n /** Host functions: returns a promise and must be awaited. */\n readonly async?: boolean\n /** Host functions: receives a HostCall (context and signal) as its first argument. */\n readonly call?: boolean\n readonly host?: boolean\n /** Host functions: steps charged per call (default 32). */\n readonly cost?: number\n}\n\nexport const T: TypeVar = Object.freeze({ kind: 'var', name: 'T' })\nexport const U: TypeVar = Object.freeze({ kind: 'var', name: 'U' })\nexport const K: TypeVar = Object.freeze({ kind: 'var', name: 'K' })\n\nexport function fnType(params: readonly Type[], result: Type): FunctionType {\n return Object.freeze({ kind: 'function', params: Object.freeze([...params]), result })\n}\n\n/** An overload's signature as messages and hovers show it, e.g. `slice(string, number, number?): string`. */\nexport function signatureText(name: string, candidate: Overload): string {\n const required = candidate.required ?? candidate.params.length\n const params = candidate.params.map((param, i) => `${typeText(param)}${i >= required ? '?' : ''}`)\n if (candidate.rest !== undefined) params.push(`...${typeText(candidate.rest)}`)\n return `${name}(${params.join(', ')}): ${typeText(candidate.result)}`\n}\n\n/** Whether parameter `index` of any overload is function-typed (lambda position). */\nexport function isLambdaPosition(def: FunctionDef, index: number): boolean {\n return def.overloads.some((candidate) => candidate.params[index]?.kind === 'function')\n}\n\n/**\n * Whether `.` may stand for the lambda at `index`: its first parameter must be\n * the current item (the element of the list argument). In reduce it is the\n * accumulator, so `.` there would silently mean the wrong value.\n */\nexport function isItemLambdaPosition(def: FunctionDef, index: number): boolean {\n return def.overloads.some((candidate) => {\n const lambda = candidate.params[index]\n const items = candidate.params[0]\n if (lambda?.kind !== 'function' || items?.kind !== 'list') return false\n const first = lambda.params[0]\n const element = items.element\n return (\n first === element ||\n (first?.kind === 'var' && element.kind === 'var' && first.name === element.name)\n )\n })\n}\n\n/** Shallow runtime test used to pick an overload by argument kinds. */\nexport function matchesKind(value: unknown, type: Type): boolean {\n switch (type.kind) {\n case 'any':\n case 'var':\n return true\n case 'never':\n return false\n case 'null':\n return value === null || value === undefined\n case 'boolean':\n return typeof value === 'boolean'\n case 'number':\n return typeof value === 'number'\n case 'string':\n return typeof value === 'string'\n case 'literal':\n return value === type.value\n case 'list':\n return Array.isArray(value)\n case 'map':\n return isMap(value)\n case 'timestamp':\n return value instanceof Date\n case 'duration':\n return isValidDuration(value)\n case 'union':\n return type.types.some((member) => matchesKind(value, member))\n case 'opaque':\n return (\n value !== null &&\n value !== undefined &&\n (typeof value === 'function' ||\n typeof value === 'symbol' ||\n typeof value === 'bigint' ||\n typeof value === 'object')\n )\n case 'function':\n default:\n return typeof value === 'function'\n }\n}\n\n/** Deep runtime conformance, used to guard host function parameters. */\nexport function conforms(value: unknown, type: Type, state: State, depth = 0): boolean {\n if (depth > state.limits.maxValueDepth) return false\n switch (type.kind) {\n case 'list': {\n if (!Array.isArray(value)) return false\n if (type.element.kind === 'any') return true\n state.charge(value.length)\n // oxlint-disable-next-line typescript/prefer-for-of -- indexing never invokes a host array's own Symbol.iterator\n for (let i = 0; i < value.length; i++)\n if (!conforms(value[i], type.element, state, depth + 1)) return false\n return true\n }\n case 'map': {\n if (!isMap(value)) return false\n const fields = Object.entries(type.fields)\n state.charge(1 + fields.length)\n for (const [key, fieldType] of fields) {\n if (!conforms(Object.hasOwn(value, key) ? value[key] : null, fieldType, state, depth + 1))\n return false\n }\n if (type.rest !== undefined && type.rest.kind !== 'any') {\n const keys = Object.keys(value)\n state.charge(keyListCost(keys.length))\n for (const key of keys) {\n // A key holding undefined is absent (see holdsKey).\n const field = value[key]\n if (\n field !== undefined &&\n !Object.hasOwn(type.fields, key) &&\n !conforms(field, type.rest, state, depth + 1)\n )\n return false\n }\n }\n return true\n }\n case 'union':\n // Each member may be tried: a union of thousands of literals is real work.\n state.charge(type.types.length)\n return type.types.some((member) => conforms(value, member, state, depth))\n case 'timestamp':\n // An invalid Date is an opaque host value, not a timestamp.\n return isTimestamp(value)\n case 'duration':\n return isValidDuration(value)\n case 'any':\n case 'boolean':\n case 'function':\n case 'literal':\n case 'never':\n case 'null':\n case 'number':\n case 'opaque':\n case 'string':\n case 'var':\n default:\n return matchesKind(value, type)\n }\n}\n\nconst CLOCK_SAMPLE = 1024\n\nfunction describeValue(value: unknown): string {\n if (value === null) return 'null (missing)'\n if (Array.isArray(value)) return 'a list'\n if (value instanceof Date) return isTimestamp(value) ? 'a timestamp' : 'an invalid Date'\n if (value instanceof Duration && !isValidDuration(value)) return 'an invalid Duration'\n if (typeof value === 'object') return 'a map'\n if (typeof value === 'string') return `string ${shown(value)}`\n if (typeof value === 'number' || typeof value === 'boolean')\n return `${typeof value} ${String(value)}`\n return typeof value\n}\n\n/** Bounds context validation like evaluation: depth, work, and wall-clock time. */\nexport interface ValidationBudget {\n readonly maxDepth: number\n remaining: number\n readonly deadline: number\n readonly onExhausted: (reason: 'steps' | 'time' | 'depth') => never\n}\n\n/** Why `value` does not conform to `type` (naming the path), or undefined when it does. */\nexport function describeMismatch(\n value: unknown,\n type: Type,\n path: string,\n budget: ValidationBudget,\n depth = 0,\n): string | undefined {\n if (depth > budget.maxDepth) budget.onExhausted('depth')\n if (--budget.remaining < 0) budget.onExhausted('steps')\n if (\n budget.deadline !== 0 &&\n budget.remaining % CLOCK_SAMPLE === 0 &&\n performance.now() > budget.deadline\n ) {\n budget.onExhausted('time')\n }\n const actual = value === undefined ? null : value\n if (type.kind === 'list' && Array.isArray(actual)) {\n for (let i = 0; i < actual.length; i++) {\n const problem = describeMismatch(actual[i], type.element, `${path}[${i}]`, budget, depth + 1)\n if (problem !== undefined) return problem\n }\n return undefined\n }\n if (type.kind === 'map' && isMap(actual)) {\n for (const [key, fieldType] of Object.entries(type.fields)) {\n const field = Object.hasOwn(actual, key) ? actual[key] : null\n const problem = describeMismatch(field, fieldType, `${path}.${key}`, budget, depth + 1)\n if (problem !== undefined) return problem\n }\n if (type.rest !== undefined) {\n const keys = Object.keys(actual)\n // Listing a map's keys costs per key (see keyListCost), and more for a large one.\n budget.remaining -= keyListCost(keys.length)\n if (budget.remaining < 0) budget.onExhausted('steps')\n for (const key of keys) {\n // Keys the language can never read, or that hold undefined, are not part of the data.\n if (Object.hasOwn(type.fields, key) || BLOCKED_NAMES.has(key) || actual[key] === undefined)\n continue\n const problem = describeMismatch(\n actual[key],\n type.rest,\n `${path}.${key}`,\n budget,\n depth + 1,\n )\n if (problem !== undefined) return problem\n }\n }\n return undefined\n }\n if (type.kind === 'union') {\n if (\n type.types.some(\n (member) => describeMismatch(actual, member, path, budget, depth) === undefined,\n )\n ) {\n return undefined\n }\n } else if (type.kind === 'timestamp') {\n if (isTimestamp(actual)) return undefined\n } else if (type.kind === 'duration') {\n if (isValidDuration(actual)) return undefined\n } else if (type.kind === 'number') {\n // The language has no NaN or infinities, so a context holding one is not data it can read.\n if (typeof actual === 'number' && Number.isFinite(actual)) return undefined\n } else if (type.kind !== 'list' && type.kind !== 'map' && matchesKind(actual, type)) {\n return undefined\n }\n return `${path} should be ${formatType(type)}, got ${describeValue(actual)}`\n}\n\nexport function overload(\n params: readonly Type[],\n result: Type,\n run: Overload['run'],\n options: {\n required?: number\n rest?: Type\n runAsync?: Overload['runAsync']\n ordered?: readonly string[]\n literals?: Overload['literals']\n } = {},\n): Overload {\n return Object.freeze({ params: Object.freeze([...params]), result, run, ...options })\n}\n\nexport function define(\n name: string,\n description: string,\n overloads: readonly Overload[],\n): FunctionDef {\n return Object.freeze({ name, description, overloads: Object.freeze([...overloads]) })\n}\n\n// === validation of host-supplied declarations ===\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'an array'\n return typeof value\n}\n\nconst TYPE_DEPTH_LIMIT = 64\n\n/**\n * Throws a TypeError unless `value` is a well-formed Type (as built by `t`).\n * Types come from configuration, so a malformed one is a programming error.\n */\nexport function assertType(value: unknown, path: string, depth = 0): asserts value is Type {\n if (depth > TYPE_DEPTH_LIMIT) throw new TypeError(`${path} nests deeper than ${TYPE_DEPTH_LIMIT}`)\n if (!isRecord(value) || typeof value.kind !== 'string') {\n throw new TypeError(`${path} must be a type built with t (got ${describe(value)})`)\n }\n switch (value.kind) {\n case 'any':\n case 'never':\n case 'null':\n case 'boolean':\n case 'number':\n case 'string':\n case 'timestamp':\n case 'duration':\n return\n case 'literal':\n if (!['string', 'number', 'boolean'].includes(typeof value.value)) {\n throw new TypeError(\n `${path} is a literal type whose value is not a string, number, or boolean`,\n )\n }\n if (typeof value.value === 'number' && !Number.isFinite(value.value))\n throw new TypeError(`${path} is a literal type whose number is not finite`)\n return\n case 'list':\n assertType(value.element, `${path}.element`, depth + 1)\n return\n case 'map':\n if (!isRecord(value.fields)) throw new TypeError(`${path}.fields must be an object`)\n for (const [key, field] of Object.entries(value.fields))\n assertType(field, `${path}.fields.${key}`, depth + 1)\n if (value.rest !== undefined) assertType(value.rest, `${path}.rest`, depth + 1)\n return\n case 'union':\n if (!Array.isArray(value.types)) throw new TypeError(`${path}.types must be an array`)\n // By index, so a hole (which forEach would skip) is refused like any non-type.\n for (let i = 0; i < value.types.length; i++)\n assertType(value.types[i], `${path}.types[${i}]`, depth + 1)\n return\n case 'opaque':\n if (typeof value.name !== 'string')\n throw new TypeError(`${path} is an opaque type without a name`)\n return\n case 'function':\n case 'var':\n default:\n throw new TypeError(`${path} has an unsupported type kind \"${value.kind}\"`)\n }\n}\n\nconst HOST_SPEC_KEYS = new Set([\n 'params',\n 'returns',\n 'required',\n 'rest',\n 'async',\n 'call',\n 'description',\n 'cost',\n 'run',\n])\n\n/**\n * Throws unless `spec` is a well-formed host function declaration. `label`\n * names it in messages, e.g. `Function \"rate\"`.\n */\nexport function assertHostSpec(spec: unknown, label: string): void {\n if (!isRecord(spec)) throw new TypeError(`${label} must be declared with fn()`)\n for (const key of Object.keys(spec)) {\n if (key === 'context') {\n throw new TypeError(\n `${label}: \"context: true\" is now \"call: true\"; run receives { context, signal } first`,\n )\n }\n if (!HOST_SPEC_KEYS.has(key)) throw new TypeError(`${label} has an unknown option \"${key}\"`)\n }\n if (!Array.isArray(spec.params)) throw new TypeError(`${label} needs a params array`)\n spec.params.forEach((param: unknown, i: number) => {\n assertType(param, `Parameter ${i + 1} of ${label}`)\n })\n assertType(spec.returns, `The returns type of ${label}`)\n if (spec.required !== undefined) {\n if (typeof spec.required !== 'number')\n throw new TypeError(`\"required\" of ${label} must be a number`)\n if (!Number.isInteger(spec.required) || spec.required < 0 || spec.required > spec.params.length)\n throw new RangeError(\n `\"required\" of ${label} must be an integer from 0 to ${spec.params.length}`,\n )\n }\n if (spec.rest !== undefined) assertType(spec.rest, `The rest type of ${label}`)\n for (const flag of ['async', 'call'] as const) {\n if (spec[flag] !== undefined && typeof spec[flag] !== 'boolean')\n throw new TypeError(`\"${flag}\" of ${label} must be a boolean`)\n }\n if (spec.description !== undefined && typeof spec.description !== 'string')\n throw new TypeError(`\"description\" of ${label} must be a string`)\n if (spec.cost !== undefined) {\n if (typeof spec.cost !== 'number') throw new TypeError(`\"cost\" of ${label} must be a number`)\n if (!Number.isSafeInteger(spec.cost) || spec.cost < 0)\n throw new RangeError(`\"cost\" of ${label} must be a non-negative integer`)\n }\n if (typeof spec.run !== 'function') throw new TypeError(`${label} needs a run function`)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"define.mjs","names":[],"sources":["../../src/functions/define.ts"],"sourcesContent":["import { BLOCKED_NAMES } from '../syntax/lexer.js'\nimport type { Span } from '../errors.js'\nimport type { State } from '../runtime/state.js'\nimport {\n Duration,\n isMap,\n isTimestamp,\n isValidDuration,\n keyListCost,\n shown,\n} from '../runtime/values.js'\nimport { formatType, typeText, type FunctionType, type Type, type TypeVar } from '../types.js'\n\n/** A lambda as seen by a built-in: called with the item and its index. */\nexport type Lambda = (item: unknown, index: number) => unknown\n\n/** Call-site information handed to a built-in implementation. */\nexport interface CallSite {\n readonly state: State\n readonly span: Span\n}\n\n/**\n * What async evaluation resolves to: a value in a box. JavaScript adopts (runs\n * `then` on) any thenable an async function returns or awaits, and data from\n * the host is opaque, never awaited, so values cross an `await` boxed.\n */\nexport class Out {\n readonly v: unknown\n constructor(v: unknown) {\n this.v = v\n }\n}\n\nexport interface Overload {\n /** Parameter types. Function-typed parameters take lambdas. */\n readonly params: readonly Type[]\n /** How many leading parameters are required (default: all). */\n readonly required?: number\n /** Type of any further arguments (variadic). */\n readonly rest?: Type\n /** Result type; may reference type variables bound by the parameters. */\n readonly result: Type\n readonly run: (args: unknown[], site: CallSite) => unknown\n /**\n * Variant used when a lambda argument is asynchronous. Its lambdas resolve\n * to an {@link Out}, and so does it.\n */\n readonly runAsync?: ((args: unknown[], site: CallSite) => Promise<Out>) | undefined\n /** Type variables that must bind to one orderable kind (number, string, timestamp, duration). */\n readonly ordered?: readonly string[]\n /**\n * Static validation of literal arguments (undefined where an argument is\n * not a literal). Returns an error message, so mistakes in constant\n * patterns are reported when the expression is checked.\n */\n readonly literals?: ((values: readonly unknown[]) => string | undefined) | undefined\n}\n\nexport interface FunctionDef {\n readonly name: string\n /** A host function's description; built-ins' are in descriptions.ts (see descriptionOf). */\n readonly description?: string\n readonly overloads: readonly Overload[]\n /** Host functions: returns a promise and must be awaited. */\n readonly async?: boolean\n /** Host functions: receives a HostCall (context and signal) as its first argument. */\n readonly call?: boolean\n readonly host?: boolean\n /** Host functions: steps charged per call (default 32). */\n readonly cost?: number\n /**\n * Built-ins with a time zone parameter: its position, and the function that\n * loads a zone's data, so compiling warms the zones a call names. Kept on the\n * definition so bundles without such a built-in leave the zone code out.\n */\n readonly zone?: ZoneParameter\n}\n\nexport interface ZoneParameter {\n readonly at: number\n readonly warm: (zone: string | undefined) => void\n}\n\nexport const T: TypeVar = Object.freeze({ kind: 'var', name: 'T' })\nexport const U: TypeVar = Object.freeze({ kind: 'var', name: 'U' })\nexport const K: TypeVar = Object.freeze({ kind: 'var', name: 'K' })\n\nexport function fnType(params: readonly Type[], result: Type): FunctionType {\n return Object.freeze({ kind: 'function', params: Object.freeze([...params]), result })\n}\n\n/** An overload's signature as messages and hovers show it, e.g. `slice(string, number, number?): string`. */\nexport function signatureText(name: string, candidate: Overload): string {\n const required = candidate.required ?? candidate.params.length\n const params = candidate.params.map((param, i) => `${typeText(param)}${i >= required ? '?' : ''}`)\n if (candidate.rest !== undefined) params.push(`...${typeText(candidate.rest)}`)\n return `${name}(${params.join(', ')}): ${typeText(candidate.result)}`\n}\n\n/** Whether parameter `index` of any overload is function-typed (lambda position). */\nexport function isLambdaPosition(def: FunctionDef, index: number): boolean {\n return def.overloads.some((candidate) => candidate.params[index]?.kind === 'function')\n}\n\n/**\n * Whether `.` may stand for the lambda at `index`: its first parameter must be\n * the current item (the element of the list argument). In reduce it is the\n * accumulator, so `.` there would silently mean the wrong value.\n */\nexport function isItemLambdaPosition(def: FunctionDef, index: number): boolean {\n return def.overloads.some((candidate) => {\n const lambda = candidate.params[index]\n const items = candidate.params[0]\n if (lambda?.kind !== 'function' || items?.kind !== 'list') return false\n const first = lambda.params[0]\n const element = items.element\n return (\n first === element ||\n (first?.kind === 'var' && element.kind === 'var' && first.name === element.name)\n )\n })\n}\n\n/** Shallow runtime test used to pick an overload by argument kinds. */\nexport function matchesKind(value: unknown, type: Type): boolean {\n switch (type.kind) {\n case 'any':\n case 'var':\n return true\n case 'never':\n return false\n case 'null':\n return value === null || value === undefined\n case 'boolean':\n return typeof value === 'boolean'\n case 'number':\n return typeof value === 'number'\n case 'string':\n return typeof value === 'string'\n case 'literal':\n return value === type.value\n case 'list':\n return Array.isArray(value)\n case 'map':\n return isMap(value)\n case 'timestamp':\n return value instanceof Date\n case 'duration':\n return isValidDuration(value)\n case 'union':\n return type.types.some((member) => matchesKind(value, member))\n case 'opaque':\n return (\n value !== null &&\n value !== undefined &&\n (typeof value === 'function' ||\n typeof value === 'symbol' ||\n typeof value === 'bigint' ||\n typeof value === 'object')\n )\n case 'function':\n default:\n return typeof value === 'function'\n }\n}\n\n/** Deep runtime conformance, used to guard host function parameters. */\nexport function conforms(value: unknown, type: Type, state: State, depth = 0): boolean {\n if (depth > state.limits.maxValueDepth) return false\n switch (type.kind) {\n case 'list': {\n if (!Array.isArray(value)) return false\n if (type.element.kind === 'any') return true\n state.charge(value.length)\n // oxlint-disable-next-line typescript/prefer-for-of -- indexing never invokes a host array's own Symbol.iterator\n for (let i = 0; i < value.length; i++)\n if (!conforms(value[i], type.element, state, depth + 1)) return false\n return true\n }\n case 'map': {\n if (!isMap(value)) return false\n const fields = Object.entries(type.fields)\n state.charge(1 + fields.length)\n for (const [key, fieldType] of fields) {\n if (!conforms(Object.hasOwn(value, key) ? value[key] : null, fieldType, state, depth + 1))\n return false\n }\n if (type.rest !== undefined && type.rest.kind !== 'any') {\n const keys = Object.keys(value)\n state.charge(keyListCost(keys.length))\n for (const key of keys) {\n // A key holding undefined is absent (see holdsKey).\n const field = value[key]\n if (\n field !== undefined &&\n !Object.hasOwn(type.fields, key) &&\n !conforms(field, type.rest, state, depth + 1)\n )\n return false\n }\n }\n return true\n }\n case 'union':\n // Each member may be tried: a union of thousands of literals is real work.\n state.charge(type.types.length)\n return type.types.some((member) => conforms(value, member, state, depth))\n case 'timestamp':\n // An invalid Date is an opaque host value, not a timestamp.\n return isTimestamp(value)\n case 'duration':\n return isValidDuration(value)\n case 'any':\n case 'boolean':\n case 'function':\n case 'literal':\n case 'never':\n case 'null':\n case 'number':\n case 'opaque':\n case 'string':\n case 'var':\n default:\n return matchesKind(value, type)\n }\n}\n\nconst CLOCK_SAMPLE = 1024\n\nfunction describeValue(value: unknown): string {\n if (value === null) return 'null (missing)'\n if (Array.isArray(value)) return 'a list'\n if (value instanceof Date) return isTimestamp(value) ? 'a timestamp' : 'an invalid Date'\n if (value instanceof Duration && !isValidDuration(value)) return 'an invalid Duration'\n if (typeof value === 'object') return 'a map'\n if (typeof value === 'string') return `string ${shown(value)}`\n if (typeof value === 'number' || typeof value === 'boolean')\n return `${typeof value} ${String(value)}`\n return typeof value\n}\n\n/** Bounds context validation like evaluation: depth, work, and wall-clock time. */\nexport interface ValidationBudget {\n readonly maxDepth: number\n remaining: number\n readonly deadline: number\n readonly onExhausted: (reason: 'steps' | 'time' | 'depth') => never\n}\n\n/** Why `value` does not conform to `type` (naming the path), or undefined when it does. */\nexport function describeMismatch(\n value: unknown,\n type: Type,\n path: string,\n budget: ValidationBudget,\n depth = 0,\n): string | undefined {\n if (depth > budget.maxDepth) budget.onExhausted('depth')\n if (--budget.remaining < 0) budget.onExhausted('steps')\n if (\n budget.deadline !== 0 &&\n budget.remaining % CLOCK_SAMPLE === 0 &&\n performance.now() > budget.deadline\n ) {\n budget.onExhausted('time')\n }\n const actual = value === undefined ? null : value\n if (type.kind === 'list' && Array.isArray(actual)) {\n for (let i = 0; i < actual.length; i++) {\n const problem = describeMismatch(actual[i], type.element, `${path}[${i}]`, budget, depth + 1)\n if (problem !== undefined) return problem\n }\n return undefined\n }\n if (type.kind === 'map' && isMap(actual)) {\n for (const [key, fieldType] of Object.entries(type.fields)) {\n const field = Object.hasOwn(actual, key) ? actual[key] : null\n const problem = describeMismatch(field, fieldType, `${path}.${key}`, budget, depth + 1)\n if (problem !== undefined) return problem\n }\n if (type.rest !== undefined) {\n const keys = Object.keys(actual)\n // Listing a map's keys costs per key (see keyListCost), and more for a large one.\n budget.remaining -= keyListCost(keys.length)\n if (budget.remaining < 0) budget.onExhausted('steps')\n for (const key of keys) {\n // Keys the language can never read, or that hold undefined, are not part of the data.\n if (Object.hasOwn(type.fields, key) || BLOCKED_NAMES.has(key) || actual[key] === undefined)\n continue\n const problem = describeMismatch(\n actual[key],\n type.rest,\n `${path}.${key}`,\n budget,\n depth + 1,\n )\n if (problem !== undefined) return problem\n }\n }\n return undefined\n }\n if (type.kind === 'union') {\n if (\n type.types.some(\n (member) => describeMismatch(actual, member, path, budget, depth) === undefined,\n )\n ) {\n return undefined\n }\n } else if (type.kind === 'timestamp') {\n if (isTimestamp(actual)) return undefined\n } else if (type.kind === 'duration') {\n if (isValidDuration(actual)) return undefined\n } else if (type.kind === 'number') {\n // The language has no NaN or infinities, so a context holding one is not data it can read.\n if (typeof actual === 'number' && Number.isFinite(actual)) return undefined\n } else if (type.kind !== 'list' && type.kind !== 'map' && matchesKind(actual, type)) {\n return undefined\n }\n return `${path} should be ${formatType(type)}, got ${describeValue(actual)}`\n}\n\nexport function overload(\n params: readonly Type[],\n result: Type,\n run: Overload['run'],\n options: {\n required?: number\n rest?: Type\n runAsync?: Overload['runAsync']\n ordered?: readonly string[]\n literals?: Overload['literals']\n } = {},\n): Overload {\n return Object.freeze({ params: Object.freeze([...params]), result, run, ...options })\n}\n\n/** A built-in; its description is in descriptions.ts. */\nexport function define(\n name: string,\n overloads: readonly Overload[],\n zone?: ZoneParameter,\n): FunctionDef {\n const def: FunctionDef = {\n name,\n overloads: Object.freeze([...overloads]),\n ...(zone === undefined ? {} : { zone }),\n }\n // Not enumerable, so a copy made by spreading a built-in does not carry it.\n Object.defineProperty(def, BUILTIN_BRAND, { value: true })\n DEFINED.add(Object.freeze(def))\n return def\n}\n\n/** The built-ins this copy of the package defined (the `builtins` option takes only these). */\nconst DEFINED = new WeakSet<FunctionDef>()\n/** Marks a built-in of any copy of the package, so one from another copy is named as such. */\nconst BUILTIN_BRAND = Symbol.for('bonsai-js.builtin')\n\n/**\n * Reads the `builtins` option: built-ins from bonsai-js/builtins, added to\n * `base` (an extended environment's or runtime's own).\n */\nexport function builtinsOption(\n value: unknown,\n base: ReadonlyMap<string, FunctionDef>,\n): ReadonlyMap<string, FunctionDef> {\n if (value === undefined) return base\n if (!Array.isArray(value))\n throw new TypeError('builtins must be an array of built-ins from bonsai-js/builtins')\n let items: unknown[]\n try {\n // By index, so a replaced iterator is never run.\n items = Array.prototype.slice.call(value) as unknown[]\n } catch (error) {\n throw new TypeError('builtins could not be read', { cause: error })\n }\n const out = new Map(base)\n for (const item of items) {\n if (DEFINED.has(item as FunctionDef)) {\n out.set((item as FunctionDef).name, item as FunctionDef)\n continue\n }\n let branded = false\n try {\n branded = typeof item === 'object' && item !== null && Object.hasOwn(item, BUILTIN_BRAND)\n } catch {\n // A value that cannot be read is not a built-in either.\n }\n throw new TypeError(\n branded\n ? 'builtins holds a built-in that is not from this copy of bonsai-js (another installed copy, or a wrapper of one); import built-ins from the copy that runs them'\n : 'builtins must hold built-ins from bonsai-js/builtins',\n )\n }\n return out\n}\n\n// === validation of host-supplied declarations ===\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'an array'\n return typeof value\n}\n\nconst TYPE_DEPTH_LIMIT = 64\n\n/**\n * Throws a TypeError unless `value` is a well-formed Type (as built by `t`).\n * Types come from configuration, so a malformed one is a programming error.\n */\nexport function assertType(value: unknown, path: string, depth = 0): asserts value is Type {\n if (depth > TYPE_DEPTH_LIMIT) throw new TypeError(`${path} nests deeper than ${TYPE_DEPTH_LIMIT}`)\n if (!isRecord(value) || typeof value.kind !== 'string') {\n throw new TypeError(`${path} must be a type built with t (got ${describe(value)})`)\n }\n switch (value.kind) {\n case 'any':\n case 'never':\n case 'null':\n case 'boolean':\n case 'number':\n case 'string':\n case 'timestamp':\n case 'duration':\n return\n case 'literal':\n if (!['string', 'number', 'boolean'].includes(typeof value.value)) {\n throw new TypeError(\n `${path} is a literal type whose value is not a string, number, or boolean`,\n )\n }\n if (typeof value.value === 'number' && !Number.isFinite(value.value))\n throw new TypeError(`${path} is a literal type whose number is not finite`)\n return\n case 'list':\n assertType(value.element, `${path}.element`, depth + 1)\n return\n case 'map':\n if (!isRecord(value.fields)) throw new TypeError(`${path}.fields must be an object`)\n for (const [key, field] of Object.entries(value.fields))\n assertType(field, `${path}.fields.${key}`, depth + 1)\n if (value.rest !== undefined) assertType(value.rest, `${path}.rest`, depth + 1)\n return\n case 'union':\n if (!Array.isArray(value.types)) throw new TypeError(`${path}.types must be an array`)\n // By index, so a hole (which forEach would skip) is refused like any non-type.\n for (let i = 0; i < value.types.length; i++)\n assertType(value.types[i], `${path}.types[${i}]`, depth + 1)\n return\n case 'opaque':\n if (typeof value.name !== 'string')\n throw new TypeError(`${path} is an opaque type without a name`)\n return\n case 'function':\n case 'var':\n default:\n throw new TypeError(`${path} has an unsupported type kind \"${value.kind}\"`)\n }\n}\n\nconst HOST_SPEC_KEYS = new Set([\n 'params',\n 'returns',\n 'required',\n 'rest',\n 'async',\n 'call',\n 'description',\n 'cost',\n 'run',\n])\n\n/**\n * Throws unless `spec` is a well-formed host function declaration. `label`\n * names it in messages, e.g. `Function \"rate\"`.\n */\nexport function assertHostSpec(spec: unknown, label: string): void {\n if (!isRecord(spec)) throw new TypeError(`${label} must be declared with fn()`)\n for (const key of Object.keys(spec)) {\n if (key === 'context') {\n throw new TypeError(\n `${label}: \"context: true\" is now \"call: true\"; run receives { context, signal } first`,\n )\n }\n if (!HOST_SPEC_KEYS.has(key)) throw new TypeError(`${label} has an unknown option \"${key}\"`)\n }\n if (!Array.isArray(spec.params)) throw new TypeError(`${label} needs a params array`)\n spec.params.forEach((param: unknown, i: number) => {\n assertType(param, `Parameter ${i + 1} of ${label}`)\n })\n assertType(spec.returns, `The returns type of ${label}`)\n if (spec.required !== undefined) {\n if (typeof spec.required !== 'number')\n throw new TypeError(`\"required\" of ${label} must be a number`)\n if (!Number.isInteger(spec.required) || spec.required < 0 || spec.required > spec.params.length)\n throw new RangeError(\n `\"required\" of ${label} must be an integer from 0 to ${spec.params.length}`,\n )\n }\n if (spec.rest !== undefined) assertType(spec.rest, `The rest type of ${label}`)\n for (const flag of ['async', 'call'] as const) {\n if (spec[flag] !== undefined && typeof spec[flag] !== 'boolean')\n throw new TypeError(`\"${flag}\" of ${label} must be a boolean`)\n }\n if (spec.description !== undefined && typeof spec.description !== 'string')\n throw new TypeError(`\"description\" of ${label} must be a string`)\n if (spec.cost !== undefined) {\n if (typeof spec.cost !== 'number') throw new TypeError(`\"cost\" of ${label} must be a number`)\n if (!Number.isSafeInteger(spec.cost) || spec.cost < 0)\n throw new RangeError(`\"cost\" of ${label} must be a non-negative integer`)\n }\n if (typeof spec.run !== 'function') throw new TypeError(`${label} needs a run function`)\n}\n"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { BUILTINS } from "./builtins.mjs";
2
+ //#region src/functions/descriptions.ts
3
+ /**
4
+ * What each built-in does, in BUILTINS order, for describeFunction(),
5
+ * listFunctions(), and the language service. Kept apart from the definitions,
6
+ * so bundles that only evaluate (bonsai-js/runtime) leave the text out, and
7
+ * by position, so the names are not repeated (each entry's comment names its
8
+ * function, and tests/descriptions.test.ts checks them against BUILTINS).
9
+ */
10
+ const TEXTS = [
11
+ "Upper-cases text.",
12
+ "Lower-cases text.",
13
+ "Removes leading and trailing whitespace.",
14
+ "Removes leading whitespace.",
15
+ "Removes trailing whitespace.",
16
+ "Whether text starts with a prefix.",
17
+ "Whether text ends with a suffix.",
18
+ "Whether text contains a substring, or a list contains a value.",
19
+ "Position of the first match, or -1.",
20
+ "Position of the last match, or -1.",
21
+ "A section of text or a list; negative positions count from the end.",
22
+ "Splits text on a separator.",
23
+ "Replaces the first occurrence of some text (no patterns).",
24
+ "Replaces every occurrence of some text (no patterns).",
25
+ "Pads text at the start to a length.",
26
+ "Pads text at the end to a length.",
27
+ "Repeats text a number of times.",
28
+ "The item or character at a position; negative positions count from the end.",
29
+ "Whether text contains a match for a regular expression (JavaScript syntax without backreferences or lookaround, linear time; anchor with ^ and $; prefix (?i) to ignore case).",
30
+ "Renders a value as text, as a template would.",
31
+ "Parses decimal text as a number, e.g. \"-12.5\" or \"1e3\" (surrounding whitespace is ignored; 0x and other bases are not accepted).",
32
+ "Rounds half away from zero, optionally to a number of decimal digits.",
33
+ "Rounds down.",
34
+ "Rounds up.",
35
+ "Drops the fractional part.",
36
+ "Absolute value.",
37
+ "Square root.",
38
+ "Limits a number to a range.",
39
+ "Formats a number with a fixed number of decimals.",
40
+ "Formats a number with grouping, e.g. 1,234.5, with optional decimals and locale (default \"en-US\"; an unsupported locale is an error).",
41
+ "Formats an amount in a currency (ISO 4217 code such as \"EUR\"; a code without a known symbol prints as the code), with an optional locale (default \"en-US\"; an unsupported locale is an error).",
42
+ "The smallest value (nulls are skipped); null for an empty list.",
43
+ "The largest value (nulls are skipped); null for an empty list.",
44
+ "The sum of the numbers in a list (nulls are skipped), added left to right as a + b + c is.",
45
+ "The mean of the numbers or durations in a list (nulls are skipped, summed as sum does); null for an empty list.",
46
+ "Transforms each item.",
47
+ "Keeps the items for which the lambda is true.",
48
+ "The first item for which the lambda is true, or null.",
49
+ "The position of the first item for which the lambda is true, or -1.",
50
+ "Whether the lambda is true for at least one item.",
51
+ "Whether the lambda is true for every item.",
52
+ "Whether the lambda is false for every item.",
53
+ "Transforms each item and flattens list results one level.",
54
+ "Sorts by a key; pass \"desc\" to reverse. Nulls sort first (\"asc\") or last (\"desc\").",
55
+ "Groups items into a map of lists by a key. Map keys are text, so a key is its text form: 1 and \"1\", or true and \"true\", share a group. A key must be a string, number, or boolean (null is an error). Keys are listed integer-like first, ascending, then in first-seen order.",
56
+ "The number of items, or of items for which the lambda is true.",
57
+ "Folds a list into one value: reduce(list, (acc, item) => ..., initial). Name both parameters; \".\" is not allowed, since the first one is the accumulator.",
58
+ "Sorts numbers, text, timestamps, or durations; pass \"desc\" to reverse.",
59
+ "The items in reverse order.",
60
+ "The items without duplicates (by value), in first-seen order.",
61
+ "Flattens nested lists one level.",
62
+ "The first item, or null.",
63
+ "The last item, or null.",
64
+ "Joins items into text with a separator (default \",\").",
65
+ "Whether a list, text, or map has no items; null is empty.",
66
+ "The keys of a map.",
67
+ "The values of a map.",
68
+ "The entries of a map, as a list of { key, value } maps (not [key, value] pairs as in JavaScript), e.g. m.entries().map(.key + \"=\" + .value).",
69
+ "The kind of a value: \"null\", \"boolean\", \"number\", \"string\", \"list\", \"map\", \"timestamp\", \"duration\", or \"opaque\".",
70
+ "The current time, fixed for one evaluation.",
71
+ "Parses ISO-8601 text (years 0000-9999, or six digits with a sign such as +012345, as a timestamp renders) or epoch milliseconds (a fraction is dropped toward zero, as Date does) into a timestamp.",
72
+ "Parses ISO-8601 duration text in weeks, days, hours, minutes, and seconds (e.g. \"PT1H30M\", \"-P2DT0.5S\"), the text a duration renders as.",
73
+ "A duration of n weeks.",
74
+ "A duration of n days (24 hours each).",
75
+ "A duration of n hours.",
76
+ "A duration of n minutes.",
77
+ "A duration of n seconds.",
78
+ "A duration of n milliseconds. Durations are whole milliseconds: a fraction rounds to the nearest one, halves away from zero.",
79
+ "A duration as a (fractional) number of days.",
80
+ "A duration as a number of hours.",
81
+ "A duration as a number of minutes.",
82
+ "A duration as a number of seconds.",
83
+ "A duration as a number of milliseconds.",
84
+ "The calendar year, in a time zone (default UTC).",
85
+ "The month 1-12, in a time zone (default UTC).",
86
+ "The day of the month, in a time zone (default UTC).",
87
+ "The hour 0-23, in a time zone (default UTC).",
88
+ "The minute, in a time zone (default UTC).",
89
+ "The second, in a time zone (default UTC).",
90
+ "The ISO weekday (Monday 1 to Sunday 7), in a time zone (default UTC).",
91
+ "Midnight at the start of the day, in a time zone (default UTC).",
92
+ "Midnight on the first of the month, in a time zone (default UTC).",
93
+ "Midnight on January 1, in a time zone (default UTC).",
94
+ "Adds calendar days (keeps the wall-clock time across DST), in a time zone.",
95
+ "Adds calendar months, clamping the day to the month length.",
96
+ "Adds calendar years (Feb 29 becomes Feb 28).",
97
+ "Formats a timestamp in a time zone (default UTC). Tokens: yyyy yy MMMM MMM MM M dd d EEEE EEE HH H hh h a mm m ss s SSS; quote literal text ('at'), and write '' for a quote ('it''s')."
98
+ ];
99
+ let positions;
100
+ /** A function's description: a host function's own, or a built-in's from the table. */
101
+ function descriptionOf(def) {
102
+ if (def.description !== void 0) return def.description;
103
+ positions ??= new Map([...BUILTINS.keys()].map((name, i) => [name, i]));
104
+ const at = positions.get(def.name);
105
+ return at === void 0 ? "" : TEXTS[at];
106
+ }
107
+ //#endregion
108
+ export { descriptionOf };
109
+
110
+ //# sourceMappingURL=descriptions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptions.mjs","names":[],"sources":["../../src/functions/descriptions.ts"],"sourcesContent":["import { BUILTINS } from './builtins.js'\n\n/**\n * What each built-in does, in BUILTINS order, for describeFunction(),\n * listFunctions(), and the language service. Kept apart from the definitions,\n * so bundles that only evaluate (bonsai-js/runtime) leave the text out, and\n * by position, so the names are not repeated (each entry's comment names its\n * function, and tests/descriptions.test.ts checks them against BUILTINS).\n */\nconst TEXTS: readonly string[] = [\n // toUpperCase\n 'Upper-cases text.',\n // toLowerCase\n 'Lower-cases text.',\n // trim\n 'Removes leading and trailing whitespace.',\n // trimStart\n 'Removes leading whitespace.',\n // trimEnd\n 'Removes trailing whitespace.',\n // startsWith\n 'Whether text starts with a prefix.',\n // endsWith\n 'Whether text ends with a suffix.',\n // includes\n 'Whether text contains a substring, or a list contains a value.',\n // indexOf\n 'Position of the first match, or -1.',\n // lastIndexOf\n 'Position of the last match, or -1.',\n // slice\n 'A section of text or a list; negative positions count from the end.',\n // split\n 'Splits text on a separator.',\n // replace\n 'Replaces the first occurrence of some text (no patterns).',\n // replaceAll\n 'Replaces every occurrence of some text (no patterns).',\n // padStart\n 'Pads text at the start to a length.',\n // padEnd\n 'Pads text at the end to a length.',\n // repeat\n 'Repeats text a number of times.',\n // at\n 'The item or character at a position; negative positions count from the end.',\n // matches\n 'Whether text contains a match for a regular expression (JavaScript syntax without backreferences or lookaround, linear time; anchor with ^ and $; prefix (?i) to ignore case).',\n // toString\n 'Renders a value as text, as a template would.',\n // toNumber\n 'Parses decimal text as a number, e.g. \"-12.5\" or \"1e3\" (surrounding whitespace is ignored; 0x and other bases are not accepted).',\n // round\n 'Rounds half away from zero, optionally to a number of decimal digits.',\n // floor\n 'Rounds down.',\n // ceil\n 'Rounds up.',\n // trunc\n 'Drops the fractional part.',\n // abs\n 'Absolute value.',\n // sqrt\n 'Square root.',\n // clamp\n 'Limits a number to a range.',\n // toFixed\n 'Formats a number with a fixed number of decimals.',\n // formatNumber\n 'Formats a number with grouping, e.g. 1,234.5, with optional decimals and locale (default \"en-US\"; an unsupported locale is an error).',\n // formatCurrency\n 'Formats an amount in a currency (ISO 4217 code such as \"EUR\"; a code without a known symbol prints as the code), with an optional locale (default \"en-US\"; an unsupported locale is an error).',\n // min\n 'The smallest value (nulls are skipped); null for an empty list.',\n // max\n 'The largest value (nulls are skipped); null for an empty list.',\n // sum\n 'The sum of the numbers in a list (nulls are skipped), added left to right as a + b + c is.',\n // avg\n 'The mean of the numbers or durations in a list (nulls are skipped, summed as sum does); null for an empty list.',\n // map\n 'Transforms each item.',\n // filter\n 'Keeps the items for which the lambda is true.',\n // find\n 'The first item for which the lambda is true, or null.',\n // findIndex\n 'The position of the first item for which the lambda is true, or -1.',\n // some\n 'Whether the lambda is true for at least one item.',\n // every\n 'Whether the lambda is true for every item.',\n // none\n 'Whether the lambda is false for every item.',\n // flatMap\n 'Transforms each item and flattens list results one level.',\n // sortBy\n 'Sorts by a key; pass \"desc\" to reverse. Nulls sort first (\"asc\") or last (\"desc\").',\n // groupBy\n 'Groups items into a map of lists by a key. Map keys are text, so a key is its text form: 1 and \"1\", or true and \"true\", share a group. A key must be a string, number, or boolean (null is an error). Keys are listed integer-like first, ascending, then in first-seen order.',\n // count\n 'The number of items, or of items for which the lambda is true.',\n // reduce\n 'Folds a list into one value: reduce(list, (acc, item) => ..., initial). Name both parameters; \".\" is not allowed, since the first one is the accumulator.',\n // sort\n 'Sorts numbers, text, timestamps, or durations; pass \"desc\" to reverse.',\n // reverse\n 'The items in reverse order.',\n // unique\n 'The items without duplicates (by value), in first-seen order.',\n // flat\n 'Flattens nested lists one level.',\n // first\n 'The first item, or null.',\n // last\n 'The last item, or null.',\n // join\n 'Joins items into text with a separator (default \",\").',\n // isEmpty\n 'Whether a list, text, or map has no items; null is empty.',\n // keys\n 'The keys of a map.',\n // values\n 'The values of a map.',\n // entries\n 'The entries of a map, as a list of { key, value } maps (not [key, value] pairs as in JavaScript), e.g. m.entries().map(.key + \"=\" + .value).',\n // type\n 'The kind of a value: \"null\", \"boolean\", \"number\", \"string\", \"list\", \"map\", \"timestamp\", \"duration\", or \"opaque\".',\n // now\n 'The current time, fixed for one evaluation.',\n // timestamp\n 'Parses ISO-8601 text (years 0000-9999, or six digits with a sign such as +012345, as a timestamp renders) or epoch milliseconds (a fraction is dropped toward zero, as Date does) into a timestamp.',\n // duration\n 'Parses ISO-8601 duration text in weeks, days, hours, minutes, and seconds (e.g. \"PT1H30M\", \"-P2DT0.5S\"), the text a duration renders as.',\n // weeks\n 'A duration of n weeks.',\n // days\n 'A duration of n days (24 hours each).',\n // hours\n 'A duration of n hours.',\n // minutes\n 'A duration of n minutes.',\n // seconds\n 'A duration of n seconds.',\n // milliseconds\n 'A duration of n milliseconds. Durations are whole milliseconds: a fraction rounds to the nearest one, halves away from zero.',\n // inDays\n 'A duration as a (fractional) number of days.',\n // inHours\n 'A duration as a number of hours.',\n // inMinutes\n 'A duration as a number of minutes.',\n // inSeconds\n 'A duration as a number of seconds.',\n // inMilliseconds\n 'A duration as a number of milliseconds.',\n // year\n 'The calendar year, in a time zone (default UTC).',\n // month\n 'The month 1-12, in a time zone (default UTC).',\n // day\n 'The day of the month, in a time zone (default UTC).',\n // hour\n 'The hour 0-23, in a time zone (default UTC).',\n // minute\n 'The minute, in a time zone (default UTC).',\n // second\n 'The second, in a time zone (default UTC).',\n // dayOfWeek\n 'The ISO weekday (Monday 1 to Sunday 7), in a time zone (default UTC).',\n // startOfDay\n 'Midnight at the start of the day, in a time zone (default UTC).',\n // startOfMonth\n 'Midnight on the first of the month, in a time zone (default UTC).',\n // startOfYear\n 'Midnight on January 1, in a time zone (default UTC).',\n // addDays\n 'Adds calendar days (keeps the wall-clock time across DST), in a time zone.',\n // addMonths\n 'Adds calendar months, clamping the day to the month length.',\n // addYears\n 'Adds calendar years (Feb 29 becomes Feb 28).',\n // formatDate\n \"Formats a timestamp in a time zone (default UTC). Tokens: yyyy yy MMMM MMM MM M dd d EEEE EEE HH H hh h a mm m ss s SSS; quote literal text ('at'), and write '' for a quote ('it''s').\",\n]\n\nlet positions: Map<string, number> | undefined\n\n/** A function's description: a host function's own, or a built-in's from the table. */\nexport function descriptionOf(def: {\n readonly name: string\n readonly description?: string\n}): string {\n if (def.description !== undefined) return def.description\n positions ??= new Map([...BUILTINS.keys()].map((name, i) => [name, i]))\n const at = positions.get(def.name)\n return at === undefined ? '' : TEXTS[at]\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,QAA2B;CAE/B;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;AACF;AAEA,IAAI;;AAGJ,SAAgB,cAAc,KAGnB;CACT,IAAI,IAAI,gBAAgB,KAAA,GAAW,OAAO,IAAI;CAC9C,cAAc,IAAI,IAAI,CAAC,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;CACtE,MAAM,KAAK,UAAU,IAAI,IAAI,IAAI;CACjC,OAAO,OAAO,KAAA,IAAY,KAAK,MAAM;AACvC"}
package/dist/index.d.mts CHANGED
@@ -4,9 +4,9 @@ import { ExplanationJSON, Iteration, IterationJSON, Trace, TraceJSON } from "./r
4
4
  import { Duration } from "./runtime/values.mjs";
5
5
  import { AnyType, BooleanType, DurationType, FunctionType, Infer, InferVariables, ListType, LiteralType, MapType, NeverType, NullType, NumberType, OpaqueType, StringType, TimestampType, Type, TypeVar, UnionType, formatType, isAssignable, t } from "./types.mjs";
6
6
  import { PartialData, PartialOptions, PartialResult, ResidualResult } from "./partial.mjs";
7
- import { AbortSignalLike, CheckResult, CompileOptions, Environment, EnvironmentOptions, EvaluateOptions, ExplainOptions, Explanation, FnSpec, FunctionInfo, HostCall, HostFunction, Library, Limits, Program, fn, withContext } from "./environment.mjs";
7
+ import { AbortSignalLike, Builtin, CheckResult, CompileOptions, Environment, EnvironmentOptions, EvaluateOptions, ExplainOptions, Explanation, FnSpec, FunctionInfo, HostCall, HostFunction, Library, Limits, Program, fn, withContext } from "./environment.mjs";
8
8
  import { bonsai } from "./bonsai.mjs";
9
9
  import { Artifact, Plan, toArtifact } from "./artifact.mjs";
10
10
  import { ParseOptions, parse } from "./parse.mjs";
11
11
  import { PrintOptions, print } from "./syntax/printer.mjs";
12
- export { type AbortSignalLike, type AnyType, type Artifact, type ArtifactProblem, type BinaryNode, type BinaryOperator, BonsaiArtifactError, BonsaiCheckError, BonsaiError, type BonsaiErrorJSON, type BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, BonsaiSyntaxError, BonsaiTranslationError, type BooleanType, type CallNode, type CallStyle, type CheckResult, type CompileOptions, type ConditionalNode, type Diagnostic, type DiagnosticCode, type DiagnosticJSON, Duration, type DurationType, type Environment, type EnvironmentOptions, type ErrorCode, type EvaluateOptions, type ExplainOptions, type Explanation, type ExplanationJSON, type FnSpec, type FunctionInfo, type FunctionType, type HasNode, type HostCall, type HostFunction, type IndexNode, type Infer, type InferVariables, type ItNode, type Iteration, type IterationJSON, type LambdaNode, type LetNode, type Library, type LimitName, type Limits, type ListNode, type ListType, type LiteralNode, type LiteralType, type LocalNode, type MapEntry, type MapNode, type MapType, type MemberNode, type NeverType, type Node, type NullType, type NumberType, type OpaqueType, type ParseOptions, type PartialData, type PartialOptions, type PartialResult, type Plan, type PrintOptions, type Program, type ResidualResult, type Span, type SpreadNode, type StringType, type TemplateNode, type TimestampType, type Trace, type TraceJSON, type TryNode, type Type, type TypeVar, type UnaryNode, type UnaryOperator, type UnionType, type VariableNode, bonsai, fn, forEachChild, formatType, isAssignable, isBonsaiError, mapChildren, parse, print, t, toArtifact, withContext };
12
+ export { type AbortSignalLike, type AnyType, type Artifact, type ArtifactProblem, type BinaryNode, type BinaryOperator, BonsaiArtifactError, BonsaiCheckError, BonsaiError, type BonsaiErrorJSON, type BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, BonsaiSyntaxError, BonsaiTranslationError, type BooleanType, type Builtin, type CallNode, type CallStyle, type CheckResult, type CompileOptions, type ConditionalNode, type Diagnostic, type DiagnosticCode, type DiagnosticJSON, Duration, type DurationType, type Environment, type EnvironmentOptions, type ErrorCode, type EvaluateOptions, type ExplainOptions, type Explanation, type ExplanationJSON, type FnSpec, type FunctionInfo, type FunctionType, type HasNode, type HostCall, type HostFunction, type IndexNode, type Infer, type InferVariables, type ItNode, type Iteration, type IterationJSON, type LambdaNode, type LetNode, type Library, type LimitName, type Limits, type ListNode, type ListType, type LiteralNode, type LiteralType, type LocalNode, type MapEntry, type MapNode, type MapType, type MemberNode, type NeverType, type Node, type NullType, type NumberType, type OpaqueType, type ParseOptions, type PartialData, type PartialOptions, type PartialResult, type Plan, type PrintOptions, type Program, type ResidualResult, type Span, type SpreadNode, type StringType, type TemplateNode, type TimestampType, type Trace, type TraceJSON, type TryNode, type Type, type TypeVar, type UnaryNode, type UnaryOperator, type UnionType, type VariableNode, bonsai, fn, forEachChild, formatType, isAssignable, isBonsaiError, mapChildren, parse, print, t, toArtifact, withContext };
@@ -2,7 +2,7 @@ import { ArtifactProblem, BonsaiArtifactError, BonsaiCheckError, BonsaiError, Bo
2
2
  import { BinaryNode, BinaryOperator, CallNode, CallStyle, ConditionalNode, HasNode, IndexNode, ItNode, LambdaNode, LetNode, ListNode, LiteralNode, LocalNode, MapEntry, MapNode, MemberNode, Node, SpreadNode, TemplateNode, TryNode, UnaryNode, UnaryOperator, VariableNode, forEachChild, mapChildren } from "../syntax/ast.mjs";
3
3
  import { Duration } from "../runtime/values.mjs";
4
4
  import { AnyType, BooleanType, DurationType, FunctionType, Infer, InferVariables, ListType, LiteralType, MapType, NeverType, NullType, NumberType, OpaqueType, StringType, TimestampType, Type, TypeVar, UnionType, formatType, isAssignable, t } from "../types.mjs";
5
- import { AbortSignalLike, CompileOptions, ContextFor, EnvironmentOptions, EvaluateOptions, FnSpec, FunctionInfo, HostCall, HostFunction, Library, Limits, LiteCheckResult, LiteEnvironment, LiteProgram, NoVariables, fn, withContext } from "../environment.mjs";
5
+ import { AbortSignalLike, Builtin, CompileOptions, ContextFor, EnvironmentOptions, EvaluateOptions, FnSpec, FunctionInfo, HostCall, HostFunction, Library, Limits, LiteCheckResult, LiteEnvironment, LiteProgram, NoVariables, fn, withContext } from "../environment.mjs";
6
6
  import { Artifact, Plan, toArtifact } from "../artifact.mjs";
7
7
  import { ParseOptions, parse } from "../parse.mjs";
8
8
  import { PrintOptions, print } from "../syntax/printer.mjs";
@@ -21,4 +21,4 @@ export declare function bonsai<const V extends Readonly<Record<string, Type>> =
21
21
  readonly strict?: S;
22
22
  }): LiteEnvironment<ContextFor<V, L, S>>;
23
23
  //#endregion
24
- export { type AbortSignalLike, type AnyType, type Artifact, type ArtifactProblem, type BinaryNode, type BinaryOperator, BonsaiArtifactError, BonsaiCheckError, BonsaiError, type BonsaiErrorJSON, type BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, BonsaiSyntaxError, type BooleanType, type CallNode, type CallStyle, type CompileOptions, type ConditionalNode, type Diagnostic, type DiagnosticCode, type DiagnosticJSON, Duration, type DurationType, type EnvironmentOptions, type ErrorCode, type EvaluateOptions, type FnSpec, type FunctionInfo, type FunctionType, type HasNode, type HostCall, type HostFunction, type IndexNode, type Infer, type InferVariables, type ItNode, type LambdaNode, type LetNode, type Library, type LimitName, type Limits, type ListNode, type ListType, type LiteCheckResult, type LiteEnvironment, type LiteProgram, type LiteralNode, type LiteralType, type LocalNode, type MapEntry, type MapNode, type MapType, type MemberNode, type NeverType, type Node, type NullType, type NumberType, type OpaqueType, type ParseOptions, type Plan, type PrintOptions, type Span, type SpreadNode, type StringType, type TemplateNode, type TimestampType, type TryNode, type Type, type TypeVar, type UnaryNode, type UnaryOperator, type UnionType, type VariableNode, fn, forEachChild, formatType, isAssignable, isBonsaiError, mapChildren, parse, print, t, toArtifact, withContext };
24
+ export { type AbortSignalLike, type AnyType, type Artifact, type ArtifactProblem, type BinaryNode, type BinaryOperator, BonsaiArtifactError, BonsaiCheckError, BonsaiError, type BonsaiErrorJSON, type BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, BonsaiSyntaxError, type BooleanType, type Builtin, type CallNode, type CallStyle, type CompileOptions, type ConditionalNode, type Diagnostic, type DiagnosticCode, type DiagnosticJSON, Duration, type DurationType, type EnvironmentOptions, type ErrorCode, type EvaluateOptions, type FnSpec, type FunctionInfo, type FunctionType, type HasNode, type HostCall, type HostFunction, type IndexNode, type Infer, type InferVariables, type ItNode, type LambdaNode, type LetNode, type Library, type LimitName, type Limits, type ListNode, type ListType, type LiteCheckResult, type LiteEnvironment, type LiteProgram, type LiteralNode, type LiteralType, type LocalNode, type MapEntry, type MapNode, type MapType, type MemberNode, type NeverType, type Node, type NullType, type NumberType, type OpaqueType, type ParseOptions, type Plan, type PrintOptions, type Span, type SpreadNode, type StringType, type TemplateNode, type TimestampType, type TryNode, type Type, type TypeVar, type UnaryNode, type UnaryOperator, type UnionType, type VariableNode, fn, forEachChild, formatType, isAssignable, isBonsaiError, mapChildren, parse, print, t, toArtifact, withContext };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/lite/index.ts"],"sourcesContent":["/**\n * `bonsai-js/lite`: the `bonsai-js` API without explaining and partial\n * evaluation, for bundles that only check and evaluate expressions.\n * Environments and programs from this entry point have no `explain`,\n * `explainSync`, or `partial`; everything else behaves as in `bonsai-js`.\n */\nimport {\n environmentWith,\n type ContextFor,\n type EnvironmentOptions,\n type Library,\n type LiteEnvironment,\n type NoVariables,\n} from '../environment.js'\nimport type { Type } from '../types.js'\n\n/**\n * Creates an environment without explaining and partial evaluation: the\n * variables, functions, and limits expressions are checked and evaluated against.\n *\n * ```ts\n * const env = bonsai({ variables: { user: t.object({ age: t.number() }) } })\n * const adult = env.compile('user.age >= 18', { expect: t.boolean() })\n * adult.evaluateSync({ user: { age: 30 } }) // true\n * ```\n */\nexport function bonsai<\n const V extends Readonly<Record<string, Type>> = NoVariables,\n const L extends readonly Library[] = [],\n const S extends boolean | undefined = undefined,\n>(\n options: EnvironmentOptions<V, L> & { readonly strict?: S } = {},\n): LiteEnvironment<ContextFor<V, L, S>> {\n return environmentWith<ContextFor<V, L, S>>(undefined, options)\n}\n\nexport { fn, withContext } from '../environment.js'\nexport type {\n AbortSignalLike,\n CompileOptions,\n EnvironmentOptions,\n EvaluateOptions,\n FnSpec,\n FunctionInfo,\n HostCall,\n HostFunction,\n Library,\n Limits,\n LiteCheckResult,\n LiteEnvironment,\n LiteProgram,\n} from '../environment.js'\nexport { t, formatType, isAssignable } from '../types.js'\nexport type {\n AnyType,\n BooleanType,\n DurationType,\n FunctionType,\n Infer,\n InferVariables,\n ListType,\n LiteralType,\n MapType,\n NeverType,\n NullType,\n NumberType,\n OpaqueType,\n StringType,\n TimestampType,\n Type,\n TypeVar,\n UnionType,\n} from '../types.js'\nexport { Duration } from '../runtime/values.js'\nexport {\n BonsaiError,\n BonsaiSyntaxError,\n BonsaiCheckError,\n BonsaiLimitError,\n BonsaiRuntimeError,\n BonsaiArtifactError,\n isBonsaiError,\n} from '../errors.js'\nexport type {\n ArtifactProblem,\n BonsaiErrorJSON,\n BonsaiErrorName,\n Diagnostic,\n DiagnosticJSON,\n DiagnosticCode,\n ErrorCode,\n LimitName,\n Span,\n} from '../errors.js'\nexport { forEachChild, mapChildren } from '../syntax/ast.js'\nexport type {\n BinaryNode,\n BinaryOperator,\n CallNode,\n CallStyle,\n ConditionalNode,\n HasNode,\n IndexNode,\n ItNode,\n LambdaNode,\n LetNode,\n ListNode,\n LiteralNode,\n LocalNode,\n MapEntry,\n MapNode,\n MemberNode,\n Node,\n SpreadNode,\n TemplateNode,\n TryNode,\n UnaryNode,\n UnaryOperator,\n VariableNode,\n} from '../syntax/ast.js'\nexport { toArtifact, type Artifact, type Plan } from '../artifact.js'\nexport { parse, type ParseOptions } from '../parse.js'\nexport { print, type PrintOptions } from '../syntax/printer.js'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,OAKd,UAA8D,CAAC,GACzB;CACtC,OAAO,gBAAqC,KAAA,GAAW,OAAO;AAChE"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/lite/index.ts"],"sourcesContent":["/**\n * `bonsai-js/lite`: the `bonsai-js` API without explaining and partial\n * evaluation, for bundles that only check and evaluate expressions.\n * Environments and programs from this entry point have no `explain`,\n * `explainSync`, or `partial`; everything else behaves as in `bonsai-js`.\n */\nimport {\n environmentWith,\n type ContextFor,\n type EnvironmentOptions,\n type Library,\n type LiteEnvironment,\n type NoVariables,\n} from '../environment.js'\nimport type { Type } from '../types.js'\n\n/**\n * Creates an environment without explaining and partial evaluation: the\n * variables, functions, and limits expressions are checked and evaluated against.\n *\n * ```ts\n * const env = bonsai({ variables: { user: t.object({ age: t.number() }) } })\n * const adult = env.compile('user.age >= 18', { expect: t.boolean() })\n * adult.evaluateSync({ user: { age: 30 } }) // true\n * ```\n */\nexport function bonsai<\n const V extends Readonly<Record<string, Type>> = NoVariables,\n const L extends readonly Library[] = [],\n const S extends boolean | undefined = undefined,\n>(\n options: EnvironmentOptions<V, L> & { readonly strict?: S } = {},\n): LiteEnvironment<ContextFor<V, L, S>> {\n return environmentWith<ContextFor<V, L, S>>(undefined, options)\n}\n\nexport { fn, withContext } from '../environment.js'\nexport type {\n AbortSignalLike,\n Builtin,\n CompileOptions,\n EnvironmentOptions,\n EvaluateOptions,\n FnSpec,\n FunctionInfo,\n HostCall,\n HostFunction,\n Library,\n Limits,\n LiteCheckResult,\n LiteEnvironment,\n LiteProgram,\n} from '../environment.js'\nexport { t, formatType, isAssignable } from '../types.js'\nexport type {\n AnyType,\n BooleanType,\n DurationType,\n FunctionType,\n Infer,\n InferVariables,\n ListType,\n LiteralType,\n MapType,\n NeverType,\n NullType,\n NumberType,\n OpaqueType,\n StringType,\n TimestampType,\n Type,\n TypeVar,\n UnionType,\n} from '../types.js'\nexport { Duration } from '../runtime/values.js'\nexport {\n BonsaiError,\n BonsaiSyntaxError,\n BonsaiCheckError,\n BonsaiLimitError,\n BonsaiRuntimeError,\n BonsaiArtifactError,\n isBonsaiError,\n} from '../errors.js'\nexport type {\n ArtifactProblem,\n BonsaiErrorJSON,\n BonsaiErrorName,\n Diagnostic,\n DiagnosticJSON,\n DiagnosticCode,\n ErrorCode,\n LimitName,\n Span,\n} from '../errors.js'\nexport { forEachChild, mapChildren } from '../syntax/ast.js'\nexport type {\n BinaryNode,\n BinaryOperator,\n CallNode,\n CallStyle,\n ConditionalNode,\n HasNode,\n IndexNode,\n ItNode,\n LambdaNode,\n LetNode,\n ListNode,\n LiteralNode,\n LocalNode,\n MapEntry,\n MapNode,\n MemberNode,\n Node,\n SpreadNode,\n TemplateNode,\n TryNode,\n UnaryNode,\n UnaryOperator,\n VariableNode,\n} from '../syntax/ast.js'\nexport { toArtifact, type Artifact, type Plan } from '../artifact.js'\nexport { parse, type ParseOptions } from '../parse.js'\nexport { print, type PrintOptions } from '../syntax/printer.js'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,OAKd,UAA8D,CAAC,GACzB;CACtC,OAAO,gBAAqC,KAAA,GAAW,OAAO;AAChE"}
package/dist/load.mjs CHANGED
@@ -2,7 +2,6 @@ import { BonsaiArtifactError, BonsaiError } from "./errors.mjs";
2
2
  import { assertType } from "./functions/define.mjs";
3
3
  import { forEachChild } from "./syntax/ast.mjs";
4
4
  import { bindImplicit } from "./check/bind.mjs";
5
- import { BUILTINS } from "./functions/builtins.mjs";
6
5
  import { parse } from "./syntax/parser.mjs";
7
6
  import { programRunner, warmZones } from "./program.mjs";
8
7
  import { encodeType, lengthPrefixed } from "./digest.mjs";
@@ -237,7 +236,7 @@ function loadRead(artifact, settings, expect) {
237
236
  if (!(error instanceof BonsaiError)) throw error;
238
237
  return fail(error.code === "SYNTAX" ? "syntax" : "limit", `The artifact's source cannot be parsed: ${error.message}`, error);
239
238
  }
240
- const lookup = (name) => settings.host.get(name) ?? BUILTINS.get(name);
239
+ const lookup = (name) => settings.host.get(name) ?? settings.builtins.get(name);
241
240
  const names = treeNames(parsed);
242
241
  for (const name of names.functions) if (lookup(name) === void 0) fail("function", `The artifact calls ${name}(), which this runtime does not define`);
243
242
  if (settings.strict) {
package/dist/load.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"load.mjs","names":[],"sources":["../src/load.ts"],"sourcesContent":["import {\n ARTIFACT_FORMAT,\n MAX_EXPECT_NODES,\n artifactDigest,\n treeNames,\n type Artifact,\n type Plan,\n} from './artifact.js'\nimport { bindImplicit } from './check/bind.js'\nimport type { CallPlan } from './check/checker.js'\nimport { encodeType, lengthPrefixed } from './digest.js'\nimport type { Settings } from './environment.js'\nimport { BonsaiArtifactError, BonsaiError, type ArtifactProblem } from './errors.js'\nimport { BUILTINS } from './functions/builtins.js'\nimport { assertType, type FunctionDef } from './functions/define.js'\nimport { programRunner, warmZones, type ProgramAnalysis } from './program.js'\nimport { forEachChild, type CallNode, type Node } from './syntax/ast.js'\nimport { parse } from './syntax/parser.js'\nimport type { Type } from './types.js'\n\nconst ARTIFACT_KEYS: ReadonlySet<string> = new Set([\n 'bonsai',\n 'by',\n 'source',\n 'plans',\n 'expect',\n 'guard',\n 'async',\n 'digest',\n])\n\n/** The nesting assertType accepts. */\nconst MAX_TYPE_DEPTH = 64\n\n/** A type object holds its kind and at most two other fields (`fields` and `rest`). */\nconst MAX_TYPE_KEYS = 3\n\n/**\n * A plan holds a name position, a name, and fewer overload indices than the\n * function has overloads (built-ins have at most five, host functions one);\n * this bounds how much of a hostile entry is read.\n */\nconst MAX_PLAN_LENGTH = 34\n\n/**\n * The errors the current readArtifact call raises itself, as opposed to\n * anything a host object throws while it is read (including a loader error\n * from another call, rethrown by a Proxy trap). Reads nest when a trap loads\n * another artifact, so each call has its own set.\n */\nlet raisedHere: WeakSet<WeakKey> | undefined\n\nconst fail = (reason: ArtifactProblem, message: string, cause?: unknown): never => {\n const error = new BonsaiArtifactError(reason, message, cause === undefined ? {} : { cause })\n raisedHere?.add(error)\n throw error\n}\n\nfunction notArtifact(): never {\n const error = new TypeError('An artifact must be an object made by toArtifact()')\n raisedHere?.add(error)\n throw error\n}\n\n/**\n * Reads the own properties of an object exactly once each, through their\n * descriptors, so no getter runs (a Proxy's traps run once per property).\n * More than `maxKeys` keys, accessors, and symbol keys are refused.\n */\nfunction ownData(\n value: object,\n what: string,\n maxKeys: number,\n known: ReadonlyMap<string, unknown> = new Map(),\n): Map<string, unknown> {\n const keys = Reflect.ownKeys(value)\n if (keys.length > maxKeys) fail('limit', `${what} has more than ${maxKeys} keys`)\n const out = new Map<string, unknown>()\n for (const key of keys) {\n if (typeof key !== 'string') return fail('shape', `${what} has a symbol key`)\n // Fields read already are not read again, so each trap runs once per field.\n if (known.has(key)) {\n out.set(key, known.get(key))\n continue\n }\n const descriptor = Reflect.getOwnPropertyDescriptor(value, key)\n if (descriptor === undefined) continue\n if (!('value' in descriptor)) fail('shape', `${what}.${key} is an accessor`)\n out.set(key, descriptor.value)\n }\n return out\n}\n\n/** The elements of an array, read once each, its length checked against `max` before any element is read. */\nfunction arrayData(value: unknown, max: number, what: string, tooLong: ArtifactProblem): unknown[] {\n if (!Array.isArray(value)) return fail('shape', `${what} must be an array`)\n const length = Reflect.getOwnPropertyDescriptor(value, 'length')?.value as unknown\n if (typeof length !== 'number') return fail('shape', `${what} has no length`)\n if (length > max) return fail(tooLong, `${what} has more than ${max} entries`)\n const out: unknown[] = []\n for (let i = 0; i < length; i++) {\n const descriptor = Reflect.getOwnPropertyDescriptor(value, String(i))\n if (descriptor !== undefined && !('value' in descriptor))\n fail('shape', `${what}[${i}] is an accessor`)\n out.push(descriptor?.value)\n }\n return out\n}\n\n/**\n * A copy of an expected type, read by its kind so only a type's own fields are\n * read, with field records as null-prototype objects (so a `__proto__` key\n * stays a field). Type nodes are counted along every path, as toArtifact\n * counts them, so shared subtrees cannot hide its size.\n */\nfunction copyType(value: unknown): Type {\n let nodes = 0\n const copy = (node: unknown, depth: number): unknown => {\n if (typeof node !== 'object' || node === null || Array.isArray(node))\n return fail('shape', \"The artifact's expected type must be a type built with t\")\n if (++nodes > MAX_EXPECT_NODES)\n fail('limit', `The artifact's expected type has more than ${MAX_EXPECT_NODES} type nodes`)\n if (depth > MAX_TYPE_DEPTH)\n fail('shape', `The artifact's expected type nests deeper than ${MAX_TYPE_DEPTH}`)\n const data = ownData(node, 'expect', MAX_TYPE_KEYS)\n const kind = data.get('kind')\n switch (kind) {\n case 'list':\n return Object.freeze({ kind, element: copy(data.get('element'), depth + 1) })\n case 'union': {\n // Every member is at least one more node, so a longer list cannot fit.\n const members = arrayData(\n data.get('types'),\n MAX_EXPECT_NODES - nodes,\n 'expect.types',\n 'limit',\n )\n return Object.freeze({ kind, types: Object.freeze(members.map((m) => copy(m, depth + 1))) })\n }\n case 'map': {\n const record = data.get('fields')\n if (typeof record !== 'object' || record === null)\n return fail('shape', \"The artifact's expected type has map fields that are not an object\")\n const fields = Object.create(null) as Record<string, unknown>\n for (const [key, field] of ownData(record, 'expect.fields', MAX_EXPECT_NODES - nodes))\n Object.defineProperty(fields, key, { value: copy(field, depth + 1), enumerable: true })\n const rest = data.get('rest')\n return Object.freeze({\n kind,\n fields: Object.freeze(fields),\n ...(rest === undefined ? {} : { rest: copy(rest, depth + 1) }),\n })\n }\n case 'literal':\n return Object.freeze({ kind, value: data.get('value') })\n case 'opaque':\n return Object.freeze({ kind, name: data.get('name') })\n default:\n return Object.freeze({ kind })\n }\n }\n const type = copy(value, 0)\n try {\n assertType(type, \"The artifact's expected type\")\n } catch (error) {\n return fail('shape', (error as Error).message, error)\n }\n return type\n}\n\n/**\n * An artifact's fields, copied once and checked for shape and size before\n * anything is parsed. A value that is not an artifact is a TypeError, and so\n * is anything thrown while reading it (a Proxy trap), with the thrown value as\n * its cause.\n */\nexport function readArtifact(value: unknown, settings: Settings): Artifact {\n const outer = raisedHere\n const mine = new WeakSet()\n raisedHere = mine\n try {\n return read(value, settings)\n } catch (error) {\n if (typeof error === 'object' && error !== null && mine.has(error)) throw error\n throw new TypeError('The artifact could not be read', { cause: error })\n } finally {\n raisedHere = outer\n }\n}\n\nfunction read(value: unknown, settings: Settings): Artifact {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) notArtifact()\n // The format is read first and alone: a later format may rename or add any other field.\n const formatField = Reflect.getOwnPropertyDescriptor(value, 'bonsai')\n if (formatField === undefined) return notArtifact()\n if (!('value' in formatField)) fail('shape', 'The artifact format is an accessor')\n const format: unknown = formatField.value\n if (typeof format !== 'number' || !Number.isSafeInteger(format) || format < 1)\n fail('shape', 'The artifact format must be a positive integer')\n if (format !== ARTIFACT_FORMAT)\n fail(\n 'version',\n `Artifact format ${String(format)} is not supported (this runtime reads format ${ARTIFACT_FORMAT})`,\n )\n const data = ownData(value, 'The artifact', ARTIFACT_KEYS.size, new Map([['bonsai', format]]))\n for (const key of data.keys()) {\n if (!ARTIFACT_KEYS.has(key))\n fail('version', `The artifact has a field \"${key}\" this runtime does not know`)\n }\n const string = (key: string): string => {\n const field = data.get(key)\n return typeof field === 'string'\n ? field\n : fail('shape', `The artifact's ${key} must be a string`)\n }\n const flag = (key: string): boolean => {\n const field = data.get(key)\n return typeof field === 'boolean'\n ? field\n : fail('shape', `The artifact's ${key} must be true or false`)\n }\n const source = string('source')\n if (source.length > settings.parseLimits.maxSourceLength)\n fail(\n 'limit',\n `The artifact's source is longer than ${settings.parseLimits.maxSourceLength} characters`,\n )\n const plans = arrayData(data.get('plans'), settings.parseLimits.maxNodes, 'plans', 'limit').map(\n (entry, i): Plan => {\n const items = arrayData(entry, MAX_PLAN_LENGTH, `plans[${i}]`, 'plan')\n const [start, name, ...candidates] = items\n if (\n typeof start !== 'number' ||\n typeof name !== 'string' ||\n !candidates.every((c) => typeof c === 'number')\n )\n fail('shape', `plans[${i}] must be [nameStart, name, ...overload indices]`)\n return [start as number, name as string, ...(candidates as number[])]\n },\n )\n const expect = data.has('expect') ? copyType(data.get('expect')) : undefined\n return {\n bonsai: ARTIFACT_FORMAT,\n by: string('by'),\n source,\n plans,\n ...(expect === undefined ? {} : { expect }),\n guard: flag('guard'),\n async: flag('async'),\n digest: string('digest'),\n }\n}\n\n/** The calls of a bound tree by where their names start. */\nfunction callsByStart(root: Node): Map<number, CallNode> {\n const calls = new Map<number, CallNode>()\n const stack: Node[] = [root]\n while (stack.length > 0) {\n const node = stack.pop() as Node\n if (node.type === 'Call' && node.nameStart !== undefined) calls.set(node.nameStart, node)\n forEachChild(node, (child) => {\n stack.push(child)\n })\n }\n return calls\n}\n\n/** What a loaded artifact runs with: its analysis, result guard, and source. */\nexport interface Loaded {\n readonly source: string\n readonly analysis: ProgramAnalysis\n readonly guard: Type | undefined\n}\n\n/**\n * Loads an artifact against a runtime's definitions, refusing it with a\n * BonsaiArtifactError when it does not reproduce the checked program exactly:\n * a different format, source, plan, function, or declared variable type.\n */\nexport function loadArtifact(\n artifact: Artifact,\n settings: Settings,\n expect: Type | undefined,\n): Loaded {\n // Loading is not part of any read: a load that a Proxy trap runs while an\n // outer artifact is read must not mark its errors as that read's own.\n const outer = raisedHere\n raisedHere = undefined\n try {\n return loadRead(artifact, settings, expect)\n } finally {\n raisedHere = outer\n }\n}\n\nfunction loadRead(artifact: Artifact, settings: Settings, expect: Type | undefined): Loaded {\n const { source } = artifact\n let parsed: Node\n try {\n parsed = parse(source, settings.parseLimits)\n } catch (error) {\n if (!(error instanceof BonsaiError)) throw error\n return fail(\n error.code === 'SYNTAX' ? 'syntax' : 'limit',\n `The artifact's source cannot be parsed: ${error.message}`,\n error,\n )\n }\n const lookup = (name: string): FunctionDef | undefined =>\n settings.host.get(name) ?? BUILTINS.get(name)\n // Binding adds lambdas around arguments but no calls or variables, so the\n // digest is computed from the parsed tree as it is from the checked one:\n // different definitions are reported as such, before binding with them.\n const names = treeNames(parsed)\n for (const name of names.functions) {\n if (lookup(name) === undefined)\n fail('function', `The artifact calls ${name}(), which this runtime does not define`)\n }\n // A strict checker refuses every undeclared variable, so a strict runtime\n // refuses artifacts that read one rather than reading it from the context.\n if (settings.strict) {\n for (const name of names.variables) {\n if (settings.variables === undefined || !Object.hasOwn(settings.variables, name))\n fail('variable', `The artifact reads ${name}, which this strict runtime does not declare`)\n }\n }\n const { digest, ...fields } = artifact\n if (artifactDigest(fields, parsed, lookup, settings.variables) !== digest)\n fail(\n 'digest',\n 'The artifact was made for different definitions than this runtime has; make it again from its source',\n )\n const bound = bindImplicit(parsed, lookup)\n if (bound.problems.length > 0) fail('binding', bound.problems[0].message)\n const root = bound.root\n\n const calls = new Map<CallNode, CallPlan>()\n const byStart = callsByStart(root)\n for (const call of byStart.values()) {\n const def = lookup(call.name) as FunctionDef\n calls.set(call, { def, candidates: def.overloads.map((_, i) => i) })\n }\n let previous = -1\n for (const [start, name, ...candidates] of artifact.plans) {\n const call = byStart.get(start)\n if (start <= previous || call === undefined || call.name !== name)\n fail('plan', `The artifact's plan for ${name}() at ${start} matches no call`)\n previous = start\n const def = lookup(name) as FunctionDef\n const count = def.overloads.length\n if (\n candidates.length === 0 ||\n candidates.length >= count ||\n !candidates.every(\n (c, i) =>\n Number.isSafeInteger(c) && c >= 0 && c < count && (i === 0 || c > candidates[i - 1]),\n )\n )\n fail(\n 'plan',\n `The artifact's plan for ${name}() at ${start} is not a narrower set of its overloads`,\n )\n calls.set(call as CallNode, { def, candidates })\n }\n\n if (\n expect !== undefined &&\n (artifact.expect === undefined || encodeType(artifact.expect) !== encodeType(expect))\n )\n fail('expect', 'The artifact was not compiled for the expected type passed to load()')\n const analysis: ProgramAnalysis = { root, calls, async: artifact.async }\n warmZones(analysis)\n return {\n source,\n analysis,\n // The host's own expected type is always checked; the artifact's only where the checker asked.\n guard: expect ?? (artifact.guard ? artifact.expect : undefined),\n }\n}\n\n/** A key equal for two artifacts exactly when every field is (read with readArtifact). */\nexport function artifactKey(artifact: Artifact): string {\n return [\n lengthPrefixed(artifact.digest),\n lengthPrefixed(artifact.by),\n lengthPrefixed(artifact.source),\n lengthPrefixed(JSON.stringify(artifact.plans)),\n // As written, not canonically: equal types written differently report\n // themselves differently in result-mismatch messages.\n artifact.expect === undefined ? '-' : lengthPrefixed(JSON.stringify(artifact.expect)),\n artifact.guard ? 'g' : '-',\n artifact.async ? 'a' : '-',\n ].join('')\n}\n\n/** Runs a loaded artifact as a program does. */\nexport function loadedRunner<R>(loaded: Loaded, settings: Settings) {\n return programRunner<R>({ ...loaded, settings })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"load.mjs","names":[],"sources":["../src/load.ts"],"sourcesContent":["import {\n ARTIFACT_FORMAT,\n MAX_EXPECT_NODES,\n artifactDigest,\n treeNames,\n type Artifact,\n type Plan,\n} from './artifact.js'\nimport { bindImplicit } from './check/bind.js'\nimport type { CallPlan } from './check/checker.js'\nimport { encodeType, lengthPrefixed } from './digest.js'\nimport type { Settings } from './environment.js'\nimport { BonsaiArtifactError, BonsaiError, type ArtifactProblem } from './errors.js'\nimport { assertType, type FunctionDef } from './functions/define.js'\nimport { programRunner, warmZones, type ProgramAnalysis } from './program.js'\nimport { forEachChild, type CallNode, type Node } from './syntax/ast.js'\nimport { parse } from './syntax/parser.js'\nimport type { Type } from './types.js'\n\nconst ARTIFACT_KEYS: ReadonlySet<string> = new Set([\n 'bonsai',\n 'by',\n 'source',\n 'plans',\n 'expect',\n 'guard',\n 'async',\n 'digest',\n])\n\n/** The nesting assertType accepts. */\nconst MAX_TYPE_DEPTH = 64\n\n/** A type object holds its kind and at most two other fields (`fields` and `rest`). */\nconst MAX_TYPE_KEYS = 3\n\n/**\n * A plan holds a name position, a name, and fewer overload indices than the\n * function has overloads (built-ins have at most five, host functions one);\n * this bounds how much of a hostile entry is read.\n */\nconst MAX_PLAN_LENGTH = 34\n\n/**\n * The errors the current readArtifact call raises itself, as opposed to\n * anything a host object throws while it is read (including a loader error\n * from another call, rethrown by a Proxy trap). Reads nest when a trap loads\n * another artifact, so each call has its own set.\n */\nlet raisedHere: WeakSet<WeakKey> | undefined\n\nconst fail = (reason: ArtifactProblem, message: string, cause?: unknown): never => {\n const error = new BonsaiArtifactError(reason, message, cause === undefined ? {} : { cause })\n raisedHere?.add(error)\n throw error\n}\n\nfunction notArtifact(): never {\n const error = new TypeError('An artifact must be an object made by toArtifact()')\n raisedHere?.add(error)\n throw error\n}\n\n/**\n * Reads the own properties of an object exactly once each, through their\n * descriptors, so no getter runs (a Proxy's traps run once per property).\n * More than `maxKeys` keys, accessors, and symbol keys are refused.\n */\nfunction ownData(\n value: object,\n what: string,\n maxKeys: number,\n known: ReadonlyMap<string, unknown> = new Map(),\n): Map<string, unknown> {\n const keys = Reflect.ownKeys(value)\n if (keys.length > maxKeys) fail('limit', `${what} has more than ${maxKeys} keys`)\n const out = new Map<string, unknown>()\n for (const key of keys) {\n if (typeof key !== 'string') return fail('shape', `${what} has a symbol key`)\n // Fields read already are not read again, so each trap runs once per field.\n if (known.has(key)) {\n out.set(key, known.get(key))\n continue\n }\n const descriptor = Reflect.getOwnPropertyDescriptor(value, key)\n if (descriptor === undefined) continue\n if (!('value' in descriptor)) fail('shape', `${what}.${key} is an accessor`)\n out.set(key, descriptor.value)\n }\n return out\n}\n\n/** The elements of an array, read once each, its length checked against `max` before any element is read. */\nfunction arrayData(value: unknown, max: number, what: string, tooLong: ArtifactProblem): unknown[] {\n if (!Array.isArray(value)) return fail('shape', `${what} must be an array`)\n const length = Reflect.getOwnPropertyDescriptor(value, 'length')?.value as unknown\n if (typeof length !== 'number') return fail('shape', `${what} has no length`)\n if (length > max) return fail(tooLong, `${what} has more than ${max} entries`)\n const out: unknown[] = []\n for (let i = 0; i < length; i++) {\n const descriptor = Reflect.getOwnPropertyDescriptor(value, String(i))\n if (descriptor !== undefined && !('value' in descriptor))\n fail('shape', `${what}[${i}] is an accessor`)\n out.push(descriptor?.value)\n }\n return out\n}\n\n/**\n * A copy of an expected type, read by its kind so only a type's own fields are\n * read, with field records as null-prototype objects (so a `__proto__` key\n * stays a field). Type nodes are counted along every path, as toArtifact\n * counts them, so shared subtrees cannot hide its size.\n */\nfunction copyType(value: unknown): Type {\n let nodes = 0\n const copy = (node: unknown, depth: number): unknown => {\n if (typeof node !== 'object' || node === null || Array.isArray(node))\n return fail('shape', \"The artifact's expected type must be a type built with t\")\n if (++nodes > MAX_EXPECT_NODES)\n fail('limit', `The artifact's expected type has more than ${MAX_EXPECT_NODES} type nodes`)\n if (depth > MAX_TYPE_DEPTH)\n fail('shape', `The artifact's expected type nests deeper than ${MAX_TYPE_DEPTH}`)\n const data = ownData(node, 'expect', MAX_TYPE_KEYS)\n const kind = data.get('kind')\n switch (kind) {\n case 'list':\n return Object.freeze({ kind, element: copy(data.get('element'), depth + 1) })\n case 'union': {\n // Every member is at least one more node, so a longer list cannot fit.\n const members = arrayData(\n data.get('types'),\n MAX_EXPECT_NODES - nodes,\n 'expect.types',\n 'limit',\n )\n return Object.freeze({ kind, types: Object.freeze(members.map((m) => copy(m, depth + 1))) })\n }\n case 'map': {\n const record = data.get('fields')\n if (typeof record !== 'object' || record === null)\n return fail('shape', \"The artifact's expected type has map fields that are not an object\")\n const fields = Object.create(null) as Record<string, unknown>\n for (const [key, field] of ownData(record, 'expect.fields', MAX_EXPECT_NODES - nodes))\n Object.defineProperty(fields, key, { value: copy(field, depth + 1), enumerable: true })\n const rest = data.get('rest')\n return Object.freeze({\n kind,\n fields: Object.freeze(fields),\n ...(rest === undefined ? {} : { rest: copy(rest, depth + 1) }),\n })\n }\n case 'literal':\n return Object.freeze({ kind, value: data.get('value') })\n case 'opaque':\n return Object.freeze({ kind, name: data.get('name') })\n default:\n return Object.freeze({ kind })\n }\n }\n const type = copy(value, 0)\n try {\n assertType(type, \"The artifact's expected type\")\n } catch (error) {\n return fail('shape', (error as Error).message, error)\n }\n return type\n}\n\n/**\n * An artifact's fields, copied once and checked for shape and size before\n * anything is parsed. A value that is not an artifact is a TypeError, and so\n * is anything thrown while reading it (a Proxy trap), with the thrown value as\n * its cause.\n */\nexport function readArtifact(value: unknown, settings: Settings): Artifact {\n const outer = raisedHere\n const mine = new WeakSet()\n raisedHere = mine\n try {\n return read(value, settings)\n } catch (error) {\n if (typeof error === 'object' && error !== null && mine.has(error)) throw error\n throw new TypeError('The artifact could not be read', { cause: error })\n } finally {\n raisedHere = outer\n }\n}\n\nfunction read(value: unknown, settings: Settings): Artifact {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) notArtifact()\n // The format is read first and alone: a later format may rename or add any other field.\n const formatField = Reflect.getOwnPropertyDescriptor(value, 'bonsai')\n if (formatField === undefined) return notArtifact()\n if (!('value' in formatField)) fail('shape', 'The artifact format is an accessor')\n const format: unknown = formatField.value\n if (typeof format !== 'number' || !Number.isSafeInteger(format) || format < 1)\n fail('shape', 'The artifact format must be a positive integer')\n if (format !== ARTIFACT_FORMAT)\n fail(\n 'version',\n `Artifact format ${String(format)} is not supported (this runtime reads format ${ARTIFACT_FORMAT})`,\n )\n const data = ownData(value, 'The artifact', ARTIFACT_KEYS.size, new Map([['bonsai', format]]))\n for (const key of data.keys()) {\n if (!ARTIFACT_KEYS.has(key))\n fail('version', `The artifact has a field \"${key}\" this runtime does not know`)\n }\n const string = (key: string): string => {\n const field = data.get(key)\n return typeof field === 'string'\n ? field\n : fail('shape', `The artifact's ${key} must be a string`)\n }\n const flag = (key: string): boolean => {\n const field = data.get(key)\n return typeof field === 'boolean'\n ? field\n : fail('shape', `The artifact's ${key} must be true or false`)\n }\n const source = string('source')\n if (source.length > settings.parseLimits.maxSourceLength)\n fail(\n 'limit',\n `The artifact's source is longer than ${settings.parseLimits.maxSourceLength} characters`,\n )\n const plans = arrayData(data.get('plans'), settings.parseLimits.maxNodes, 'plans', 'limit').map(\n (entry, i): Plan => {\n const items = arrayData(entry, MAX_PLAN_LENGTH, `plans[${i}]`, 'plan')\n const [start, name, ...candidates] = items\n if (\n typeof start !== 'number' ||\n typeof name !== 'string' ||\n !candidates.every((c) => typeof c === 'number')\n )\n fail('shape', `plans[${i}] must be [nameStart, name, ...overload indices]`)\n return [start as number, name as string, ...(candidates as number[])]\n },\n )\n const expect = data.has('expect') ? copyType(data.get('expect')) : undefined\n return {\n bonsai: ARTIFACT_FORMAT,\n by: string('by'),\n source,\n plans,\n ...(expect === undefined ? {} : { expect }),\n guard: flag('guard'),\n async: flag('async'),\n digest: string('digest'),\n }\n}\n\n/** The calls of a bound tree by where their names start. */\nfunction callsByStart(root: Node): Map<number, CallNode> {\n const calls = new Map<number, CallNode>()\n const stack: Node[] = [root]\n while (stack.length > 0) {\n const node = stack.pop() as Node\n if (node.type === 'Call' && node.nameStart !== undefined) calls.set(node.nameStart, node)\n forEachChild(node, (child) => {\n stack.push(child)\n })\n }\n return calls\n}\n\n/** What a loaded artifact runs with: its analysis, result guard, and source. */\nexport interface Loaded {\n readonly source: string\n readonly analysis: ProgramAnalysis\n readonly guard: Type | undefined\n}\n\n/**\n * Loads an artifact against a runtime's definitions, refusing it with a\n * BonsaiArtifactError when it does not reproduce the checked program exactly:\n * a different format, source, plan, function, or declared variable type.\n */\nexport function loadArtifact(\n artifact: Artifact,\n settings: Settings,\n expect: Type | undefined,\n): Loaded {\n // Loading is not part of any read: a load that a Proxy trap runs while an\n // outer artifact is read must not mark its errors as that read's own.\n const outer = raisedHere\n raisedHere = undefined\n try {\n return loadRead(artifact, settings, expect)\n } finally {\n raisedHere = outer\n }\n}\n\nfunction loadRead(artifact: Artifact, settings: Settings, expect: Type | undefined): Loaded {\n const { source } = artifact\n let parsed: Node\n try {\n parsed = parse(source, settings.parseLimits)\n } catch (error) {\n if (!(error instanceof BonsaiError)) throw error\n return fail(\n error.code === 'SYNTAX' ? 'syntax' : 'limit',\n `The artifact's source cannot be parsed: ${error.message}`,\n error,\n )\n }\n const lookup = (name: string): FunctionDef | undefined =>\n settings.host.get(name) ?? settings.builtins.get(name)\n // Binding adds lambdas around arguments but no calls or variables, so the\n // digest is computed from the parsed tree as it is from the checked one:\n // different definitions are reported as such, before binding with them.\n const names = treeNames(parsed)\n for (const name of names.functions) {\n if (lookup(name) === undefined)\n fail('function', `The artifact calls ${name}(), which this runtime does not define`)\n }\n // A strict checker refuses every undeclared variable, so a strict runtime\n // refuses artifacts that read one rather than reading it from the context.\n if (settings.strict) {\n for (const name of names.variables) {\n if (settings.variables === undefined || !Object.hasOwn(settings.variables, name))\n fail('variable', `The artifact reads ${name}, which this strict runtime does not declare`)\n }\n }\n const { digest, ...fields } = artifact\n if (artifactDigest(fields, parsed, lookup, settings.variables) !== digest)\n fail(\n 'digest',\n 'The artifact was made for different definitions than this runtime has; make it again from its source',\n )\n const bound = bindImplicit(parsed, lookup)\n if (bound.problems.length > 0) fail('binding', bound.problems[0].message)\n const root = bound.root\n\n const calls = new Map<CallNode, CallPlan>()\n const byStart = callsByStart(root)\n for (const call of byStart.values()) {\n const def = lookup(call.name) as FunctionDef\n calls.set(call, { def, candidates: def.overloads.map((_, i) => i) })\n }\n let previous = -1\n for (const [start, name, ...candidates] of artifact.plans) {\n const call = byStart.get(start)\n if (start <= previous || call === undefined || call.name !== name)\n fail('plan', `The artifact's plan for ${name}() at ${start} matches no call`)\n previous = start\n const def = lookup(name) as FunctionDef\n const count = def.overloads.length\n if (\n candidates.length === 0 ||\n candidates.length >= count ||\n !candidates.every(\n (c, i) =>\n Number.isSafeInteger(c) && c >= 0 && c < count && (i === 0 || c > candidates[i - 1]),\n )\n )\n fail(\n 'plan',\n `The artifact's plan for ${name}() at ${start} is not a narrower set of its overloads`,\n )\n calls.set(call as CallNode, { def, candidates })\n }\n\n if (\n expect !== undefined &&\n (artifact.expect === undefined || encodeType(artifact.expect) !== encodeType(expect))\n )\n fail('expect', 'The artifact was not compiled for the expected type passed to load()')\n const analysis: ProgramAnalysis = { root, calls, async: artifact.async }\n warmZones(analysis)\n return {\n source,\n analysis,\n // The host's own expected type is always checked; the artifact's only where the checker asked.\n guard: expect ?? (artifact.guard ? artifact.expect : undefined),\n }\n}\n\n/** A key equal for two artifacts exactly when every field is (read with readArtifact). */\nexport function artifactKey(artifact: Artifact): string {\n return [\n lengthPrefixed(artifact.digest),\n lengthPrefixed(artifact.by),\n lengthPrefixed(artifact.source),\n lengthPrefixed(JSON.stringify(artifact.plans)),\n // As written, not canonically: equal types written differently report\n // themselves differently in result-mismatch messages.\n artifact.expect === undefined ? '-' : lengthPrefixed(JSON.stringify(artifact.expect)),\n artifact.guard ? 'g' : '-',\n artifact.async ? 'a' : '-',\n ].join('')\n}\n\n/** Runs a loaded artifact as a program does. */\nexport function loadedRunner<R>(loaded: Loaded, settings: Settings) {\n return programRunner<R>({ ...loaded, settings })\n}\n"],"mappings":"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"}
package/dist/program.mjs CHANGED
@@ -2,8 +2,6 @@ import { BonsaiLimitError, BonsaiRuntimeError } from "./errors.mjs";
2
2
  import { errorText, isMap } from "./runtime/values.mjs";
3
3
  import { formatType } from "./types.mjs";
4
4
  import { conforms, describeMismatch, isRecord } from "./functions/define.mjs";
5
- import { warmZone } from "./runtime/time.mjs";
6
- import { zoneParameter } from "./functions/builtins.mjs";
7
5
  import { isStackOverflow, tooDeep } from "./runtime/overflow.mjs";
8
6
  import { escaping, isOwn, raised } from "./runtime/attribution.mjs";
9
7
  import { State } from "./runtime/state.mjs";
@@ -185,13 +183,13 @@ const WARMED_ZONES = 16;
185
183
  function warmZones(analysis) {
186
184
  const seen = /* @__PURE__ */ new Set();
187
185
  for (const [call, plan] of analysis.calls) {
188
- const index = zoneParameter(plan.def);
189
- const arg = index === void 0 ? void 0 : call.args[index];
186
+ const zone = plan.def.zone;
187
+ const arg = zone === void 0 ? void 0 : call.args[zone.at];
190
188
  if (arg === void 0) continue;
191
- const zone = arg.type === "Literal" ? arg.value : void 0;
192
- if (zone !== void 0 && typeof zone !== "string" || seen.has(zone)) continue;
193
- seen.add(zone);
194
- warmZone(zone);
189
+ const name = arg.type === "Literal" ? arg.value : void 0;
190
+ if (name !== void 0 && typeof name !== "string" || seen.has(name)) continue;
191
+ seen.add(name);
192
+ zone?.warm(name);
195
193
  if (seen.size >= WARMED_ZONES) return;
196
194
  }
197
195
  }