bonsai-js 1.0.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +24 -4
  3. package/dist/artifact.d.mts +47 -0
  4. package/dist/artifact.mjs +139 -0
  5. package/dist/artifact.mjs.map +1 -0
  6. package/dist/builtins/index.d.mts +91 -0
  7. package/dist/builtins/index.mjs +93 -0
  8. package/dist/builtins/index.mjs.map +1 -0
  9. package/dist/check/bind.mjs +117 -0
  10. package/dist/check/bind.mjs.map +1 -0
  11. package/dist/check/checker.mjs +34 -120
  12. package/dist/check/checker.mjs.map +1 -1
  13. package/dist/compile/compiler.mjs +2 -3
  14. package/dist/compile/compiler.mjs.map +1 -1
  15. package/dist/declared.mjs +8 -1
  16. package/dist/declared.mjs.map +1 -1
  17. package/dist/digest.mjs +109 -0
  18. package/dist/digest.mjs.map +1 -0
  19. package/dist/environment.d.mts +14 -1
  20. package/dist/environment.mjs +65 -315
  21. package/dist/environment.mjs.map +1 -1
  22. package/dist/errors.d.mts +21 -2
  23. package/dist/errors.mjs +17 -1
  24. package/dist/errors.mjs.map +1 -1
  25. package/dist/features.mjs +3 -3
  26. package/dist/features.mjs.map +1 -1
  27. package/dist/functions/builtins.mjs +551 -474
  28. package/dist/functions/builtins.mjs.map +1 -1
  29. package/dist/functions/define.mjs +53 -11
  30. package/dist/functions/define.mjs.map +1 -1
  31. package/dist/functions/descriptions.mjs +110 -0
  32. package/dist/functions/descriptions.mjs.map +1 -0
  33. package/dist/index.d.mts +4 -3
  34. package/dist/index.mjs +3 -2
  35. package/dist/lite/index.d.mts +4 -3
  36. package/dist/lite/index.mjs +3 -2
  37. package/dist/lite/index.mjs.map +1 -1
  38. package/dist/load.mjs +307 -0
  39. package/dist/load.mjs.map +1 -0
  40. package/dist/program.mjs +321 -0
  41. package/dist/program.mjs.map +1 -0
  42. package/dist/query/index.mjs +1 -1
  43. package/dist/runtime/core.d.mts +18 -0
  44. package/dist/runtime/core.mjs +31 -0
  45. package/dist/runtime/core.mjs.map +1 -0
  46. package/dist/runtime/create.d.mts +61 -0
  47. package/dist/runtime/create.mjs +76 -0
  48. package/dist/runtime/create.mjs.map +1 -0
  49. package/dist/runtime/index.d.mts +24 -0
  50. package/dist/runtime/index.mjs +34 -0
  51. package/dist/runtime/index.mjs.map +1 -0
  52. package/dist/service/index.mjs +12 -8
  53. package/dist/service/index.mjs.map +1 -1
  54. package/dist/syntax/parser.mjs +1 -1
  55. package/dist/syntax/printer.mjs +1 -1
  56. package/dist/types.mjs +103 -23
  57. package/dist/types.mjs.map +1 -1
  58. package/dist/version.mjs +7 -0
  59. package/dist/version.mjs.map +1 -0
  60. package/package.json +26 -2
