bonsai-js 0.2.1 → 0.3.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 +36 -0
- package/README.md +144 -1
- 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/execution-context-DqYC3L92.mjs +564 -0
- package/dist/execution-context-DqYC3L92.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +67 -574
- package/dist/index.mjs.map +1 -1
- package/dist/stdlib/index.d.mts +1 -1
- package/dist/{types-ClpZDF5z.d.mts → types-BhsZZsLY.d.mts} +19 -2
- package/dist/types-BhsZZsLY.d.mts.map +1 -0
- package/package.json +5 -1
- package/dist/types-ClpZDF5z.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,477 +1,5 @@
|
|
|
1
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-t5i9AEaf.mjs";
|
|
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
|
|
2
|
+
import { i as tokenize, n as ExecutionContext, r as SecurityPolicy } from "./execution-context-DqYC3L92.mjs";
|
|
475
3
|
//#region src/parser.ts
|
|
476
4
|
const PIPE_PRECEDENCE = 5;
|
|
477
5
|
const PRECEDENCE = {
|
|
@@ -1418,19 +946,24 @@ function evaluate(node, context, transforms, functions, guard, source) {
|
|
|
1418
946
|
});
|
|
1419
947
|
}
|
|
1420
948
|
function evalNode(node, env) {
|
|
1421
|
-
|
|
949
|
+
switch (node.type) {
|
|
950
|
+
case "NumberLiteral":
|
|
951
|
+
case "StringLiteral":
|
|
952
|
+
case "BooleanLiteral": return node.value;
|
|
953
|
+
case "NullLiteral": return null;
|
|
954
|
+
case "UndefinedLiteral": return;
|
|
955
|
+
case "Identifier":
|
|
956
|
+
env.g.checkNameAccess(node.name, "identifier");
|
|
957
|
+
return Object.hasOwn(env.ctx, node.name) ? env.ctx[node.name] : void 0;
|
|
958
|
+
}
|
|
959
|
+
return evalCompound(node, env);
|
|
960
|
+
}
|
|
961
|
+
function evalCompound(node, env) {
|
|
962
|
+
const { g, s } = env;
|
|
1422
963
|
g.enterDepth();
|
|
1423
964
|
g.step();
|
|
1424
965
|
try {
|
|
1425
966
|
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
967
|
case "UnaryExpression": return applyUnaryOp(node.operator, evalNode(node.operand, env));
|
|
1435
968
|
case "BinaryExpression": {
|
|
1436
969
|
const op = node.operator;
|
|
@@ -1654,19 +1187,24 @@ async function evaluateAsync(node, context, transforms, functions, guard, source
|
|
|
1654
1187
|
return result;
|
|
1655
1188
|
}
|
|
1656
1189
|
async function evalNodeAsync(node, env) {
|
|
1657
|
-
|
|
1190
|
+
switch (node.type) {
|
|
1191
|
+
case "NumberLiteral":
|
|
1192
|
+
case "StringLiteral":
|
|
1193
|
+
case "BooleanLiteral": return node.value;
|
|
1194
|
+
case "NullLiteral": return null;
|
|
1195
|
+
case "UndefinedLiteral": return;
|
|
1196
|
+
case "Identifier":
|
|
1197
|
+
env.g.checkNameAccess(node.name, "identifier");
|
|
1198
|
+
return Object.hasOwn(env.ctx, node.name) ? env.ctx[node.name] : void 0;
|
|
1199
|
+
}
|
|
1200
|
+
return evalCompoundAsync(node, env);
|
|
1201
|
+
}
|
|
1202
|
+
async function evalCompoundAsync(node, env) {
|
|
1203
|
+
const { g, s } = env;
|
|
1658
1204
|
g.enterDepth();
|
|
1659
1205
|
g.step();
|
|
1660
1206
|
try {
|
|
1661
1207
|
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
1208
|
case "UnaryExpression": return applyUnaryOp(node.operator, await evalNodeAsync(node.operand, env));
|
|
1671
1209
|
case "BinaryExpression": {
|
|
1672
1210
|
const op = node.operator;
|
|
@@ -1937,99 +1475,23 @@ function makeLambdaAccessor(property, guard) {
|
|
|
1937
1475
|
};
|
|
1938
1476
|
}
|
|
1939
1477
|
//#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
1478
|
//#region src/cache.ts
|
|
2022
1479
|
var LRUCache = class {
|
|
2023
1480
|
max;
|
|
2024
1481
|
map = /* @__PURE__ */ new Map();
|
|
1482
|
+
lastKey;
|
|
1483
|
+
lastValue;
|
|
2025
1484
|
constructor(max) {
|
|
2026
1485
|
this.max = max > 0 ? max : 0;
|
|
2027
1486
|
}
|
|
2028
1487
|
get(key) {
|
|
1488
|
+
if (key === this.lastKey) return this.lastValue;
|
|
2029
1489
|
if (!this.map.has(key)) return void 0;
|
|
2030
1490
|
const value = this.map.get(key);
|
|
2031
1491
|
this.map.delete(key);
|
|
2032
1492
|
this.map.set(key, value);
|
|
1493
|
+
this.lastKey = key;
|
|
1494
|
+
this.lastValue = value;
|
|
2033
1495
|
return value;
|
|
2034
1496
|
}
|
|
2035
1497
|
set(key, value) {
|
|
@@ -2038,14 +1500,22 @@ var LRUCache = class {
|
|
|
2038
1500
|
else if (this.map.size >= this.max) {
|
|
2039
1501
|
const oldest = this.map.keys().next().value;
|
|
2040
1502
|
this.map.delete(oldest);
|
|
1503
|
+
if (this.lastKey === oldest) {
|
|
1504
|
+
this.lastKey = void 0;
|
|
1505
|
+
this.lastValue = void 0;
|
|
1506
|
+
}
|
|
2041
1507
|
}
|
|
2042
1508
|
this.map.set(key, value);
|
|
1509
|
+
this.lastKey = key;
|
|
1510
|
+
this.lastValue = value;
|
|
2043
1511
|
}
|
|
2044
1512
|
get size() {
|
|
2045
1513
|
return this.map.size;
|
|
2046
1514
|
}
|
|
2047
1515
|
clear() {
|
|
2048
1516
|
this.map.clear();
|
|
1517
|
+
this.lastKey = void 0;
|
|
1518
|
+
this.lastValue = void 0;
|
|
2049
1519
|
}
|
|
2050
1520
|
};
|
|
2051
1521
|
//#endregion
|
|
@@ -2136,6 +1606,8 @@ function bonsai(options = {}) {
|
|
|
2136
1606
|
const cache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
2137
1607
|
const astCache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
2138
1608
|
const policy = new SecurityPolicy(options);
|
|
1609
|
+
const syncCtx = new ExecutionContext(policy);
|
|
1610
|
+
let syncCtxInUse = false;
|
|
2139
1611
|
function createExecutionContext() {
|
|
2140
1612
|
return new ExecutionContext(policy);
|
|
2141
1613
|
}
|
|
@@ -2157,7 +1629,14 @@ function bonsai(options = {}) {
|
|
|
2157
1629
|
return evaluateAsync(optimized, context, registry.transforms, registry.functions, createExecutionContext(), source);
|
|
2158
1630
|
},
|
|
2159
1631
|
evaluateSync(context = {}) {
|
|
2160
|
-
return evaluate(optimized, context, registry.transforms, registry.functions, createExecutionContext(), source);
|
|
1632
|
+
if (syncCtxInUse) return evaluate(optimized, context, registry.transforms, registry.functions, createExecutionContext(), source);
|
|
1633
|
+
syncCtxInUse = true;
|
|
1634
|
+
try {
|
|
1635
|
+
syncCtx.reset();
|
|
1636
|
+
return evaluate(optimized, context, registry.transforms, registry.functions, syncCtx, source);
|
|
1637
|
+
} finally {
|
|
1638
|
+
syncCtxInUse = false;
|
|
1639
|
+
}
|
|
2161
1640
|
}
|
|
2162
1641
|
};
|
|
2163
1642
|
cache.set(source, compiled);
|
|
@@ -2194,6 +1673,12 @@ function bonsai(options = {}) {
|
|
|
2194
1673
|
listFunctions() {
|
|
2195
1674
|
return registry.getFunctionNames();
|
|
2196
1675
|
},
|
|
1676
|
+
getPolicy() {
|
|
1677
|
+
return {
|
|
1678
|
+
...policy.allowedProperties ? { allowedProperties: [...policy.allowedProperties] } : {},
|
|
1679
|
+
...policy.deniedProperties ? { deniedProperties: [...policy.deniedProperties] } : {}
|
|
1680
|
+
};
|
|
1681
|
+
},
|
|
2197
1682
|
clearCache() {
|
|
2198
1683
|
cache.clear();
|
|
2199
1684
|
astCache.clear();
|
|
@@ -2205,7 +1690,15 @@ function bonsai(options = {}) {
|
|
|
2205
1690
|
return evaluateAsync(getAst(expression), context, registry.transforms, registry.functions, createExecutionContext(), expression);
|
|
2206
1691
|
},
|
|
2207
1692
|
evaluateSync(expression, context = {}) {
|
|
2208
|
-
|
|
1693
|
+
const ast = getAst(expression);
|
|
1694
|
+
if (syncCtxInUse) return evaluate(ast, context, registry.transforms, registry.functions, createExecutionContext(), expression);
|
|
1695
|
+
syncCtxInUse = true;
|
|
1696
|
+
try {
|
|
1697
|
+
syncCtx.reset();
|
|
1698
|
+
return evaluate(ast, context, registry.transforms, registry.functions, syncCtx, expression);
|
|
1699
|
+
} finally {
|
|
1700
|
+
syncCtxInUse = false;
|
|
1701
|
+
}
|
|
2209
1702
|
},
|
|
2210
1703
|
validate(expression) {
|
|
2211
1704
|
try {
|