bonsai-js 0.2.0 → 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 +45 -0
- package/README.md +187 -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 +94 -577
- 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 = {
|
|
@@ -1273,19 +801,35 @@ function isAllowedReceiver(obj, methodName) {
|
|
|
1273
801
|
case "charAt":
|
|
1274
802
|
case "charCodeAt":
|
|
1275
803
|
case "repeat":
|
|
804
|
+
case "trim":
|
|
1276
805
|
case "trimStart":
|
|
1277
806
|
case "trimEnd":
|
|
807
|
+
case "toLowerCase":
|
|
808
|
+
case "toUpperCase":
|
|
1278
809
|
case "replace":
|
|
1279
|
-
case "replaceAll":
|
|
810
|
+
case "replaceAll":
|
|
811
|
+
case "padStart":
|
|
812
|
+
case "padEnd":
|
|
813
|
+
case "split": return t === "string";
|
|
1280
814
|
case "includes":
|
|
1281
815
|
case "indexOf":
|
|
816
|
+
case "lastIndexOf":
|
|
1282
817
|
case "slice":
|
|
1283
|
-
case "at":
|
|
818
|
+
case "at":
|
|
819
|
+
case "concat": return t === "string" || Array.isArray(obj);
|
|
1284
820
|
case "filter":
|
|
1285
821
|
case "map":
|
|
1286
822
|
case "find":
|
|
823
|
+
case "findIndex":
|
|
1287
824
|
case "some":
|
|
1288
|
-
case "every":
|
|
825
|
+
case "every":
|
|
826
|
+
case "flatMap": return Array.isArray(obj);
|
|
827
|
+
case "join":
|
|
828
|
+
case "flat":
|
|
829
|
+
case "toReversed":
|
|
830
|
+
case "toSorted":
|
|
831
|
+
case "toSpliced":
|
|
832
|
+
case "with": return Array.isArray(obj);
|
|
1289
833
|
case "toFixed": return t === "number";
|
|
1290
834
|
case "toString": return t === "string" || t === "number";
|
|
1291
835
|
default: return false;
|
|
@@ -1402,19 +946,24 @@ function evaluate(node, context, transforms, functions, guard, source) {
|
|
|
1402
946
|
});
|
|
1403
947
|
}
|
|
1404
948
|
function evalNode(node, env) {
|
|
1405
|
-
|
|
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;
|
|
1406
963
|
g.enterDepth();
|
|
1407
964
|
g.step();
|
|
1408
965
|
try {
|
|
1409
966
|
switch (node.type) {
|
|
1410
|
-
case "NumberLiteral":
|
|
1411
|
-
case "StringLiteral":
|
|
1412
|
-
case "BooleanLiteral": return node.value;
|
|
1413
|
-
case "NullLiteral": return null;
|
|
1414
|
-
case "UndefinedLiteral": return;
|
|
1415
|
-
case "Identifier":
|
|
1416
|
-
g.checkNameAccess(node.name, "identifier");
|
|
1417
|
-
return Object.hasOwn(ctx, node.name) ? ctx[node.name] : void 0;
|
|
1418
967
|
case "UnaryExpression": return applyUnaryOp(node.operator, evalNode(node.operand, env));
|
|
1419
968
|
case "BinaryExpression": {
|
|
1420
969
|
const op = node.operator;
|
|
@@ -1638,19 +1187,24 @@ async function evaluateAsync(node, context, transforms, functions, guard, source
|
|
|
1638
1187
|
return result;
|
|
1639
1188
|
}
|
|
1640
1189
|
async function evalNodeAsync(node, env) {
|
|
1641
|
-
|
|
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;
|
|
1642
1204
|
g.enterDepth();
|
|
1643
1205
|
g.step();
|
|
1644
1206
|
try {
|
|
1645
1207
|
switch (node.type) {
|
|
1646
|
-
case "NumberLiteral":
|
|
1647
|
-
case "StringLiteral":
|
|
1648
|
-
case "BooleanLiteral": return node.value;
|
|
1649
|
-
case "NullLiteral": return null;
|
|
1650
|
-
case "UndefinedLiteral": return;
|
|
1651
|
-
case "Identifier":
|
|
1652
|
-
g.checkNameAccess(node.name, "identifier");
|
|
1653
|
-
return Object.hasOwn(ctx, node.name) ? ctx[node.name] : void 0;
|
|
1654
1208
|
case "UnaryExpression": return applyUnaryOp(node.operator, await evalNodeAsync(node.operand, env));
|
|
1655
1209
|
case "BinaryExpression": {
|
|
1656
1210
|
const op = node.operator;
|
|
@@ -1803,6 +1357,14 @@ async function evalAsyncArrayMethod(methodName, arr, predicate) {
|
|
|
1803
1357
|
const results = arr.map(predicate);
|
|
1804
1358
|
return { value: (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).every(Boolean) };
|
|
1805
1359
|
}
|
|
1360
|
+
case "findIndex": {
|
|
1361
|
+
const results = arr.map(predicate);
|
|
1362
|
+
return { value: (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).findIndex(Boolean) };
|
|
1363
|
+
}
|
|
1364
|
+
case "flatMap": {
|
|
1365
|
+
const results = arr.map(predicate);
|
|
1366
|
+
return { value: (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).flat() };
|
|
1367
|
+
}
|
|
1806
1368
|
default: return;
|
|
1807
1369
|
}
|
|
1808
1370
|
}
|
|
@@ -1913,99 +1475,23 @@ function makeLambdaAccessor(property, guard) {
|
|
|
1913
1475
|
};
|
|
1914
1476
|
}
|
|
1915
1477
|
//#endregion
|
|
1916
|
-
//#region src/execution-context.ts
|
|
1917
|
-
const BLOCKED_PROPERTIES = new Set([
|
|
1918
|
-
"__proto__",
|
|
1919
|
-
"constructor",
|
|
1920
|
-
"prototype"
|
|
1921
|
-
]);
|
|
1922
|
-
const MAX_INDEX_DIGITS = 10;
|
|
1923
|
-
function isCanonicalIndex(key) {
|
|
1924
|
-
if (key.length === 0 || key.length > MAX_INDEX_DIGITS) return false;
|
|
1925
|
-
const n = Number(key);
|
|
1926
|
-
return Number.isInteger(n) && n >= 0 && String(n) === key;
|
|
1927
|
-
}
|
|
1928
|
-
const TIMEOUT_CHECK_INTERVAL = 1e3;
|
|
1929
|
-
const DEFAULT_MAX_DEPTH = 100;
|
|
1930
|
-
const DEFAULT_MAX_ARRAY_LENGTH = 1e5;
|
|
1931
|
-
/** Immutable per-instance security configuration derived from BonsaiOptions. */
|
|
1932
|
-
var SecurityPolicy = class {
|
|
1933
|
-
maxDepth;
|
|
1934
|
-
maxArrayLength;
|
|
1935
|
-
timeout;
|
|
1936
|
-
allowedProperties;
|
|
1937
|
-
deniedProperties;
|
|
1938
|
-
constructor(options = {}) {
|
|
1939
|
-
this.maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
1940
|
-
this.maxArrayLength = options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH;
|
|
1941
|
-
this.timeout = options.timeout ?? 0;
|
|
1942
|
-
this.allowedProperties = options.allowedProperties ? new Set(options.allowedProperties) : void 0;
|
|
1943
|
-
this.deniedProperties = options.deniedProperties ? new Set(options.deniedProperties) : void 0;
|
|
1944
|
-
}
|
|
1945
|
-
};
|
|
1946
|
-
/** Mutable per-evaluation state: tracks depth, step count, and deadline for a single evaluation. */
|
|
1947
|
-
var ExecutionContext = class {
|
|
1948
|
-
stepCount = 0;
|
|
1949
|
-
depth = 0;
|
|
1950
|
-
deadline;
|
|
1951
|
-
policy;
|
|
1952
|
-
now;
|
|
1953
|
-
constructor(policy, now = Date.now) {
|
|
1954
|
-
this.policy = policy;
|
|
1955
|
-
this.now = now;
|
|
1956
|
-
this.deadline = policy.timeout ? now() + policy.timeout : 0;
|
|
1957
|
-
}
|
|
1958
|
-
step() {
|
|
1959
|
-
if (this.deadline) {
|
|
1960
|
-
this.stepCount++;
|
|
1961
|
-
if (this.stepCount % TIMEOUT_CHECK_INTERVAL === 0) this.checkTimeout();
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
checkTimeout() {
|
|
1965
|
-
if (this.deadline && this.now() >= this.deadline) throw new BonsaiSecurityError("TIMEOUT", `Expression timeout: exceeded ${this.policy.timeout}ms`);
|
|
1966
|
-
}
|
|
1967
|
-
enterDepth() {
|
|
1968
|
-
if (++this.depth > this.policy.maxDepth) throw new BonsaiSecurityError("MAX_DEPTH", `Maximum expression depth (${this.policy.maxDepth}) exceeded`);
|
|
1969
|
-
}
|
|
1970
|
-
exitDepth() {
|
|
1971
|
-
this.depth--;
|
|
1972
|
-
}
|
|
1973
|
-
withDepth(fn) {
|
|
1974
|
-
this.enterDepth();
|
|
1975
|
-
try {
|
|
1976
|
-
return fn();
|
|
1977
|
-
} finally {
|
|
1978
|
-
this.exitDepth();
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
checkNameAccess(key, kind) {
|
|
1982
|
-
if (BLOCKED_PROPERTIES.has(key)) throw new BonsaiSecurityError("BLOCKED_PROPERTY", `Blocked: access to "${key}" is not allowed`);
|
|
1983
|
-
if (kind === "identifier" || kind === "object-key") return;
|
|
1984
|
-
if (isCanonicalIndex(key)) return;
|
|
1985
|
-
if (this.policy.allowedProperties) {
|
|
1986
|
-
if (!this.policy.allowedProperties.has(key)) throw new BonsaiSecurityError("PROPERTY_NOT_ALLOWED", `Blocked: "${key}" is not in allowed properties`);
|
|
1987
|
-
}
|
|
1988
|
-
if (this.policy.deniedProperties) {
|
|
1989
|
-
if (this.policy.deniedProperties.has(key)) throw new BonsaiSecurityError("PROPERTY_DENIED", `Blocked: "${key}" is in denied properties`);
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
checkArrayLength(length) {
|
|
1993
|
-
if (length > this.policy.maxArrayLength) throw new BonsaiSecurityError("MAX_ARRAY_LENGTH", `Array length (${length}) exceeds maximum (${this.policy.maxArrayLength})`);
|
|
1994
|
-
}
|
|
1995
|
-
};
|
|
1996
|
-
//#endregion
|
|
1997
1478
|
//#region src/cache.ts
|
|
1998
1479
|
var LRUCache = class {
|
|
1999
1480
|
max;
|
|
2000
1481
|
map = /* @__PURE__ */ new Map();
|
|
1482
|
+
lastKey;
|
|
1483
|
+
lastValue;
|
|
2001
1484
|
constructor(max) {
|
|
2002
1485
|
this.max = max > 0 ? max : 0;
|
|
2003
1486
|
}
|
|
2004
1487
|
get(key) {
|
|
1488
|
+
if (key === this.lastKey) return this.lastValue;
|
|
2005
1489
|
if (!this.map.has(key)) return void 0;
|
|
2006
1490
|
const value = this.map.get(key);
|
|
2007
1491
|
this.map.delete(key);
|
|
2008
1492
|
this.map.set(key, value);
|
|
1493
|
+
this.lastKey = key;
|
|
1494
|
+
this.lastValue = value;
|
|
2009
1495
|
return value;
|
|
2010
1496
|
}
|
|
2011
1497
|
set(key, value) {
|
|
@@ -2014,14 +1500,22 @@ var LRUCache = class {
|
|
|
2014
1500
|
else if (this.map.size >= this.max) {
|
|
2015
1501
|
const oldest = this.map.keys().next().value;
|
|
2016
1502
|
this.map.delete(oldest);
|
|
1503
|
+
if (this.lastKey === oldest) {
|
|
1504
|
+
this.lastKey = void 0;
|
|
1505
|
+
this.lastValue = void 0;
|
|
1506
|
+
}
|
|
2017
1507
|
}
|
|
2018
1508
|
this.map.set(key, value);
|
|
1509
|
+
this.lastKey = key;
|
|
1510
|
+
this.lastValue = value;
|
|
2019
1511
|
}
|
|
2020
1512
|
get size() {
|
|
2021
1513
|
return this.map.size;
|
|
2022
1514
|
}
|
|
2023
1515
|
clear() {
|
|
2024
1516
|
this.map.clear();
|
|
1517
|
+
this.lastKey = void 0;
|
|
1518
|
+
this.lastValue = void 0;
|
|
2025
1519
|
}
|
|
2026
1520
|
};
|
|
2027
1521
|
//#endregion
|
|
@@ -2112,6 +1606,8 @@ function bonsai(options = {}) {
|
|
|
2112
1606
|
const cache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
2113
1607
|
const astCache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
2114
1608
|
const policy = new SecurityPolicy(options);
|
|
1609
|
+
const syncCtx = new ExecutionContext(policy);
|
|
1610
|
+
let syncCtxInUse = false;
|
|
2115
1611
|
function createExecutionContext() {
|
|
2116
1612
|
return new ExecutionContext(policy);
|
|
2117
1613
|
}
|
|
@@ -2133,7 +1629,14 @@ function bonsai(options = {}) {
|
|
|
2133
1629
|
return evaluateAsync(optimized, context, registry.transforms, registry.functions, createExecutionContext(), source);
|
|
2134
1630
|
},
|
|
2135
1631
|
evaluateSync(context = {}) {
|
|
2136
|
-
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
|
+
}
|
|
2137
1640
|
}
|
|
2138
1641
|
};
|
|
2139
1642
|
cache.set(source, compiled);
|
|
@@ -2170,6 +1673,12 @@ function bonsai(options = {}) {
|
|
|
2170
1673
|
listFunctions() {
|
|
2171
1674
|
return registry.getFunctionNames();
|
|
2172
1675
|
},
|
|
1676
|
+
getPolicy() {
|
|
1677
|
+
return {
|
|
1678
|
+
...policy.allowedProperties ? { allowedProperties: [...policy.allowedProperties] } : {},
|
|
1679
|
+
...policy.deniedProperties ? { deniedProperties: [...policy.deniedProperties] } : {}
|
|
1680
|
+
};
|
|
1681
|
+
},
|
|
2173
1682
|
clearCache() {
|
|
2174
1683
|
cache.clear();
|
|
2175
1684
|
astCache.clear();
|
|
@@ -2181,7 +1690,15 @@ function bonsai(options = {}) {
|
|
|
2181
1690
|
return evaluateAsync(getAst(expression), context, registry.transforms, registry.functions, createExecutionContext(), expression);
|
|
2182
1691
|
},
|
|
2183
1692
|
evaluateSync(expression, context = {}) {
|
|
2184
|
-
|
|
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
|
+
}
|
|
2185
1702
|
},
|
|
2186
1703
|
validate(expression) {
|
|
2187
1704
|
try {
|