@@ -0,0 +1,321 @@
1
+ import { BonsaiLimitError, BonsaiRuntimeError } from "./errors.mjs";
2
+ import { errorText, isMap } from "./runtime/values.mjs";
3
+ import { formatType } from "./types.mjs";
4
+ import { conforms, describeMismatch, isRecord } from "./functions/define.mjs";
5
+ import { isStackOverflow, tooDeep } from "./runtime/overflow.mjs";
6
+ import { escaping, isOwn, raised } from "./runtime/attribution.mjs";
7
+ import { State } from "./runtime/state.mjs";
8
+ import { compileProgram } from "./compile/compiler.mjs";
9
+ //#region src/program.ts
10
+ const EMPTY_CONTEXT = Object.freeze({});
11
+ /** A number option: TypeError when it is not a number, RangeError when out of range. */
12
+ function numberOption(name, value, fallback, minimum) {
13
+ if (value === void 0) return fallback;
14
+ if (typeof value !== "number") throw new TypeError(`${name} must be a number`);
15
+ if (!Number.isInteger(value) || value < minimum) throw new RangeError(minimum === 0 ? `${name} must be a non-negative integer (0 disables it)` : `${name} must be a positive integer`);
16
+ return value;
17
+ }
18
+ /**
19
+ * A timeout in milliseconds: 0 turns it off; any other non-negative number,
20
+ * fractions included, is a limit, so a budget computed down to its last
21
+ * fraction of a millisecond stays a limit rather than becoming 0.
22
+ */
23
+ function timeoutOption(name, value, fallback) {
24
+ if (value === void 0) return fallback;
25
+ if (typeof value !== "number") throw new TypeError(`${name} must be a number`);
26
+ if (!Number.isFinite(value) || value < 0) throw new RangeError(`${name} must be a non-negative number of milliseconds (0 disables it)`);
27
+ return value;
28
+ }
29
+ function assertKeys(value, allowed, what) {
30
+ if (!isRecord(value)) throw new TypeError(`${what} must be an object`);
31
+ for (const key of Object.keys(value)) if (!allowed.has(key)) throw new TypeError(`Unknown ${what.toLowerCase()} key "${key}" (expected one of: ${[...allowed].join(", ")})`);
32
+ }
33
+ const EVALUATE_OPTION_KEYS = /* @__PURE__ */ new Set([
34
+ "timeout",
35
+ "maxSteps",
36
+ "signal",
37
+ "now",
38
+ "validateContext"
39
+ ]);
40
+ /** Whether a value is a valid Date (an object that only inherits from Date.prototype is not). */
41
+ function isValidDate(value) {
42
+ try {
43
+ return value instanceof Date && !Number.isNaN(Date.prototype.getTime.call(value));
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ /** Whether a value has an AbortSignal's members; a signal whose getters throw does not. */
49
+ function looksLikeSignal(signal) {
50
+ try {
51
+ const s = signal;
52
+ return typeof s.aborted === "boolean" && typeof s.addEventListener === "function" && typeof s.removeEventListener === "function";
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+ /** Validates per-evaluation options the way `limits` are validated; nothing fails open. */
58
+ function evaluationLimits(options, settings) {
59
+ if (options === void 0) return {
60
+ maxSteps: settings.runtimeLimits.maxSteps,
61
+ timeout: settings.timeout,
62
+ signal: void 0,
63
+ now: void 0,
64
+ validateContext: void 0
65
+ };
66
+ let o;
67
+ try {
68
+ assertKeys(options, EVALUATE_OPTION_KEYS, "Evaluate option");
69
+ const read = options;
70
+ o = {
71
+ maxSteps: read.maxSteps,
72
+ timeout: read.timeout,
73
+ signal: read.signal,
74
+ now: read.now,
75
+ validateContext: read.validateContext
76
+ };
77
+ } catch (error) {
78
+ if (error instanceof TypeError || error instanceof RangeError) throw error;
79
+ throw new TypeError(`Evaluate options could not be read: ${errorText(error)}`, { cause: error });
80
+ }
81
+ const signal = o.signal;
82
+ if (signal !== void 0 && (typeof signal !== "object" || signal === null || !looksLikeSignal(signal))) throw new TypeError("signal must be an AbortSignal");
83
+ if (o.now !== void 0 && !isValidDate(o.now)) throw new TypeError("now must be a valid Date");
84
+ if (o.validateContext !== void 0 && typeof o.validateContext !== "boolean") throw new TypeError("validateContext must be a boolean");
85
+ return {
86
+ maxSteps: numberOption("maxSteps", o.maxSteps, settings.runtimeLimits.maxSteps, 0),
87
+ timeout: timeoutOption("timeout", o.timeout, settings.timeout),
88
+ signal,
89
+ now: o.now,
90
+ validateContext: o.validateContext
91
+ };
92
+ }
93
+ function validationBudget(source, limits) {
94
+ return {
95
+ maxDepth: limits.maxDepth,
96
+ remaining: limits.maxSteps > 0 ? limits.maxSteps : Number.POSITIVE_INFINITY,
97
+ deadline: limits.timeout > 0 ? performance.now() + limits.timeout : 0,
98
+ onExhausted: (reason) => {
99
+ if (reason === "depth") throw raised(new BonsaiLimitError("VALUE_DEPTH_LIMIT", `Context nests deeper than ${limits.maxDepth}`, { source }));
100
+ if (reason === "time") throw raised(new BonsaiLimitError("TIMEOUT", "Context validation timed out", { source }));
101
+ throw raised(new BonsaiLimitError("STEP_LIMIT", "Context validation exceeded the step limit", { source }));
102
+ }
103
+ };
104
+ }
105
+ function invalidContext(problem, source) {
106
+ if (problem !== void 0) throw raised(new BonsaiRuntimeError("INVALID_CONTEXT", `Invalid context: ${problem}`, { source }));
107
+ }
108
+ function validateContext(ctx, variables, source, limits) {
109
+ const budget = validationBudget(source, limits);
110
+ for (const [name, type] of Object.entries(variables)) {
111
+ const value = Object.hasOwn(ctx, name) ? ctx[name] : void 0;
112
+ invalidContext(describeMismatch(value, type, name, budget), source);
113
+ }
114
+ }
115
+ /**
116
+ * Why the value read along `segments` (from index `at`) does not conform, or
117
+ * undefined. Each step is judged by its own declared type: an absent or null
118
+ * value is fine only where that type is nullable; a declared field is followed
119
+ * (a missing required one fails); an undeclared key of an object (objects are
120
+ * open) or an absent record entry ends the check; past a type that is not a
121
+ * map, and at the end of the path, the value is checked whole, as a context is.
122
+ * Fields the path does not name are not checked.
123
+ */
124
+ function describeAlong(value, type, segments, at, path, budget) {
125
+ if (at === segments.length) return describeMismatch(value, type, path, budget);
126
+ if (--budget.remaining < 0) budget.onExhausted("steps");
127
+ if (type.kind === "union") {
128
+ let first;
129
+ for (const member of type.types) {
130
+ const problem = describeAlong(value, member, segments, at, path, budget);
131
+ if (problem === void 0) return void 0;
132
+ first ??= problem;
133
+ }
134
+ return first;
135
+ }
136
+ const actual = value === void 0 ? null : value;
137
+ if (type.kind !== "map" || !isMap(actual)) return describeMismatch(value, type, path, budget);
138
+ const key = segments[at];
139
+ const field = Object.hasOwn(actual, key) ? actual[key] : void 0;
140
+ if (Object.hasOwn(type.fields, key)) return describeAlong(field, type.fields[key], segments, at + 1, `${path}.${key}`, budget);
141
+ if (type.rest === void 0 || field === void 0) return void 0;
142
+ return describeAlong(field, type.rest, segments, at + 1, `${path}.${key}`, budget);
143
+ }
144
+ /**
145
+ * Validates the values a residual reads: each path in `dependsOn`, step by
146
+ * step against the types declared along it (see describeAlong), so a context
147
+ * holding exactly those paths passes and invalid data on them fails as it
148
+ * would for the program.
149
+ */
150
+ function validatePaths(ctx, variables, paths, source, limits) {
151
+ const budget = validationBudget(source, limits);
152
+ for (const path of paths) {
153
+ const segments = path.split(".");
154
+ const name = segments[0];
155
+ if (!Object.hasOwn(variables, name)) continue;
156
+ invalidContext(describeAlong(Object.hasOwn(ctx, name) ? ctx[name] : void 0, variables[name], segments, 1, name, budget), source);
157
+ }
158
+ }
159
+ /**
160
+ * Reading host data (a getter, a Proxy trap, a `then` hook) can throw. Such a
161
+ * failure is a HOST_ERROR, never a raw JavaScript error. Only the errors this
162
+ * evaluation raised pass through: any other Bonsai error (from another
163
+ * evaluation, from parsing, checking, or translating, or built by the host)
164
+ * that host data threw is the host data's failure too.
165
+ */
166
+ function hostDataFailure(error, source) {
167
+ if (isOwn(error)) return error;
168
+ if (isStackOverflow(error)) return raised(tooDeep(source, error));
169
+ return raised(new BonsaiRuntimeError("HOST_ERROR", `Reading host data failed: ${errorText(error)}`, {
170
+ source,
171
+ cause: error
172
+ }));
173
+ }
174
+ /** The most zones one program warms when it is compiled. */
175
+ const WARMED_ZONES = 16;
176
+ /**
177
+ * Loads the time zone data a program's calls need, so its first evaluation
178
+ * does not pay for it inside its deadline. Only built-in functions that take a
179
+ * zone count: a zone written as text is loaded itself, and any other zone
180
+ * argument loads the data every zone shares. Each zone is warmed once, and at
181
+ * most WARMED_ZONES of them, so a source full of zone names compiles quickly.
182
+ */
183
+ function warmZones(analysis) {
184
+ const seen = /* @__PURE__ */ new Set();
185
+ for (const [call, plan] of analysis.calls) {
186
+ const zone = plan.def.zone;
187
+ const arg = zone === void 0 ? void 0 : call.args[zone.at];
188
+ if (arg === void 0) continue;
189
+ const name = arg.type === "Literal" ? arg.value : void 0;
190
+ if (name !== void 0 && typeof name !== "string" || seen.has(name)) continue;
191
+ seen.add(name);
192
+ zone?.warm(name);
193
+ if (seen.size >= WARMED_ZONES) return;
194
+ }
195
+ }
196
+ function contextOf(value) {
197
+ if (value === void 0 || value === null) return EMPTY_CONTEXT;
198
+ let map;
199
+ try {
200
+ map = typeof value === "object" && isMap(value);
201
+ } catch (error) {
202
+ throw new BonsaiRuntimeError("HOST_ERROR", "Reading the evaluation context failed", { cause: error });
203
+ }
204
+ if (!map) throw new TypeError("The evaluation context must be a plain object or class instance");
205
+ return value;
206
+ }
207
+ /**
208
+ * How a compiled program runs: per-evaluation limits, context validation, the
209
+ * pooled synchronous State, the result guard, and settling async results.
210
+ * Shared by environments and by anything else that builds programs, so every
211
+ * program evaluates the same way.
212
+ */
213
+ function programRunner({ source, analysis, guard, settings, constants, residualOf }) {
214
+ const checked = (result, state) => {
215
+ if (guard !== void 0 && !conforms(result, guard, state)) throw raised(new BonsaiRuntimeError("TYPE_ERROR", `The result does not match the expected type ${formatType(guard)}`, { source }));
216
+ };
217
+ const defaultLimits = evaluationLimits(void 0, settings);
218
+ const limitsOf = (options) => options === void 0 ? defaultLimits : evaluationLimits(options, settings);
219
+ let syncCode;
220
+ let asyncCode;
221
+ let pooled;
222
+ let pooledInUse = false;
223
+ const compileOptions = constants === void 0 ? {} : {
224
+ constants,
225
+ shapes: residualOf?.shapes
226
+ };
227
+ const syncProgram = () => syncCode ??= compileProgram(analysis, "sync", compileOptions);
228
+ const asyncProgram = () => asyncCode ??= analysis.async ? compileProgram(analysis, "async", compileOptions) : syncProgram();
229
+ function prepare(state, ctx, limits, locals) {
230
+ state.reset(ctx, source, locals, limits.maxSteps, limits.timeout, limits.signal);
231
+ if (limits.now !== void 0) state.nowValue = limits.now;
232
+ const residual = residualOf;
233
+ if (residual !== void 0 && residual.readsContext) {
234
+ const roots = residual.knownRoots;
235
+ state.charge(roots.length);
236
+ const declared = settings.strict ? settings.variables : void 0;
237
+ const missing = roots.find((name) => {
238
+ if (declared !== void 0 && !Object.hasOwn(declared, name)) return false;
239
+ const descriptor = Object.getOwnPropertyDescriptor(ctx, name);
240
+ return descriptor === void 0 || "value" in descriptor && descriptor.value === void 0;
241
+ });
242
+ if (missing !== void 0) throw raised(new BonsaiRuntimeError("INVALID_CONTEXT", `Invalid context: ${missing} is missing; this residual calls a call: true host function, so pass the full context, as for the program`, { source }));
243
+ }
244
+ const validation = limits.validateContext ?? settings.validateContext ? settings.variables : void 0;
245
+ if (validation !== void 0) {
246
+ const validationLimits = {
247
+ maxDepth: settings.runtimeLimits.maxValueDepth,
248
+ maxSteps: limits.maxSteps,
249
+ timeout: limits.timeout
250
+ };
251
+ if (residual === void 0 || residual.readsContext) validateContext(ctx, validation, source, validationLimits);
252
+ else validatePaths(ctx, validation, residual.dependsOn, source, validationLimits);
253
+ }
254
+ }
255
+ function runSync(context, options) {
256
+ if (analysis.async) throw escaping(new BonsaiRuntimeError("ASYNC_IN_SYNC", "This expression calls an async function; use evaluate() instead of evaluateSync()", { source }));
257
+ const code = syncProgram();
258
+ const limits = limitsOf(options);
259
+ const ctx = contextOf(context);
260
+ let state;
261
+ const reuse = !pooledInUse;
262
+ if (reuse) {
263
+ pooled ??= new State(settings.runtimeLimits, settings.clock);
264
+ state = pooled;
265
+ pooledInUse = true;
266
+ } else state = new State(settings.runtimeLimits, settings.clock);
267
+ try {
268
+ prepare(state, ctx, limits, code.localCount);
269
+ const result = code.run(state);
270
+ checked(result, state);
271
+ state.checkTime();
272
+ return result;
273
+ } catch (error) {
274
+ throw escaping(hostDataFailure(error, source));
275
+ } finally {
276
+ state.release();
277
+ if (reuse) pooledInUse = false;
278
+ }
279
+ }
280
+ async function runAsync(context, options) {
281
+ if (!analysis.async) {
282
+ const result = runSync(context, options);
283
+ try {
284
+ return settle(result);
285
+ } catch (error) {
286
+ throw escaping(hostDataFailure(error, source));
287
+ }
288
+ }
289
+ const code = asyncProgram();
290
+ const limits = limitsOf(options);
291
+ const ctx = contextOf(context);
292
+ const state = new State(settings.runtimeLimits, settings.clock);
293
+ try {
294
+ prepare(state, ctx, limits, code.localCount);
295
+ const result = (await code.run(state)).v;
296
+ checked(result, state);
297
+ state.checkTime();
298
+ return settle(result);
299
+ } catch (error) {
300
+ throw escaping(hostDataFailure(error, source));
301
+ } finally {
302
+ state.release();
303
+ }
304
+ }
305
+ function settle(result) {
306
+ if (result !== null && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") throw raised(new BonsaiRuntimeError("TYPE_ERROR", "evaluate() cannot return a promise-like value; use evaluateSync()", { source }));
307
+ return result;
308
+ }
309
+ return {
310
+ prepare,
311
+ checked,
312
+ settle,
313
+ runSync,
314
+ runAsync,
315
+ compileOptions
316
+ };
317
+ }
318
+ //#endregion
319
+ export { EVALUATE_OPTION_KEYS, assertKeys, contextOf, evaluationLimits, hostDataFailure, invalidContext, isValidDate, numberOption, programRunner, timeoutOption, validationBudget, warmZones };
320
+
321
+ //# sourceMappingURL=program.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.mjs","names":[],"sources":["../src/program.ts"],"sourcesContent":["import type { Analysis } from './check/checker.js'\nimport { compileProgram, type CompiledProgram } from './compile/compiler.js'\nimport type { EvaluateOptions, ResidualOf, Settings } from './environment.js'\nimport { BonsaiLimitError, BonsaiRuntimeError, type BonsaiError } from './errors.js'\nimport {\n conforms,\n describeMismatch,\n isRecord,\n type Out,\n type ValidationBudget,\n} from './functions/define.js'\nimport { escaping, isOwn, raised } from './runtime/attribution.js'\nimport { isStackOverflow, tooDeep } from './runtime/overflow.js'\nimport { State } from './runtime/state.js'\nimport { errorText, isMap } from './runtime/values.js'\nimport { formatType, type Type } from './types.js'\n\n/** What running a program needs from its analysis. */\nexport type ProgramAnalysis = Pick<Analysis, 'root' | 'calls' | 'async'>\n\nconst EMPTY_CONTEXT: Record<string, unknown> = Object.freeze({})\n\n/** A number option: TypeError when it is not a number, RangeError when out of range. */\nexport function numberOption(\n name: string,\n value: unknown,\n fallback: number,\n minimum: number,\n): number {\n if (value === undefined) return fallback\n if (typeof value !== 'number') throw new TypeError(`${name} must be a number`)\n if (!Number.isInteger(value) || value < minimum) {\n throw new RangeError(\n minimum === 0\n ? `${name} must be a non-negative integer (0 disables it)`\n : `${name} must be a positive integer`,\n )\n }\n return value\n}\n\n/**\n * A timeout in milliseconds: 0 turns it off; any other non-negative number,\n * fractions included, is a limit, so a budget computed down to its last\n * fraction of a millisecond stays a limit rather than becoming 0.\n */\nexport function timeoutOption(name: string, value: unknown, fallback: number): number {\n if (value === undefined) return fallback\n if (typeof value !== 'number') throw new TypeError(`${name} must be a number`)\n if (!Number.isFinite(value) || value < 0) {\n throw new RangeError(`${name} must be a non-negative number of milliseconds (0 disables it)`)\n }\n return value\n}\n\nexport function assertKeys(value: unknown, allowed: ReadonlySet<string>, what: string): void {\n if (!isRecord(value)) throw new TypeError(`${what} must be an object`)\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) {\n throw new TypeError(\n `Unknown ${what.toLowerCase()} key \"${key}\" (expected one of: ${[...allowed].join(', ')})`,\n )\n }\n }\n}\n\nexport const EVALUATE_OPTION_KEYS = new Set([\n 'timeout',\n 'maxSteps',\n 'signal',\n 'now',\n 'validateContext',\n])\n\nexport interface EvaluationLimits {\n readonly maxSteps: number\n readonly timeout: number\n readonly signal: AbortSignal | undefined\n /** The time now() returns, when the caller fixed it. */\n readonly now: Date | undefined\n /** Whether to validate the context, when the caller overrides the environment. */\n readonly validateContext: boolean | undefined\n}\n\n/** Whether a value is a valid Date (an object that only inherits from Date.prototype is not). */\nexport function isValidDate(value: unknown): value is Date {\n try {\n return value instanceof Date && !Number.isNaN(Date.prototype.getTime.call(value))\n } catch {\n return false\n }\n}\n\n/** Whether a value has an AbortSignal's members; a signal whose getters throw does not. */\nfunction looksLikeSignal(signal: object): boolean {\n try {\n const s = signal as Record<string, unknown>\n return (\n typeof s.aborted === 'boolean' &&\n typeof s.addEventListener === 'function' &&\n typeof s.removeEventListener === 'function'\n )\n } catch {\n return false\n }\n}\n\n/** Validates per-evaluation options the way `limits` are validated; nothing fails open. */\nexport function evaluationLimits(options: unknown, settings: Settings): EvaluationLimits {\n if (options === undefined) {\n return {\n maxSteps: settings.runtimeLimits.maxSteps,\n timeout: settings.timeout,\n signal: undefined,\n now: undefined,\n validateContext: undefined,\n }\n }\n // Options are the caller's own object; one whose getters or Proxy traps throw\n // is invalid options (a TypeError), never an error from inside evaluation.\n let o: Record<string, unknown>\n try {\n assertKeys(options, EVALUATE_OPTION_KEYS, 'Evaluate option')\n const read = options as Record<string, unknown>\n o = {\n maxSteps: read.maxSteps,\n timeout: read.timeout,\n signal: read.signal,\n now: read.now,\n validateContext: read.validateContext,\n }\n } catch (error) {\n if (error instanceof TypeError || error instanceof RangeError) throw error\n throw new TypeError(`Evaluate options could not be read: ${errorText(error)}`, { cause: error })\n }\n const signal = o.signal\n if (\n signal !== undefined &&\n (typeof signal !== 'object' || signal === null || !looksLikeSignal(signal))\n ) {\n throw new TypeError('signal must be an AbortSignal')\n }\n if (o.now !== undefined && !isValidDate(o.now)) throw new TypeError('now must be a valid Date')\n if (o.validateContext !== undefined && typeof o.validateContext !== 'boolean')\n throw new TypeError('validateContext must be a boolean')\n return {\n maxSteps: numberOption('maxSteps', o.maxSteps, settings.runtimeLimits.maxSteps, 0),\n timeout: timeoutOption('timeout', o.timeout, settings.timeout),\n // A structurally checked AbortSignalLike; evaluation only reads the members checked above.\n signal: signal as AbortSignal | undefined,\n now: o.now,\n validateContext: o.validateContext,\n }\n}\n\ninterface ValidationLimits {\n readonly maxDepth: number\n readonly maxSteps: number\n readonly timeout: number\n}\n\nexport function validationBudget(source: string, limits: ValidationLimits): ValidationBudget {\n return {\n maxDepth: limits.maxDepth,\n remaining: limits.maxSteps > 0 ? limits.maxSteps : Number.POSITIVE_INFINITY,\n deadline: limits.timeout > 0 ? performance.now() + limits.timeout : 0,\n onExhausted: (reason) => {\n if (reason === 'depth') {\n throw raised(\n new BonsaiLimitError(\n 'VALUE_DEPTH_LIMIT',\n `Context nests deeper than ${limits.maxDepth}`,\n {\n source,\n },\n ),\n )\n }\n if (reason === 'time')\n throw raised(new BonsaiLimitError('TIMEOUT', 'Context validation timed out', { source }))\n throw raised(\n new BonsaiLimitError('STEP_LIMIT', 'Context validation exceeded the step limit', {\n source,\n }),\n )\n },\n }\n}\n\nexport function invalidContext(problem: string | undefined, source: string): void {\n if (problem !== undefined) {\n throw raised(\n new BonsaiRuntimeError('INVALID_CONTEXT', `Invalid context: ${problem}`, { source }),\n )\n }\n}\n\nfunction validateContext(\n ctx: Record<string, unknown>,\n variables: Readonly<Record<string, Type>>,\n source: string,\n limits: ValidationLimits,\n): void {\n const budget = validationBudget(source, limits)\n for (const [name, type] of Object.entries(variables)) {\n const value = Object.hasOwn(ctx, name) ? ctx[name] : undefined\n invalidContext(describeMismatch(value, type, name, budget), source)\n }\n}\n\n/**\n * Why the value read along `segments` (from index `at`) does not conform, or\n * undefined. Each step is judged by its own declared type: an absent or null\n * value is fine only where that type is nullable; a declared field is followed\n * (a missing required one fails); an undeclared key of an object (objects are\n * open) or an absent record entry ends the check; past a type that is not a\n * map, and at the end of the path, the value is checked whole, as a context is.\n * Fields the path does not name are not checked.\n */\nfunction describeAlong(\n value: unknown,\n type: Type,\n segments: readonly string[],\n at: number,\n path: string,\n budget: ValidationBudget,\n): string | undefined {\n if (at === segments.length) return describeMismatch(value, type, path, budget)\n if (--budget.remaining < 0) budget.onExhausted('steps')\n if (type.kind === 'union') {\n let first: string | undefined\n for (const member of type.types) {\n const problem = describeAlong(value, member, segments, at, path, budget)\n if (problem === undefined) return undefined\n first ??= problem\n }\n return first\n }\n const actual = value === undefined ? null : value\n if (type.kind !== 'map' || !isMap(actual)) return describeMismatch(value, type, path, budget)\n const key = segments[at]\n const field = Object.hasOwn(actual, key) ? actual[key] : undefined\n if (Object.hasOwn(type.fields, key))\n return describeAlong(field, type.fields[key], segments, at + 1, `${path}.${key}`, budget)\n if (type.rest === undefined || field === undefined) return undefined\n return describeAlong(field, type.rest, segments, at + 1, `${path}.${key}`, budget)\n}\n\n/**\n * Validates the values a residual reads: each path in `dependsOn`, step by\n * step against the types declared along it (see describeAlong), so a context\n * holding exactly those paths passes and invalid data on them fails as it\n * would for the program.\n */\nfunction validatePaths(\n ctx: Record<string, unknown>,\n variables: Readonly<Record<string, Type>>,\n paths: readonly string[],\n source: string,\n limits: ValidationLimits,\n): void {\n const budget = validationBudget(source, limits)\n for (const path of paths) {\n const segments = path.split('.')\n const name = segments[0]\n if (!Object.hasOwn(variables, name)) continue\n const value = Object.hasOwn(ctx, name) ? ctx[name] : undefined\n invalidContext(describeAlong(value, variables[name], segments, 1, name, budget), source)\n }\n}\n\n/**\n * Reading host data (a getter, a Proxy trap, a `then` hook) can throw. Such a\n * failure is a HOST_ERROR, never a raw JavaScript error. Only the errors this\n * evaluation raised pass through: any other Bonsai error (from another\n * evaluation, from parsing, checking, or translating, or built by the host)\n * that host data threw is the host data's failure too.\n */\nexport function hostDataFailure(error: unknown, source: string): BonsaiError {\n if (isOwn(error)) return error\n if (isStackOverflow(error)) return raised(tooDeep(source, error))\n return raised(\n new BonsaiRuntimeError('HOST_ERROR', `Reading host data failed: ${errorText(error)}`, {\n source,\n cause: error,\n }),\n )\n}\n\n/** The most zones one program warms when it is compiled. */\nconst WARMED_ZONES = 16\n\n/**\n * Loads the time zone data a program's calls need, so its first evaluation\n * does not pay for it inside its deadline. Only built-in functions that take a\n * zone count: a zone written as text is loaded itself, and any other zone\n * argument loads the data every zone shares. Each zone is warmed once, and at\n * most WARMED_ZONES of them, so a source full of zone names compiles quickly.\n */\nexport function warmZones(analysis: Pick<Analysis, 'calls'>): void {\n const seen = new Set<string | undefined>()\n for (const [call, plan] of analysis.calls) {\n const zone = plan.def.zone\n const arg = zone === undefined ? undefined : call.args[zone.at]\n if (arg === undefined) continue\n const name = arg.type === 'Literal' ? arg.value : undefined\n if ((name !== undefined && typeof name !== 'string') || seen.has(name)) continue\n seen.add(name)\n zone?.warm(name)\n if (seen.size >= WARMED_ZONES) return\n }\n}\n\nexport function contextOf(value: unknown): Record<string, unknown> {\n if (value === undefined || value === null) return EMPTY_CONTEXT\n let map: boolean\n try {\n // A Proxy (even a revoked one) answers these through its traps.\n map = typeof value === 'object' && isMap(value)\n } catch (error) {\n throw new BonsaiRuntimeError('HOST_ERROR', 'Reading the evaluation context failed', {\n cause: error,\n })\n }\n // A caller's mistake, like invalid options, not an error in the expression's data.\n if (!map) throw new TypeError('The evaluation context must be a plain object or class instance')\n return value as Record<string, unknown>\n}\n\nexport interface RunnerInput {\n readonly source: string\n readonly analysis: ProgramAnalysis\n /** The type the result is checked against at run time, when the checker could not prove it. */\n readonly guard: Type | undefined\n readonly settings: Settings\n readonly constants?: ReadonlyMap<string, unknown> | undefined\n readonly residualOf?: ResidualOf | undefined\n}\n\n/**\n * How a compiled program runs: per-evaluation limits, context validation, the\n * pooled synchronous State, the result guard, and settling async results.\n * Shared by environments and by anything else that builds programs, so every\n * program evaluates the same way.\n */\nexport function programRunner<R>({\n source,\n analysis,\n guard,\n settings,\n constants,\n residualOf,\n}: RunnerInput) {\n const checked = (result: unknown, state: State): void => {\n if (guard !== undefined && !conforms(result, guard, state)) {\n throw raised(\n new BonsaiRuntimeError(\n 'TYPE_ERROR',\n `The result does not match the expected type ${formatType(guard)}`,\n { source },\n ),\n )\n }\n }\n // The limits of an evaluation without options: built once, not per run.\n const defaultLimits = evaluationLimits(undefined, settings)\n const limitsOf = (options: EvaluateOptions | undefined): EvaluationLimits =>\n options === undefined ? defaultLimits : evaluationLimits(options, settings)\n let syncCode: CompiledProgram | undefined\n let asyncCode: CompiledProgram | undefined\n let pooled: State | undefined\n let pooledInUse = false\n\n const compileOptions = constants === undefined ? {} : { constants, shapes: residualOf?.shapes }\n const syncProgram = (): CompiledProgram =>\n (syncCode ??= compileProgram(analysis, 'sync', compileOptions))\n const asyncProgram = (): CompiledProgram =>\n (asyncCode ??= analysis.async\n ? compileProgram(analysis, 'async', compileOptions)\n : syncProgram())\n\n // Options and the context's shape are the caller's own mistakes (TypeError,\n // RangeError, INVALID_ARGUMENT), so they are checked before anything reads\n // host data and are never reported as host failures.\n function prepare(\n state: State,\n ctx: Record<string, unknown>,\n limits: EvaluationLimits,\n locals: number,\n ): void {\n state.reset(ctx, source, locals, limits.maxSteps, limits.timeout, limits.signal)\n if (limits.now !== undefined) state.nowValue = limits.now\n const residual = residualOf\n if (residual !== undefined && residual.readsContext) {\n // call: true functions read the whole context, which must then hold\n // the variables partial() knew, as the program's context would.\n const roots = residual.knownRoots\n state.charge(roots.length)\n // In a strict environment, a known key that is not a declared variable is\n // not part of any context the program accepts, so it is not required.\n const declared = settings.strict ? settings.variables : undefined\n // Decided from the property itself, never running a getter: an accessor is\n // present, and so is a data property unless it holds undefined.\n const missing = roots.find((name) => {\n if (declared !== undefined && !Object.hasOwn(declared, name)) return false\n const descriptor = Object.getOwnPropertyDescriptor(ctx, name)\n return descriptor === undefined || ('value' in descriptor && descriptor.value === undefined)\n })\n if (missing !== undefined) {\n throw raised(\n new BonsaiRuntimeError(\n 'INVALID_CONTEXT',\n `Invalid context: ${missing} is missing; this residual calls a call: true host function, so pass the full context, as for the program`,\n { source },\n ),\n )\n }\n }\n const validation =\n (limits.validateContext ?? settings.validateContext) ? settings.variables : undefined\n if (validation !== undefined) {\n const validationLimits = {\n maxDepth: settings.runtimeLimits.maxValueDepth,\n maxSteps: limits.maxSteps,\n timeout: limits.timeout,\n }\n if (residual === undefined || residual.readsContext)\n validateContext(ctx, validation, source, validationLimits)\n else validatePaths(ctx, validation, residual.dependsOn, source, validationLimits)\n }\n }\n\n function runSync(context: unknown, options: EvaluateOptions | undefined): R {\n if (analysis.async) {\n throw escaping(\n new BonsaiRuntimeError(\n 'ASYNC_IN_SYNC',\n 'This expression calls an async function; use evaluate() instead of evaluateSync()',\n { source },\n ),\n )\n }\n const code = syncProgram()\n const limits = limitsOf(options)\n const ctx = contextOf(context)\n let state: State\n const reuse = !pooledInUse\n if (reuse) {\n pooled ??= new State(settings.runtimeLimits, settings.clock)\n state = pooled\n pooledInUse = true\n } else {\n state = new State(settings.runtimeLimits, settings.clock)\n }\n try {\n prepare(state, ctx, limits, code.localCount)\n const result = code.run(state)\n checked(result, state)\n state.checkTime()\n return result as R\n } catch (error) {\n throw escaping(hostDataFailure(error, source))\n } finally {\n state.release()\n if (reuse) pooledInUse = false\n }\n }\n\n async function runAsync(context: unknown, options: EvaluateOptions | undefined): Promise<R> {\n if (!analysis.async) {\n const result = runSync(context, options)\n try {\n return settle(result)\n } catch (error) {\n throw escaping(hostDataFailure(error, source))\n }\n }\n const code = asyncProgram()\n const limits = limitsOf(options)\n const ctx = contextOf(context)\n const state = new State(settings.runtimeLimits, settings.clock)\n try {\n prepare(state, ctx, limits, code.localCount)\n const result = ((await code.run(state)) as Out).v\n checked(result, state)\n state.checkTime()\n return settle(result as R)\n } catch (error) {\n throw escaping(hostDataFailure(error, source))\n } finally {\n state.release()\n }\n }\n\n // Returning a thenable from an async function would call its `then`\n // (an ORM query object would execute), so it is an error instead.\n function settle(result: R): R {\n if (\n result !== null &&\n (typeof result === 'object' || typeof result === 'function') &&\n typeof (result as { then?: unknown }).then === 'function'\n ) {\n throw raised(\n new BonsaiRuntimeError(\n 'TYPE_ERROR',\n 'evaluate() cannot return a promise-like value; use evaluateSync()',\n { source },\n ),\n )\n }\n return result\n }\n return { prepare, checked, settle, runSync, runAsync, compileOptions }\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { BonsaiLimitError, BonsaiTranslationError } from "../errors.mjs";
2
2
  import { Duration, isMap, isValidDuration } from "../runtime/values.mjs";
3
3
  import { formatType } from "../types.mjs";
4
- import { closest, didYouMean } from "../suggest.mjs";
5
4
  import { forEachChild } from "../syntax/ast.mjs";
5
+ import { closest, didYouMean } from "../suggest.mjs";
6
6
  import { declaredVariables } from "../declared.mjs";
7
7
  import { failureAt, translating } from "../partial.mjs";
8
8
  //#region src/query/index.ts
@@ -0,0 +1,18 @@
1
+ import { ArtifactProblem, BonsaiArtifactError, BonsaiError, BonsaiErrorJSON, BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, ErrorCode, Span, isBonsaiError } from "../errors.mjs";
2
+ import { Duration } from "./values.mjs";
3
+ import { Infer, InferVariables, Type, formatType, t } from "../types.mjs";
4
+ import { AbortSignalLike, Builtin, ContextFor, EvaluateOptions, FnSpec, HostCall, HostFunction, Library, Limits, NoVariables, fn, withContext } from "../environment.mjs";
5
+ import { Artifact, Plan } from "../artifact.mjs";
6
+ import { CoreRuntime, CoreRuntimeOptions, LoadOptions, RuntimeProgram } from "./create.mjs";
7
+ //#region src/runtime/core.d.ts
8
+ /**
9
+ * Creates a runtime with the built-ins in `builtins` (default none). An
10
+ * artifact that calls a function the runtime does not define is refused with
11
+ * reason `function`, so pass the same list to the bonsai() that checks the
12
+ * rules, which then reports such a call when the rule is saved.
13
+ */
14
+ export declare function runtime<const V extends Readonly<Record<string, Type>> = NoVariables, const L extends readonly Library[] = [], const S extends boolean | undefined = undefined>(options?: CoreRuntimeOptions<V, L> & {
15
+ readonly strict?: S;
16
+ }): CoreRuntime<ContextFor<V, L, S>>;
17
+ //#endregion
18
+ export { type AbortSignalLike, type Artifact, type ArtifactProblem, BonsaiArtifactError, BonsaiError, type BonsaiErrorJSON, type BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, type Builtin, type CoreRuntime, type CoreRuntimeOptions, Duration, type ErrorCode, type EvaluateOptions, type FnSpec, type HostCall, type HostFunction, type Infer, type InferVariables, type Library, type Limits, type LoadOptions, type Plan, type RuntimeProgram, type Span, type Type, fn, formatType, isBonsaiError, t, withContext };
@@ -0,0 +1,31 @@
1
+ import { BonsaiArtifactError, BonsaiError, BonsaiLimitError, BonsaiRuntimeError, isBonsaiError } from "../errors.mjs";
2
+ import { Duration } from "./values.mjs";
3
+ import { formatType, t } from "../types.mjs";
4
+ import { fn, settingsFrom, withContext } from "../environment.mjs";
5
+ import { assertRuntimeOptions, createRuntime } from "./create.mjs";
6
+ //#region src/runtime/core.ts
7
+ /**
8
+ * `bonsai-js/runtime/core`: bonsai-js/runtime with only the built-ins passed
9
+ * in `builtins`, so a bundle carries just the functions its rules call.
10
+ *
11
+ * ```ts
12
+ * import { runtime } from 'bonsai-js/runtime/core'
13
+ * import * as b from 'bonsai-js/builtins'
14
+ *
15
+ * const rt = runtime({ variables, builtins: [b.includes, b.some] })
16
+ * ```
17
+ */
18
+ /**
19
+ * Creates a runtime with the built-ins in `builtins` (default none). An
20
+ * artifact that calls a function the runtime does not define is refused with
21
+ * reason `function`, so pass the same list to the bonsai() that checks the
22
+ * rules, which then reports such a call when the rule is saved.
23
+ */
24
+ function runtime(options = {}) {
25
+ assertRuntimeOptions(options, true);
26
+ return createRuntime(settingsFrom(void 0, options), true);
27
+ }
28
+ //#endregion
29
+ export { BonsaiArtifactError, BonsaiError, BonsaiLimitError, BonsaiRuntimeError, Duration, fn, formatType, isBonsaiError, runtime, t, withContext };
30
+
31
+ //# sourceMappingURL=core.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.mjs","names":[],"sources":["../../src/runtime/core.ts"],"sourcesContent":["/**\n * `bonsai-js/runtime/core`: bonsai-js/runtime with only the built-ins passed\n * in `builtins`, so a bundle carries just the functions its rules call.\n *\n * ```ts\n * import { runtime } from 'bonsai-js/runtime/core'\n * import * as b from 'bonsai-js/builtins'\n *\n * const rt = runtime({ variables, builtins: [b.includes, b.some] })\n * ```\n */\nimport { settingsFrom, type ContextFor, type Library, type NoVariables } from '../environment.js'\nimport type { Type } from '../types.js'\nimport {\n assertRuntimeOptions,\n createRuntime,\n type CoreRuntime,\n type CoreRuntimeOptions,\n} from './create.js'\n\n/**\n * Creates a runtime with the built-ins in `builtins` (default none). An\n * artifact that calls a function the runtime does not define is refused with\n * reason `function`, so pass the same list to the bonsai() that checks the\n * rules, which then reports such a call when the rule is saved.\n */\nexport function runtime<\n const V extends Readonly<Record<string, Type>> = NoVariables,\n const L extends readonly Library[] = [],\n const S extends boolean | undefined = undefined,\n>(\n options: CoreRuntimeOptions<V, L> & { readonly strict?: S } = {},\n): CoreRuntime<ContextFor<V, L, S>> {\n assertRuntimeOptions(options, true)\n // createRuntime builds both kinds; with `core`, its extend takes `builtins`.\n return createRuntime<ContextFor<V, L, S>>(\n settingsFrom(undefined, options),\n true,\n ) as unknown as CoreRuntime<ContextFor<V, L, S>>\n}\n\nexport type { CoreRuntime, CoreRuntimeOptions } from './create.js'\nexport type { Builtin } from '../environment.js'\nexport * from './exports.js'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,QAKd,UAA8D,CAAC,GAC7B;CAClC,qBAAqB,SAAS,IAAI;CAElC,OAAO,cACL,aAAa,KAAA,GAAW,OAAO,GAC/B,IACF;AACF"}
@@ -0,0 +1,61 @@
1
+ import { AnyType, Infer, Type } from "../types.mjs";
2
+ import { Args, EnvironmentOptions, Extended, Library, NoVariables } from "../environment.mjs";
3
+ import { Artifact } from "../artifact.mjs";
4
+ //#region src/runtime/create.d.ts
5
+ /**
6
+ * The options of `runtime()`: those of `bonsai()` that affect evaluation.
7
+ * `strict` has bonsai()'s default and must match the producer's: a strict
8
+ * runtime refuses artifacts that read undeclared variables. Every built-in is
9
+ * available; bonsai-js/runtime/core takes `builtins` instead.
10
+ */
11
+ export type RuntimeOptions<V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>, L extends readonly Library[] = readonly Library[]> = Omit<EnvironmentOptions<V, L>, 'builtins'> & {
12
+ /** Not an option here: bonsai-js/runtime has every built-in (see bonsai-js/runtime/core). */
13
+ readonly builtins?: never;
14
+ };
15
+ /**
16
+ * The options of bonsai-js/runtime/core's `runtime()`: those of
17
+ * bonsai-js/runtime, and `builtins`, the built-ins available (default none).
18
+ */
19
+ export type CoreRuntimeOptions<V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>, L extends readonly Library[] = readonly Library[]> = EnvironmentOptions<V, L>;
20
+ /** A loaded artifact: evaluates as the program it was made from does. Immutable. */
21
+ export interface RuntimeProgram<Ctx = any, R = unknown> {
22
+ readonly source: string;
23
+ /** Whether the expression calls an async host function (evaluateSync rejects it). */
24
+ readonly async: boolean;
25
+ evaluate: (...args: Args<Ctx>) => Promise<R>;
26
+ evaluateSync: (...args: Args<Ctx>) => R;
27
+ }
28
+ export interface LoadOptions<E extends Type = Type> {
29
+ /**
30
+ * The type the host relies on the result having. The artifact must have been
31
+ * compiled for exactly this type, and every result is checked against it.
32
+ */
33
+ readonly expect?: E | undefined;
34
+ }
35
+ /** Loads and evaluates artifacts against one set of definitions. Immutable. */
36
+ export interface Runtime<Ctx = any> extends Loader<Ctx> {
37
+ /** A runtime with more or replaced definitions; see `bonsai().extend`. */
38
+ extend: <const V2 extends Readonly<Record<string, Type>> = NoVariables, const L2 extends readonly Library[] = [], const S2 extends boolean | undefined = undefined>(options: RuntimeOptions<V2, L2> & {
39
+ readonly strict?: S2;
40
+ }) => Runtime<Extended<Ctx, V2, L2, S2>>;
41
+ }
42
+ /** A runtime from bonsai-js/runtime/core. Immutable. */
43
+ export interface CoreRuntime<Ctx = any> extends Loader<Ctx> {
44
+ /** A runtime with more or replaced definitions; `builtins` are added to this one's. */
45
+ extend: <const V2 extends Readonly<Record<string, Type>> = NoVariables, const L2 extends readonly Library[] = [], const S2 extends boolean | undefined = undefined>(options: CoreRuntimeOptions<V2, L2> & {
46
+ readonly strict?: S2;
47
+ }) => CoreRuntime<Extended<Ctx, V2, L2, S2>>;
48
+ }
49
+ interface Loader<Ctx = any> {
50
+ /**
51
+ * Loads an artifact. Throws BonsaiArtifactError (code ARTIFACT, with a
52
+ * `reason`) when the artifact does not reproduce the checked program with
53
+ * this runtime's definitions, and TypeError when the value is not an artifact.
54
+ */
55
+ load: <E extends Type = AnyType>(artifact: Artifact, options?: LoadOptions<E>) => RuntimeProgram<Ctx, Infer<E>>;
56
+ /** Loads (cached) and evaluates. `R` is an unchecked type, as for `env.evaluate`. */
57
+ evaluate: <R = unknown>(artifact: Artifact, ...args: Args<Ctx>) => Promise<R>;
58
+ /** Loads (cached) and evaluates synchronously. */
59
+ evaluateSync: <R = unknown>(artifact: Artifact, ...args: Args<Ctx>) => R;
60
+ }
61
+ //#endregion
@@ -0,0 +1,76 @@
1
+ import { ProgramCache, expectOption, settingsFrom } from "../environment.mjs";
2
+ import { assertExpectSize } from "../artifact.mjs";
3
+ import { artifactKey, loadArtifact, loadedRunner, readArtifact } from "../load.mjs";
4
+ //#region src/runtime/create.ts
5
+ /**
6
+ * What bonsai-js/runtime and bonsai-js/runtime/core share: loading and
7
+ * evaluating artifacts made by `toArtifact()`. They differ only in their
8
+ * built-ins: every one, or those passed in `builtins`.
9
+ */
10
+ const RUNTIME_KEYS = [
11
+ "variables",
12
+ "functions",
13
+ "libraries",
14
+ "limits",
15
+ "cacheSize",
16
+ "clock",
17
+ "validateContext",
18
+ "strict"
19
+ ];
20
+ /** The runtime takes bonsai()'s options (`builtins` only in bonsai-js/runtime/core). */
21
+ function assertRuntimeOptions(options, core) {
22
+ if (typeof options !== "object" || options === null) return;
23
+ let keys;
24
+ try {
25
+ keys = Object.keys(options);
26
+ } catch (error) {
27
+ throw new TypeError("Runtime options could not be read", { cause: error });
28
+ }
29
+ const allowed = core ? [...RUNTIME_KEYS, "builtins"] : RUNTIME_KEYS;
30
+ for (const key of keys) if (!allowed.includes(key)) throw new TypeError(key === "builtins" ? "Unknown runtime option key \"builtins\" (bonsai-js/runtime has every built-in; import runtime from bonsai-js/runtime/core to choose them)" : `Unknown runtime option key "${key}" (expected one of: ${allowed.join(", ")})`);
31
+ }
32
+ function createRuntime(settings, core) {
33
+ const cache = new ProgramCache(settings.cacheSize);
34
+ function load(artifact, expect) {
35
+ const loaded = loadArtifact(artifact, settings, expect);
36
+ const { runSync, runAsync } = loadedRunner(loaded, settings);
37
+ return Object.freeze({
38
+ source: loaded.source,
39
+ async: loaded.analysis.async,
40
+ evaluate: (...args) => runAsync(args[0], args[1]),
41
+ evaluateSync: (...args) => runSync(args[0], args[1])
42
+ });
43
+ }
44
+ function cached(artifact) {
45
+ const read = readArtifact(artifact, settings);
46
+ const key = artifactKey(read);
47
+ const known = cache.get(key);
48
+ if (known !== void 0) return known;
49
+ const program = load(read, void 0);
50
+ cache.set(key, program);
51
+ return program;
52
+ }
53
+ return Object.freeze({
54
+ load: (artifact, options) => {
55
+ const expect = expectOption(options, "Load");
56
+ if (expect !== void 0) assertExpectSize(expect, "Load option \"expect\"");
57
+ return load(readArtifact(artifact, settings), expect);
58
+ },
59
+ evaluate(artifact, ...args) {
60
+ try {
61
+ return cached(artifact).evaluate(...args);
62
+ } catch (error) {
63
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
64
+ }
65
+ },
66
+ evaluateSync: (artifact, ...args) => cached(artifact).evaluateSync(...args),
67
+ extend: (options) => {
68
+ assertRuntimeOptions(options, core);
69
+ return createRuntime(settingsFrom(settings, options), core);
70
+ }
71
+ });
72
+ }
73
+ //#endregion
74
+ export { assertRuntimeOptions, createRuntime };
75
+
76
+ //# sourceMappingURL=create.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.mjs","names":[],"sources":["../../src/runtime/create.ts"],"sourcesContent":["/**\n * What bonsai-js/runtime and bonsai-js/runtime/core share: loading and\n * evaluating artifacts made by `toArtifact()`. They differ only in their\n * built-ins: every one, or those passed in `builtins`.\n */\nimport { assertExpectSize, type Artifact } from '../artifact.js'\nimport {\n ProgramCache,\n expectOption,\n settingsFrom,\n type Args,\n type EnvironmentOptions,\n type Extended,\n type Library,\n type NoVariables,\n type Settings,\n} from '../environment.js'\nimport { artifactKey, loadArtifact, loadedRunner, readArtifact } from '../load.js'\nimport type { AnyType, Infer, Type } from '../types.js'\n\n/**\n * The options of `runtime()`: those of `bonsai()` that affect evaluation.\n * `strict` has bonsai()'s default and must match the producer's: a strict\n * runtime refuses artifacts that read undeclared variables. Every built-in is\n * available; bonsai-js/runtime/core takes `builtins` instead.\n */\nexport type RuntimeOptions<\n V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>,\n L extends readonly Library[] = readonly Library[],\n> = Omit<EnvironmentOptions<V, L>, 'builtins'> & {\n /** Not an option here: bonsai-js/runtime has every built-in (see bonsai-js/runtime/core). */\n readonly builtins?: never\n}\n\n/**\n * The options of bonsai-js/runtime/core's `runtime()`: those of\n * bonsai-js/runtime, and `builtins`, the built-ins available (default none).\n */\nexport type CoreRuntimeOptions<\n V extends Readonly<Record<string, Type>> = Readonly<Record<string, Type>>,\n L extends readonly Library[] = readonly Library[],\n> = EnvironmentOptions<V, L>\n\n/** A loaded artifact: evaluates as the program it was made from does. Immutable. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context, as for Program\nexport interface RuntimeProgram<Ctx = any, R = unknown> {\n readonly source: string\n /** Whether the expression calls an async host function (evaluateSync rejects it). */\n readonly async: boolean\n evaluate: (...args: Args<Ctx>) => Promise<R>\n evaluateSync: (...args: Args<Ctx>) => R\n}\n\nexport interface LoadOptions<E extends Type = Type> {\n /**\n * The type the host relies on the result having. The artifact must have been\n * compiled for exactly this type, and every result is checked against it.\n */\n readonly expect?: E | undefined\n}\n\n/** Loads and evaluates artifacts against one set of definitions. Immutable. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context, as for Environment\nexport interface Runtime<Ctx = any> extends Loader<Ctx> {\n /** A runtime with more or replaced definitions; see `bonsai().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: RuntimeOptions<V2, L2> & { readonly strict?: S2 },\n ) => Runtime<Extended<Ctx, V2, L2, S2>>\n}\n\n/** A runtime from bonsai-js/runtime/core. Immutable. */\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context, as for Environment\nexport interface CoreRuntime<Ctx = any> extends Loader<Ctx> {\n /** A runtime with more or replaced definitions; `builtins` are added to this one's. */\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: CoreRuntimeOptions<V2, L2> & { readonly strict?: S2 },\n ) => CoreRuntime<Extended<Ctx, V2, L2, S2>>\n}\n\n// oxlint-disable-next-line typescript/no-explicit-any -- the erased context, as for Environment\ninterface Loader<Ctx = any> {\n /**\n * Loads an artifact. Throws BonsaiArtifactError (code ARTIFACT, with a\n * `reason`) when the artifact does not reproduce the checked program with\n * this runtime's definitions, and TypeError when the value is not an artifact.\n */\n load: <E extends Type = AnyType>(\n artifact: Artifact,\n options?: LoadOptions<E>,\n ) => RuntimeProgram<Ctx, Infer<E>>\n /** Loads (cached) and evaluates. `R` is an unchecked type, as for `env.evaluate`. */\n evaluate: <R = unknown>(artifact: Artifact, ...args: Args<Ctx>) => Promise<R>\n /** Loads (cached) and evaluates synchronously. */\n evaluateSync: <R = unknown>(artifact: Artifact, ...args: Args<Ctx>) => R\n}\n\nconst RUNTIME_KEYS = [\n 'variables',\n 'functions',\n 'libraries',\n 'limits',\n 'cacheSize',\n 'clock',\n 'validateContext',\n 'strict',\n]\n\n/** The runtime takes bonsai()'s options (`builtins` only in bonsai-js/runtime/core). */\nexport function assertRuntimeOptions(options: unknown, core: boolean): void {\n if (typeof options !== 'object' || options === null) return\n let keys: string[]\n try {\n keys = Object.keys(options)\n } catch (error) {\n throw new TypeError('Runtime options could not be read', { cause: error })\n }\n const allowed = core ? [...RUNTIME_KEYS, 'builtins'] : RUNTIME_KEYS\n for (const key of keys) {\n if (!allowed.includes(key))\n throw new TypeError(\n key === 'builtins'\n ? 'Unknown runtime option key \"builtins\" (bonsai-js/runtime has every built-in; import runtime from bonsai-js/runtime/core to choose them)'\n : `Unknown runtime option key \"${key}\" (expected one of: ${allowed.join(', ')})`,\n )\n }\n}\n\nexport function createRuntime<Ctx>(settings: Settings, core: boolean): Runtime<Ctx> {\n const cache = new ProgramCache<RuntimeProgram<Ctx>>(settings.cacheSize)\n\n function load<R>(artifact: Artifact, expect: Type | undefined): RuntimeProgram<Ctx, R> {\n const loaded = loadArtifact(artifact, settings, expect)\n const { runSync, runAsync } = loadedRunner<R>(loaded, settings)\n return Object.freeze({\n source: loaded.source,\n async: loaded.analysis.async,\n evaluate: (...args: Args<Ctx>) => runAsync(args[0], args[1]),\n evaluateSync: (...args: Args<Ctx>) => runSync(args[0], args[1]),\n })\n }\n\n function cached(artifact: unknown): RuntimeProgram<Ctx> {\n const read = readArtifact(artifact, settings)\n const key = artifactKey(read)\n const known = cache.get(key)\n if (known !== undefined) return known\n const program = load(read, undefined)\n cache.set(key, program)\n return program\n }\n\n const rt: Runtime<Ctx> = {\n load: <E extends Type = AnyType>(artifact: Artifact, options?: LoadOptions<E>) => {\n const expect = expectOption(options, 'Load')\n if (expect !== undefined) assertExpectSize(expect, 'Load option \"expect\"')\n return load<Infer<E>>(readArtifact(artifact, settings), expect)\n },\n evaluate<R>(artifact: Artifact, ...args: Args<Ctx>): Promise<R> {\n try {\n return cached(artifact).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>(artifact: Artifact, ...args: Args<Ctx>) =>\n cached(artifact).evaluateSync(...args) as R,\n extend: (options) => {\n assertRuntimeOptions(options, core)\n return createRuntime(settingsFrom(settings, options), core)\n },\n }\n return Object.freeze(rt)\n}\n"],"mappings":";;;;;;;;;AAwGA,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAgB,qBAAqB,SAAkB,MAAqB;CAC1E,IAAI,OAAO,YAAY,YAAY,YAAY,MAAM;CACrD,IAAI;CACJ,IAAI;EACF,OAAO,OAAO,KAAK,OAAO;CAC5B,SAAS,OAAO;EACd,MAAM,IAAI,UAAU,qCAAqC,EAAE,OAAO,MAAM,CAAC;CAC3E;CACA,MAAM,UAAU,OAAO,CAAC,GAAG,cAAc,UAAU,IAAI;CACvD,KAAK,MAAM,OAAO,MAChB,IAAI,CAAC,QAAQ,SAAS,GAAG,GACvB,MAAM,IAAI,UACR,QAAQ,aACJ,8IACA,+BAA+B,IAAI,sBAAsB,QAAQ,KAAK,IAAI,EAAE,EAClF;AAEN;AAEA,SAAgB,cAAmB,UAAoB,MAA6B;CAClF,MAAM,QAAQ,IAAI,aAAkC,SAAS,SAAS;CAEtE,SAAS,KAAQ,UAAoB,QAAkD;EACrF,MAAM,SAAS,aAAa,UAAU,UAAU,MAAM;EACtD,MAAM,EAAE,SAAS,aAAa,aAAgB,QAAQ,QAAQ;EAC9D,OAAO,OAAO,OAAO;GACnB,QAAQ,OAAO;GACf,OAAO,OAAO,SAAS;GACvB,WAAW,GAAG,SAAoB,SAAS,KAAK,IAAI,KAAK,EAAE;GAC3D,eAAe,GAAG,SAAoB,QAAQ,KAAK,IAAI,KAAK,EAAE;EAChE,CAAC;CACH;CAEA,SAAS,OAAO,UAAwC;EACtD,MAAM,OAAO,aAAa,UAAU,QAAQ;EAC5C,MAAM,MAAM,YAAY,IAAI;EAC5B,MAAM,QAAQ,MAAM,IAAI,GAAG;EAC3B,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,MAAM,UAAU,KAAK,MAAM,KAAA,CAAS;EACpC,MAAM,IAAI,KAAK,OAAO;EACtB,OAAO;CACT;CAsBA,OAAO,OAAO,OAAO;EAnBnB,OAAiC,UAAoB,YAA6B;GAChF,MAAM,SAAS,aAAa,SAAS,MAAM;GAC3C,IAAI,WAAW,KAAA,GAAW,iBAAiB,QAAQ,wBAAsB;GACzE,OAAO,KAAe,aAAa,UAAU,QAAQ,GAAG,MAAM;EAChE;EACA,SAAY,UAAoB,GAAG,MAA6B;GAC9D,IAAI;IACF,OAAO,OAAO,QAAQ,CAAC,CAAC,SAAS,GAAG,IAAI;GAC1C,SAAS,OAAO;IACd,OAAO,QAAQ,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;GACjF;EACF;EACA,eAAkB,UAAoB,GAAG,SACvC,OAAO,QAAQ,CAAC,CAAC,aAAa,GAAG,IAAI;EACvC,SAAS,YAAY;GACnB,qBAAqB,SAAS,IAAI;GAClC,OAAO,cAAc,aAAa,UAAU,OAAO,GAAG,IAAI;EAC5D;CAEoB,CAAC;AACzB"}
@@ -0,0 +1,24 @@
1
+ import { ArtifactProblem, BonsaiArtifactError, BonsaiError, BonsaiErrorJSON, BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, ErrorCode, Span, isBonsaiError } from "../errors.mjs";
2
+ import { Duration } from "./values.mjs";
3
+ import { Infer, InferVariables, Type, formatType, t } from "../types.mjs";
4
+ import { AbortSignalLike, ContextFor, EvaluateOptions, FnSpec, HostCall, HostFunction, Library, Limits, NoVariables, fn, withContext } from "../environment.mjs";
5
+ import { Artifact, Plan } from "../artifact.mjs";
6
+ import { LoadOptions, Runtime, RuntimeOptions, RuntimeProgram } from "./create.mjs";
7
+ //#region src/runtime/index.d.ts
8
+ /**
9
+ * Creates a runtime: the host functions, variables, and limits artifacts are
10
+ * loaded and evaluated against. Declare them in a module the producing
11
+ * environment shares, since an artifact loads only where its called functions
12
+ * and read variables are declared exactly as where it was made.
13
+ *
14
+ * ```ts
15
+ * const rt = runtime({ variables, functions })
16
+ * const rule = rt.load(artifact, { expect: t.boolean() })
17
+ * rule.evaluateSync({ user: { age: 30 } }) // true
18
+ * ```
19
+ */
20
+ export declare function runtime<const V extends Readonly<Record<string, Type>> = NoVariables, const L extends readonly Library[] = [], const S extends boolean | undefined = undefined>(options?: RuntimeOptions<V, L> & {
21
+ readonly strict?: S;
22
+ }): Runtime<ContextFor<V, L, S>>;
23
+ //#endregion
24
+ export { type AbortSignalLike, type Artifact, type ArtifactProblem, BonsaiArtifactError, BonsaiError, type BonsaiErrorJSON, type BonsaiErrorName, BonsaiLimitError, BonsaiRuntimeError, Duration, type ErrorCode, type EvaluateOptions, type FnSpec, type HostCall, type HostFunction, type Infer, type InferVariables, type Library, type Limits, type LoadOptions, type Plan, type Runtime, type RuntimeOptions, type RuntimeProgram, type Span, type Type, fn, formatType, isBonsaiError, t, withContext };