bonsai-js 0.2.1 → 0.4.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.
- package/CHANGELOG.md +55 -5
- package/README.md +211 -8
- package/dist/autocomplete/index.d.mts +47 -0
- package/dist/autocomplete/index.d.mts.map +1 -0
- package/dist/autocomplete/index.mjs +1038 -0
- package/dist/autocomplete/index.mjs.map +1 -0
- package/dist/{errors-t5i9AEaf.mjs → errors-BONB6P_5.mjs} +2 -3
- package/dist/{errors-t5i9AEaf.mjs.map → errors-BONB6P_5.mjs.map} +1 -1
- package/dist/execution-context-B-5hQ92X.mjs +564 -0
- package/dist/execution-context-B-5hQ92X.mjs.map +1 -0
- package/dist/index.d.mts +16 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +148 -601
- package/dist/index.mjs.map +1 -1
- package/dist/stdlib/index.d.mts +1 -1
- package/dist/stdlib/index.mjs +1 -1
- package/dist/stdlib/index.mjs.map +1 -1
- package/dist/{types-ClpZDF5z.d.mts → types-C58c6SlH.d.mts} +73 -16
- package/dist/types-C58c6SlH.d.mts.map +1 -0
- package/package.json +6 -2
- package/dist/types-ClpZDF5z.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,477 +1,5 @@
|
|
|
1
|
-
import { a as attachLocation, c as offsetToPosition, i as ExpressionError, l as suggest, n as BonsaiSecurityError, o as formatBonsaiError, r as BonsaiTypeError, s as formatError, t as BonsaiReferenceError } from "./errors-
|
|
2
|
-
|
|
3
|
-
const KEYWORDS = new Map([
|
|
4
|
-
["true", {
|
|
5
|
-
type: "Boolean",
|
|
6
|
-
value: "true"
|
|
7
|
-
}],
|
|
8
|
-
["false", {
|
|
9
|
-
type: "Boolean",
|
|
10
|
-
value: "false"
|
|
11
|
-
}],
|
|
12
|
-
["null", {
|
|
13
|
-
type: "Null",
|
|
14
|
-
value: "null"
|
|
15
|
-
}],
|
|
16
|
-
["undefined", {
|
|
17
|
-
type: "Undefined",
|
|
18
|
-
value: "undefined"
|
|
19
|
-
}],
|
|
20
|
-
["in", {
|
|
21
|
-
type: "Operator",
|
|
22
|
-
value: "in"
|
|
23
|
-
}],
|
|
24
|
-
["not", {
|
|
25
|
-
type: "Operator",
|
|
26
|
-
value: "not"
|
|
27
|
-
}]
|
|
28
|
-
]);
|
|
29
|
-
function isDigit(ch) {
|
|
30
|
-
return ch >= "0" && ch <= "9";
|
|
31
|
-
}
|
|
32
|
-
function isAlpha(ch) {
|
|
33
|
-
return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_" || ch === "$";
|
|
34
|
-
}
|
|
35
|
-
function isAlphaNumeric(ch) {
|
|
36
|
-
return isAlpha(ch) || isDigit(ch);
|
|
37
|
-
}
|
|
38
|
-
function isHexDigit(ch) {
|
|
39
|
-
return isDigit(ch) || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
|
|
40
|
-
}
|
|
41
|
-
function tokenize(source) {
|
|
42
|
-
const tokens = [];
|
|
43
|
-
let i = 0;
|
|
44
|
-
while (i < source.length) {
|
|
45
|
-
const ch = source[i];
|
|
46
|
-
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
47
|
-
i++;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (isDigit(ch)) {
|
|
51
|
-
const start = i;
|
|
52
|
-
if (ch === "0" && i + 1 < source.length && (source[i + 1] === "x" || source[i + 1] === "X")) {
|
|
53
|
-
i += 2;
|
|
54
|
-
while (i < source.length && (isHexDigit(source[i]) || source[i] === "_")) i++;
|
|
55
|
-
tokens.push({
|
|
56
|
-
type: "Number",
|
|
57
|
-
value: source.slice(start, i),
|
|
58
|
-
start,
|
|
59
|
-
end: i
|
|
60
|
-
});
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (ch === "0" && i + 1 < source.length && (source[i + 1] === "b" || source[i + 1] === "B")) {
|
|
64
|
-
i += 2;
|
|
65
|
-
while (i < source.length && (source[i] === "0" || source[i] === "1" || source[i] === "_")) i++;
|
|
66
|
-
tokens.push({
|
|
67
|
-
type: "Number",
|
|
68
|
-
value: source.slice(start, i),
|
|
69
|
-
start,
|
|
70
|
-
end: i
|
|
71
|
-
});
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
if (ch === "0" && i + 1 < source.length && (source[i + 1] === "o" || source[i + 1] === "O")) {
|
|
75
|
-
i += 2;
|
|
76
|
-
while (i < source.length && (source[i] >= "0" && source[i] <= "7" || source[i] === "_")) i++;
|
|
77
|
-
tokens.push({
|
|
78
|
-
type: "Number",
|
|
79
|
-
value: source.slice(start, i),
|
|
80
|
-
start,
|
|
81
|
-
end: i
|
|
82
|
-
});
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
while (i < source.length && (isDigit(source[i]) || source[i] === "_")) i++;
|
|
86
|
-
if (i < source.length && source[i] === "." && i + 1 < source.length && isDigit(source[i + 1])) {
|
|
87
|
-
i++;
|
|
88
|
-
while (i < source.length && (isDigit(source[i]) || source[i] === "_")) i++;
|
|
89
|
-
}
|
|
90
|
-
if (i < source.length && (source[i] === "e" || source[i] === "E")) {
|
|
91
|
-
i++;
|
|
92
|
-
if (i < source.length && (source[i] === "+" || source[i] === "-")) i++;
|
|
93
|
-
while (i < source.length && (isDigit(source[i]) || source[i] === "_")) i++;
|
|
94
|
-
}
|
|
95
|
-
tokens.push({
|
|
96
|
-
type: "Number",
|
|
97
|
-
value: source.slice(start, i),
|
|
98
|
-
start,
|
|
99
|
-
end: i
|
|
100
|
-
});
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (ch === "\"" || ch === "'") {
|
|
104
|
-
const quote = ch;
|
|
105
|
-
const start = i;
|
|
106
|
-
i++;
|
|
107
|
-
let value = "";
|
|
108
|
-
while (i < source.length && source[i] !== quote) {
|
|
109
|
-
if (source[i] === "\\") {
|
|
110
|
-
i++;
|
|
111
|
-
const escaped = source[i];
|
|
112
|
-
switch (escaped) {
|
|
113
|
-
case "n":
|
|
114
|
-
value += "\n";
|
|
115
|
-
break;
|
|
116
|
-
case "t":
|
|
117
|
-
value += " ";
|
|
118
|
-
break;
|
|
119
|
-
case "r":
|
|
120
|
-
value += "\r";
|
|
121
|
-
break;
|
|
122
|
-
case "\\":
|
|
123
|
-
value += "\\";
|
|
124
|
-
break;
|
|
125
|
-
case "0":
|
|
126
|
-
value += "\0";
|
|
127
|
-
break;
|
|
128
|
-
case "x": {
|
|
129
|
-
const hex = source.slice(i + 1, i + 3);
|
|
130
|
-
if (hex.length < 2 || !isHexDigit(hex[0]) || !isHexDigit(hex[1])) throw new ExpressionError("Invalid \\x escape: expected 2 hex digits", {
|
|
131
|
-
source,
|
|
132
|
-
start,
|
|
133
|
-
end: i + 3
|
|
134
|
-
});
|
|
135
|
-
value += String.fromCharCode(parseInt(hex, 16));
|
|
136
|
-
i += 2;
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
case "u":
|
|
140
|
-
if (i + 1 < source.length && source[i + 1] === "{") {
|
|
141
|
-
const closeBrace = source.indexOf("}", i + 2);
|
|
142
|
-
if (closeBrace === -1) throw new ExpressionError("Invalid \\u{} escape: missing closing brace", {
|
|
143
|
-
source,
|
|
144
|
-
start,
|
|
145
|
-
end: source.length
|
|
146
|
-
});
|
|
147
|
-
const hex = source.slice(i + 2, closeBrace);
|
|
148
|
-
if (hex.length === 0) throw new ExpressionError("Invalid \\u{} escape: empty code point", {
|
|
149
|
-
source,
|
|
150
|
-
start,
|
|
151
|
-
end: closeBrace + 1
|
|
152
|
-
});
|
|
153
|
-
for (const c of hex) if (!isHexDigit(c)) throw new ExpressionError(`Invalid \\u{} escape: non-hex digit "${c}"`, {
|
|
154
|
-
source,
|
|
155
|
-
start,
|
|
156
|
-
end: closeBrace + 1
|
|
157
|
-
});
|
|
158
|
-
const MAX_UNICODE_CODE_POINT = 1114111;
|
|
159
|
-
const codePoint = parseInt(hex, 16);
|
|
160
|
-
if (codePoint > MAX_UNICODE_CODE_POINT) throw new ExpressionError("Invalid \\u{} escape: code point out of range", {
|
|
161
|
-
source,
|
|
162
|
-
start,
|
|
163
|
-
end: closeBrace + 1
|
|
164
|
-
});
|
|
165
|
-
value += String.fromCodePoint(codePoint);
|
|
166
|
-
i = closeBrace;
|
|
167
|
-
} else {
|
|
168
|
-
const UNICODE_ESCAPE_LEN = 4;
|
|
169
|
-
const hex = source.slice(i + 1, i + 1 + UNICODE_ESCAPE_LEN);
|
|
170
|
-
if (hex.length < UNICODE_ESCAPE_LEN) throw new ExpressionError("Invalid \\u escape: expected 4 hex digits", {
|
|
171
|
-
source,
|
|
172
|
-
start,
|
|
173
|
-
end: i + 1 + UNICODE_ESCAPE_LEN
|
|
174
|
-
});
|
|
175
|
-
for (const c of hex) if (!isHexDigit(c)) throw new ExpressionError("Invalid \\u escape: expected 4 hex digits", {
|
|
176
|
-
source,
|
|
177
|
-
start,
|
|
178
|
-
end: i + 1 + UNICODE_ESCAPE_LEN
|
|
179
|
-
});
|
|
180
|
-
value += String.fromCharCode(parseInt(hex, 16));
|
|
181
|
-
i += UNICODE_ESCAPE_LEN;
|
|
182
|
-
}
|
|
183
|
-
break;
|
|
184
|
-
default:
|
|
185
|
-
value += escaped;
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
} else value += source[i];
|
|
189
|
-
i++;
|
|
190
|
-
}
|
|
191
|
-
if (i >= source.length) throw new ExpressionError("Unterminated string", {
|
|
192
|
-
source,
|
|
193
|
-
start,
|
|
194
|
-
end: i
|
|
195
|
-
});
|
|
196
|
-
i++;
|
|
197
|
-
tokens.push({
|
|
198
|
-
type: "String",
|
|
199
|
-
value,
|
|
200
|
-
start,
|
|
201
|
-
end: i
|
|
202
|
-
});
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (ch === "`") {
|
|
206
|
-
const start = i;
|
|
207
|
-
i++;
|
|
208
|
-
let raw = "`";
|
|
209
|
-
while (i < source.length && source[i] !== "`") if (source[i] === "$" && i + 1 < source.length && source[i + 1] === "{") {
|
|
210
|
-
raw += "${";
|
|
211
|
-
i += 2;
|
|
212
|
-
let depth = 1;
|
|
213
|
-
while (i < source.length && depth > 0) {
|
|
214
|
-
const ic = source[i];
|
|
215
|
-
if (ic === "\"" || ic === "'") {
|
|
216
|
-
raw += ic;
|
|
217
|
-
i++;
|
|
218
|
-
while (i < source.length && source[i] !== ic) {
|
|
219
|
-
if (source[i] === "\\" && i + 1 < source.length) {
|
|
220
|
-
raw += source[i] + source[i + 1];
|
|
221
|
-
i += 2;
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
raw += source[i];
|
|
225
|
-
i++;
|
|
226
|
-
}
|
|
227
|
-
if (i < source.length) {
|
|
228
|
-
raw += source[i];
|
|
229
|
-
i++;
|
|
230
|
-
}
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
if (ic === "{") depth++;
|
|
234
|
-
else if (ic === "}") depth--;
|
|
235
|
-
if (depth > 0) raw += ic;
|
|
236
|
-
i++;
|
|
237
|
-
}
|
|
238
|
-
raw += "}";
|
|
239
|
-
} else {
|
|
240
|
-
raw += source[i];
|
|
241
|
-
i++;
|
|
242
|
-
}
|
|
243
|
-
if (i >= source.length) throw new ExpressionError("Unterminated template literal", {
|
|
244
|
-
source,
|
|
245
|
-
start,
|
|
246
|
-
end: i
|
|
247
|
-
});
|
|
248
|
-
raw += "`";
|
|
249
|
-
i++;
|
|
250
|
-
tokens.push({
|
|
251
|
-
type: "TemplateLiteral",
|
|
252
|
-
value: raw,
|
|
253
|
-
start,
|
|
254
|
-
end: i
|
|
255
|
-
});
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
if (isAlpha(ch)) {
|
|
259
|
-
const start = i;
|
|
260
|
-
while (i < source.length && isAlphaNumeric(source[i])) i++;
|
|
261
|
-
const value = source.slice(start, i);
|
|
262
|
-
const kw = KEYWORDS.get(value);
|
|
263
|
-
if (kw) tokens.push({
|
|
264
|
-
type: kw.type,
|
|
265
|
-
value: kw.value,
|
|
266
|
-
start,
|
|
267
|
-
end: i
|
|
268
|
-
});
|
|
269
|
-
else tokens.push({
|
|
270
|
-
type: "Identifier",
|
|
271
|
-
value,
|
|
272
|
-
start,
|
|
273
|
-
end: i
|
|
274
|
-
});
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
const start = i;
|
|
278
|
-
if (ch === "." && i + 2 < source.length && source[i + 1] === "." && source[i + 2] === ".") {
|
|
279
|
-
tokens.push({
|
|
280
|
-
type: "Spread",
|
|
281
|
-
value: "...",
|
|
282
|
-
start,
|
|
283
|
-
end: i + 3
|
|
284
|
-
});
|
|
285
|
-
i += 3;
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
if (ch === ".") {
|
|
289
|
-
tokens.push({
|
|
290
|
-
type: "Punctuation",
|
|
291
|
-
value: ".",
|
|
292
|
-
start,
|
|
293
|
-
end: i + 1
|
|
294
|
-
});
|
|
295
|
-
i++;
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
if (ch === "|") {
|
|
299
|
-
if (i + 1 < source.length && source[i + 1] === ">") {
|
|
300
|
-
tokens.push({
|
|
301
|
-
type: "Pipe",
|
|
302
|
-
value: "|>",
|
|
303
|
-
start,
|
|
304
|
-
end: i + 2
|
|
305
|
-
});
|
|
306
|
-
i += 2;
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
if (i + 1 < source.length && source[i + 1] === "|") {
|
|
310
|
-
tokens.push({
|
|
311
|
-
type: "Operator",
|
|
312
|
-
value: "||",
|
|
313
|
-
start,
|
|
314
|
-
end: i + 2
|
|
315
|
-
});
|
|
316
|
-
i += 2;
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
if (ch === "&" && i + 1 < source.length && source[i + 1] === "&") {
|
|
321
|
-
tokens.push({
|
|
322
|
-
type: "Operator",
|
|
323
|
-
value: "&&",
|
|
324
|
-
start,
|
|
325
|
-
end: i + 2
|
|
326
|
-
});
|
|
327
|
-
i += 2;
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
if (ch === "=" && i + 1 < source.length && source[i + 1] === "=") {
|
|
331
|
-
tokens.push({
|
|
332
|
-
type: "Operator",
|
|
333
|
-
value: "==",
|
|
334
|
-
start,
|
|
335
|
-
end: i + 2
|
|
336
|
-
});
|
|
337
|
-
i += 2;
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
if (ch === "!" && i + 1 < source.length && source[i + 1] === "=") {
|
|
341
|
-
tokens.push({
|
|
342
|
-
type: "Operator",
|
|
343
|
-
value: "!=",
|
|
344
|
-
start,
|
|
345
|
-
end: i + 2
|
|
346
|
-
});
|
|
347
|
-
i += 2;
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
if (ch === "!") {
|
|
351
|
-
tokens.push({
|
|
352
|
-
type: "Operator",
|
|
353
|
-
value: "!",
|
|
354
|
-
start,
|
|
355
|
-
end: i + 1
|
|
356
|
-
});
|
|
357
|
-
i++;
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
if (ch === "<") {
|
|
361
|
-
if (i + 1 < source.length && source[i + 1] === "=") {
|
|
362
|
-
tokens.push({
|
|
363
|
-
type: "Operator",
|
|
364
|
-
value: "<=",
|
|
365
|
-
start,
|
|
366
|
-
end: i + 2
|
|
367
|
-
});
|
|
368
|
-
i += 2;
|
|
369
|
-
continue;
|
|
370
|
-
}
|
|
371
|
-
tokens.push({
|
|
372
|
-
type: "Operator",
|
|
373
|
-
value: "<",
|
|
374
|
-
start,
|
|
375
|
-
end: i + 1
|
|
376
|
-
});
|
|
377
|
-
i++;
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
if (ch === ">") {
|
|
381
|
-
if (i + 1 < source.length && source[i + 1] === "=") {
|
|
382
|
-
tokens.push({
|
|
383
|
-
type: "Operator",
|
|
384
|
-
value: ">=",
|
|
385
|
-
start,
|
|
386
|
-
end: i + 2
|
|
387
|
-
});
|
|
388
|
-
i += 2;
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
tokens.push({
|
|
392
|
-
type: "Operator",
|
|
393
|
-
value: ">",
|
|
394
|
-
start,
|
|
395
|
-
end: i + 1
|
|
396
|
-
});
|
|
397
|
-
i++;
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
if (ch === "*" && i + 1 < source.length && source[i + 1] === "*") {
|
|
401
|
-
tokens.push({
|
|
402
|
-
type: "Operator",
|
|
403
|
-
value: "**",
|
|
404
|
-
start,
|
|
405
|
-
end: i + 2
|
|
406
|
-
});
|
|
407
|
-
i += 2;
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
if (ch === "?") {
|
|
411
|
-
if (i + 1 < source.length && source[i + 1] === ".") {
|
|
412
|
-
tokens.push({
|
|
413
|
-
type: "OptionalChain",
|
|
414
|
-
value: "?.",
|
|
415
|
-
start,
|
|
416
|
-
end: i + 2
|
|
417
|
-
});
|
|
418
|
-
i += 2;
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
if (i + 1 < source.length && source[i + 1] === "?") {
|
|
422
|
-
tokens.push({
|
|
423
|
-
type: "NullishCoalescing",
|
|
424
|
-
value: "??",
|
|
425
|
-
start,
|
|
426
|
-
end: i + 2
|
|
427
|
-
});
|
|
428
|
-
i += 2;
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
tokens.push({
|
|
432
|
-
type: "Punctuation",
|
|
433
|
-
value: "?",
|
|
434
|
-
start,
|
|
435
|
-
end: i + 1
|
|
436
|
-
});
|
|
437
|
-
i++;
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
440
|
-
if (ch === "+" || ch === "-" || ch === "*" || ch === "/" || ch === "%") {
|
|
441
|
-
tokens.push({
|
|
442
|
-
type: "Operator",
|
|
443
|
-
value: ch,
|
|
444
|
-
start,
|
|
445
|
-
end: i + 1
|
|
446
|
-
});
|
|
447
|
-
i++;
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
if ("()[]{},:".includes(ch)) {
|
|
451
|
-
tokens.push({
|
|
452
|
-
type: "Punctuation",
|
|
453
|
-
value: ch,
|
|
454
|
-
start,
|
|
455
|
-
end: i + 1
|
|
456
|
-
});
|
|
457
|
-
i++;
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
throw new ExpressionError(`Unexpected character "${ch}"`, {
|
|
461
|
-
source,
|
|
462
|
-
start,
|
|
463
|
-
end: i + 1
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
tokens.push({
|
|
467
|
-
type: "EOF",
|
|
468
|
-
value: "",
|
|
469
|
-
start: i,
|
|
470
|
-
end: i
|
|
471
|
-
});
|
|
472
|
-
return tokens;
|
|
473
|
-
}
|
|
474
|
-
//#endregion
|
|
1
|
+
import { a as attachLocation, c as offsetToPosition, i as ExpressionError, l as suggest, n as BonsaiSecurityError, o as formatBonsaiError, r as BonsaiTypeError, s as formatError, t as BonsaiReferenceError } from "./errors-BONB6P_5.mjs";
|
|
2
|
+
import { i as tokenize, n as ExecutionContext, r as SecurityPolicy } from "./execution-context-B-5hQ92X.mjs";
|
|
475
3
|
//#region src/parser.ts
|
|
476
4
|
const PIPE_PRECEDENCE = 5;
|
|
477
5
|
const PRECEDENCE = {
|
|
@@ -731,7 +259,7 @@ function parse(source, _depth = 0) {
|
|
|
731
259
|
const tok = current();
|
|
732
260
|
if (tok.type === "Number") {
|
|
733
261
|
advance();
|
|
734
|
-
const raw = tok.value.replace(/_/
|
|
262
|
+
const raw = tok.value.replace(/_/gu, "");
|
|
735
263
|
return {
|
|
736
264
|
type: "NumberLiteral",
|
|
737
265
|
value: Number(raw),
|
|
@@ -1355,9 +883,16 @@ function resolveTransform(name, transforms) {
|
|
|
1355
883
|
if (!Object.hasOwn(transforms, name)) throw new BonsaiReferenceError("transform", name, suggest(name, Object.keys(transforms)));
|
|
1356
884
|
return transforms[name];
|
|
1357
885
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
886
|
+
/**
|
|
887
|
+
* Resolve a callable by name in the shared function namespace. Pure and context
|
|
888
|
+
* functions occupy one registry keyed by name, so a single lookup returns the
|
|
889
|
+
* tagged entry; the call site uses `kind` to decide how to invoke it (pure
|
|
890
|
+
* functions take only call args; context functions receive the evaluation
|
|
891
|
+
* context first, then call args).
|
|
892
|
+
*/
|
|
893
|
+
function resolveCallable(name, functions) {
|
|
894
|
+
if (Object.hasOwn(functions, name)) return functions[name];
|
|
895
|
+
throw new BonsaiReferenceError("function", name, suggest(name, Object.keys(functions)));
|
|
1361
896
|
}
|
|
1362
897
|
function getIdentifierName(node, message = "Expected identifier") {
|
|
1363
898
|
if (node.type !== "Identifier") throw new Error(message);
|
|
@@ -1408,29 +943,34 @@ function rejectPromise(value, kind, name) {
|
|
|
1408
943
|
if (value instanceof Promise) throw new BonsaiTypeError(name, `a synchronous ${kind} result — use evaluate() instead of evaluateSync() for async`, value);
|
|
1409
944
|
return value;
|
|
1410
945
|
}
|
|
1411
|
-
function evaluate(node, context,
|
|
946
|
+
function evaluate(node, context, bindings, guard, source) {
|
|
1412
947
|
return evalNode(node, {
|
|
1413
948
|
ctx: context,
|
|
1414
|
-
tr: transforms,
|
|
1415
|
-
fn: functions,
|
|
949
|
+
tr: bindings.transforms,
|
|
950
|
+
fn: bindings.functions,
|
|
1416
951
|
g: guard,
|
|
1417
952
|
s: source
|
|
1418
953
|
});
|
|
1419
954
|
}
|
|
1420
955
|
function evalNode(node, env) {
|
|
1421
|
-
|
|
956
|
+
switch (node.type) {
|
|
957
|
+
case "NumberLiteral":
|
|
958
|
+
case "StringLiteral":
|
|
959
|
+
case "BooleanLiteral": return node.value;
|
|
960
|
+
case "NullLiteral": return null;
|
|
961
|
+
case "UndefinedLiteral": return;
|
|
962
|
+
case "Identifier":
|
|
963
|
+
env.g.checkNameAccess(node.name, "identifier");
|
|
964
|
+
return Object.hasOwn(env.ctx, node.name) ? env.ctx[node.name] : void 0;
|
|
965
|
+
}
|
|
966
|
+
return evalCompound(node, env);
|
|
967
|
+
}
|
|
968
|
+
function evalCompound(node, env) {
|
|
969
|
+
const { g, s } = env;
|
|
1422
970
|
g.enterDepth();
|
|
1423
971
|
g.step();
|
|
1424
972
|
try {
|
|
1425
973
|
switch (node.type) {
|
|
1426
|
-
case "NumberLiteral":
|
|
1427
|
-
case "StringLiteral":
|
|
1428
|
-
case "BooleanLiteral": return node.value;
|
|
1429
|
-
case "NullLiteral": return null;
|
|
1430
|
-
case "UndefinedLiteral": return;
|
|
1431
|
-
case "Identifier":
|
|
1432
|
-
g.checkNameAccess(node.name, "identifier");
|
|
1433
|
-
return Object.hasOwn(ctx, node.name) ? ctx[node.name] : void 0;
|
|
1434
974
|
case "UnaryExpression": return applyUnaryOp(node.operator, evalNode(node.operand, env));
|
|
1435
975
|
case "BinaryExpression": {
|
|
1436
976
|
const op = node.operator;
|
|
@@ -1529,10 +1069,10 @@ function evalCallExpression(node, env) {
|
|
|
1529
1069
|
}
|
|
1530
1070
|
}
|
|
1531
1071
|
if (node.callee.type === "Identifier") try {
|
|
1532
|
-
const
|
|
1072
|
+
const resolved = resolveCallable(node.callee.name, fn);
|
|
1533
1073
|
const args = [];
|
|
1534
1074
|
for (const arg of node.args) pushCallArgument(args, arg, env);
|
|
1535
|
-
return rejectPromise(
|
|
1075
|
+
return rejectPromise(resolved.kind === "context" ? resolved.fn(env.ctx, ...args) : resolved.fn(...args), "function", node.callee.name);
|
|
1536
1076
|
} catch (e) {
|
|
1537
1077
|
if (s) attachLocation(e, s, node.start, node.end);
|
|
1538
1078
|
throw e;
|
|
@@ -1642,11 +1182,11 @@ function makeLambdaAccessor$1(property, guard) {
|
|
|
1642
1182
|
}
|
|
1643
1183
|
//#endregion
|
|
1644
1184
|
//#region src/evaluator-async.ts
|
|
1645
|
-
async function evaluateAsync(node, context,
|
|
1185
|
+
async function evaluateAsync(node, context, bindings, guard, source) {
|
|
1646
1186
|
const result = await evalNodeAsync(node, {
|
|
1647
1187
|
ctx: context,
|
|
1648
|
-
tr: transforms,
|
|
1649
|
-
fn: functions,
|
|
1188
|
+
tr: bindings.transforms,
|
|
1189
|
+
fn: bindings.functions,
|
|
1650
1190
|
g: guard,
|
|
1651
1191
|
s: source
|
|
1652
1192
|
});
|
|
@@ -1654,19 +1194,24 @@ async function evaluateAsync(node, context, transforms, functions, guard, source
|
|
|
1654
1194
|
return result;
|
|
1655
1195
|
}
|
|
1656
1196
|
async function evalNodeAsync(node, env) {
|
|
1657
|
-
|
|
1197
|
+
switch (node.type) {
|
|
1198
|
+
case "NumberLiteral":
|
|
1199
|
+
case "StringLiteral":
|
|
1200
|
+
case "BooleanLiteral": return node.value;
|
|
1201
|
+
case "NullLiteral": return null;
|
|
1202
|
+
case "UndefinedLiteral": return;
|
|
1203
|
+
case "Identifier":
|
|
1204
|
+
env.g.checkNameAccess(node.name, "identifier");
|
|
1205
|
+
return Object.hasOwn(env.ctx, node.name) ? env.ctx[node.name] : void 0;
|
|
1206
|
+
}
|
|
1207
|
+
return evalCompoundAsync(node, env);
|
|
1208
|
+
}
|
|
1209
|
+
async function evalCompoundAsync(node, env) {
|
|
1210
|
+
const { g, s } = env;
|
|
1658
1211
|
g.enterDepth();
|
|
1659
1212
|
g.step();
|
|
1660
1213
|
try {
|
|
1661
1214
|
switch (node.type) {
|
|
1662
|
-
case "NumberLiteral":
|
|
1663
|
-
case "StringLiteral":
|
|
1664
|
-
case "BooleanLiteral": return node.value;
|
|
1665
|
-
case "NullLiteral": return null;
|
|
1666
|
-
case "UndefinedLiteral": return;
|
|
1667
|
-
case "Identifier":
|
|
1668
|
-
g.checkNameAccess(node.name, "identifier");
|
|
1669
|
-
return Object.hasOwn(ctx, node.name) ? ctx[node.name] : void 0;
|
|
1670
1215
|
case "UnaryExpression": return applyUnaryOp(node.operator, await evalNodeAsync(node.operand, env));
|
|
1671
1216
|
case "BinaryExpression": {
|
|
1672
1217
|
const op = node.operator;
|
|
@@ -1776,10 +1321,10 @@ async function evalCallExpressionAsync(node, env) {
|
|
|
1776
1321
|
}
|
|
1777
1322
|
}
|
|
1778
1323
|
if (node.callee.type === "Identifier") try {
|
|
1779
|
-
const
|
|
1324
|
+
const resolved = resolveCallable(node.callee.name, fn);
|
|
1780
1325
|
const args = [];
|
|
1781
1326
|
for (const arg of node.args) await pushCallArgumentAsync(args, arg, env);
|
|
1782
|
-
const result = await
|
|
1327
|
+
const result = resolved.kind === "context" ? await resolved.fn(env.ctx, ...args) : await resolved.fn(...args);
|
|
1783
1328
|
g.checkTimeout();
|
|
1784
1329
|
return result;
|
|
1785
1330
|
} catch (e) {
|
|
@@ -1937,99 +1482,23 @@ function makeLambdaAccessor(property, guard) {
|
|
|
1937
1482
|
};
|
|
1938
1483
|
}
|
|
1939
1484
|
//#endregion
|
|
1940
|
-
//#region src/execution-context.ts
|
|
1941
|
-
const BLOCKED_PROPERTIES = new Set([
|
|
1942
|
-
"__proto__",
|
|
1943
|
-
"constructor",
|
|
1944
|
-
"prototype"
|
|
1945
|
-
]);
|
|
1946
|
-
const MAX_INDEX_DIGITS = 10;
|
|
1947
|
-
function isCanonicalIndex(key) {
|
|
1948
|
-
if (key.length === 0 || key.length > MAX_INDEX_DIGITS) return false;
|
|
1949
|
-
const n = Number(key);
|
|
1950
|
-
return Number.isInteger(n) && n >= 0 && String(n) === key;
|
|
1951
|
-
}
|
|
1952
|
-
const TIMEOUT_CHECK_INTERVAL = 1e3;
|
|
1953
|
-
const DEFAULT_MAX_DEPTH = 100;
|
|
1954
|
-
const DEFAULT_MAX_ARRAY_LENGTH = 1e5;
|
|
1955
|
-
/** Immutable per-instance security configuration derived from BonsaiOptions. */
|
|
1956
|
-
var SecurityPolicy = class {
|
|
1957
|
-
maxDepth;
|
|
1958
|
-
maxArrayLength;
|
|
1959
|
-
timeout;
|
|
1960
|
-
allowedProperties;
|
|
1961
|
-
deniedProperties;
|
|
1962
|
-
constructor(options = {}) {
|
|
1963
|
-
this.maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
1964
|
-
this.maxArrayLength = options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH;
|
|
1965
|
-
this.timeout = options.timeout ?? 0;
|
|
1966
|
-
this.allowedProperties = options.allowedProperties ? new Set(options.allowedProperties) : void 0;
|
|
1967
|
-
this.deniedProperties = options.deniedProperties ? new Set(options.deniedProperties) : void 0;
|
|
1968
|
-
}
|
|
1969
|
-
};
|
|
1970
|
-
/** Mutable per-evaluation state: tracks depth, step count, and deadline for a single evaluation. */
|
|
1971
|
-
var ExecutionContext = class {
|
|
1972
|
-
stepCount = 0;
|
|
1973
|
-
depth = 0;
|
|
1974
|
-
deadline;
|
|
1975
|
-
policy;
|
|
1976
|
-
now;
|
|
1977
|
-
constructor(policy, now = Date.now) {
|
|
1978
|
-
this.policy = policy;
|
|
1979
|
-
this.now = now;
|
|
1980
|
-
this.deadline = policy.timeout ? now() + policy.timeout : 0;
|
|
1981
|
-
}
|
|
1982
|
-
step() {
|
|
1983
|
-
if (this.deadline) {
|
|
1984
|
-
this.stepCount++;
|
|
1985
|
-
if (this.stepCount % TIMEOUT_CHECK_INTERVAL === 0) this.checkTimeout();
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
checkTimeout() {
|
|
1989
|
-
if (this.deadline && this.now() >= this.deadline) throw new BonsaiSecurityError("TIMEOUT", `Expression timeout: exceeded ${this.policy.timeout}ms`);
|
|
1990
|
-
}
|
|
1991
|
-
enterDepth() {
|
|
1992
|
-
if (++this.depth > this.policy.maxDepth) throw new BonsaiSecurityError("MAX_DEPTH", `Maximum expression depth (${this.policy.maxDepth}) exceeded`);
|
|
1993
|
-
}
|
|
1994
|
-
exitDepth() {
|
|
1995
|
-
this.depth--;
|
|
1996
|
-
}
|
|
1997
|
-
withDepth(fn) {
|
|
1998
|
-
this.enterDepth();
|
|
1999
|
-
try {
|
|
2000
|
-
return fn();
|
|
2001
|
-
} finally {
|
|
2002
|
-
this.exitDepth();
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
checkNameAccess(key, kind) {
|
|
2006
|
-
if (BLOCKED_PROPERTIES.has(key)) throw new BonsaiSecurityError("BLOCKED_PROPERTY", `Blocked: access to "${key}" is not allowed`);
|
|
2007
|
-
if (kind === "identifier" || kind === "object-key") return;
|
|
2008
|
-
if (isCanonicalIndex(key)) return;
|
|
2009
|
-
if (this.policy.allowedProperties) {
|
|
2010
|
-
if (!this.policy.allowedProperties.has(key)) throw new BonsaiSecurityError("PROPERTY_NOT_ALLOWED", `Blocked: "${key}" is not in allowed properties`);
|
|
2011
|
-
}
|
|
2012
|
-
if (this.policy.deniedProperties) {
|
|
2013
|
-
if (this.policy.deniedProperties.has(key)) throw new BonsaiSecurityError("PROPERTY_DENIED", `Blocked: "${key}" is in denied properties`);
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
checkArrayLength(length) {
|
|
2017
|
-
if (length > this.policy.maxArrayLength) throw new BonsaiSecurityError("MAX_ARRAY_LENGTH", `Array length (${length}) exceeds maximum (${this.policy.maxArrayLength})`);
|
|
2018
|
-
}
|
|
2019
|
-
};
|
|
2020
|
-
//#endregion
|
|
2021
1485
|
//#region src/cache.ts
|
|
2022
1486
|
var LRUCache = class {
|
|
2023
1487
|
max;
|
|
2024
1488
|
map = /* @__PURE__ */ new Map();
|
|
1489
|
+
lastKey;
|
|
1490
|
+
lastValue;
|
|
2025
1491
|
constructor(max) {
|
|
2026
1492
|
this.max = max > 0 ? max : 0;
|
|
2027
1493
|
}
|
|
2028
1494
|
get(key) {
|
|
1495
|
+
if (key === this.lastKey) return this.lastValue;
|
|
2029
1496
|
if (!this.map.has(key)) return void 0;
|
|
2030
1497
|
const value = this.map.get(key);
|
|
2031
1498
|
this.map.delete(key);
|
|
2032
1499
|
this.map.set(key, value);
|
|
1500
|
+
this.lastKey = key;
|
|
1501
|
+
this.lastValue = value;
|
|
2033
1502
|
return value;
|
|
2034
1503
|
}
|
|
2035
1504
|
set(key, value) {
|
|
@@ -2038,14 +1507,22 @@ var LRUCache = class {
|
|
|
2038
1507
|
else if (this.map.size >= this.max) {
|
|
2039
1508
|
const oldest = this.map.keys().next().value;
|
|
2040
1509
|
this.map.delete(oldest);
|
|
1510
|
+
if (this.lastKey === oldest) {
|
|
1511
|
+
this.lastKey = void 0;
|
|
1512
|
+
this.lastValue = void 0;
|
|
1513
|
+
}
|
|
2041
1514
|
}
|
|
2042
1515
|
this.map.set(key, value);
|
|
1516
|
+
this.lastKey = key;
|
|
1517
|
+
this.lastValue = value;
|
|
2043
1518
|
}
|
|
2044
1519
|
get size() {
|
|
2045
1520
|
return this.map.size;
|
|
2046
1521
|
}
|
|
2047
1522
|
clear() {
|
|
2048
1523
|
this.map.clear();
|
|
1524
|
+
this.lastKey = void 0;
|
|
1525
|
+
this.lastValue = void 0;
|
|
2049
1526
|
}
|
|
2050
1527
|
};
|
|
2051
1528
|
//#endregion
|
|
@@ -2057,13 +1534,24 @@ function createPluginRegistry() {
|
|
|
2057
1534
|
let functionsCache = {};
|
|
2058
1535
|
let transformsDirty = false;
|
|
2059
1536
|
let functionsDirty = false;
|
|
1537
|
+
let bindingsCache;
|
|
2060
1538
|
const registry = {
|
|
2061
1539
|
addTransform(name, fn) {
|
|
2062
1540
|
transformMap.set(name, fn);
|
|
2063
1541
|
transformsDirty = true;
|
|
2064
1542
|
},
|
|
2065
1543
|
addFunction(name, fn) {
|
|
2066
|
-
functionMap.set(name,
|
|
1544
|
+
functionMap.set(name, {
|
|
1545
|
+
kind: "pure",
|
|
1546
|
+
fn
|
|
1547
|
+
});
|
|
1548
|
+
functionsDirty = true;
|
|
1549
|
+
},
|
|
1550
|
+
addContextFunction(name, fn) {
|
|
1551
|
+
functionMap.set(name, {
|
|
1552
|
+
kind: "context",
|
|
1553
|
+
fn
|
|
1554
|
+
});
|
|
2067
1555
|
functionsDirty = true;
|
|
2068
1556
|
},
|
|
2069
1557
|
removeTransform(name) {
|
|
@@ -2082,6 +1570,12 @@ function createPluginRegistry() {
|
|
|
2082
1570
|
getFunction(name) {
|
|
2083
1571
|
return functionMap.get(name);
|
|
2084
1572
|
},
|
|
1573
|
+
isContextFunction(name) {
|
|
1574
|
+
return functionMap.get(name)?.kind === "context";
|
|
1575
|
+
},
|
|
1576
|
+
hasFunction(name) {
|
|
1577
|
+
return functionMap.has(name);
|
|
1578
|
+
},
|
|
2085
1579
|
getTransformNames() {
|
|
2086
1580
|
return [...transformMap.keys()];
|
|
2087
1581
|
},
|
|
@@ -2104,6 +1598,15 @@ function createPluginRegistry() {
|
|
|
2104
1598
|
functionsDirty = false;
|
|
2105
1599
|
}
|
|
2106
1600
|
return functionsCache;
|
|
1601
|
+
},
|
|
1602
|
+
get bindings() {
|
|
1603
|
+
const t = registry.transforms;
|
|
1604
|
+
const f = registry.functions;
|
|
1605
|
+
if (!bindingsCache || bindingsCache.transforms !== t || bindingsCache.functions !== f) bindingsCache = {
|
|
1606
|
+
transforms: t,
|
|
1607
|
+
functions: f
|
|
1608
|
+
};
|
|
1609
|
+
return bindingsCache;
|
|
2107
1610
|
}
|
|
2108
1611
|
};
|
|
2109
1612
|
return registry;
|
|
@@ -2111,24 +1614,36 @@ function createPluginRegistry() {
|
|
|
2111
1614
|
//#endregion
|
|
2112
1615
|
//#region src/index.ts
|
|
2113
1616
|
const DEFAULT_CACHE_SIZE = 256;
|
|
2114
|
-
let
|
|
1617
|
+
let sharedInstance;
|
|
2115
1618
|
/**
|
|
2116
1619
|
* Evaluate a single expression with default options. Uses a shared instance internally.
|
|
2117
1620
|
* For repeated evaluation or custom configuration, use `bonsai()` to create a dedicated instance.
|
|
2118
1621
|
*/
|
|
2119
1622
|
function evaluateExpression(expression, context) {
|
|
2120
|
-
if (!
|
|
2121
|
-
return
|
|
1623
|
+
if (!sharedInstance) sharedInstance = bonsai();
|
|
1624
|
+
return sharedInstance.evaluateSync(expression, context);
|
|
2122
1625
|
}
|
|
2123
1626
|
/**
|
|
2124
1627
|
* Create a new Bonsai instance with optional safety and caching configuration.
|
|
2125
|
-
* Register transforms and functions via `.use()`, `.addTransform()`,
|
|
1628
|
+
* Register transforms and functions via `.use()`, `.addTransform()`, `.addFunction()`,
|
|
1629
|
+
* and `.addContextFunction()`.
|
|
1630
|
+
*
|
|
1631
|
+
* Pass a context type argument to get end-to-end type safety on the evaluation
|
|
1632
|
+
* context: `bonsai<MyContext>()` will type-check `evaluate(expr, ctx)` calls,
|
|
1633
|
+
* require a context argument when `MyContext` has required fields, and
|
|
1634
|
+
* propagate `MyContext` into context-aware function signatures.
|
|
2126
1635
|
*
|
|
2127
1636
|
* @example
|
|
2128
1637
|
* ```ts
|
|
2129
1638
|
* const expr = bonsai({ timeout: 50 })
|
|
2130
1639
|
* expr.use(strings)
|
|
2131
1640
|
* expr.evaluateSync('name |> trim |> upper', { name: ' hello ' }) // "HELLO"
|
|
1641
|
+
*
|
|
1642
|
+
* type AppCtx = { userId: string; perms: string[] }
|
|
1643
|
+
* const app = bonsai<AppCtx>()
|
|
1644
|
+
* app.addContextFunction('hasPermission', (ctx, action) =>
|
|
1645
|
+
* ctx.perms.includes(String(action)))
|
|
1646
|
+
* app.evaluateSync('hasPermission("write")', { userId: 'u_1', perms: ['write'] })
|
|
2132
1647
|
* ```
|
|
2133
1648
|
*/
|
|
2134
1649
|
function bonsai(options = {}) {
|
|
@@ -2136,6 +1651,8 @@ function bonsai(options = {}) {
|
|
|
2136
1651
|
const cache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
2137
1652
|
const astCache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
2138
1653
|
const policy = new SecurityPolicy(options);
|
|
1654
|
+
const syncCtx = new ExecutionContext(policy);
|
|
1655
|
+
let syncCtxInUse = false;
|
|
2139
1656
|
function createExecutionContext() {
|
|
2140
1657
|
return new ExecutionContext(policy);
|
|
2141
1658
|
}
|
|
@@ -2153,11 +1670,19 @@ function bonsai(options = {}) {
|
|
|
2153
1670
|
const compiled = {
|
|
2154
1671
|
ast: optimized,
|
|
2155
1672
|
source,
|
|
2156
|
-
async evaluate(
|
|
2157
|
-
return evaluateAsync(optimized,
|
|
1673
|
+
async evaluate(...args) {
|
|
1674
|
+
return evaluateAsync(optimized, args[0] ?? {}, registry.bindings, createExecutionContext(), source);
|
|
2158
1675
|
},
|
|
2159
|
-
evaluateSync(
|
|
2160
|
-
|
|
1676
|
+
evaluateSync(...args) {
|
|
1677
|
+
const ctx = args[0] ?? {};
|
|
1678
|
+
if (syncCtxInUse) return evaluate(optimized, ctx, registry.bindings, createExecutionContext(), source);
|
|
1679
|
+
syncCtxInUse = true;
|
|
1680
|
+
try {
|
|
1681
|
+
syncCtx.reset();
|
|
1682
|
+
return evaluate(optimized, ctx, registry.bindings, syncCtx, source);
|
|
1683
|
+
} finally {
|
|
1684
|
+
syncCtxInUse = false;
|
|
1685
|
+
}
|
|
2161
1686
|
}
|
|
2162
1687
|
};
|
|
2163
1688
|
cache.set(source, compiled);
|
|
@@ -2176,6 +1701,10 @@ function bonsai(options = {}) {
|
|
|
2176
1701
|
registry.addFunction(name, fn);
|
|
2177
1702
|
return instance;
|
|
2178
1703
|
},
|
|
1704
|
+
addContextFunction(name, fn) {
|
|
1705
|
+
registry.addContextFunction(name, fn);
|
|
1706
|
+
return instance;
|
|
1707
|
+
},
|
|
2179
1708
|
removeTransform(name) {
|
|
2180
1709
|
return registry.removeTransform(name);
|
|
2181
1710
|
},
|
|
@@ -2186,7 +1715,10 @@ function bonsai(options = {}) {
|
|
|
2186
1715
|
return registry.getTransform(name) !== void 0;
|
|
2187
1716
|
},
|
|
2188
1717
|
hasFunction(name) {
|
|
2189
|
-
return registry.
|
|
1718
|
+
return registry.hasFunction(name);
|
|
1719
|
+
},
|
|
1720
|
+
isContextFunction(name) {
|
|
1721
|
+
return registry.isContextFunction(name);
|
|
2190
1722
|
},
|
|
2191
1723
|
listTransforms() {
|
|
2192
1724
|
return registry.getTransformNames();
|
|
@@ -2194,6 +1726,12 @@ function bonsai(options = {}) {
|
|
|
2194
1726
|
listFunctions() {
|
|
2195
1727
|
return registry.getFunctionNames();
|
|
2196
1728
|
},
|
|
1729
|
+
getPolicy() {
|
|
1730
|
+
return {
|
|
1731
|
+
...policy.allowedProperties ? { allowedProperties: [...policy.allowedProperties] } : {},
|
|
1732
|
+
...policy.deniedProperties ? { deniedProperties: [...policy.deniedProperties] } : {}
|
|
1733
|
+
};
|
|
1734
|
+
},
|
|
2197
1735
|
clearCache() {
|
|
2198
1736
|
cache.clear();
|
|
2199
1737
|
astCache.clear();
|
|
@@ -2201,11 +1739,20 @@ function bonsai(options = {}) {
|
|
|
2201
1739
|
compile(expression) {
|
|
2202
1740
|
return compileExpr(expression);
|
|
2203
1741
|
},
|
|
2204
|
-
async evaluate(expression,
|
|
2205
|
-
return evaluateAsync(getAst(expression),
|
|
1742
|
+
async evaluate(expression, ...args) {
|
|
1743
|
+
return evaluateAsync(getAst(expression), args[0] ?? {}, registry.bindings, createExecutionContext(), expression);
|
|
2206
1744
|
},
|
|
2207
|
-
evaluateSync(expression,
|
|
2208
|
-
|
|
1745
|
+
evaluateSync(expression, ...args) {
|
|
1746
|
+
const ast = getAst(expression);
|
|
1747
|
+
const ctx = args[0] ?? {};
|
|
1748
|
+
if (syncCtxInUse) return evaluate(ast, ctx, registry.bindings, createExecutionContext(), expression);
|
|
1749
|
+
syncCtxInUse = true;
|
|
1750
|
+
try {
|
|
1751
|
+
syncCtx.reset();
|
|
1752
|
+
return evaluate(ast, ctx, registry.bindings, syncCtx, expression);
|
|
1753
|
+
} finally {
|
|
1754
|
+
syncCtxInUse = false;
|
|
1755
|
+
}
|
|
2209
1756
|
},
|
|
2210
1757
|
validate(expression) {
|
|
2211
1758
|
try {
|