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.
@@ -214,6 +214,19 @@ export interface EnvironmentOptions<V extends Readonly<Record<string, Type>> = R
214
214
  * naming the first mismatching path. Default: false.
215
215
  */
216
216
  readonly validateContext?: boolean | undefined;
217
+ /**
218
+ * The built-ins available, from bonsai-js/builtins. Default: all of them
219
+ * (none in bonsai-js/runtime/core). Calling one left out is a CHECK error, so
220
+ * an environment that checks rules for bonsai-js/runtime/core passes the same
221
+ * list as the runtime. On extend(), these are added to the base's.
222
+ */
223
+ readonly builtins?: readonly Builtin[] | undefined;
224
+ }
225
+ declare const builtinBrand: unique symbol;
226
+ /** A built-in function from bonsai-js/builtins, for the `builtins` option. */
227
+ export interface Builtin {
228
+ readonly name: string;
229
+ readonly [builtinBrand]: true;
217
230
  }
218
231
  /**
219
232
  * What check() found. Only `ok`, `type`, and `diagnostics` are data (and the
@@ -1,11 +1,12 @@
1
1
  import { BonsaiCheckError, BonsaiError, BonsaiLimitError, BonsaiRuntimeError, locate } from "./errors.mjs";
2
2
  import { isName } from "./syntax/lexer.mjs";
3
3
  import { isExact, isNullable, typeData } from "./types.mjs";
4
- import { assertHostSpec, assertType, isRecord, overload } from "./functions/define.mjs";
4
+ import { assertHostSpec, assertType, builtinsOption, isRecord, overload } from "./functions/define.mjs";
5
5
  import { BUILTINS } from "./functions/builtins.mjs";
6
6
  import { DEFAULT_PARSE_LIMITS, parse } from "./syntax/parser.mjs";
7
7
  import { analyze } from "./check/checker.mjs";
8
8
  import { raised } from "./runtime/attribution.mjs";
9
+ import { descriptionOf } from "./functions/descriptions.mjs";
9
10
  import { DEFAULT_RUNTIME_LIMITS, MAX_VALUE_DEPTH_LIMIT } from "./runtime/state.mjs";
10
11
  import { recordCompiled, recordDeclared } from "./declared.mjs";
11
12
  import { assertKeys, numberOption, programRunner, timeoutOption, warmZones } from "./program.mjs";
@@ -112,7 +113,8 @@ const OPTION_KEYS = /* @__PURE__ */ new Set([
112
113
  "limits",
113
114
  "cacheSize",
114
115
  "clock",
115
- "validateContext"
116
+ "validateContext",
117
+ "builtins"
116
118
  ]);
