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/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
- //#region src/lexer.ts
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": return t === "string";
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": return t === "string" || Array.isArray(obj);
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": return Array.isArray(obj);
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
- const { ctx, g, s } = env;
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
- const { ctx, g, s } = env;
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
- return evaluate(getAst(expression), context, registry.transforms, registry.functions, createExecutionContext(), expression);
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 {