117
119
  const LIMIT_KEYS = /* @__PURE__ */ new Set([
118
120
  ...Object.keys(DEFAULT_PARSE_LIMITS),
@@ -165,7 +167,11 @@ const DEPTH_CAPS = {
165
167
  maxDepth: 256,
166
168
  maxValueDepth: MAX_VALUE_DEPTH_LIMIT
167
169
  };
168
- function settingsFrom(base, options) {
170
+ /**
171
+ * Settings from options, over a base (extend) or, for a new environment or
172
+ * runtime, over the entry point's built-ins.
173
+ */
174
+ function settingsFrom(base, options, builtins = /* @__PURE__ */ new Map()) {
169
175
  assertOptions(options);
170
176
  const limits = options.limits ?? {};
171
177
  const variables = mergeVariables(base?.variables, options);
@@ -175,6 +181,7 @@ function settingsFrom(base, options) {
175
181
  strictOption,
176
182
  strict: strictOption ?? variables !== void 0,
177
183
  host: mergeFunctions(base?.host ?? /* @__PURE__ */ new Map(), options),
184
+ builtins: builtinsOption(options.builtins, base?.builtins ?? (options.builtins === void 0 ? builtins : /* @__PURE__ */ new Map())),
178
185
  parseLimits: pickLimits(DEFAULT_PARSE_LIMITS, base?.parseLimits ?? DEFAULT_PARSE_LIMITS, limits),
179
186
  runtimeLimits: pickLimits(DEFAULT_RUNTIME_LIMITS, base?.runtimeLimits ?? DEFAULT_RUNTIME_LIMITS, limits),
180
187
  timeout: timeoutOption("Limit \"timeout\"", limits.timeout, base?.timeout ?? 0),
@@ -333,10 +340,10 @@ function createEnvironment(settings) {
333
340
  const checkEnv = {
334
341
  variables: settings.variables,
335
342
  strict: settings.strict,
336
- lookup: (name) => settings.host.get(name) ?? BUILTINS.get(name),
343
+ lookup: (name) => settings.host.get(name) ?? settings.builtins.get(name),
337
344
  *functionNames() {
338
345
  yield* settings.host.keys();
339
- for (const name of BUILTINS.keys()) if (!settings.host.has(name)) yield name;
346
+ for (const name of settings.builtins.keys()) if (!settings.host.has(name)) yield name;
340
347
  }
341
348
  };
342
349
  const cache = new ProgramCache(settings.cacheSize);
@@ -449,7 +456,7 @@ function createEnvironment(settings) {
449
456
  function info(def) {
450
457
  return {
451
458
  name: def.name,
452
- description: def.description,
459
+ description: descriptionOf(def),
453
460
  host: def.host === true,
454
461
  async: def.async === true,
455
462
  call: def.call === true,
@@ -532,7 +539,7 @@ function createEnvironment(settings) {
532
539
  */
533
540
  function environmentWith(features, options) {
534
541
  return createEnvironment({
535
- ...settingsFrom(void 0, options),
542
+ ...settingsFrom(void 0, options, BUILTINS),
536
543
  features
537
544
  });
538
545
  }
@@ -1 +1 @@
1
- {"version":3,"file":"environment.mjs","names":[],"sources":["../src/environment.ts"],"sourcesContent":["import { analyze, type Analysis, type CheckEnv } from './check/checker.js'\nimport type { CompileOptions as CompilerOptions } from './compile/compiler.js'\nimport { raised } from './runtime/attribution.js'\nimport {\n BonsaiCheckError,\n BonsaiError,\n BonsaiLimitError,\n BonsaiRuntimeError,\n locate,\n type Diagnostic,\n} from './errors.js'\nimport { BUILTINS } from './functions/builtins.js'\nimport {\n assertHostSpec,\n assertType,\n isRecord,\n overload,\n type FunctionDef,\n} from './functions/define.js'\nimport type { PartialData, PartialOptions, PartialResult } from './partial.js'\nimport {\n DEFAULT_RUNTIME_LIMITS,\n MAX_VALUE_DEPTH_LIMIT,\n type State,\n type RuntimeLimits,\n} from './runtime/state.js'\nimport { recordCompiled, recordDeclared } from './declared.js'\nimport {\n assertKeys,\n numberOption,\n programRunner,\n timeoutOption,\n type EvaluationLimits,\n warmZones,\n} from './program.js'\nimport type { ExplanationJSON, Trace } from './runtime/trace.js'\nimport type { Node } from './syntax/ast.js'\nimport { isName } from './syntax/lexer.js'\nimport { DEFAULT_PARSE_LIMITS, MAX_DEPTH_LIMIT, parse, type ParseLimits } from './syntax/parser.js'\nimport {\n isExact,\n isNullable,\n typeData,\n type AnyType,\n type Infer,\n type InferVariables,\n type Type,\n} from './types.js'\n\n// === public option types ===\n\n/** Every field optional, and `undefined` accepted, even under `exactOptionalPropertyTypes`. */\ntype Optional<T> = { readonly [K in keyof T]?: T[K] | undefined }\n\nexport interface Limits extends Optional<ParseLimits>, Optional<RuntimeLimits> {\n /** Wall-clock budget per evaluation in milliseconds (0 = none; fractions allowed). Default 0. */\n readonly timeout?: number | undefined\n}\n\n/**\n * The part of an `AbortSignal` evaluation reads. A standard `AbortSignal`\n * satisfies it; declaring it structurally keeps the types free of DOM or Node\n * library requirements.\n */\nexport interface AbortSignalLike {\n readonly aborted: boolean\n readonly reason?: unknown\n addEventListener: (type: 'abort', listener: () => void, options?: { once?: boolean }) => void\n removeEventListener: (type: 'abort', listener: () => void) => void\n}\n\nexport interface EvaluateOptions {\n /** Wall-clock budget for this evaluation in milliseconds (0 = none; fractions allowed). */\n readonly timeout?: number | undefined\n /** Step budget for this evaluation (0 = none; overrides the environment limit). */\n readonly maxSteps?: number | undefined\n /** Cancels the evaluation. */\n readonly signal?: AbortSignalLike | undefined\n /** The time now() returns in this evaluation, instead of the environment's clock. */\n readonly now?: Date | undefined\n /**\n * Validates the context for this evaluation (true) or not (false), instead\n * of the environment's `validateContext`; for a context already validated upstream.\n */\n readonly validateContext?: boolean | undefined\n}\n\ntype InferParams<P extends readonly Type[]> = Extract<\n { -readonly [K in keyof P]: Infer<P[K]> },\n unknown[]\n>\n\n/** The arguments `run` receives: the declared parameters, then any rest arguments. */\ntype RunArgs<P extends readonly Type[], RT> = RT extends Type\n ? [...InferParams<P>, ...Infer<RT>[]]\n : InferParams<P>\n\n/** What `run` may return: a nullable result also accepts `undefined`, which reads as null. */\ntype RunResult<R extends Type> = null extends Infer<R> ? Infer<R> | undefined : Infer<R>\n\n/**\n * The standard `AbortSignal` when the consumer's types declare one (DOM or\n * Node), so it can be passed to `fetch`; otherwise {@link AbortSignalLike}.\n */\ntype HostAbortSignal = typeof globalThis extends { AbortSignal: { prototype: infer S } }\n ? S\n : AbortSignalLike\n\n/** What a host function declared with `call: true` receives before its arguments. */\nexport interface HostCall<Ctx = Readonly<Record<string, unknown>>> {\n /** The evaluation context, read-only. */\n readonly context: Readonly<Ctx>\n /**\n * Aborted when the evaluation times out or is cancelled while it waits on\n * this call, so an async function can stop its own work (pass it to `fetch`).\n */\n readonly signal: HostAbortSignal\n}\n\n/** A host function declaration. Create one with {@link fn}. */\nexport interface HostFunction {\n readonly params: readonly Type[]\n readonly returns: Type\n readonly required?: number | undefined\n readonly rest?: Type | undefined\n readonly async?: boolean | undefined\n /** Whether `run` receives a {@link HostCall} before its arguments. */\n readonly call?: boolean | undefined\n readonly description?: string | undefined\n /** Steps charged per call (default 32). */\n readonly cost?: number | undefined\n readonly run: (...args: never[]) => unknown\n}\n\n/** The declaration passed to {@link fn}, without `run`, `async`, and `call`. */\nexport interface FnSpec<\n P extends readonly Type[] = readonly Type[],\n R extends Type = Type,\n RT extends Type | undefined = Type | undefined,\n> {\n /** Parameter types, in order. */\n readonly params: P\n /** Declared result type; results are checked against its kind. */\n readonly returns: R\n /** Number of required leading parameters (default: all). Missing optional arguments arrive as null. */\n readonly required?: number | undefined\n /** Type of further variadic arguments. */\n readonly rest?: RT\n readonly description?: string | undefined\n /**\n * Steps charged per call (default 32), so one evaluation makes at most\n * maxSteps / cost calls. Lower it for cheap pure functions; raise it for\n * expensive ones.\n */\n readonly cost?: number | undefined\n}\n\n/**\n * Declares a host function.\n *\n * ```ts\n * fn({ params: [t.string()], returns: t.boolean(), run: (perm) => user.can(perm) })\n * fn({ params: [t.string()], returns: t.string(), async: true, run: async (id) => lookup(id) })\n * fn({ params: [t.string()], returns: t.number(), async: true, call: true,\n * run: async (call, id) => (await fetch(`/stock/${id}`, { signal: call.signal })).json() })\n * ```\n */\nexport function fn<\n const P extends readonly Type[],\n R extends Type,\n RT extends Type | undefined = undefined,\n>(\n spec: FnSpec<P, R, RT> &\n (\n | {\n readonly async?: false | undefined\n readonly call?: false | undefined\n readonly run: (...args: RunArgs<P, RT>) => RunResult<R>\n }\n | {\n readonly async: true\n readonly call?: false | undefined\n readonly run: (...args: RunArgs<P, RT>) => Promise<RunResult<R>>\n }\n | {\n readonly async?: false | undefined\n readonly call: true\n readonly run: (call: HostCall, ...args: RunArgs<P, RT>) => RunResult<R>\n }\n | {\n readonly async: true\n readonly call: true\n readonly run: (call: HostCall, ...args: RunArgs<P, RT>) => Promise<RunResult<R>>\n }\n ),\n): HostFunction {\n assertHostSpec(spec, 'fn()')\n return Object.freeze({ ...spec })\n}\n\n/**\n * Declares host functions that read a typed evaluation context: like `fn`\n * with `call: true`, with `call.context` typed as `Readonly<Ctx>`.\n *\n * ```ts\n * const contextFn = withContext<{ user: { id: string } }>()\n * const functions = { userId: contextFn({ params: [], returns: t.string(), run: (call) => call.context.user.id }) }\n * ```\n */\nexport function withContext<Ctx>() {\n return <const P extends readonly Type[], R extends Type, RT extends Type | undefined = undefined>(\n spec: FnSpec<P, R, RT> &\n (\n | {\n readonly async?: false | undefined\n /** Implied; may be written out when moving a function over from `fn`. */\n readonly call?: true | undefined\n readonly run: (call: HostCall<Ctx>, ...args: RunArgs<P, RT>) => RunResult<R>\n }\n | {\n readonly async: true\n readonly call?: true | undefined\n readonly run: (call: HostCall<Ctx>, ...args: RunArgs<P, RT>) => Promise<RunResult<R>>\n }\n ),\n ): HostFunction => {\n assertHostSpec(spec, 'withContext()')\n return Object.freeze({ ...spec, call: true })\n }\n}\n\n/**\n * A reusable bundle of host functions (and optionally variables). Declare one\n * with `satisfies Library` (not `: Library`) so its variables keep their types\n * and become part of the context type of every environment that uses it.\n */\nexport interface Library<\n V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>,\n> {\n readonly name: string\n readonly functions?: Readonly<Record<string, HostFunction>> | undefined\n readonly variables?: V | undefined\n}\n\n/** No declared variables: the empty record, which adds nothing to a context type. */\n// oxlint-disable-next-line typescript/no-generated-empty-object-type -- intentionally empty\nexport type NoVariables = Record<never, never>\n\n/**\n * The variables one library declares. A library annotated `: Library` has the\n * wide default (any string key), which names no variable, so it adds nothing.\n */\ntype VariablesOf<Lib> = Lib extends { readonly variables?: infer V }\n ? string extends keyof NonNullable<V>\n ? NoVariables\n : NonNullable<V>\n : NoVariables\n\n/**\n * The variables a list of libraries declares, merged. Only a list whose\n * elements are known (written inline, or `as const`) contributes; a value\n * typed as `Library[]` adds nothing to the context type.\n */\ntype LibraryVariables<L> = L extends readonly [infer First, ...infer Rest]\n ? VariablesOf<First> & LibraryVariables<Rest>\n : NoVariables\n\n/**\n * Any further keys, as a context with `strict: false` may carry. Its values are\n * `any` because only that lets an interface or a class instance (which have no\n * index signature) still satisfy it; declared keys keep their own types.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the only index signature interfaces and classes satisfy\ntype MoreKeys = Readonly<Record<string, any>>\n\n/**\n * The context type of an environment with variables `V` and libraries `L`.\n * With `strict: false` (`S`), undeclared variables are allowed, so the context\n * may carry keys beyond the declared ones.\n */\nexport type ContextFor<V, L, S = undefined> = [S] extends [false]\n ? ContextOf<V & LibraryVariables<L>> & MoreKeys\n : ContextOf<V & LibraryVariables<L>>\n\n/**\n * `Ctx` with the variables `New` declares replacing those of the same name,\n * as extend() replaces them at run time (\"later wins\"). Remapping keys (not\n * Omit) keeps an index signature and the other keys' types.\n */\ntype Override<Ctx, New> = [keyof New] extends [never]\n ? Ctx\n : { [K in keyof Ctx as K extends keyof New ? never : K]: Ctx[K] } & New\n\n/** The context type extend() produces: re-declared variables replaced, strict: false loosened. */\nexport type Extended<Ctx, V, L, S> = [S] extends [false]\n ? Override<Ctx, ContextOf<V & LibraryVariables<L>>> & MoreKeys\n : Override<Ctx, ContextOf<V & LibraryVariables<L>>>\n\nexport interface EnvironmentOptions<\n V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>,\n L extends readonly Library[] = readonly Library[],\n> {\n /** Declared context variables and their types. */\n readonly variables?: V | undefined\n /**\n * Report unknown variables as errors. Default: true when `variables` is\n * declared (even as `{}`), false for an open environment.\n */\n readonly strict?: boolean | undefined\n /** Host functions by name. A host function replaces a built-in of the same name. */\n readonly functions?: Readonly<Record<string, HostFunction>> | undefined\n /** Libraries of host functions and variables; a name defined twice is an error. */\n readonly libraries?: L | undefined\n readonly limits?: Limits | undefined\n /**\n * Compiled programs kept for `evaluate(source)` and `evaluateSync(source)`. Default 256; 0\n * disables the cache. The cache also holds at most 256K characters of source in total, and\n * never a source longer than 16K characters.\n */\n readonly cacheSize?: number | undefined\n /** Source of now(). Default: the system clock. */\n readonly clock?: (() => Date) | undefined\n /**\n * Check the context against the declared variable types before every\n * evaluation (deep, proportional to the data) and fail with INVALID_CONTEXT\n * naming the first mismatching path. Default: false.\n */\n readonly validateContext?: boolean | undefined\n}\n\n/**\n * What check() found. Only `ok`, `type`, and `diagnostics` are data (and the\n * JSON form); `program`, `ast`, and `typeOf` are accessors computed on demand,\n * so spreading the result or `structuredClone` does not copy them.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Program\nexport type CheckResult<Ctx = any, R = unknown> = Checked<Program<Ctx, R>>\n\n/** What check() found, over the kind of program it compiles. */\ntype Checked<P> = (\n | {\n readonly ok: true\n readonly type: Type\n readonly diagnostics: readonly Diagnostic[]\n /**\n * The checked expression, compiled when first read: a program equal to\n * the one compile() returns, without parsing and checking again.\n */\n readonly program: P\n }\n | {\n readonly ok: false\n readonly type: Type | undefined\n readonly diagnostics: readonly Diagnostic[]\n }\n) & {\n /**\n * The checked syntax tree (implicit lambdas made explicit, as `program.ast`),\n * or undefined when the source did not parse.\n */\n readonly ast: Node | undefined\n /** The inferred type of a node of `ast`, or undefined for another tree's node. */\n readonly typeOf: (node: Node) => Type | undefined\n}\n\nexport type Args<Ctx> =\n Record<string, never> extends Ctx\n ? [context?: Ctx, options?: EvaluateOptions]\n : [context: Ctx, options?: EvaluateOptions]\n\n/**\n * A checked, compiled expression. Immutable and safe to share. A bare\n * `Program` accepts any context, so a program typed for one context can be\n * stored as a `Program`.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context type every typed program is assignable to\nexport interface Program<Ctx = any, R = unknown> {\n readonly source: string\n /** The syntax tree after implicit lambdas are made explicit. */\n readonly ast: Node\n /** The statically inferred result type. */\n readonly type: Type\n /** Whether the expression calls an async host function (evaluateSync rejects it). */\n readonly async: boolean\n /** Non-fatal findings from the checker. */\n readonly warnings: readonly Diagnostic[]\n readonly references: {\n readonly variables: readonly string[]\n readonly functions: readonly string[]\n }\n evaluate: (...args: Args<Ctx>) => Promise<R>\n evaluateSync: (...args: Args<Ctx>) => R\n /**\n * Evaluates and records the value of every sub-expression, so you can show\n * why the result came out as it did. Never rejects for evaluation errors:\n * they are returned in the explanation. Slower than evaluate.\n */\n explain: (...args: ExplainArgs<Ctx>) => Promise<Explanation<R>>\n /** Like explain, synchronously; an expression that calls an async host function is an ASYNC_IN_SYNC error. */\n explainSync: (...args: ExplainArgs<Ctx>) => Explanation<R>\n /**\n * Evaluates what can be evaluated from partial data. Returns the value when\n * the known data decides it, or a simplified residual expression (source and\n * syntax tree) that needs only the unknown data. Evaluating the residual with\n * the full data gives the same result as evaluating this program.\n */\n partial: (known: PartialData<Ctx> & object, options?: PartialOptions) => PartialResult<Ctx, R>\n}\n\nexport interface ExplainOptions extends EvaluateOptions {\n /** How many lambda runs to record per call (the rest are counted). Default 20. */\n readonly maxIterations?: number | undefined\n /** Sub-expressions to record in total before stopping (the result stays exact). Default 10,000. */\n readonly maxTraceNodes?: number | undefined\n /**\n * Also evaluate the side of && and || that short-circuiting would skip, so\n * reasons() lists every failing condition rather than the first. Those\n * parts are marked `extra`; their evaluation errors are ignored and the\n * result is unchanged. They spend from the same step and time budget, so a\n * limit reached there (STEP_LIMIT, TIMEOUT, a size limit) fails the\n * explanation even when evaluation alone would stay inside it. Host\n * functions on those parts do run. Default false.\n */\n readonly exhaustive?: boolean | undefined\n}\n\ntype ExplainArgs<Ctx> =\n Record<string, never> extends Ctx\n ? [context?: Ctx, options?: ExplainOptions]\n : [context: Ctx, options?: ExplainOptions]\n\n/** The result of explain(): the outcome plus a trace of every sub-expression. */\nexport type Explanation<R = unknown> = (\n | { readonly ok: true; readonly value: R }\n | { readonly ok: false; readonly error: BonsaiError }\n) & {\n /** The root of the trace; its `value` is the result. Values are live references. */\n readonly trace: Trace\n /** True when maxTraceNodes stopped recording before evaluation finished. */\n readonly truncated: boolean\n /**\n * The conditions that decided the result, following &&, ||, and ! down to\n * the comparisons and values (or the error) behind it.\n */\n reasons: () => readonly Trace[]\n /** The trace as an indented, human-readable tree. */\n toString: () => string\n /**\n * A JSON-safe snapshot: values are bounded copies (cycles, bigints, dates,\n * and durations handled) and getters are never run. `JSON.stringify` uses it.\n */\n toJSON: () => ExplanationJSON\n}\n\nexport interface CompileOptions<E extends Type = Type> {\n /** The type the expression must produce; also types the program's result. */\n readonly expect?: E | undefined\n}\n\nexport interface FunctionInfo {\n readonly name: string\n readonly description: string\n readonly host: boolean\n readonly async: boolean\n /** Whether a host function receives a {@link HostCall} (declared `call: true`). */\n readonly call: boolean\n /** Steps charged per call, when the host function declares a `cost` (otherwise 32). */\n readonly cost?: number\n readonly signatures: readonly {\n readonly params: readonly Type[]\n readonly required: number\n readonly rest?: Type\n readonly returns: Type\n }[]\n}\n\n/** An environment. A bare `Environment` accepts any context, like a bare `Program`. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context type every typed environment is assignable to\nexport interface Environment<Ctx = any> {\n /** Declared variables, or undefined for an open environment. */\n readonly variables: Readonly<Record<string, Type>> | undefined\n readonly strict: boolean\n /** Parses without checking. */\n parse: (source: string) => Node\n /**\n * Parses and checks, reporting every finding instead of throwing. When `ok`,\n * `program` is the compiled expression, so an editor needs only this call.\n */\n check: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => CheckResult<Ctx, Infer<E>>\n /**\n * Parses, checks, and compiles. Throws BonsaiSyntaxError / BonsaiCheckError /\n * BonsaiLimitError. Cached: the same source and expected type return the same program.\n */\n compile: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => Program<Ctx, Infer<E>>\n /**\n * Compiles (cached) and partially evaluates; see Program.partial. Throws for\n * syntax, check, and limit errors, and a TypeError or RangeError for invalid options.\n * `R` is an unchecked type assertion, as for evaluate().\n */\n partial: <R = unknown>(\n source: string,\n known: PartialData<Ctx> & object,\n options?: PartialOptions,\n ) => PartialResult<Ctx, R>\n /**\n * Compiles (cached) and evaluates asynchronously. `R` is an unchecked type\n * assertion on the result, like a cast; for a checked result type, use\n * `compile(source, { expect })` and evaluate the program.\n */\n evaluate: <R = unknown>(source: string, ...args: Args<Ctx>) => Promise<R>\n /**\n * Compiles (cached) and evaluates synchronously. `R` is an unchecked type\n * assertion, as for evaluate().\n */\n evaluateSync: <R = unknown>(source: string, ...args: Args<Ctx>) => R\n /**\n * Compiles (cached) and explains; see Program.explain. Rejects for syntax and\n * check errors and parse limits, and with a TypeError or RangeError for invalid options.\n * `R` is an unchecked type assertion, as for evaluate().\n */\n explain: <R = unknown>(source: string, ...args: ExplainArgs<Ctx>) => Promise<Explanation<R>>\n /**\n * Compiles (cached) and explains synchronously. Throws for syntax and check\n * errors and parse limits, and a TypeError or RangeError for invalid options.\n * `R` is an unchecked type assertion, as for evaluate().\n */\n explainSync: <R = unknown>(source: string, ...args: ExplainArgs<Ctx>) => Explanation<R>\n /** Looks up a function (host or built-in). */\n describeFunction: (name: string) => FunctionInfo | undefined\n /** Every callable function, host functions first. */\n listFunctions: () => readonly FunctionInfo[]\n /**\n * A new environment with more variables, functions, or libraries. A\n * variable or function declared again replaces the earlier one.\n */\n extend: <\n const V2 extends Readonly<Record<string, Type>> = NoVariables,\n const L2 extends readonly Library[] = [],\n const S2 extends boolean | undefined = undefined,\n >(\n options: EnvironmentOptions<V2, L2> & { readonly strict?: S2 },\n ) => Environment<Extended<Ctx, V2, L2, S2>>\n}\n\n/**\n * A program from `bonsai-js/lite`: a {@link Program} without `explain`,\n * `explainSync`, and `partial`. Every Program is also a LiteProgram, so code\n * that only evaluates can accept either.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Program\nexport type LiteProgram<Ctx = any, R = unknown> = Omit<\n Program<Ctx, R>,\n 'explain' | 'explainSync' | 'partial'\n>\n\n/** What a `bonsai-js/lite` environment's check() found. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Program\nexport type LiteCheckResult<Ctx = any, R = unknown> = Checked<LiteProgram<Ctx, R>>\n\n/**\n * An environment from `bonsai-js/lite`: an {@link Environment} without\n * `explain`, `explainSync`, and `partial`, whose programs are LitePrograms.\n * Every Environment is also a LiteEnvironment.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Environment\nexport interface LiteEnvironment<Ctx = any> extends Omit<\n Environment<Ctx>,\n 'check' | 'compile' | 'extend' | 'explain' | 'explainSync' | 'partial'\n> {\n /** See Environment.check. */\n check: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => LiteCheckResult<Ctx, Infer<E>>\n /** See Environment.compile. */\n compile: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => LiteProgram<Ctx, Infer<E>>\n /** See Environment.extend. */\n extend: <\n const V2 extends Readonly<Record<string, Type>> = NoVariables,\n const L2 extends readonly Library[] = [],\n const S2 extends boolean | undefined = undefined,\n >(\n options: EnvironmentOptions<V2, L2> & { readonly strict?: S2 },\n ) => LiteEnvironment<Extended<Ctx, V2, L2, S2>>\n}\n\n/**\n * The context type of declared variables `V`. An open environment (no\n * variables) accepts any object, including values typed by an interface, and\n * so does a schema only known at run time (`Record<string, Type>`), whose\n * variables are not known statically.\n */\ntype ContextOf<V> = [keyof V] extends [never]\n ? object\n : string extends keyof V\n ? MoreKeys\n : InferVariables<V>\n\n// === implementation ===\n\n/** Internals shared with the language service (not public API). */\nexport interface EnvironmentInternals {\n readonly checkEnv: CheckEnv\n readonly parseLimits: ParseLimits\n}\n\nconst internals = new WeakMap<object, EnvironmentInternals>()\n\nexport function internalsOf(env: object): EnvironmentInternals {\n const found = internals.get(env)\n if (found === undefined) throw new TypeError('Not a Bonsai environment')\n return found\n}\n\n/** Compiled programs kept per environment unless `cacheSize` says otherwise. */\nconst DEFAULT_CACHE_SIZE = 256\n\n/** An environment's settings (not public API). */\nexport interface Settings {\n readonly variables: Readonly<Record<string, Type>> | undefined\n /** The explicit `strict` option, if one was given (here or in a base environment). */\n readonly strictOption: boolean | undefined\n readonly strict: boolean\n readonly host: ReadonlyMap<string, FunctionDef>\n readonly parseLimits: ParseLimits\n readonly runtimeLimits: RuntimeLimits\n readonly timeout: number\n readonly cacheSize: number\n readonly clock: () => Date\n readonly validateContext: boolean\n /** Explaining and partial evaluation, when the entry point provides them. */\n readonly features: Features | undefined\n}\n\n/**\n * What a program shares with the features that extend it (not public API):\n * its source, analysis, and settings, and how it prepares, checks, and settles\n * an evaluation.\n */\nexport interface ProgramCore<R> {\n readonly source: string\n readonly analysis: Analysis\n readonly expect: Type | undefined\n readonly settings: Settings\n readonly checkEnv: CheckEnv\n readonly compileOptions: CompilerOptions\n /** Set when the program is a partial-evaluation residual. */\n readonly residualOf: ResidualOf | undefined\n prepare: (\n state: State,\n ctx: Record<string, unknown>,\n limits: EvaluationLimits,\n locals: number,\n ) => void\n checked: (result: unknown, state: State) => void\n settle: (result: R) => R\n /** Compiles a residual of this program and returns how it runs. */\n residual: (\n analysis: Analysis,\n constants: ReadonlyMap<string, unknown>,\n residualOf: ResidualOf,\n ) => ResidualRunner\n}\n\n/** The program methods a feature set provides (not public API). */\nexport interface ProgramFeatures<Ctx, R> {\n explainSync: (\n context: unknown,\n options: ExplainOptions | undefined,\n promised?: boolean,\n ) => Explanation<R>\n explainAsync: (context: unknown, options: ExplainOptions | undefined) => Promise<Explanation<R>>\n partial: (program: object, known: object, options: unknown) => PartialResult<Ctx, R>\n}\n\n/**\n * Explaining and partial evaluation, supplied by the entry point (not public\n * API): kept out of this module so a bundle that only checks and evaluates\n * does not carry them.\n */\nexport interface Features {\n program: <Ctx, R>(core: ProgramCore<R>) => ProgramFeatures<Ctx, R>\n}\n\n/** What a residual keeps from its partial evaluation. */\nexport interface ResidualOf {\n /** Its printed source: the text of its explanations. */\n readonly source: string\n /** Whether `call: true` host functions read the context it is given. */\n readonly readsContext: boolean\n /** The variables known to partial(), which a context read whole must hold. */\n readonly knownRoots: readonly string[]\n /** The context paths it reads: all a context it does not read whole needs. */\n readonly dependsOn: readonly string[]\n /** Size and depth of the lists and maps partial() built into its bindings. */\n readonly shapes: WeakMap<object, number> | undefined\n}\n\n/** How a partial-evaluation residual runs and explains, with the caller's context. */\nexport interface ResidualRunner {\n /** Whether the residual calls an async host function. */\n async: boolean\n runSync: (ctx: unknown, options: unknown) => unknown\n runAsync: (ctx: unknown, options: unknown) => Promise<unknown>\n explainSync: (ctx: unknown, options: unknown) => Explanation\n explainAsync: (ctx: unknown, options: unknown) => Promise<Explanation>\n}\n\nfunction toDef(name: string, host: HostFunction): FunctionDef {\n // `has` and `try` read as syntax, so a function with either name could never be called.\n if (!isName(name) || name === 'has' || name === 'try') {\n throw new TypeError(`Invalid function name \"${name}\"`)\n }\n assertHostSpec(host, `Function \"${name}\"`)\n const run = host.run as (...args: unknown[]) => unknown\n const count = host.params.length\n host.params.forEach((param, index) => {\n if (index >= (host.required ?? count) && !isNullable(param)) {\n throw new TypeError(\n `Optional parameter ${index + 1} of \"${name}\" receives null when omitted; declare it as t.optional(...)`,\n )\n }\n })\n return Object.freeze({\n name,\n description: host.description ?? '',\n host: true,\n async: host.async === true,\n call: host.call === true,\n ...(host.cost === undefined ? {} : { cost: host.cost }),\n overloads: [\n overload(\n host.params,\n host.returns,\n (args) => {\n // Missing optional arguments arrive as null.\n const offset = host.call === true ? 1 : 0\n while (args.length < count + offset) args.push(null)\n return run(...args)\n },\n {\n required: host.required ?? count,\n ...(host.rest === undefined ? {} : { rest: host.rest }),\n },\n ),\n ],\n })\n}\n\nfunction mergeFunctions(\n base: ReadonlyMap<string, FunctionDef>,\n options: EnvironmentOptions,\n): Map<string, FunctionDef> {\n const out = new Map(base)\n const added = new Map<string, string>()\n const add = (name: string, def: HostFunction, origin: string): void => {\n const previous = added.get(name)\n if (previous !== undefined) {\n throw new TypeError(`Function \"${name}\" is defined by both ${previous} and ${origin}`)\n }\n added.set(name, origin)\n out.set(name, toDef(name, def))\n }\n for (const library of options.libraries ?? []) {\n for (const [name, def] of Object.entries(library.functions ?? {}))\n add(name, def, `library \"${library.name}\"`)\n }\n for (const [name, def] of Object.entries(options.functions ?? {}))\n add(name, def, 'the functions option')\n return out\n}\n\nfunction mergeVariables(\n base: Readonly<Record<string, Type>> | undefined,\n options: EnvironmentOptions,\n): Readonly<Record<string, Type>> | undefined {\n // Declaring `variables` (even as {}) makes the environment declared, and so strict by default.\n let out: Record<string, Type> | undefined =\n base === undefined && options.variables === undefined ? undefined : { ...base }\n const added = new Map<string, string>()\n const add = (name: string, type: Type, origin: string): void => {\n if (!isName(name)) {\n throw new TypeError(`Invalid variable name \"${name}\" (in ${origin})`)\n }\n const previous = added.get(name)\n if (previous !== undefined) {\n throw new TypeError(`Variable \"${name}\" is declared by both ${previous} and ${origin}`)\n }\n added.set(name, origin)\n assertType(type, `Variable \"${name}\"`)\n out ??= {}\n out[name] = type\n }\n for (const library of options.libraries ?? []) {\n for (const [k, v] of Object.entries(library.variables ?? {}))\n add(k, v, `library \"${library.name}\"`)\n }\n for (const [k, v] of Object.entries(options.variables ?? {})) add(k, v, 'the variables option')\n return out === undefined ? undefined : Object.freeze(out)\n}\n\nconst OPTION_KEYS = new Set([\n 'variables',\n 'strict',\n 'functions',\n 'libraries',\n 'limits',\n 'cacheSize',\n 'clock',\n 'validateContext',\n])\nconst LIMIT_KEYS: ReadonlySet<string> = new Set([\n ...Object.keys(DEFAULT_PARSE_LIMITS),\n ...Object.keys(DEFAULT_RUNTIME_LIMITS),\n 'timeout',\n])\nconst LIBRARY_KEYS = new Set(['name', 'functions', 'variables'])\n\n/** Environment options come from configuration: mistakes there are programming errors. */\nfunction assertOptions(options: unknown): asserts options is EnvironmentOptions {\n assertKeys(options, OPTION_KEYS, 'Options')\n const o = options as Record<string, unknown>\n if (o.limits !== undefined) assertKeys(o.limits, LIMIT_KEYS, 'Limits')\n if (o.variables !== undefined && !isRecord(o.variables))\n throw new TypeError('variables must be an object of types')\n if (o.functions !== undefined && !isRecord(o.functions))\n throw new TypeError('functions must be an object of fn() declarations')\n if (o.libraries !== undefined) {\n if (!Array.isArray(o.libraries)) throw new TypeError('libraries must be an array')\n const names = new Set<string>()\n for (const library of o.libraries) {\n assertKeys(library, LIBRARY_KEYS, 'Library')\n const l = library as Record<string, unknown>\n if (typeof l.name !== 'string' || l.name === '') throw new TypeError('A library needs a name')\n if (names.has(l.name)) throw new TypeError(`Library \"${l.name}\" is listed twice`)\n names.add(l.name)\n if (l.functions !== undefined && !isRecord(l.functions))\n throw new TypeError(`functions of library \"${l.name}\" must be an object`)\n if (l.variables !== undefined && !isRecord(l.variables))\n throw new TypeError(`variables of library \"${l.name}\" must be an object`)\n }\n }\n for (const flag of ['strict', 'validateContext'] as const) {\n if (o[flag] !== undefined && typeof o[flag] !== 'boolean')\n throw new TypeError(`${flag} must be a boolean`)\n }\n if (o.clock !== undefined && typeof o.clock !== 'function')\n throw new TypeError('clock must be a function returning a Date')\n}\n\ntype Numbers = Readonly<Record<string, number>>\n\n/**\n * The limits in `defaults` (parse or runtime), overridden by `limits` over\n * `current`. 0 turns `maxSteps` off; every other limit must be at least 1.\n */\nfunction pickLimits(\n defaults: object,\n current: object,\n limits: Readonly<Record<string, unknown>>,\n): Numbers {\n const base = current as Numbers\n return Object.freeze(\n Object.fromEntries(\n Object.keys(defaults).map((name) => {\n const value = numberOption(\n `Limit \"${name}\"`,\n limits[name],\n base[name],\n name === 'maxSteps' ? 0 : 1,\n )\n const cap = DEPTH_CAPS[name]\n if (cap !== undefined && value > cap)\n throw new RangeError(`Limit \"${name}\" must be at most ${cap}`)\n return [name, value]\n }),\n ),\n )\n}\n\n/** Depth limits are capped: deeper nesting would run out of call stack. */\nconst DEPTH_CAPS: Readonly<Record<string, number>> = {\n maxDepth: MAX_DEPTH_LIMIT,\n maxValueDepth: MAX_VALUE_DEPTH_LIMIT,\n}\n\nexport function settingsFrom(base: Settings | undefined, options: unknown): Settings {\n assertOptions(options)\n const limits = (options.limits ?? {}) as Readonly<Record<string, unknown>>\n const variables = mergeVariables(base?.variables, options)\n const strictOption = options.strict ?? base?.strictOption\n return {\n variables,\n strictOption,\n strict: strictOption ?? variables !== undefined,\n host: mergeFunctions(base?.host ?? new Map(), options),\n parseLimits: pickLimits(\n DEFAULT_PARSE_LIMITS,\n base?.parseLimits ?? DEFAULT_PARSE_LIMITS,\n limits,\n ) as unknown as ParseLimits,\n runtimeLimits: pickLimits(\n DEFAULT_RUNTIME_LIMITS,\n base?.runtimeLimits ?? DEFAULT_RUNTIME_LIMITS,\n limits,\n ) as unknown as RuntimeLimits,\n timeout: timeoutOption('Limit \"timeout\"', limits.timeout, base?.timeout ?? 0),\n cacheSize: numberOption(\n 'cacheSize',\n options.cacheSize,\n base?.cacheSize ?? DEFAULT_CACHE_SIZE,\n 0,\n ),\n clock: options.clock === undefined ? (base?.clock ?? systemClock) : checkedClock(options.clock),\n validateContext: options.validateContext ?? base?.validateContext ?? false,\n features: base?.features,\n }\n}\n\n/**\n * Source characters the program cache holds in total, and the longest source it\n * keeps. A compiled program can retain a hundred times its source in memory, so\n * the cache is bounded by size as well as by count.\n */\nconst CACHE_CHARS = 262_144\nconst CACHE_MAX_SOURCE = 16_384\n\n/**\n * Approximately least recently used (\"second chance\"): a hit only marks its\n * entry, so the hot path is one lookup. When the cache is full, the oldest\n * entry is evicted, unless it was used since it last reached the front (or is\n * the entry being added), in which case it moves to the back unmarked.\n */\nexport class ProgramCache<V> {\n private readonly map = new Map<string, { readonly value: V; used: boolean }>()\n private readonly size: number\n private chars = 0\n\n constructor(size: number) {\n this.size = size\n }\n\n get(key: string): V | undefined {\n const slot = this.map.get(key)\n if (slot === undefined) return undefined\n slot.used = true\n return slot.value\n }\n\n set(key: string, value: V): void {\n if (this.size === 0 || key.length > CACHE_MAX_SOURCE) return\n if (this.map.delete(key)) this.chars -= key.length\n this.map.set(key, { value, used: false })\n this.chars += key.length\n while (this.map.size > this.size || this.chars > CACHE_CHARS) {\n const [oldest, slot] = this.map.entries().next().value as [\n string,\n { readonly value: V; used: boolean },\n ]\n this.map.delete(oldest)\n if (slot.used || oldest === key) {\n slot.used = false\n this.map.set(oldest, slot)\n } else {\n this.chars -= oldest.length\n }\n }\n }\n}\n\nconst NO_DIAGNOSTICS: readonly Diagnostic[] = Object.freeze([])\n\n/**\n * What check() found. `ok`, `type`, and `diagnostics` are the JSON form; the\n * tree is read on demand (a prototype getter), so an editor checking on every\n * keystroke pays only for what it uses.\n */\nclass CheckFailure {\n readonly ok: boolean = false\n readonly type: Type | undefined\n readonly diagnostics: readonly Diagnostic[]\n /** The inferred type of a node of `ast`, or undefined for another tree's node. */\n readonly typeOf: (node: Node) => Type | undefined\n readonly #analysis: Analysis | undefined\n\n constructor(type: Type | undefined, diagnostics: readonly Diagnostic[], analysis?: Analysis) {\n this.type = type\n this.diagnostics = diagnostics\n this.#analysis = analysis\n this.typeOf = (node) => {\n const found = analysis?.types.get(node)\n return found === undefined ? undefined : deepFreeze(found)\n }\n }\n\n get ast(): Node | undefined {\n return this.#analysis === undefined ? undefined : deepFreeze(this.#analysis.root)\n }\n}\n\n/** A check that passed: also the compiled program, made the first time it is read. */\nclass CheckSuccess extends CheckFailure {\n override readonly ok: boolean = true\n readonly #program: () => unknown\n\n constructor(\n type: Type,\n diagnostics: readonly Diagnostic[],\n analysis: Analysis,\n program: () => unknown,\n ) {\n super(type, diagnostics, analysis)\n this.#program = program\n }\n\n get program(): unknown {\n return this.#program()\n }\n}\n\n/** Freezes a syntax tree (or any tree of plain objects and arrays) in place, without recursion. */\nfunction deepFreeze<T>(root: T): T {\n // Visits frozen objects too: a frozen type can hold a type the checker built\n // unfrozen (and shares between analyses).\n const seen = new Set<object>()\n const pending: unknown[] = [root]\n while (pending.length > 0) {\n const value = pending.pop()\n if (typeof value !== 'object' || value === null || seen.has(value)) continue\n seen.add(value)\n Object.freeze(value)\n for (const child of Object.values(value)) pending.push(child)\n }\n return root\n}\n\nconst systemClock = (): Date => new Date()\n\n/** A host clock whose results are checked: now() must be a valid timestamp. */\nfunction checkedClock(clock: () => Date): () => Date {\n return () => {\n let value: unknown\n try {\n value = clock()\n } catch (error) {\n throw raised(new BonsaiRuntimeError('HOST_ERROR', 'The clock failed', { cause: error }))\n }\n if (!(value instanceof Date) || Number.isNaN(Date.prototype.getTime.call(value))) {\n throw raised(\n new BonsaiRuntimeError(\n 'HOST_CONTRACT',\n 'The clock must return a valid Date (for example () => new Date())',\n ),\n )\n }\n return value\n }\n}\n\n/** Whether a type has an exact map type anywhere in it. */\nfunction holdsExact(type: Type): boolean {\n const stack: Type[] = [type]\n const seen = new Set<Type>()\n while (stack.length > 0) {\n const next = stack.pop() as Type\n if (seen.has(next)) continue\n seen.add(next)\n if (isExact(next)) return true\n if (next.kind === 'list') stack.push(next.element)\n else if (next.kind === 'union') stack.push(...next.types)\n else if (next.kind === 'map') {\n stack.push(...Object.values(next.fields))\n if (next.rest !== undefined) stack.push(next.rest)\n }\n }\n return false\n}\n\n/** Reads compile(), check(), and the runtime's load() options: only `expect`, which must be a type built with t. Returns a frozen copy of it. */\nexport function expectOption(options: unknown, what: 'Compile' | 'Load'): Type | undefined {\n if (options === undefined) return undefined\n if (typeof options !== 'object' || options === null || Array.isArray(options))\n throw new TypeError(`${what} options must be an object`)\n let expect: unknown\n try {\n for (const key of Object.keys(options)) {\n if (key !== 'expect')\n throw new TypeError(\n `Unknown ${what.toLowerCase()} option key \"${key}\" (expected one of: expect)`,\n )\n }\n expect = (options as { expect?: unknown }).expect\n } catch (error) {\n if (error instanceof TypeError) throw error\n throw new TypeError(`${what} options could not be read`, { cause: error })\n }\n if (expect === undefined) return undefined\n // Checked, copied, and the copy checked: the program and its cache key use\n // one private copy, so no getter, toJSON, or later change can split them.\n assertType(expect, `${what} option \"expect\"`)\n const own = typeData(expect)\n if (own !== expect) assertType(own, `${what} option \"expect\"`)\n return own\n}\n\nfunction createEnvironment<Ctx>(settings: Settings): Environment<Ctx> {\n const checkEnv: CheckEnv = {\n variables: settings.variables,\n strict: settings.strict,\n lookup: (name) => settings.host.get(name) ?? BUILTINS.get(name),\n *functionNames() {\n yield* settings.host.keys()\n for (const name of BUILTINS.keys()) if (!settings.host.has(name)) yield name\n },\n }\n // Each entry records the source and expected type it was compiled for: a key\n // alone could collide (a comment may end a source with what looks like a type).\n const cache = new ProgramCache<{\n readonly program: Program<Ctx>\n readonly source: string\n readonly expect: string | undefined\n }>(settings.cacheSize)\n\n function parseSource(source: string): Node {\n if (typeof source !== 'string') throw new TypeError('An expression must be a string')\n return parse(source, settings.parseLimits)\n }\n\n function analyzeSource(source: string, expect: Type | undefined): Analysis {\n const root = parseSource(source)\n try {\n return analyze(root, checkEnv, { expected: expect })\n } catch (error) {\n // The checker has no source text; attach it so the error can show where.\n if (error instanceof BonsaiLimitError && error.source === undefined) {\n throw new BonsaiLimitError(error.code, error.message, {\n source,\n ...(error.span === undefined ? {} : { span: error.span }),\n limit: error.limit ?? null,\n })\n }\n throw error\n }\n }\n\n function makeProgram<R>(source: string, analysis: Analysis, expect?: Type): Program<Ctx, R>\n function makeProgram(\n source: string,\n analysis: Analysis,\n expect: Type | undefined,\n constants: ReadonlyMap<string, unknown>,\n residualOf: ResidualOf,\n ): ResidualRunner\n function makeProgram<R>(\n source: string,\n analysis: Analysis,\n expect?: Type,\n constants?: ReadonlyMap<string, unknown>,\n residualOf?: ResidualOf,\n ): Program<Ctx, R> | ResidualRunner {\n // Residuals are not warmed: partial() runs often, and a zone it writes into\n // the residual as text loads on first use (the shared data is already loaded).\n if (residualOf === undefined) warmZones(analysis)\n // When the checker cannot prove the result matches `expect` (part of it is\n // `any`, or an open object may hold an unlisted key), it is checked at run\n // time, so the declared result type holds.\n const guard = expect !== undefined && analysis.checkResult ? expect : undefined\n const { prepare, checked, settle, runSync, runAsync, compileOptions } = programRunner<R>({\n source,\n analysis,\n guard,\n settings,\n constants,\n residualOf,\n })\n\n const features = settings.features?.program<Ctx, R>({\n source,\n analysis,\n expect,\n settings,\n checkEnv,\n compileOptions,\n residualOf,\n prepare,\n checked,\n settle,\n residual: (residualAnalysis, residualConstants, residualFrom) =>\n makeProgram(source, residualAnalysis, expect, residualConstants, residualFrom),\n })\n\n if (residualOf !== undefined) {\n // Only partial() makes residuals, so the features that made this one are present.\n const made = features as ProgramFeatures<Ctx, R>\n // A residual is used only through its runner, so no Program is built for\n // it (freezing its type and references would be work on every partial());\n // its tree is the result's `ast`, and compiles lazily, so it is frozen.\n deepFreeze(analysis.root)\n const runner: ResidualRunner = {\n async: analysis.async,\n runSync: (ctx, options) => runSync(ctx, options as EvaluateOptions | undefined),\n runAsync: (ctx, options) => runAsync(ctx, options as EvaluateOptions | undefined),\n explainSync: (ctx, options) => made.explainSync(ctx, options as ExplainOptions | undefined),\n explainAsync: (ctx, options) =>\n made.explainAsync(ctx, options as ExplainOptions | undefined),\n }\n return runner\n }\n // Programs are shared: the tree compiles lazily, so it must not change afterwards.\n // A lite program is typed as a LiteProgram by its entry point.\n const program = Object.freeze({\n // Without features (bonsai-js/lite), a program has no explain or partial.\n ...(features === undefined\n ? {}\n : {\n explain: (...args: ExplainArgs<Ctx>) => features.explainAsync(args[0], args[1]),\n explainSync: (...args: ExplainArgs<Ctx>) => features.explainSync(args[0], args[1]),\n partial: (known: object, options?: PartialOptions) =>\n features.partial(program, known, options),\n }),\n source,\n ast: deepFreeze(analysis.root),\n type: deepFreeze(analysis.type),\n async: analysis.async,\n warnings: deepFreeze(\n locate(\n source,\n analysis.diagnostics.filter((d) => d.severity === 'warning'),\n ),\n ),\n references: deepFreeze(analysis.references),\n evaluate: (...args: Args<Ctx>) => runAsync(args[0], args[1]),\n evaluateSync: (...args: Args<Ctx>) => runSync(args[0], args[1]),\n })\n recordDeclared(program, settings.variables)\n recordCompiled(program, {\n source,\n analysis,\n expect,\n lookup: checkEnv.lookup,\n variables: settings.variables,\n })\n return program as Program<Ctx, R>\n }\n\n function compile<R>(source: string, expect: Type | undefined): Program<Ctx, R> {\n const analysis = analyzeSource(source, expect)\n const errors = analysis.diagnostics.filter((d) => d.severity === 'error')\n if (errors.length > 0) throw new BonsaiCheckError(source, errors)\n return makeProgram<R>(source, analysis, expect)\n }\n\n /**\n * compile(), through the program cache: programs are immutable, so the same\n * source with an equal expected type shares one. `expect` is expectOption's\n * plain copy, so its JSON is the type the program is checked against.\n */\n function cachedCompile<R>(source: string, expect: Type | undefined): Program<Ctx, R> {\n if (typeof source !== 'string') throw new TypeError('An expression must be a string')\n // An exact map type (a checked program's inferred type passed back as\n // `expect`) checks differently from a declared one with the same JSON.\n if (expect !== undefined && holdsExact(expect)) return compile<R>(source, expect)\n const expectKey = expect === undefined ? undefined : JSON.stringify(expect)\n const key = expectKey === undefined ? source : `${source}\\u0000${expectKey}`\n const entry = cache.get(key)\n if (entry !== undefined && entry.source === source && entry.expect === expectKey)\n return entry.program as Program<Ctx, R>\n const program = compile<R>(source, expect)\n cache.set(key, { program, source, expect: expectKey })\n return program\n }\n\n function cached(source: string): Program<Ctx> {\n return cachedCompile(source, undefined)\n }\n\n function info(def: FunctionDef): FunctionInfo {\n return {\n name: def.name,\n description: def.description,\n host: def.host === true,\n async: def.async === true,\n call: def.call === true,\n ...(def.cost === undefined ? {} : { cost: def.cost }),\n signatures: def.overloads.map((o) => ({\n params: o.params,\n required: o.required ?? o.params.length,\n returns: o.result,\n ...(o.rest === undefined ? {} : { rest: o.rest }),\n })),\n }\n }\n\n // A lite environment is typed as a LiteEnvironment by its entry point.\n const env = Object.freeze({\n variables: settings.variables,\n strict: settings.strict,\n parse: parseSource,\n check<E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ): CheckResult<Ctx, Infer<E>> {\n const expect = expectOption(options, 'Compile')\n let analysis: Analysis\n try {\n analysis = analyzeSource(source, expect)\n } catch (error) {\n if (!(error instanceof BonsaiError)) throw error\n const diagnostics = locate(source, [\n {\n code: error.code === 'SYNTAX' ? 'SYNTAX' : 'LIMIT',\n message: error.message,\n severity: 'error',\n start: error.span?.start ?? 0,\n end: error.span?.end ?? source.length,\n },\n ])\n return Object.freeze(new CheckFailure(undefined, diagnostics, undefined)) as CheckResult<\n Ctx,\n Infer<E>\n >\n }\n const diagnostics =\n analysis.diagnostics.length === 0 ? NO_DIAGNOSTICS : locate(source, analysis.diagnostics)\n const type = deepFreeze(analysis.type)\n if (analysis.diagnostics.some((d) => d.severity === 'error'))\n return Object.freeze(new CheckFailure(type, diagnostics, analysis)) as CheckResult<\n Ctx,\n Infer<E>\n >\n let program: Program<Ctx, Infer<E>> | undefined\n return Object.freeze(\n new CheckSuccess(\n type,\n diagnostics,\n analysis,\n () => (program ??= makeProgram<Infer<E>>(source, analysis, expect)),\n ),\n ) as CheckResult<Ctx, Infer<E>>\n },\n compile: <E extends Type = AnyType>(source: string, options?: CompileOptions<E>) =>\n cachedCompile<Infer<E>>(source, expectOption(options, 'Compile')),\n // Without features (bonsai-js/lite), an environment has no partial or explain.\n ...(settings.features === undefined\n ? {}\n : {\n partial: <R>(\n source: string,\n known: PartialData<Ctx> & object,\n options?: PartialOptions,\n ) => cached(source).partial(known, options) as PartialResult<Ctx, R>,\n }),\n evaluate<R>(source: string, ...args: Args<Ctx>): Promise<R> {\n try {\n return cached(source).evaluate(...args) as Promise<R>\n } catch (error) {\n return Promise.reject(error instanceof Error ? error : new Error(String(error)))\n }\n },\n evaluateSync: <R>(source: string, ...args: Args<Ctx>) =>\n cached(source).evaluateSync(...args) as R,\n ...(settings.features === undefined\n ? {}\n : {\n explain<R>(source: string, ...args: ExplainArgs<Ctx>): Promise<Explanation<R>> {\n try {\n return cached(source).explain(...args) as Promise<Explanation<R>>\n } catch (error) {\n return Promise.reject(error instanceof Error ? error : new Error(String(error)))\n }\n },\n explainSync: <R>(source: string, ...args: ExplainArgs<Ctx>) =>\n cached(source).explainSync(...args) as Explanation<R>,\n }),\n describeFunction(name: string): FunctionInfo | undefined {\n const def = checkEnv.lookup(name)\n return def === undefined ? undefined : info(def)\n },\n listFunctions(): FunctionInfo[] {\n return [...checkEnv.functionNames()].map((name) => info(checkEnv.lookup(name) as FunctionDef))\n },\n extend<\n V2 extends Readonly<Record<string, Type>>,\n L2 extends readonly Library[],\n S2 extends boolean | undefined,\n >(options: EnvironmentOptions<V2, L2> & { readonly strict?: S2 }) {\n return createEnvironment<Extended<Ctx, V2, L2, S2>>(settingsFrom(settings, options))\n },\n })\n internals.set(env, { checkEnv, parseLimits: settings.parseLimits })\n return env as Environment<Ctx>\n}\n\n/**\n * An environment from `bonsai()` options, with the features its entry point\n * provides (not public API): `bonsai-js` passes explaining and partial\n * evaluation, `bonsai-js/lite` passes none.\n */\nexport function environmentWith<Ctx>(\n features: Features | undefined,\n options: unknown,\n): Environment<Ctx> {\n return createEnvironment<Ctx>({ ...settingsFrom(undefined, options), features })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"environment.mjs","names":[],"sources":["../src/environment.ts"],"sourcesContent":["import { analyze, type Analysis, type CheckEnv } from './check/checker.js'\nimport type { CompileOptions as CompilerOptions } from './compile/compiler.js'\nimport { raised } from './runtime/attribution.js'\nimport {\n BonsaiCheckError,\n BonsaiError,\n BonsaiLimitError,\n BonsaiRuntimeError,\n locate,\n type Diagnostic,\n} from './errors.js'\nimport { BUILTINS } from './functions/builtins.js'\nimport { descriptionOf } from './functions/descriptions.js'\nimport {\n assertHostSpec,\n assertType,\n builtinsOption,\n isRecord,\n overload,\n type FunctionDef,\n} from './functions/define.js'\nimport type { PartialData, PartialOptions, PartialResult } from './partial.js'\nimport {\n DEFAULT_RUNTIME_LIMITS,\n MAX_VALUE_DEPTH_LIMIT,\n type State,\n type RuntimeLimits,\n} from './runtime/state.js'\nimport { recordCompiled, recordDeclared } from './declared.js'\nimport {\n assertKeys,\n numberOption,\n programRunner,\n timeoutOption,\n type EvaluationLimits,\n warmZones,\n} from './program.js'\nimport type { ExplanationJSON, Trace } from './runtime/trace.js'\nimport type { Node } from './syntax/ast.js'\nimport { isName } from './syntax/lexer.js'\nimport { DEFAULT_PARSE_LIMITS, MAX_DEPTH_LIMIT, parse, type ParseLimits } from './syntax/parser.js'\nimport {\n isExact,\n isNullable,\n typeData,\n type AnyType,\n type Infer,\n type InferVariables,\n type Type,\n} from './types.js'\n\n// === public option types ===\n\n/** Every field optional, and `undefined` accepted, even under `exactOptionalPropertyTypes`. */\ntype Optional<T> = { readonly [K in keyof T]?: T[K] | undefined }\n\nexport interface Limits extends Optional<ParseLimits>, Optional<RuntimeLimits> {\n /** Wall-clock budget per evaluation in milliseconds (0 = none; fractions allowed). Default 0. */\n readonly timeout?: number | undefined\n}\n\n/**\n * The part of an `AbortSignal` evaluation reads. A standard `AbortSignal`\n * satisfies it; declaring it structurally keeps the types free of DOM or Node\n * library requirements.\n */\nexport interface AbortSignalLike {\n readonly aborted: boolean\n readonly reason?: unknown\n addEventListener: (type: 'abort', listener: () => void, options?: { once?: boolean }) => void\n removeEventListener: (type: 'abort', listener: () => void) => void\n}\n\nexport interface EvaluateOptions {\n /** Wall-clock budget for this evaluation in milliseconds (0 = none; fractions allowed). */\n readonly timeout?: number | undefined\n /** Step budget for this evaluation (0 = none; overrides the environment limit). */\n readonly maxSteps?: number | undefined\n /** Cancels the evaluation. */\n readonly signal?: AbortSignalLike | undefined\n /** The time now() returns in this evaluation, instead of the environment's clock. */\n readonly now?: Date | undefined\n /**\n * Validates the context for this evaluation (true) or not (false), instead\n * of the environment's `validateContext`; for a context already validated upstream.\n */\n readonly validateContext?: boolean | undefined\n}\n\ntype InferParams<P extends readonly Type[]> = Extract<\n { -readonly [K in keyof P]: Infer<P[K]> },\n unknown[]\n>\n\n/** The arguments `run` receives: the declared parameters, then any rest arguments. */\ntype RunArgs<P extends readonly Type[], RT> = RT extends Type\n ? [...InferParams<P>, ...Infer<RT>[]]\n : InferParams<P>\n\n/** What `run` may return: a nullable result also accepts `undefined`, which reads as null. */\ntype RunResult<R extends Type> = null extends Infer<R> ? Infer<R> | undefined : Infer<R>\n\n/**\n * The standard `AbortSignal` when the consumer's types declare one (DOM or\n * Node), so it can be passed to `fetch`; otherwise {@link AbortSignalLike}.\n */\ntype HostAbortSignal = typeof globalThis extends { AbortSignal: { prototype: infer S } }\n ? S\n : AbortSignalLike\n\n/** What a host function declared with `call: true` receives before its arguments. */\nexport interface HostCall<Ctx = Readonly<Record<string, unknown>>> {\n /** The evaluation context, read-only. */\n readonly context: Readonly<Ctx>\n /**\n * Aborted when the evaluation times out or is cancelled while it waits on\n * this call, so an async function can stop its own work (pass it to `fetch`).\n */\n readonly signal: HostAbortSignal\n}\n\n/** A host function declaration. Create one with {@link fn}. */\nexport interface HostFunction {\n readonly params: readonly Type[]\n readonly returns: Type\n readonly required?: number | undefined\n readonly rest?: Type | undefined\n readonly async?: boolean | undefined\n /** Whether `run` receives a {@link HostCall} before its arguments. */\n readonly call?: boolean | undefined\n readonly description?: string | undefined\n /** Steps charged per call (default 32). */\n readonly cost?: number | undefined\n readonly run: (...args: never[]) => unknown\n}\n\n/** The declaration passed to {@link fn}, without `run`, `async`, and `call`. */\nexport interface FnSpec<\n P extends readonly Type[] = readonly Type[],\n R extends Type = Type,\n RT extends Type | undefined = Type | undefined,\n> {\n /** Parameter types, in order. */\n readonly params: P\n /** Declared result type; results are checked against its kind. */\n readonly returns: R\n /** Number of required leading parameters (default: all). Missing optional arguments arrive as null. */\n readonly required?: number | undefined\n /** Type of further variadic arguments. */\n readonly rest?: RT\n readonly description?: string | undefined\n /**\n * Steps charged per call (default 32), so one evaluation makes at most\n * maxSteps / cost calls. Lower it for cheap pure functions; raise it for\n * expensive ones.\n */\n readonly cost?: number | undefined\n}\n\n/**\n * Declares a host function.\n *\n * ```ts\n * fn({ params: [t.string()], returns: t.boolean(), run: (perm) => user.can(perm) })\n * fn({ params: [t.string()], returns: t.string(), async: true, run: async (id) => lookup(id) })\n * fn({ params: [t.string()], returns: t.number(), async: true, call: true,\n * run: async (call, id) => (await fetch(`/stock/${id}`, { signal: call.signal })).json() })\n * ```\n */\nexport function fn<\n const P extends readonly Type[],\n R extends Type,\n RT extends Type | undefined = undefined,\n>(\n spec: FnSpec<P, R, RT> &\n (\n | {\n readonly async?: false | undefined\n readonly call?: false | undefined\n readonly run: (...args: RunArgs<P, RT>) => RunResult<R>\n }\n | {\n readonly async: true\n readonly call?: false | undefined\n readonly run: (...args: RunArgs<P, RT>) => Promise<RunResult<R>>\n }\n | {\n readonly async?: false | undefined\n readonly call: true\n readonly run: (call: HostCall, ...args: RunArgs<P, RT>) => RunResult<R>\n }\n | {\n readonly async: true\n readonly call: true\n readonly run: (call: HostCall, ...args: RunArgs<P, RT>) => Promise<RunResult<R>>\n }\n ),\n): HostFunction {\n assertHostSpec(spec, 'fn()')\n return Object.freeze({ ...spec })\n}\n\n/**\n * Declares host functions that read a typed evaluation context: like `fn`\n * with `call: true`, with `call.context` typed as `Readonly<Ctx>`.\n *\n * ```ts\n * const contextFn = withContext<{ user: { id: string } }>()\n * const functions = { userId: contextFn({ params: [], returns: t.string(), run: (call) => call.context.user.id }) }\n * ```\n */\nexport function withContext<Ctx>() {\n return <const P extends readonly Type[], R extends Type, RT extends Type | undefined = undefined>(\n spec: FnSpec<P, R, RT> &\n (\n | {\n readonly async?: false | undefined\n /** Implied; may be written out when moving a function over from `fn`. */\n readonly call?: true | undefined\n readonly run: (call: HostCall<Ctx>, ...args: RunArgs<P, RT>) => RunResult<R>\n }\n | {\n readonly async: true\n readonly call?: true | undefined\n readonly run: (call: HostCall<Ctx>, ...args: RunArgs<P, RT>) => Promise<RunResult<R>>\n }\n ),\n ): HostFunction => {\n assertHostSpec(spec, 'withContext()')\n return Object.freeze({ ...spec, call: true })\n }\n}\n\n/**\n * A reusable bundle of host functions (and optionally variables). Declare one\n * with `satisfies Library` (not `: Library`) so its variables keep their types\n * and become part of the context type of every environment that uses it.\n */\nexport interface Library<\n V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>,\n> {\n readonly name: string\n readonly functions?: Readonly<Record<string, HostFunction>> | undefined\n readonly variables?: V | undefined\n}\n\n/** No declared variables: the empty record, which adds nothing to a context type. */\n// oxlint-disable-next-line typescript/no-generated-empty-object-type -- intentionally empty\nexport type NoVariables = Record<never, never>\n\n/**\n * The variables one library declares. A library annotated `: Library` has the\n * wide default (any string key), which names no variable, so it adds nothing.\n */\ntype VariablesOf<Lib> = Lib extends { readonly variables?: infer V }\n ? string extends keyof NonNullable<V>\n ? NoVariables\n : NonNullable<V>\n : NoVariables\n\n/**\n * The variables a list of libraries declares, merged. Only a list whose\n * elements are known (written inline, or `as const`) contributes; a value\n * typed as `Library[]` adds nothing to the context type.\n */\ntype LibraryVariables<L> = L extends readonly [infer First, ...infer Rest]\n ? VariablesOf<First> & LibraryVariables<Rest>\n : NoVariables\n\n/**\n * Any further keys, as a context with `strict: false` may carry. Its values are\n * `any` because only that lets an interface or a class instance (which have no\n * index signature) still satisfy it; declared keys keep their own types.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the only index signature interfaces and classes satisfy\ntype MoreKeys = Readonly<Record<string, any>>\n\n/**\n * The context type of an environment with variables `V` and libraries `L`.\n * With `strict: false` (`S`), undeclared variables are allowed, so the context\n * may carry keys beyond the declared ones.\n */\nexport type ContextFor<V, L, S = undefined> = [S] extends [false]\n ? ContextOf<V & LibraryVariables<L>> & MoreKeys\n : ContextOf<V & LibraryVariables<L>>\n\n/**\n * `Ctx` with the variables `New` declares replacing those of the same name,\n * as extend() replaces them at run time (\"later wins\"). Remapping keys (not\n * Omit) keeps an index signature and the other keys' types.\n */\ntype Override<Ctx, New> = [keyof New] extends [never]\n ? Ctx\n : { [K in keyof Ctx as K extends keyof New ? never : K]: Ctx[K] } & New\n\n/** The context type extend() produces: re-declared variables replaced, strict: false loosened. */\nexport type Extended<Ctx, V, L, S> = [S] extends [false]\n ? Override<Ctx, ContextOf<V & LibraryVariables<L>>> & MoreKeys\n : Override<Ctx, ContextOf<V & LibraryVariables<L>>>\n\nexport interface EnvironmentOptions<\n V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>,\n L extends readonly Library[] = readonly Library[],\n> {\n /** Declared context variables and their types. */\n readonly variables?: V | undefined\n /**\n * Report unknown variables as errors. Default: true when `variables` is\n * declared (even as `{}`), false for an open environment.\n */\n readonly strict?: boolean | undefined\n /** Host functions by name. A host function replaces a built-in of the same name. */\n readonly functions?: Readonly<Record<string, HostFunction>> | undefined\n /** Libraries of host functions and variables; a name defined twice is an error. */\n readonly libraries?: L | undefined\n readonly limits?: Limits | undefined\n /**\n * Compiled programs kept for `evaluate(source)` and `evaluateSync(source)`. Default 256; 0\n * disables the cache. The cache also holds at most 256K characters of source in total, and\n * never a source longer than 16K characters.\n */\n readonly cacheSize?: number | undefined\n /** Source of now(). Default: the system clock. */\n readonly clock?: (() => Date) | undefined\n /**\n * Check the context against the declared variable types before every\n * evaluation (deep, proportional to the data) and fail with INVALID_CONTEXT\n * naming the first mismatching path. Default: false.\n */\n readonly validateContext?: boolean | undefined\n /**\n * The built-ins available, from bonsai-js/builtins. Default: all of them\n * (none in bonsai-js/runtime/core). Calling one left out is a CHECK error, so\n * an environment that checks rules for bonsai-js/runtime/core passes the same\n * list as the runtime. On extend(), these are added to the base's.\n */\n readonly builtins?: readonly Builtin[] | undefined\n}\n\ndeclare const builtinBrand: unique symbol\n\n/** A built-in function from bonsai-js/builtins, for the `builtins` option. */\nexport interface Builtin {\n readonly name: string\n readonly [builtinBrand]: true\n}\n\n/**\n * What check() found. Only `ok`, `type`, and `diagnostics` are data (and the\n * JSON form); `program`, `ast`, and `typeOf` are accessors computed on demand,\n * so spreading the result or `structuredClone` does not copy them.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Program\nexport type CheckResult<Ctx = any, R = unknown> = Checked<Program<Ctx, R>>\n\n/** What check() found, over the kind of program it compiles. */\ntype Checked<P> = (\n | {\n readonly ok: true\n readonly type: Type\n readonly diagnostics: readonly Diagnostic[]\n /**\n * The checked expression, compiled when first read: a program equal to\n * the one compile() returns, without parsing and checking again.\n */\n readonly program: P\n }\n | {\n readonly ok: false\n readonly type: Type | undefined\n readonly diagnostics: readonly Diagnostic[]\n }\n) & {\n /**\n * The checked syntax tree (implicit lambdas made explicit, as `program.ast`),\n * or undefined when the source did not parse.\n */\n readonly ast: Node | undefined\n /** The inferred type of a node of `ast`, or undefined for another tree's node. */\n readonly typeOf: (node: Node) => Type | undefined\n}\n\nexport type Args<Ctx> =\n Record<string, never> extends Ctx\n ? [context?: Ctx, options?: EvaluateOptions]\n : [context: Ctx, options?: EvaluateOptions]\n\n/**\n * A checked, compiled expression. Immutable and safe to share. A bare\n * `Program` accepts any context, so a program typed for one context can be\n * stored as a `Program`.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context type every typed program is assignable to\nexport interface Program<Ctx = any, R = unknown> {\n readonly source: string\n /** The syntax tree after implicit lambdas are made explicit. */\n readonly ast: Node\n /** The statically inferred result type. */\n readonly type: Type\n /** Whether the expression calls an async host function (evaluateSync rejects it). */\n readonly async: boolean\n /** Non-fatal findings from the checker. */\n readonly warnings: readonly Diagnostic[]\n readonly references: {\n readonly variables: readonly string[]\n readonly functions: readonly string[]\n }\n evaluate: (...args: Args<Ctx>) => Promise<R>\n evaluateSync: (...args: Args<Ctx>) => R\n /**\n * Evaluates and records the value of every sub-expression, so you can show\n * why the result came out as it did. Never rejects for evaluation errors:\n * they are returned in the explanation. Slower than evaluate.\n */\n explain: (...args: ExplainArgs<Ctx>) => Promise<Explanation<R>>\n /** Like explain, synchronously; an expression that calls an async host function is an ASYNC_IN_SYNC error. */\n explainSync: (...args: ExplainArgs<Ctx>) => Explanation<R>\n /**\n * Evaluates what can be evaluated from partial data. Returns the value when\n * the known data decides it, or a simplified residual expression (source and\n * syntax tree) that needs only the unknown data. Evaluating the residual with\n * the full data gives the same result as evaluating this program.\n */\n partial: (known: PartialData<Ctx> & object, options?: PartialOptions) => PartialResult<Ctx, R>\n}\n\nexport interface ExplainOptions extends EvaluateOptions {\n /** How many lambda runs to record per call (the rest are counted). Default 20. */\n readonly maxIterations?: number | undefined\n /** Sub-expressions to record in total before stopping (the result stays exact). Default 10,000. */\n readonly maxTraceNodes?: number | undefined\n /**\n * Also evaluate the side of && and || that short-circuiting would skip, so\n * reasons() lists every failing condition rather than the first. Those\n * parts are marked `extra`; their evaluation errors are ignored and the\n * result is unchanged. They spend from the same step and time budget, so a\n * limit reached there (STEP_LIMIT, TIMEOUT, a size limit) fails the\n * explanation even when evaluation alone would stay inside it. Host\n * functions on those parts do run. Default false.\n */\n readonly exhaustive?: boolean | undefined\n}\n\ntype ExplainArgs<Ctx> =\n Record<string, never> extends Ctx\n ? [context?: Ctx, options?: ExplainOptions]\n : [context: Ctx, options?: ExplainOptions]\n\n/** The result of explain(): the outcome plus a trace of every sub-expression. */\nexport type Explanation<R = unknown> = (\n | { readonly ok: true; readonly value: R }\n | { readonly ok: false; readonly error: BonsaiError }\n) & {\n /** The root of the trace; its `value` is the result. Values are live references. */\n readonly trace: Trace\n /** True when maxTraceNodes stopped recording before evaluation finished. */\n readonly truncated: boolean\n /**\n * The conditions that decided the result, following &&, ||, and ! down to\n * the comparisons and values (or the error) behind it.\n */\n reasons: () => readonly Trace[]\n /** The trace as an indented, human-readable tree. */\n toString: () => string\n /**\n * A JSON-safe snapshot: values are bounded copies (cycles, bigints, dates,\n * and durations handled) and getters are never run. `JSON.stringify` uses it.\n */\n toJSON: () => ExplanationJSON\n}\n\nexport interface CompileOptions<E extends Type = Type> {\n /** The type the expression must produce; also types the program's result. */\n readonly expect?: E | undefined\n}\n\nexport interface FunctionInfo {\n readonly name: string\n readonly description: string\n readonly host: boolean\n readonly async: boolean\n /** Whether a host function receives a {@link HostCall} (declared `call: true`). */\n readonly call: boolean\n /** Steps charged per call, when the host function declares a `cost` (otherwise 32). */\n readonly cost?: number\n readonly signatures: readonly {\n readonly params: readonly Type[]\n readonly required: number\n readonly rest?: Type\n readonly returns: Type\n }[]\n}\n\n/** An environment. A bare `Environment` accepts any context, like a bare `Program`. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context type every typed environment is assignable to\nexport interface Environment<Ctx = any> {\n /** Declared variables, or undefined for an open environment. */\n readonly variables: Readonly<Record<string, Type>> | undefined\n readonly strict: boolean\n /** Parses without checking. */\n parse: (source: string) => Node\n /**\n * Parses and checks, reporting every finding instead of throwing. When `ok`,\n * `program` is the compiled expression, so an editor needs only this call.\n */\n check: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => CheckResult<Ctx, Infer<E>>\n /**\n * Parses, checks, and compiles. Throws BonsaiSyntaxError / BonsaiCheckError /\n * BonsaiLimitError. Cached: the same source and expected type return the same program.\n */\n compile: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => Program<Ctx, Infer<E>>\n /**\n * Compiles (cached) and partially evaluates; see Program.partial. Throws for\n * syntax, check, and limit errors, and a TypeError or RangeError for invalid options.\n * `R` is an unchecked type assertion, as for evaluate().\n */\n partial: <R = unknown>(\n source: string,\n known: PartialData<Ctx> & object,\n options?: PartialOptions,\n ) => PartialResult<Ctx, R>\n /**\n * Compiles (cached) and evaluates asynchronously. `R` is an unchecked type\n * assertion on the result, like a cast; for a checked result type, use\n * `compile(source, { expect })` and evaluate the program.\n */\n evaluate: <R = unknown>(source: string, ...args: Args<Ctx>) => Promise<R>\n /**\n * Compiles (cached) and evaluates synchronously. `R` is an unchecked type\n * assertion, as for evaluate().\n */\n evaluateSync: <R = unknown>(source: string, ...args: Args<Ctx>) => R\n /**\n * Compiles (cached) and explains; see Program.explain. Rejects for syntax and\n * check errors and parse limits, and with a TypeError or RangeError for invalid options.\n * `R` is an unchecked type assertion, as for evaluate().\n */\n explain: <R = unknown>(source: string, ...args: ExplainArgs<Ctx>) => Promise<Explanation<R>>\n /**\n * Compiles (cached) and explains synchronously. Throws for syntax and check\n * errors and parse limits, and a TypeError or RangeError for invalid options.\n * `R` is an unchecked type assertion, as for evaluate().\n */\n explainSync: <R = unknown>(source: string, ...args: ExplainArgs<Ctx>) => Explanation<R>\n /** Looks up a function (host or built-in). */\n describeFunction: (name: string) => FunctionInfo | undefined\n /** Every callable function, host functions first. */\n listFunctions: () => readonly FunctionInfo[]\n /**\n * A new environment with more variables, functions, or libraries. A\n * variable or function declared again replaces the earlier one.\n */\n extend: <\n const V2 extends Readonly<Record<string, Type>> = NoVariables,\n const L2 extends readonly Library[] = [],\n const S2 extends boolean | undefined = undefined,\n >(\n options: EnvironmentOptions<V2, L2> & { readonly strict?: S2 },\n ) => Environment<Extended<Ctx, V2, L2, S2>>\n}\n\n/**\n * A program from `bonsai-js/lite`: a {@link Program} without `explain`,\n * `explainSync`, and `partial`. Every Program is also a LiteProgram, so code\n * that only evaluates can accept either.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Program\nexport type LiteProgram<Ctx = any, R = unknown> = Omit<\n Program<Ctx, R>,\n 'explain' | 'explainSync' | 'partial'\n>\n\n/** What a `bonsai-js/lite` environment's check() found. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Program\nexport type LiteCheckResult<Ctx = any, R = unknown> = Checked<LiteProgram<Ctx, R>>\n\n/**\n * An environment from `bonsai-js/lite`: an {@link Environment} without\n * `explain`, `explainSync`, and `partial`, whose programs are LitePrograms.\n * Every Environment is also a LiteEnvironment.\n */\n// oxlint-disable-next-line typescript/no-explicit-any -- the same erased context as a bare Environment\nexport interface LiteEnvironment<Ctx = any> extends Omit<\n Environment<Ctx>,\n 'check' | 'compile' | 'extend' | 'explain' | 'explainSync' | 'partial'\n> {\n /** See Environment.check. */\n check: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => LiteCheckResult<Ctx, Infer<E>>\n /** See Environment.compile. */\n compile: <E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ) => LiteProgram<Ctx, Infer<E>>\n /** See Environment.extend. */\n extend: <\n const V2 extends Readonly<Record<string, Type>> = NoVariables,\n const L2 extends readonly Library[] = [],\n const S2 extends boolean | undefined = undefined,\n >(\n options: EnvironmentOptions<V2, L2> & { readonly strict?: S2 },\n ) => LiteEnvironment<Extended<Ctx, V2, L2, S2>>\n}\n\n/**\n * The context type of declared variables `V`. An open environment (no\n * variables) accepts any object, including values typed by an interface, and\n * so does a schema only known at run time (`Record<string, Type>`), whose\n * variables are not known statically.\n */\ntype ContextOf<V> = [keyof V] extends [never]\n ? object\n : string extends keyof V\n ? MoreKeys\n : InferVariables<V>\n\n// === implementation ===\n\n/** Internals shared with the language service (not public API). */\nexport interface EnvironmentInternals {\n readonly checkEnv: CheckEnv\n readonly parseLimits: ParseLimits\n}\n\nconst internals = new WeakMap<object, EnvironmentInternals>()\n\nexport function internalsOf(env: object): EnvironmentInternals {\n const found = internals.get(env)\n if (found === undefined) throw new TypeError('Not a Bonsai environment')\n return found\n}\n\n/** Compiled programs kept per environment unless `cacheSize` says otherwise. */\nconst DEFAULT_CACHE_SIZE = 256\n\n/** An environment's settings (not public API). */\nexport interface Settings {\n readonly variables: Readonly<Record<string, Type>> | undefined\n /** The explicit `strict` option, if one was given (here or in a base environment). */\n readonly strictOption: boolean | undefined\n readonly strict: boolean\n readonly host: ReadonlyMap<string, FunctionDef>\n /** The built-ins available (host functions of the same name replace them). */\n readonly builtins: ReadonlyMap<string, FunctionDef>\n readonly parseLimits: ParseLimits\n readonly runtimeLimits: RuntimeLimits\n readonly timeout: number\n readonly cacheSize: number\n readonly clock: () => Date\n readonly validateContext: boolean\n /** Explaining and partial evaluation, when the entry point provides them. */\n readonly features: Features | undefined\n}\n\n/**\n * What a program shares with the features that extend it (not public API):\n * its source, analysis, and settings, and how it prepares, checks, and settles\n * an evaluation.\n */\nexport interface ProgramCore<R> {\n readonly source: string\n readonly analysis: Analysis\n readonly expect: Type | undefined\n readonly settings: Settings\n readonly checkEnv: CheckEnv\n readonly compileOptions: CompilerOptions\n /** Set when the program is a partial-evaluation residual. */\n readonly residualOf: ResidualOf | undefined\n prepare: (\n state: State,\n ctx: Record<string, unknown>,\n limits: EvaluationLimits,\n locals: number,\n ) => void\n checked: (result: unknown, state: State) => void\n settle: (result: R) => R\n /** Compiles a residual of this program and returns how it runs. */\n residual: (\n analysis: Analysis,\n constants: ReadonlyMap<string, unknown>,\n residualOf: ResidualOf,\n ) => ResidualRunner\n}\n\n/** The program methods a feature set provides (not public API). */\nexport interface ProgramFeatures<Ctx, R> {\n explainSync: (\n context: unknown,\n options: ExplainOptions | undefined,\n promised?: boolean,\n ) => Explanation<R>\n explainAsync: (context: unknown, options: ExplainOptions | undefined) => Promise<Explanation<R>>\n partial: (program: object, known: object, options: unknown) => PartialResult<Ctx, R>\n}\n\n/**\n * Explaining and partial evaluation, supplied by the entry point (not public\n * API): kept out of this module so a bundle that only checks and evaluates\n * does not carry them.\n */\nexport interface Features {\n program: <Ctx, R>(core: ProgramCore<R>) => ProgramFeatures<Ctx, R>\n}\n\n/** What a residual keeps from its partial evaluation. */\nexport interface ResidualOf {\n /** Its printed source: the text of its explanations. */\n readonly source: string\n /** Whether `call: true` host functions read the context it is given. */\n readonly readsContext: boolean\n /** The variables known to partial(), which a context read whole must hold. */\n readonly knownRoots: readonly string[]\n /** The context paths it reads: all a context it does not read whole needs. */\n readonly dependsOn: readonly string[]\n /** Size and depth of the lists and maps partial() built into its bindings. */\n readonly shapes: WeakMap<object, number> | undefined\n}\n\n/** How a partial-evaluation residual runs and explains, with the caller's context. */\nexport interface ResidualRunner {\n /** Whether the residual calls an async host function. */\n async: boolean\n runSync: (ctx: unknown, options: unknown) => unknown\n runAsync: (ctx: unknown, options: unknown) => Promise<unknown>\n explainSync: (ctx: unknown, options: unknown) => Explanation\n explainAsync: (ctx: unknown, options: unknown) => Promise<Explanation>\n}\n\nfunction toDef(name: string, host: HostFunction): FunctionDef {\n // `has` and `try` read as syntax, so a function with either name could never be called.\n if (!isName(name) || name === 'has' || name === 'try') {\n throw new TypeError(`Invalid function name \"${name}\"`)\n }\n assertHostSpec(host, `Function \"${name}\"`)\n const run = host.run as (...args: unknown[]) => unknown\n const count = host.params.length\n host.params.forEach((param, index) => {\n if (index >= (host.required ?? count) && !isNullable(param)) {\n throw new TypeError(\n `Optional parameter ${index + 1} of \"${name}\" receives null when omitted; declare it as t.optional(...)`,\n )\n }\n })\n return Object.freeze({\n name,\n description: host.description ?? '',\n host: true,\n async: host.async === true,\n call: host.call === true,\n ...(host.cost === undefined ? {} : { cost: host.cost }),\n overloads: [\n overload(\n host.params,\n host.returns,\n (args) => {\n // Missing optional arguments arrive as null.\n const offset = host.call === true ? 1 : 0\n while (args.length < count + offset) args.push(null)\n return run(...args)\n },\n {\n required: host.required ?? count,\n ...(host.rest === undefined ? {} : { rest: host.rest }),\n },\n ),\n ],\n })\n}\n\nfunction mergeFunctions(\n base: ReadonlyMap<string, FunctionDef>,\n options: EnvironmentOptions,\n): Map<string, FunctionDef> {\n const out = new Map(base)\n const added = new Map<string, string>()\n const add = (name: string, def: HostFunction, origin: string): void => {\n const previous = added.get(name)\n if (previous !== undefined) {\n throw new TypeError(`Function \"${name}\" is defined by both ${previous} and ${origin}`)\n }\n added.set(name, origin)\n out.set(name, toDef(name, def))\n }\n for (const library of options.libraries ?? []) {\n for (const [name, def] of Object.entries(library.functions ?? {}))\n add(name, def, `library \"${library.name}\"`)\n }\n for (const [name, def] of Object.entries(options.functions ?? {}))\n add(name, def, 'the functions option')\n return out\n}\n\nfunction mergeVariables(\n base: Readonly<Record<string, Type>> | undefined,\n options: EnvironmentOptions,\n): Readonly<Record<string, Type>> | undefined {\n // Declaring `variables` (even as {}) makes the environment declared, and so strict by default.\n let out: Record<string, Type> | undefined =\n base === undefined && options.variables === undefined ? undefined : { ...base }\n const added = new Map<string, string>()\n const add = (name: string, type: Type, origin: string): void => {\n if (!isName(name)) {\n throw new TypeError(`Invalid variable name \"${name}\" (in ${origin})`)\n }\n const previous = added.get(name)\n if (previous !== undefined) {\n throw new TypeError(`Variable \"${name}\" is declared by both ${previous} and ${origin}`)\n }\n added.set(name, origin)\n assertType(type, `Variable \"${name}\"`)\n out ??= {}\n out[name] = type\n }\n for (const library of options.libraries ?? []) {\n for (const [k, v] of Object.entries(library.variables ?? {}))\n add(k, v, `library \"${library.name}\"`)\n }\n for (const [k, v] of Object.entries(options.variables ?? {})) add(k, v, 'the variables option')\n return out === undefined ? undefined : Object.freeze(out)\n}\n\nconst OPTION_KEYS = new Set([\n 'variables',\n 'strict',\n 'functions',\n 'libraries',\n 'limits',\n 'cacheSize',\n 'clock',\n 'validateContext',\n 'builtins',\n])\nconst LIMIT_KEYS: ReadonlySet<string> = new Set([\n ...Object.keys(DEFAULT_PARSE_LIMITS),\n ...Object.keys(DEFAULT_RUNTIME_LIMITS),\n 'timeout',\n])\nconst LIBRARY_KEYS = new Set(['name', 'functions', 'variables'])\n\n/** Environment options come from configuration: mistakes there are programming errors. */\nfunction assertOptions(options: unknown): asserts options is EnvironmentOptions {\n assertKeys(options, OPTION_KEYS, 'Options')\n const o = options as Record<string, unknown>\n if (o.limits !== undefined) assertKeys(o.limits, LIMIT_KEYS, 'Limits')\n if (o.variables !== undefined && !isRecord(o.variables))\n throw new TypeError('variables must be an object of types')\n if (o.functions !== undefined && !isRecord(o.functions))\n throw new TypeError('functions must be an object of fn() declarations')\n if (o.libraries !== undefined) {\n if (!Array.isArray(o.libraries)) throw new TypeError('libraries must be an array')\n const names = new Set<string>()\n for (const library of o.libraries) {\n assertKeys(library, LIBRARY_KEYS, 'Library')\n const l = library as Record<string, unknown>\n if (typeof l.name !== 'string' || l.name === '') throw new TypeError('A library needs a name')\n if (names.has(l.name)) throw new TypeError(`Library \"${l.name}\" is listed twice`)\n names.add(l.name)\n if (l.functions !== undefined && !isRecord(l.functions))\n throw new TypeError(`functions of library \"${l.name}\" must be an object`)\n if (l.variables !== undefined && !isRecord(l.variables))\n throw new TypeError(`variables of library \"${l.name}\" must be an object`)\n }\n }\n for (const flag of ['strict', 'validateContext'] as const) {\n if (o[flag] !== undefined && typeof o[flag] !== 'boolean')\n throw new TypeError(`${flag} must be a boolean`)\n }\n if (o.clock !== undefined && typeof o.clock !== 'function')\n throw new TypeError('clock must be a function returning a Date')\n}\n\ntype Numbers = Readonly<Record<string, number>>\n\n/**\n * The limits in `defaults` (parse or runtime), overridden by `limits` over\n * `current`. 0 turns `maxSteps` off; every other limit must be at least 1.\n */\nfunction pickLimits(\n defaults: object,\n current: object,\n limits: Readonly<Record<string, unknown>>,\n): Numbers {\n const base = current as Numbers\n return Object.freeze(\n Object.fromEntries(\n Object.keys(defaults).map((name) => {\n const value = numberOption(\n `Limit \"${name}\"`,\n limits[name],\n base[name],\n name === 'maxSteps' ? 0 : 1,\n )\n const cap = DEPTH_CAPS[name]\n if (cap !== undefined && value > cap)\n throw new RangeError(`Limit \"${name}\" must be at most ${cap}`)\n return [name, value]\n }),\n ),\n )\n}\n\n/** Depth limits are capped: deeper nesting would run out of call stack. */\nconst DEPTH_CAPS: Readonly<Record<string, number>> = {\n maxDepth: MAX_DEPTH_LIMIT,\n maxValueDepth: MAX_VALUE_DEPTH_LIMIT,\n}\n\n/**\n * Settings from options, over a base (extend) or, for a new environment or\n * runtime, over the entry point's built-ins.\n */\nexport function settingsFrom(\n base: Settings | undefined,\n options: unknown,\n builtins: ReadonlyMap<string, FunctionDef> = new Map(),\n): Settings {\n assertOptions(options)\n const limits = (options.limits ?? {}) as Readonly<Record<string, unknown>>\n const variables = mergeVariables(base?.variables, options)\n const strictOption = options.strict ?? base?.strictOption\n return {\n variables,\n strictOption,\n strict: strictOption ?? variables !== undefined,\n host: mergeFunctions(base?.host ?? new Map(), options),\n builtins: builtinsOption(\n options.builtins,\n base?.builtins ?? (options.builtins === undefined ? builtins : new Map()),\n ),\n parseLimits: pickLimits(\n DEFAULT_PARSE_LIMITS,\n base?.parseLimits ?? DEFAULT_PARSE_LIMITS,\n limits,\n ) as unknown as ParseLimits,\n runtimeLimits: pickLimits(\n DEFAULT_RUNTIME_LIMITS,\n base?.runtimeLimits ?? DEFAULT_RUNTIME_LIMITS,\n limits,\n ) as unknown as RuntimeLimits,\n timeout: timeoutOption('Limit \"timeout\"', limits.timeout, base?.timeout ?? 0),\n cacheSize: numberOption(\n 'cacheSize',\n options.cacheSize,\n base?.cacheSize ?? DEFAULT_CACHE_SIZE,\n 0,\n ),\n clock: options.clock === undefined ? (base?.clock ?? systemClock) : checkedClock(options.clock),\n validateContext: options.validateContext ?? base?.validateContext ?? false,\n features: base?.features,\n }\n}\n\n/**\n * Source characters the program cache holds in total, and the longest source it\n * keeps. A compiled program can retain a hundred times its source in memory, so\n * the cache is bounded by size as well as by count.\n */\nconst CACHE_CHARS = 262_144\nconst CACHE_MAX_SOURCE = 16_384\n\n/**\n * Approximately least recently used (\"second chance\"): a hit only marks its\n * entry, so the hot path is one lookup. When the cache is full, the oldest\n * entry is evicted, unless it was used since it last reached the front (or is\n * the entry being added), in which case it moves to the back unmarked.\n */\nexport class ProgramCache<V> {\n private readonly map = new Map<string, { readonly value: V; used: boolean }>()\n private readonly size: number\n private chars = 0\n\n constructor(size: number) {\n this.size = size\n }\n\n get(key: string): V | undefined {\n const slot = this.map.get(key)\n if (slot === undefined) return undefined\n slot.used = true\n return slot.value\n }\n\n set(key: string, value: V): void {\n if (this.size === 0 || key.length > CACHE_MAX_SOURCE) return\n if (this.map.delete(key)) this.chars -= key.length\n this.map.set(key, { value, used: false })\n this.chars += key.length\n while (this.map.size > this.size || this.chars > CACHE_CHARS) {\n const [oldest, slot] = this.map.entries().next().value as [\n string,\n { readonly value: V; used: boolean },\n ]\n this.map.delete(oldest)\n if (slot.used || oldest === key) {\n slot.used = false\n this.map.set(oldest, slot)\n } else {\n this.chars -= oldest.length\n }\n }\n }\n}\n\nconst NO_DIAGNOSTICS: readonly Diagnostic[] = Object.freeze([])\n\n/**\n * What check() found. `ok`, `type`, and `diagnostics` are the JSON form; the\n * tree is read on demand (a prototype getter), so an editor checking on every\n * keystroke pays only for what it uses.\n */\nclass CheckFailure {\n readonly ok: boolean = false\n readonly type: Type | undefined\n readonly diagnostics: readonly Diagnostic[]\n /** The inferred type of a node of `ast`, or undefined for another tree's node. */\n readonly typeOf: (node: Node) => Type | undefined\n readonly #analysis: Analysis | undefined\n\n constructor(type: Type | undefined, diagnostics: readonly Diagnostic[], analysis?: Analysis) {\n this.type = type\n this.diagnostics = diagnostics\n this.#analysis = analysis\n this.typeOf = (node) => {\n const found = analysis?.types.get(node)\n return found === undefined ? undefined : deepFreeze(found)\n }\n }\n\n get ast(): Node | undefined {\n return this.#analysis === undefined ? undefined : deepFreeze(this.#analysis.root)\n }\n}\n\n/** A check that passed: also the compiled program, made the first time it is read. */\nclass CheckSuccess extends CheckFailure {\n override readonly ok: boolean = true\n readonly #program: () => unknown\n\n constructor(\n type: Type,\n diagnostics: readonly Diagnostic[],\n analysis: Analysis,\n program: () => unknown,\n ) {\n super(type, diagnostics, analysis)\n this.#program = program\n }\n\n get program(): unknown {\n return this.#program()\n }\n}\n\n/** Freezes a syntax tree (or any tree of plain objects and arrays) in place, without recursion. */\nfunction deepFreeze<T>(root: T): T {\n // Visits frozen objects too: a frozen type can hold a type the checker built\n // unfrozen (and shares between analyses).\n const seen = new Set<object>()\n const pending: unknown[] = [root]\n while (pending.length > 0) {\n const value = pending.pop()\n if (typeof value !== 'object' || value === null || seen.has(value)) continue\n seen.add(value)\n Object.freeze(value)\n for (const child of Object.values(value)) pending.push(child)\n }\n return root\n}\n\nconst systemClock = (): Date => new Date()\n\n/** A host clock whose results are checked: now() must be a valid timestamp. */\nfunction checkedClock(clock: () => Date): () => Date {\n return () => {\n let value: unknown\n try {\n value = clock()\n } catch (error) {\n throw raised(new BonsaiRuntimeError('HOST_ERROR', 'The clock failed', { cause: error }))\n }\n if (!(value instanceof Date) || Number.isNaN(Date.prototype.getTime.call(value))) {\n throw raised(\n new BonsaiRuntimeError(\n 'HOST_CONTRACT',\n 'The clock must return a valid Date (for example () => new Date())',\n ),\n )\n }\n return value\n }\n}\n\n/** Whether a type has an exact map type anywhere in it. */\nfunction holdsExact(type: Type): boolean {\n const stack: Type[] = [type]\n const seen = new Set<Type>()\n while (stack.length > 0) {\n const next = stack.pop() as Type\n if (seen.has(next)) continue\n seen.add(next)\n if (isExact(next)) return true\n if (next.kind === 'list') stack.push(next.element)\n else if (next.kind === 'union') stack.push(...next.types)\n else if (next.kind === 'map') {\n stack.push(...Object.values(next.fields))\n if (next.rest !== undefined) stack.push(next.rest)\n }\n }\n return false\n}\n\n/** Reads compile(), check(), and the runtime's load() options: only `expect`, which must be a type built with t. Returns a frozen copy of it. */\nexport function expectOption(options: unknown, what: 'Compile' | 'Load'): Type | undefined {\n if (options === undefined) return undefined\n if (typeof options !== 'object' || options === null || Array.isArray(options))\n throw new TypeError(`${what} options must be an object`)\n let expect: unknown\n try {\n for (const key of Object.keys(options)) {\n if (key !== 'expect')\n throw new TypeError(\n `Unknown ${what.toLowerCase()} option key \"${key}\" (expected one of: expect)`,\n )\n }\n expect = (options as { expect?: unknown }).expect\n } catch (error) {\n if (error instanceof TypeError) throw error\n throw new TypeError(`${what} options could not be read`, { cause: error })\n }\n if (expect === undefined) return undefined\n // Checked, copied, and the copy checked: the program and its cache key use\n // one private copy, so no getter, toJSON, or later change can split them.\n assertType(expect, `${what} option \"expect\"`)\n const own = typeData(expect)\n if (own !== expect) assertType(own, `${what} option \"expect\"`)\n return own\n}\n\nfunction createEnvironment<Ctx>(settings: Settings): Environment<Ctx> {\n const checkEnv: CheckEnv = {\n variables: settings.variables,\n strict: settings.strict,\n lookup: (name) => settings.host.get(name) ?? settings.builtins.get(name),\n *functionNames() {\n yield* settings.host.keys()\n for (const name of settings.builtins.keys()) if (!settings.host.has(name)) yield name\n },\n }\n // Each entry records the source and expected type it was compiled for: a key\n // alone could collide (a comment may end a source with what looks like a type).\n const cache = new ProgramCache<{\n readonly program: Program<Ctx>\n readonly source: string\n readonly expect: string | undefined\n }>(settings.cacheSize)\n\n function parseSource(source: string): Node {\n if (typeof source !== 'string') throw new TypeError('An expression must be a string')\n return parse(source, settings.parseLimits)\n }\n\n function analyzeSource(source: string, expect: Type | undefined): Analysis {\n const root = parseSource(source)\n try {\n return analyze(root, checkEnv, { expected: expect })\n } catch (error) {\n // The checker has no source text; attach it so the error can show where.\n if (error instanceof BonsaiLimitError && error.source === undefined) {\n throw new BonsaiLimitError(error.code, error.message, {\n source,\n ...(error.span === undefined ? {} : { span: error.span }),\n limit: error.limit ?? null,\n })\n }\n throw error\n }\n }\n\n function makeProgram<R>(source: string, analysis: Analysis, expect?: Type): Program<Ctx, R>\n function makeProgram(\n source: string,\n analysis: Analysis,\n expect: Type | undefined,\n constants: ReadonlyMap<string, unknown>,\n residualOf: ResidualOf,\n ): ResidualRunner\n function makeProgram<R>(\n source: string,\n analysis: Analysis,\n expect?: Type,\n constants?: ReadonlyMap<string, unknown>,\n residualOf?: ResidualOf,\n ): Program<Ctx, R> | ResidualRunner {\n // Residuals are not warmed: partial() runs often, and a zone it writes into\n // the residual as text loads on first use (the shared data is already loaded).\n if (residualOf === undefined) warmZones(analysis)\n // When the checker cannot prove the result matches `expect` (part of it is\n // `any`, or an open object may hold an unlisted key), it is checked at run\n // time, so the declared result type holds.\n const guard = expect !== undefined && analysis.checkResult ? expect : undefined\n const { prepare, checked, settle, runSync, runAsync, compileOptions } = programRunner<R>({\n source,\n analysis,\n guard,\n settings,\n constants,\n residualOf,\n })\n\n const features = settings.features?.program<Ctx, R>({\n source,\n analysis,\n expect,\n settings,\n checkEnv,\n compileOptions,\n residualOf,\n prepare,\n checked,\n settle,\n residual: (residualAnalysis, residualConstants, residualFrom) =>\n makeProgram(source, residualAnalysis, expect, residualConstants, residualFrom),\n })\n\n if (residualOf !== undefined) {\n // Only partial() makes residuals, so the features that made this one are present.\n const made = features as ProgramFeatures<Ctx, R>\n // A residual is used only through its runner, so no Program is built for\n // it (freezing its type and references would be work on every partial());\n // its tree is the result's `ast`, and compiles lazily, so it is frozen.\n deepFreeze(analysis.root)\n const runner: ResidualRunner = {\n async: analysis.async,\n runSync: (ctx, options) => runSync(ctx, options as EvaluateOptions | undefined),\n runAsync: (ctx, options) => runAsync(ctx, options as EvaluateOptions | undefined),\n explainSync: (ctx, options) => made.explainSync(ctx, options as ExplainOptions | undefined),\n explainAsync: (ctx, options) =>\n made.explainAsync(ctx, options as ExplainOptions | undefined),\n }\n return runner\n }\n // Programs are shared: the tree compiles lazily, so it must not change afterwards.\n // A lite program is typed as a LiteProgram by its entry point.\n const program = Object.freeze({\n // Without features (bonsai-js/lite), a program has no explain or partial.\n ...(features === undefined\n ? {}\n : {\n explain: (...args: ExplainArgs<Ctx>) => features.explainAsync(args[0], args[1]),\n explainSync: (...args: ExplainArgs<Ctx>) => features.explainSync(args[0], args[1]),\n partial: (known: object, options?: PartialOptions) =>\n features.partial(program, known, options),\n }),\n source,\n ast: deepFreeze(analysis.root),\n type: deepFreeze(analysis.type),\n async: analysis.async,\n warnings: deepFreeze(\n locate(\n source,\n analysis.diagnostics.filter((d) => d.severity === 'warning'),\n ),\n ),\n references: deepFreeze(analysis.references),\n evaluate: (...args: Args<Ctx>) => runAsync(args[0], args[1]),\n evaluateSync: (...args: Args<Ctx>) => runSync(args[0], args[1]),\n })\n recordDeclared(program, settings.variables)\n recordCompiled(program, {\n source,\n analysis,\n expect,\n lookup: checkEnv.lookup,\n variables: settings.variables,\n })\n return program as Program<Ctx, R>\n }\n\n function compile<R>(source: string, expect: Type | undefined): Program<Ctx, R> {\n const analysis = analyzeSource(source, expect)\n const errors = analysis.diagnostics.filter((d) => d.severity === 'error')\n if (errors.length > 0) throw new BonsaiCheckError(source, errors)\n return makeProgram<R>(source, analysis, expect)\n }\n\n /**\n * compile(), through the program cache: programs are immutable, so the same\n * source with an equal expected type shares one. `expect` is expectOption's\n * plain copy, so its JSON is the type the program is checked against.\n */\n function cachedCompile<R>(source: string, expect: Type | undefined): Program<Ctx, R> {\n if (typeof source !== 'string') throw new TypeError('An expression must be a string')\n // An exact map type (a checked program's inferred type passed back as\n // `expect`) checks differently from a declared one with the same JSON.\n if (expect !== undefined && holdsExact(expect)) return compile<R>(source, expect)\n const expectKey = expect === undefined ? undefined : JSON.stringify(expect)\n const key = expectKey === undefined ? source : `${source}\\u0000${expectKey}`\n const entry = cache.get(key)\n if (entry !== undefined && entry.source === source && entry.expect === expectKey)\n return entry.program as Program<Ctx, R>\n const program = compile<R>(source, expect)\n cache.set(key, { program, source, expect: expectKey })\n return program\n }\n\n function cached(source: string): Program<Ctx> {\n return cachedCompile(source, undefined)\n }\n\n function info(def: FunctionDef): FunctionInfo {\n return {\n name: def.name,\n description: descriptionOf(def),\n host: def.host === true,\n async: def.async === true,\n call: def.call === true,\n ...(def.cost === undefined ? {} : { cost: def.cost }),\n signatures: def.overloads.map((o) => ({\n params: o.params,\n required: o.required ?? o.params.length,\n returns: o.result,\n ...(o.rest === undefined ? {} : { rest: o.rest }),\n })),\n }\n }\n\n // A lite environment is typed as a LiteEnvironment by its entry point.\n const env = Object.freeze({\n variables: settings.variables,\n strict: settings.strict,\n parse: parseSource,\n check<E extends Type = AnyType>(\n source: string,\n options?: CompileOptions<E>,\n ): CheckResult<Ctx, Infer<E>> {\n const expect = expectOption(options, 'Compile')\n let analysis: Analysis\n try {\n analysis = analyzeSource(source, expect)\n } catch (error) {\n if (!(error instanceof BonsaiError)) throw error\n const diagnostics = locate(source, [\n {\n code: error.code === 'SYNTAX' ? 'SYNTAX' : 'LIMIT',\n message: error.message,\n severity: 'error',\n start: error.span?.start ?? 0,\n end: error.span?.end ?? source.length,\n },\n ])\n return Object.freeze(new CheckFailure(undefined, diagnostics, undefined)) as CheckResult<\n Ctx,\n Infer<E>\n >\n }\n const diagnostics =\n analysis.diagnostics.length === 0 ? NO_DIAGNOSTICS : locate(source, analysis.diagnostics)\n const type = deepFreeze(analysis.type)\n if (analysis.diagnostics.some((d) => d.severity === 'error'))\n return Object.freeze(new CheckFailure(type, diagnostics, analysis)) as CheckResult<\n Ctx,\n Infer<E>\n >\n let program: Program<Ctx, Infer<E>> | undefined\n return Object.freeze(\n new CheckSuccess(\n type,\n diagnostics,\n analysis,\n () => (program ??= makeProgram<Infer<E>>(source, analysis, expect)),\n ),\n ) as CheckResult<Ctx, Infer<E>>\n },\n compile: <E extends Type = AnyType>(source: string, options?: CompileOptions<E>) =>\n cachedCompile<Infer<E>>(source, expectOption(options, 'Compile')),\n // Without features (bonsai-js/lite), an environment has no partial or explain.\n ...(settings.features === undefined\n ? {}\n : {\n partial: <R>(\n source: string,\n known: PartialData<Ctx> & object,\n options?: PartialOptions,\n ) => cached(source).partial(known, options) as PartialResult<Ctx, R>,\n }),\n evaluate<R>(source: string, ...args: Args<Ctx>): Promise<R> {\n try {\n return cached(source).evaluate(...args) as Promise<R>\n } catch (error) {\n return Promise.reject(error instanceof Error ? error : new Error(String(error)))\n }\n },\n evaluateSync: <R>(source: string, ...args: Args<Ctx>) =>\n cached(source).evaluateSync(...args) as R,\n ...(settings.features === undefined\n ? {}\n : {\n explain<R>(source: string, ...args: ExplainArgs<Ctx>): Promise<Explanation<R>> {\n try {\n return cached(source).explain(...args) as Promise<Explanation<R>>\n } catch (error) {\n return Promise.reject(error instanceof Error ? error : new Error(String(error)))\n }\n },\n explainSync: <R>(source: string, ...args: ExplainArgs<Ctx>) =>\n cached(source).explainSync(...args) as Explanation<R>,\n }),\n describeFunction(name: string): FunctionInfo | undefined {\n const def = checkEnv.lookup(name)\n return def === undefined ? undefined : info(def)\n },\n listFunctions(): FunctionInfo[] {\n return [...checkEnv.functionNames()].map((name) => info(checkEnv.lookup(name) as FunctionDef))\n },\n extend<\n V2 extends Readonly<Record<string, Type>>,\n L2 extends readonly Library[],\n S2 extends boolean | undefined,\n >(options: EnvironmentOptions<V2, L2> & { readonly strict?: S2 }) {\n return createEnvironment<Extended<Ctx, V2, L2, S2>>(settingsFrom(settings, options))\n },\n })\n internals.set(env, { checkEnv, parseLimits: settings.parseLimits })\n return env as Environment<Ctx>\n}\n\n/**\n * An environment from `bonsai()` options, with the features its entry point\n * provides (not public API): `bonsai-js` passes explaining and partial\n * evaluation, `bonsai-js/lite` passes none.\n */\nexport function environmentWith<Ctx>(\n features: Features | undefined,\n options: unknown,\n): Environment<Ctx> {\n return createEnvironment<Ctx>({ ...settingsFrom(undefined, options, BUILTINS), features })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyKA,SAAgB,GAKd,MAuBc;CACd,eAAe,MAAM,MAAM;CAC3B,OAAO,OAAO,OAAO,EAAE,GAAG,KAAK,CAAC;AAClC;;;;;;;;;;AAWA,SAAgB,cAAmB;CACjC,QACE,SAciB;EACjB,eAAe,MAAM,eAAe;EACpC,OAAO,OAAO,OAAO;GAAE,GAAG;GAAM,MAAM;EAAK,CAAC;CAC9C;AACF;AAiZA,MAAM,4BAAY,IAAI,QAAsC;AAE5D,SAAgB,YAAY,KAAmC;CAC7D,MAAM,QAAQ,UAAU,IAAI,GAAG;CAC/B,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,UAAU,0BAA0B;CACvE,OAAO;AACT;;AAGA,MAAM,qBAAqB;AA+F3B,SAAS,MAAM,MAAc,MAAiC;CAE5D,IAAI,CAAC,OAAO,IAAI,KAAK,SAAS,SAAS,SAAS,OAC9C,MAAM,IAAI,UAAU,0BAA0B,KAAK,EAAE;CAEvD,eAAe,MAAM,aAAa,KAAK,EAAE;CACzC,MAAM,MAAM,KAAK;CACjB,MAAM,QAAQ,KAAK,OAAO;CAC1B,KAAK,OAAO,SAAS,OAAO,UAAU;EACpC,IAAI,UAAU,KAAK,YAAY,UAAU,CAAC,WAAW,KAAK,GACxD,MAAM,IAAI,UACR,sBAAsB,QAAQ,EAAE,OAAO,KAAK,4DAC9C;CAEJ,CAAC;CACD,OAAO,OAAO,OAAO;EACnB;EACA,aAAa,KAAK,eAAe;EACjC,MAAM;EACN,OAAO,KAAK,UAAU;EACtB,MAAM,KAAK,SAAS;EACpB,GAAI,KAAK,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK;EACrD,WAAW,CACT,SACE,KAAK,QACL,KAAK,UACJ,SAAS;GAER,MAAM,SAAS,KAAK,SAAS,OAAO,IAAI;GACxC,OAAO,KAAK,SAAS,QAAQ,QAAQ,KAAK,KAAK,IAAI;GACnD,OAAO,IAAI,GAAG,IAAI;EACpB,GACA;GACE,UAAU,KAAK,YAAY;GAC3B,GAAI,KAAK,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK;EACvD,CACF,CACF;CACF,CAAC;AACH;AAEA,SAAS,eACP,MACA,SAC0B;CAC1B,MAAM,MAAM,IAAI,IAAI,IAAI;CACxB,MAAM,wBAAQ,IAAI,IAAoB;CACtC,MAAM,OAAO,MAAc,KAAmB,WAAyB;EACrE,MAAM,WAAW,MAAM,IAAI,IAAI;EAC/B,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,UAAU,aAAa,KAAK,uBAAuB,SAAS,OAAO,QAAQ;EAEvF,MAAM,IAAI,MAAM,MAAM;EACtB,IAAI,IAAI,MAAM,MAAM,MAAM,GAAG,CAAC;CAChC;CACA,KAAK,MAAM,WAAW,QAAQ,aAAa,CAAC,GAC1C,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAC,GAC9D,IAAI,MAAM,KAAK,YAAY,QAAQ,KAAK,EAAE;CAE9C,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAC,GAC9D,IAAI,MAAM,KAAK,sBAAsB;CACvC,OAAO;AACT;AAEA,SAAS,eACP,MACA,SAC4C;CAE5C,IAAI,MACF,SAAS,KAAA,KAAa,QAAQ,cAAc,KAAA,IAAY,KAAA,IAAY,EAAE,GAAG,KAAK;CAChF,MAAM,wBAAQ,IAAI,IAAoB;CACtC,MAAM,OAAO,MAAc,MAAY,WAAyB;EAC9D,IAAI,CAAC,OAAO,IAAI,GACd,MAAM,IAAI,UAAU,0BAA0B,KAAK,QAAQ,OAAO,EAAE;EAEtE,MAAM,WAAW,MAAM,IAAI,IAAI;EAC/B,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,UAAU,aAAa,KAAK,wBAAwB,SAAS,OAAO,QAAQ;EAExF,MAAM,IAAI,MAAM,MAAM;EACtB,WAAW,MAAM,aAAa,KAAK,EAAE;EACrC,QAAQ,CAAC;EACT,IAAI,QAAQ;CACd;CACA,KAAK,MAAM,WAAW,QAAQ,aAAa,CAAC,GAC1C,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAC,GACzD,IAAI,GAAG,GAAG,YAAY,QAAQ,KAAK,EAAE;CAEzC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,sBAAsB;CAC9F,OAAO,QAAQ,KAAA,IAAY,KAAA,IAAY,OAAO,OAAO,GAAG;AAC1D;AAEA,MAAM,8BAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,6BAAkC,IAAI,IAAI;CAC9C,GAAG,OAAO,KAAK,oBAAoB;CACnC,GAAG,OAAO,KAAK,sBAAsB;CACrC;AACF,CAAC;AACD,MAAM,+BAAe,IAAI,IAAI;CAAC;CAAQ;CAAa;AAAW,CAAC;;AAG/D,SAAS,cAAc,SAAyD;CAC9E,WAAW,SAAS,aAAa,SAAS;CAC1C,MAAM,IAAI;CACV,IAAI,EAAE,WAAW,KAAA,GAAW,WAAW,EAAE,QAAQ,YAAY,QAAQ;CACrE,IAAI,EAAE,cAAc,KAAA,KAAa,CAAC,SAAS,EAAE,SAAS,GACpD,MAAM,IAAI,UAAU,sCAAsC;CAC5D,IAAI,EAAE,cAAc,KAAA,KAAa,CAAC,SAAS,EAAE,SAAS,GACpD,MAAM,IAAI,UAAU,kDAAkD;CACxE,IAAI,EAAE,cAAc,KAAA,GAAW;EAC7B,IAAI,CAAC,MAAM,QAAQ,EAAE,SAAS,GAAG,MAAM,IAAI,UAAU,4BAA4B;EACjF,MAAM,wBAAQ,IAAI,IAAY;EAC9B,KAAK,MAAM,WAAW,EAAE,WAAW;GACjC,WAAW,SAAS,cAAc,SAAS;GAC3C,MAAM,IAAI;GACV,IAAI,OAAO,EAAE,SAAS,YAAY,EAAE,SAAS,IAAI,MAAM,IAAI,UAAU,wBAAwB;GAC7F,IAAI,MAAM,IAAI,EAAE,IAAI,GAAG,MAAM,IAAI,UAAU,YAAY,EAAE,KAAK,kBAAkB;GAChF,MAAM,IAAI,EAAE,IAAI;GAChB,IAAI,EAAE,cAAc,KAAA,KAAa,CAAC,SAAS,EAAE,SAAS,GACpD,MAAM,IAAI,UAAU,yBAAyB,EAAE,KAAK,oBAAoB;GAC1E,IAAI,EAAE,cAAc,KAAA,KAAa,CAAC,SAAS,EAAE,SAAS,GACpD,MAAM,IAAI,UAAU,yBAAyB,EAAE,KAAK,oBAAoB;EAC5E;CACF;CACA,KAAK,MAAM,QAAQ,CAAC,UAAU,iBAAiB,GAC7C,IAAI,EAAE,UAAU,KAAA,KAAa,OAAO,EAAE,UAAU,WAC9C,MAAM,IAAI,UAAU,GAAG,KAAK,mBAAmB;CAEnD,IAAI,EAAE,UAAU,KAAA,KAAa,OAAO,EAAE,UAAU,YAC9C,MAAM,IAAI,UAAU,2CAA2C;AACnE;;;;;AAQA,SAAS,WACP,UACA,SACA,QACS;CACT,MAAM,OAAO;CACb,OAAO,OAAO,OACZ,OAAO,YACL,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,SAAS;EAClC,MAAM,QAAQ,aACZ,UAAU,KAAK,IACf,OAAO,OACP,KAAK,OACL,SAAS,aAAa,IAAI,CAC5B;EACA,MAAM,MAAM,WAAW;EACvB,IAAI,QAAQ,KAAA,KAAa,QAAQ,KAC/B,MAAM,IAAI,WAAW,UAAU,KAAK,oBAAoB,KAAK;EAC/D,OAAO,CAAC,MAAM,KAAK;CACrB,CAAC,CACH,CACF;AACF;;AAGA,MAAM,aAA+C;CACnD,UAAA;CACA,eAAe;AACjB;;;;;AAMA,SAAgB,aACd,MACA,SACA,2BAA6C,IAAI,IAAI,GAC3C;CACV,cAAc,OAAO;CACrB,MAAM,SAAU,QAAQ,UAAU,CAAC;CACnC,MAAM,YAAY,eAAe,MAAM,WAAW,OAAO;CACzD,MAAM,eAAe,QAAQ,UAAU,MAAM;CAC7C,OAAO;EACL;EACA;EACA,QAAQ,gBAAgB,cAAc,KAAA;EACtC,MAAM,eAAe,MAAM,wBAAQ,IAAI,IAAI,GAAG,OAAO;EACrD,UAAU,eACR,QAAQ,UACR,MAAM,aAAa,QAAQ,aAAa,KAAA,IAAY,2BAAW,IAAI,IAAI,EACzE;EACA,aAAa,WACX,sBACA,MAAM,eAAe,sBACrB,MACF;EACA,eAAe,WACb,wBACA,MAAM,iBAAiB,wBACvB,MACF;EACA,SAAS,cAAc,qBAAmB,OAAO,SAAS,MAAM,WAAW,CAAC;EAC5E,WAAW,aACT,aACA,QAAQ,WACR,MAAM,aAAa,oBACnB,CACF;EACA,OAAO,QAAQ,UAAU,KAAA,IAAa,MAAM,SAAS,cAAe,aAAa,QAAQ,KAAK;EAC9F,iBAAiB,QAAQ,mBAAmB,MAAM,mBAAmB;EACrE,UAAU,MAAM;CAClB;AACF;;;;;;AAOA,MAAM,cAAc;AACpB,MAAM,mBAAmB;;;;;;;AAQzB,IAAa,eAAb,MAA6B;CAC3B,sBAAuB,IAAI,IAAkD;CAC7E;CACA,QAAgB;CAEhB,YAAY,MAAc;EACxB,KAAK,OAAO;CACd;CAEA,IAAI,KAA4B;EAC9B,MAAM,OAAO,KAAK,IAAI,IAAI,GAAG;EAC7B,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,KAAK,OAAO;EACZ,OAAO,KAAK;CACd;CAEA,IAAI,KAAa,OAAgB;EAC/B,IAAI,KAAK,SAAS,KAAK,IAAI,SAAS,kBAAkB;EACtD,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,KAAK,SAAS,IAAI;EAC5C,KAAK,IAAI,IAAI,KAAK;GAAE;GAAO,MAAM;EAAM,CAAC;EACxC,KAAK,SAAS,IAAI;EAClB,OAAO,KAAK,IAAI,OAAO,KAAK,QAAQ,KAAK,QAAQ,aAAa;GAC5D,MAAM,CAAC,QAAQ,QAAQ,KAAK,IAAI,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC;GAIjD,KAAK,IAAI,OAAO,MAAM;GACtB,IAAI,KAAK,QAAQ,WAAW,KAAK;IAC/B,KAAK,OAAO;IACZ,KAAK,IAAI,IAAI,QAAQ,IAAI;GAC3B,OACE,KAAK,SAAS,OAAO;EAEzB;CACF;AACF;AAEA,MAAM,iBAAwC,OAAO,OAAO,CAAC,CAAC;;;;;;AAO9D,IAAM,eAAN,MAAmB;CACjB,KAAuB;CACvB;CACA;;CAEA;CACA;CAEA,YAAY,MAAwB,aAAoC,UAAqB;EAC3F,KAAK,OAAO;EACZ,KAAK,cAAc;EACnB,KAAK,YAAY;EACjB,KAAK,UAAU,SAAS;GACtB,MAAM,QAAQ,UAAU,MAAM,IAAI,IAAI;GACtC,OAAO,UAAU,KAAA,IAAY,KAAA,IAAY,WAAW,KAAK;EAC3D;CACF;CAEA,IAAI,MAAwB;EAC1B,OAAO,KAAK,cAAc,KAAA,IAAY,KAAA,IAAY,WAAW,KAAK,UAAU,IAAI;CAClF;AACF;;AAGA,IAAM,eAAN,cAA2B,aAAa;CACtC,KAAgC;CAChC;CAEA,YACE,MACA,aACA,UACA,SACA;EACA,MAAM,MAAM,aAAa,QAAQ;EACjC,KAAK,WAAW;CAClB;CAEA,IAAI,UAAmB;EACrB,OAAO,KAAK,SAAS;CACvB;AACF;;AAGA,SAAS,WAAc,MAAY;CAGjC,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,UAAqB,CAAC,IAAI;CAChC,OAAO,QAAQ,SAAS,GAAG;EACzB,MAAM,QAAQ,QAAQ,IAAI;EAC1B,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,KAAK,IAAI,KAAK,GAAG;EACpE,KAAK,IAAI,KAAK;EACd,OAAO,OAAO,KAAK;EACnB,KAAK,MAAM,SAAS,OAAO,OAAO,KAAK,GAAG,QAAQ,KAAK,KAAK;CAC9D;CACA,OAAO;AACT;AAEA,MAAM,oCAA0B,IAAI,KAAK;;AAGzC,SAAS,aAAa,OAA+B;CACnD,aAAa;EACX,IAAI;EACJ,IAAI;GACF,QAAQ,MAAM;EAChB,SAAS,OAAO;GACd,MAAM,OAAO,IAAI,mBAAmB,cAAc,oBAAoB,EAAE,OAAO,MAAM,CAAC,CAAC;EACzF;EACA,IAAI,EAAE,iBAAiB,SAAS,OAAO,MAAM,KAAK,UAAU,QAAQ,KAAK,KAAK,CAAC,GAC7E,MAAM,OACJ,IAAI,mBACF,iBACA,mEACF,CACF;EAEF,OAAO;CACT;AACF;;AAGA,SAAS,WAAW,MAAqB;CACvC,MAAM,QAAgB,CAAC,IAAI;CAC3B,MAAM,uBAAO,IAAI,IAAU;CAC3B,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,OAAO,MAAM,IAAI;EACvB,IAAI,KAAK,IAAI,IAAI,GAAG;EACpB,KAAK,IAAI,IAAI;EACb,IAAI,QAAQ,IAAI,GAAG,OAAO;EAC1B,IAAI,KAAK,SAAS,QAAQ,MAAM,KAAK,KAAK,OAAO;OAC5C,IAAI,KAAK,SAAS,SAAS,MAAM,KAAK,GAAG,KAAK,KAAK;OACnD,IAAI,KAAK,SAAS,OAAO;GAC5B,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,MAAM,CAAC;GACxC,IAAI,KAAK,SAAS,KAAA,GAAW,MAAM,KAAK,KAAK,IAAI;EACnD;CACF;CACA,OAAO;AACT;;AAGA,SAAgB,aAAa,SAAkB,MAA4C;CACzF,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,MAAM,QAAQ,OAAO,GAC1E,MAAM,IAAI,UAAU,GAAG,KAAK,2BAA2B;CACzD,IAAI;CACJ,IAAI;EACF,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,QAAQ,UACV,MAAM,IAAI,UACR,WAAW,KAAK,YAAY,EAAE,eAAe,IAAI,4BACnD;EAEJ,SAAU,QAAiC;CAC7C,SAAS,OAAO;EACd,IAAI,iBAAiB,WAAW,MAAM;EACtC,MAAM,IAAI,UAAU,GAAG,KAAK,6BAA6B,EAAE,OAAO,MAAM,CAAC;CAC3E;CACA,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;CAGjC,WAAW,QAAQ,GAAG,KAAK,iBAAiB;CAC5C,MAAM,MAAM,SAAS,MAAM;CAC3B,IAAI,QAAQ,QAAQ,WAAW,KAAK,GAAG,KAAK,iBAAiB;CAC7D,OAAO;AACT;AAEA,SAAS,kBAAuB,UAAsC;CACpE,MAAM,WAAqB;EACzB,WAAW,SAAS;EACpB,QAAQ,SAAS;EACjB,SAAS,SAAS,SAAS,KAAK,IAAI,IAAI,KAAK,SAAS,SAAS,IAAI,IAAI;EACvE,CAAC,gBAAgB;GACf,OAAO,SAAS,KAAK,KAAK;GAC1B,KAAK,MAAM,QAAQ,SAAS,SAAS,KAAK,GAAG,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,MAAM;EACnF;CACF;CAGA,MAAM,QAAQ,IAAI,aAIf,SAAS,SAAS;CAErB,SAAS,YAAY,QAAsB;EACzC,IAAI,OAAO,WAAW,UAAU,MAAM,IAAI,UAAU,gCAAgC;EACpF,OAAO,MAAM,QAAQ,SAAS,WAAW;CAC3C;CAEA,SAAS,cAAc,QAAgB,QAAoC;EACzE,MAAM,OAAO,YAAY,MAAM;EAC/B,IAAI;GACF,OAAO,QAAQ,MAAM,UAAU,EAAE,UAAU,OAAO,CAAC;EACrD,SAAS,OAAO;GAEd,IAAI,iBAAiB,oBAAoB,MAAM,WAAW,KAAA,GACxD,MAAM,IAAI,iBAAiB,MAAM,MAAM,MAAM,SAAS;IACpD;IACA,GAAI,MAAM,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;IACvD,OAAO,MAAM,SAAS;GACxB,CAAC;GAEH,MAAM;EACR;CACF;CAUA,SAAS,YACP,QACA,UACA,QACA,WACA,YACkC;EAGlC,IAAI,eAAe,KAAA,GAAW,UAAU,QAAQ;EAIhD,MAAM,QAAQ,WAAW,KAAA,KAAa,SAAS,cAAc,SAAS,KAAA;EACtE,MAAM,EAAE,SAAS,SAAS,QAAQ,SAAS,UAAU,mBAAmB,cAAiB;GACvF;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,WAAW,SAAS,UAAU,QAAgB;GAClD;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW,kBAAkB,mBAAmB,iBAC9C,YAAY,QAAQ,kBAAkB,QAAQ,mBAAmB,YAAY;EACjF,CAAC;EAED,IAAI,eAAe,KAAA,GAAW;GAE5B,MAAM,OAAO;GAIb,WAAW,SAAS,IAAI;GASxB,OAAO;IAPL,OAAO,SAAS;IAChB,UAAU,KAAK,YAAY,QAAQ,KAAK,OAAsC;IAC9E,WAAW,KAAK,YAAY,SAAS,KAAK,OAAsC;IAChF,cAAc,KAAK,YAAY,KAAK,YAAY,KAAK,OAAqC;IAC1F,eAAe,KAAK,YAClB,KAAK,aAAa,KAAK,OAAqC;GAEpD;EACd;EAGA,MAAM,UAAU,OAAO,OAAO;GAE5B,GAAI,aAAa,KAAA,IACb,CAAC,IACD;IACE,UAAU,GAAG,SAA2B,SAAS,aAAa,KAAK,IAAI,KAAK,EAAE;IAC9E,cAAc,GAAG,SAA2B,SAAS,YAAY,KAAK,IAAI,KAAK,EAAE;IACjF,UAAU,OAAe,YACvB,SAAS,QAAQ,SAAS,OAAO,OAAO;GAC5C;GACJ;GACA,KAAK,WAAW,SAAS,IAAI;GAC7B,MAAM,WAAW,SAAS,IAAI;GAC9B,OAAO,SAAS;GAChB,UAAU,WACR,OACE,QACA,SAAS,YAAY,QAAQ,MAAM,EAAE,aAAa,SAAS,CAC7D,CACF;GACA,YAAY,WAAW,SAAS,UAAU;GAC1C,WAAW,GAAG,SAAoB,SAAS,KAAK,IAAI,KAAK,EAAE;GAC3D,eAAe,GAAG,SAAoB,QAAQ,KAAK,IAAI,KAAK,EAAE;EAChE,CAAC;EACD,eAAe,SAAS,SAAS,SAAS;EAC1C,eAAe,SAAS;GACtB;GACA;GACA;GACA,QAAQ,SAAS;GACjB,WAAW,SAAS;EACtB,CAAC;EACD,OAAO;CACT;CAEA,SAAS,QAAW,QAAgB,QAA2C;EAC7E,MAAM,WAAW,cAAc,QAAQ,MAAM;EAC7C,MAAM,SAAS,SAAS,YAAY,QAAQ,MAAM,EAAE,aAAa,OAAO;EACxE,IAAI,OAAO,SAAS,GAAG,MAAM,IAAI,iBAAiB,QAAQ,MAAM;EAChE,OAAO,YAAe,QAAQ,UAAU,MAAM;CAChD;;;;;;CAOA,SAAS,cAAiB,QAAgB,QAA2C;EACnF,IAAI,OAAO,WAAW,UAAU,MAAM,IAAI,UAAU,gCAAgC;EAGpF,IAAI,WAAW,KAAA,KAAa,WAAW,MAAM,GAAG,OAAO,QAAW,QAAQ,MAAM;EAChF,MAAM,YAAY,WAAW,KAAA,IAAY,KAAA,IAAY,KAAK,UAAU,MAAM;EAC1E,MAAM,MAAM,cAAc,KAAA,IAAY,SAAS,GAAG,OAAO,QAAQ;EACjE,MAAM,QAAQ,MAAM,IAAI,GAAG;EAC3B,IAAI,UAAU,KAAA,KAAa,MAAM,WAAW,UAAU,MAAM,WAAW,WACrE,OAAO,MAAM;EACf,MAAM,UAAU,QAAW,QAAQ,MAAM;EACzC,MAAM,IAAI,KAAK;GAAE;GAAS;GAAQ,QAAQ;EAAU,CAAC;EACrD,OAAO;CACT;CAEA,SAAS,OAAO,QAA8B;EAC5C,OAAO,cAAc,QAAQ,KAAA,CAAS;CACxC;CAEA,SAAS,KAAK,KAAgC;EAC5C,OAAO;GACL,MAAM,IAAI;GACV,aAAa,cAAc,GAAG;GAC9B,MAAM,IAAI,SAAS;GACnB,OAAO,IAAI,UAAU;GACrB,MAAM,IAAI,SAAS;GACnB,GAAI,IAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK;GACnD,YAAY,IAAI,UAAU,KAAK,OAAO;IACpC,QAAQ,EAAE;IACV,UAAU,EAAE,YAAY,EAAE,OAAO;IACjC,SAAS,EAAE;IACX,GAAI,EAAE,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK;GACjD,EAAE;EACJ;CACF;CAGA,MAAM,MAAM,OAAO,OAAO;EACxB,WAAW,SAAS;EACpB,QAAQ,SAAS;EACjB,OAAO;EACP,MACE,QACA,SAC4B;GAC5B,MAAM,SAAS,aAAa,SAAS,SAAS;GAC9C,IAAI;GACJ,IAAI;IACF,WAAW,cAAc,QAAQ,MAAM;GACzC,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,cAAc,MAAM;IAC3C,MAAM,cAAc,OAAO,QAAQ,CACjC;KACE,MAAM,MAAM,SAAS,WAAW,WAAW;KAC3C,SAAS,MAAM;KACf,UAAU;KACV,OAAO,MAAM,MAAM,SAAS;KAC5B,KAAK,MAAM,MAAM,OAAO,OAAO;IACjC,CACF,CAAC;IACD,OAAO,OAAO,OAAO,IAAI,aAAa,KAAA,GAAW,aAAa,KAAA,CAAS,CAAC;GAI1E;GACA,MAAM,cACJ,SAAS,YAAY,WAAW,IAAI,iBAAiB,OAAO,QAAQ,SAAS,WAAW;GAC1F,MAAM,OAAO,WAAW,SAAS,IAAI;GACrC,IAAI,SAAS,YAAY,MAAM,MAAM,EAAE,aAAa,OAAO,GACzD,OAAO,OAAO,OAAO,IAAI,aAAa,MAAM,aAAa,QAAQ,CAAC;GAIpE,IAAI;GACJ,OAAO,OAAO,OACZ,IAAI,aACF,MACA,aACA,gBACO,YAAY,YAAsB,QAAQ,UAAU,MAAM,CACnE,CACF;EACF;EACA,UAAoC,QAAgB,YAClD,cAAwB,QAAQ,aAAa,SAAS,SAAS,CAAC;EAElE,GAAI,SAAS,aAAa,KAAA,IACtB,CAAC,IACD,EACE,UACE,QACA,OACA,YACG,OAAO,MAAM,CAAC,CAAC,QAAQ,OAAO,OAAO,EAC5C;EACJ,SAAY,QAAgB,GAAG,MAA6B;GAC1D,IAAI;IACF,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,GAAG,IAAI;GACxC,SAAS,OAAO;IACd,OAAO,QAAQ,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;GACjF;EACF;EACA,eAAkB,QAAgB,GAAG,SACnC,OAAO,MAAM,CAAC,CAAC,aAAa,GAAG,IAAI;EACrC,GAAI,SAAS,aAAa,KAAA,IACtB,CAAC,IACD;GACE,QAAW,QAAgB,GAAG,MAAiD;IAC7E,IAAI;KACF,OAAO,OAAO,MAAM,CAAC,CAAC,QAAQ,GAAG,IAAI;IACvC,SAAS,OAAO;KACd,OAAO,QAAQ,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;IACjF;GACF;GACA,cAAiB,QAAgB,GAAG,SAClC,OAAO,MAAM,CAAC,CAAC,YAAY,GAAG,IAAI;EACtC;EACJ,iBAAiB,MAAwC;GACvD,MAAM,MAAM,SAAS,OAAO,IAAI;GAChC,OAAO,QAAQ,KAAA,IAAY,KAAA,IAAY,KAAK,GAAG;EACjD;EACA,gBAAgC;GAC9B,OAAO,CAAC,GAAG,SAAS,cAAc,CAAC,CAAC,CAAC,KAAK,SAAS,KAAK,SAAS,OAAO,IAAI,CAAgB,CAAC;EAC/F;EACA,OAIE,SAAgE;GAChE,OAAO,kBAA6C,aAAa,UAAU,OAAO,CAAC;EACrF;CACF,CAAC;CACD,UAAU,IAAI,KAAK;EAAE;EAAU,aAAa,SAAS;CAAY,CAAC;CAClE,OAAO;AACT;;;;;;AAOA,SAAgB,gBACd,UACA,SACkB;CAClB,OAAO,kBAAuB;EAAE,GAAG,aAAa,KAAA,GAAW,SAAS,QAAQ;EAAG;CAAS,CAAC;AAC3F"}