bonsai-js 0.4.0 → 0.5.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 +22 -0
- package/README.md +17 -6
- package/dist/autocomplete/index.d.mts +1 -1
- package/dist/autocomplete/index.d.mts.map +1 -1
- package/dist/autocomplete/index.mjs +16 -55
- package/dist/autocomplete/index.mjs.map +1 -1
- package/dist/{errors-BONB6P_5.mjs → errors-CCZDd1gK.mjs} +1 -1
- package/dist/errors-CCZDd1gK.mjs.map +1 -0
- package/dist/{execution-context-B-5hQ92X.mjs → execution-context-b6FBfEdv.mjs} +147 -16
- package/dist/execution-context-b6FBfEdv.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +144 -81
- package/dist/index.mjs.map +1 -1
- package/dist/stdlib/index.d.mts +1 -1
- package/dist/stdlib/index.d.mts.map +1 -1
- package/dist/stdlib/index.mjs +43 -11
- package/dist/stdlib/index.mjs.map +1 -1
- package/dist/{types-C58c6SlH.d.mts → types-DkXfy5IJ.d.mts} +4 -2
- package/dist/types-DkXfy5IJ.d.mts.map +1 -0
- package/package.json +37 -11
- package/dist/errors-BONB6P_5.mjs.map +0 -1
- package/dist/execution-context-B-5hQ92X.mjs.map +0 -1
- package/dist/types-C58c6SlH.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +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
|
-
import { i as
|
|
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-CCZDd1gK.mjs";
|
|
2
|
+
import { i as isMethodAllowedOn, n as ExecutionContext, o as tokenize, r as SecurityPolicy } from "./execution-context-b6FBfEdv.mjs";
|
|
3
3
|
//#region src/parser.ts
|
|
4
4
|
const PIPE_PRECEDENCE = 5;
|
|
5
5
|
const PRECEDENCE = {
|
|
@@ -23,6 +23,7 @@ const PRECEDENCE = {
|
|
|
23
23
|
"not": void 0
|
|
24
24
|
};
|
|
25
25
|
const MAX_PARSE_DEPTH = 32;
|
|
26
|
+
const MAX_GRAMMAR_DEPTH = 1e3;
|
|
26
27
|
function parse(source, _depth = 0) {
|
|
27
28
|
if (_depth > MAX_PARSE_DEPTH) throw new ExpressionError("Maximum template nesting depth exceeded", {
|
|
28
29
|
source,
|
|
@@ -31,6 +32,7 @@ function parse(source, _depth = 0) {
|
|
|
31
32
|
});
|
|
32
33
|
const tokens = tokenize(source);
|
|
33
34
|
let pos = 0;
|
|
35
|
+
let grammarDepth = 0;
|
|
34
36
|
function current() {
|
|
35
37
|
return tokens[pos];
|
|
36
38
|
}
|
|
@@ -46,7 +48,26 @@ function parse(source, _depth = 0) {
|
|
|
46
48
|
});
|
|
47
49
|
return advance();
|
|
48
50
|
}
|
|
51
|
+
function enterNesting() {
|
|
52
|
+
if (++grammarDepth > MAX_GRAMMAR_DEPTH) {
|
|
53
|
+
grammarDepth--;
|
|
54
|
+
const tok = current();
|
|
55
|
+
throw new ExpressionError("Maximum expression nesting depth exceeded", {
|
|
56
|
+
source,
|
|
57
|
+
start: tok.start,
|
|
58
|
+
end: tok.end
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
49
62
|
function parseExpression(minPrec = 0) {
|
|
63
|
+
enterNesting();
|
|
64
|
+
try {
|
|
65
|
+
return parseExpressionInner(minPrec);
|
|
66
|
+
} finally {
|
|
67
|
+
grammarDepth--;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function parseExpressionInner(minPrec = 0) {
|
|
50
71
|
let left = parseUnary();
|
|
51
72
|
if (current().type === "Punctuation" && current().value === "?" && minPrec === 0) {
|
|
52
73
|
advance();
|
|
@@ -151,6 +172,14 @@ function parse(source, _depth = 0) {
|
|
|
151
172
|
return node;
|
|
152
173
|
}
|
|
153
174
|
function parseUnary() {
|
|
175
|
+
enterNesting();
|
|
176
|
+
try {
|
|
177
|
+
return parseUnaryInner();
|
|
178
|
+
} finally {
|
|
179
|
+
grammarDepth--;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function parseUnaryInner() {
|
|
154
183
|
const tok = current();
|
|
155
184
|
if (tok.type === "Operator" && (tok.value === "!" || tok.value === "-" || tok.value === "+")) {
|
|
156
185
|
advance();
|
|
@@ -610,8 +639,18 @@ function parse(source, _depth = 0) {
|
|
|
610
639
|
let depth = 1;
|
|
611
640
|
const exprStart = i;
|
|
612
641
|
while (i < raw.length && depth > 0) {
|
|
613
|
-
|
|
614
|
-
|
|
642
|
+
const c = raw[i];
|
|
643
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
644
|
+
i++;
|
|
645
|
+
while (i < raw.length && raw[i] !== c) {
|
|
646
|
+
if (raw[i] === "\\") i++;
|
|
647
|
+
i++;
|
|
648
|
+
}
|
|
649
|
+
i++;
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (c === "{") depth++;
|
|
653
|
+
else if (c === "}") depth--;
|
|
615
654
|
if (depth > 0) i++;
|
|
616
655
|
}
|
|
617
656
|
const exprSource = raw.slice(exprStart, i);
|
|
@@ -791,50 +830,6 @@ function makeConstant(value, start, end) {
|
|
|
791
830
|
}
|
|
792
831
|
//#endregion
|
|
793
832
|
//#region src/eval-ops.ts
|
|
794
|
-
/** Receiver type check for safe methods. */
|
|
795
|
-
function isAllowedReceiver(obj, methodName) {
|
|
796
|
-
const t = typeof obj;
|
|
797
|
-
switch (methodName) {
|
|
798
|
-
case "startsWith":
|
|
799
|
-
case "endsWith":
|
|
800
|
-
case "substring":
|
|
801
|
-
case "charAt":
|
|
802
|
-
case "charCodeAt":
|
|
803
|
-
case "repeat":
|
|
804
|
-
case "trim":
|
|
805
|
-
case "trimStart":
|
|
806
|
-
case "trimEnd":
|
|
807
|
-
case "toLowerCase":
|
|
808
|
-
case "toUpperCase":
|
|
809
|
-
case "replace":
|
|
810
|
-
case "replaceAll":
|
|
811
|
-
case "padStart":
|
|
812
|
-
case "padEnd":
|
|
813
|
-
case "split": return t === "string";
|
|
814
|
-
case "includes":
|
|
815
|
-
case "indexOf":
|
|
816
|
-
case "lastIndexOf":
|
|
817
|
-
case "slice":
|
|
818
|
-
case "at":
|
|
819
|
-
case "concat": return t === "string" || Array.isArray(obj);
|
|
820
|
-
case "filter":
|
|
821
|
-
case "map":
|
|
822
|
-
case "find":
|
|
823
|
-
case "findIndex":
|
|
824
|
-
case "some":
|
|
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);
|
|
833
|
-
case "toFixed": return t === "number";
|
|
834
|
-
case "toString": return t === "string" || t === "number";
|
|
835
|
-
default: return false;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
833
|
function toPropertyKey(value) {
|
|
839
834
|
return String(value);
|
|
840
835
|
}
|
|
@@ -874,7 +869,7 @@ function applyUnaryOp(operator, operand) {
|
|
|
874
869
|
function validateMethodCall(obj, methodName, guard) {
|
|
875
870
|
guard.checkNameAccess(methodName, "method");
|
|
876
871
|
if (obj == null) throw new BonsaiTypeError(methodName, "a non-null value", obj);
|
|
877
|
-
if (!
|
|
872
|
+
if (!isMethodAllowedOn(obj, methodName)) throw new BonsaiSecurityError("METHOD_NOT_ALLOWED", `Method "${methodName}" is not allowed on ${typeof obj}`);
|
|
878
873
|
const method = obj[methodName];
|
|
879
874
|
if (typeof method !== "function") throw new BonsaiTypeError(methodName, "a method", method);
|
|
880
875
|
return method;
|
|
@@ -921,7 +916,17 @@ function expandSpreadValue(value, maxLength) {
|
|
|
921
916
|
}
|
|
922
917
|
const REPLACE_METHODS = new Set(["replace", "replaceAll"]);
|
|
923
918
|
const MAX_REPEAT_COUNT = 1e5;
|
|
924
|
-
|
|
919
|
+
const HIGHER_ORDER_METHODS = new Set([
|
|
920
|
+
"map",
|
|
921
|
+
"filter",
|
|
922
|
+
"find",
|
|
923
|
+
"findIndex",
|
|
924
|
+
"some",
|
|
925
|
+
"every",
|
|
926
|
+
"flatMap"
|
|
927
|
+
]);
|
|
928
|
+
const LAMBDA_CALLBACK_EXPECTED = "a lambda or function callback (e.g. .field or . > value)";
|
|
929
|
+
function validateMethodArgs(receiver, methodName, args, guard) {
|
|
925
930
|
if (REPLACE_METHODS.has(methodName)) for (const arg of args) {
|
|
926
931
|
if (typeof arg === "function") throw new BonsaiTypeError(methodName, "string arguments (callbacks are not allowed)", arg);
|
|
927
932
|
if (arg instanceof RegExp) throw new BonsaiTypeError(methodName, "string arguments (RegExp is not allowed)", arg);
|
|
@@ -930,7 +935,32 @@ function validateMethodArgs(methodName, args) {
|
|
|
930
935
|
if (methodName === "repeat") {
|
|
931
936
|
const count = Number(args[0]);
|
|
932
937
|
if (!Number.isFinite(count) || count < 0 || count > MAX_REPEAT_COUNT) throw new BonsaiTypeError("repeat", `a count between 0 and ${MAX_REPEAT_COUNT}`, args[0]);
|
|
938
|
+
if (typeof receiver === "string") guard.checkStringLength(receiver.length * count);
|
|
933
939
|
}
|
|
940
|
+
if (methodName === "padStart" || methodName === "padEnd") guard.checkStringLength(Number(args[0]));
|
|
941
|
+
if (HIGHER_ORDER_METHODS.has(methodName) && typeof args[0] !== "function") throw new BonsaiTypeError(methodName, LAMBDA_CALLBACK_EXPECTED, args[0]);
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Enforce the array-size limit on a value produced by a method call. Array
|
|
945
|
+
* literals and spread are checked at construction; array-returning methods
|
|
946
|
+
* (split, map, flat, concat, toSorted, ...) are checked here so maxArrayLength
|
|
947
|
+
* is a real ceiling on every array that flows through evaluation.
|
|
948
|
+
*/
|
|
949
|
+
function checkResultArrayLength(result, guard) {
|
|
950
|
+
if (Array.isArray(result)) guard.checkArrayLength(result.length);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Enforce the string-size limit on a value produced by a method call. The
|
|
954
|
+
* argument-time checks on padStart/padEnd/repeat stop the worst single-call
|
|
955
|
+
* amplifiers before allocation, but string-returning methods such as join,
|
|
956
|
+
* concat, slice, and toUpperCase can still produce a string past the ceiling
|
|
957
|
+
* (notably `arr.join(sep)`, whose output is array length times separator length
|
|
958
|
+
* in a single native call). Checking the produced length here makes
|
|
959
|
+
* maxStringLength a real ceiling on every string that flows through evaluation,
|
|
960
|
+
* mirroring checkResultArrayLength.
|
|
961
|
+
*/
|
|
962
|
+
function checkResultStringLength(result, guard) {
|
|
963
|
+
if (typeof result === "string") guard.checkStringLength(result.length);
|
|
934
964
|
}
|
|
935
965
|
function accessMember(object, propertyNode, computed, computedValue, guard) {
|
|
936
966
|
const key = computed ? toPropertyKey(computedValue) : getIdentifierName(propertyNode, "Expected identifier property");
|
|
@@ -1061,8 +1091,11 @@ function evalCallExpression(node, env) {
|
|
|
1061
1091
|
const method = validateMethodCall(obj, methodName, g);
|
|
1062
1092
|
const args = [];
|
|
1063
1093
|
for (const arg of node.args) pushCallArgument(args, arg, env);
|
|
1064
|
-
validateMethodArgs(methodName, args);
|
|
1065
|
-
|
|
1094
|
+
validateMethodArgs(obj, methodName, args, g);
|
|
1095
|
+
const result = rejectPromise(method.call(obj, ...args), "method", methodName);
|
|
1096
|
+
checkResultArrayLength(result, g);
|
|
1097
|
+
checkResultStringLength(result, g);
|
|
1098
|
+
return result;
|
|
1066
1099
|
} catch (e) {
|
|
1067
1100
|
if (s) attachLocation(e, s, node.start, node.end);
|
|
1068
1101
|
throw e;
|
|
@@ -1135,8 +1168,11 @@ function evalLambdaBody(node, item, env) {
|
|
|
1135
1168
|
const args = [];
|
|
1136
1169
|
for (const arg of node.args) if (arg.type === "SpreadElement") args.push(...expandSpreadValue(evalNode(arg.argument, env), g.policy.maxArrayLength));
|
|
1137
1170
|
else args.push(evalArg(arg, env));
|
|
1138
|
-
validateMethodArgs(methodName, args);
|
|
1139
|
-
|
|
1171
|
+
validateMethodArgs(obj, methodName, args, g);
|
|
1172
|
+
const result = rejectPromise(method.call(obj, ...args), "method", methodName);
|
|
1173
|
+
checkResultArrayLength(result, g);
|
|
1174
|
+
checkResultStringLength(result, g);
|
|
1175
|
+
return result;
|
|
1140
1176
|
}
|
|
1141
1177
|
ownDepth = false;
|
|
1142
1178
|
g.exitDepth();
|
|
@@ -1302,18 +1338,21 @@ async function evalCallExpressionAsync(node, env) {
|
|
|
1302
1338
|
const method = validateMethodCall(obj, methodName, g);
|
|
1303
1339
|
const args = [];
|
|
1304
1340
|
for (const arg of node.args) await pushCallArgumentAsync(args, arg, env);
|
|
1305
|
-
validateMethodArgs(methodName, args);
|
|
1341
|
+
validateMethodArgs(obj, methodName, args, g);
|
|
1306
1342
|
if (Array.isArray(obj) && args.length === 1 && typeof args[0] === "function") {
|
|
1307
1343
|
const arr = obj;
|
|
1308
1344
|
const predicate = args[0];
|
|
1309
1345
|
const asyncResult = await evalAsyncArrayMethod(methodName, arr, predicate);
|
|
1310
1346
|
if (asyncResult !== void 0) {
|
|
1311
1347
|
g.checkTimeout();
|
|
1348
|
+
checkResultArrayLength(asyncResult.value, g);
|
|
1312
1349
|
return asyncResult.value;
|
|
1313
1350
|
}
|
|
1314
1351
|
}
|
|
1315
1352
|
const result = await method.call(obj, ...args);
|
|
1316
1353
|
g.checkTimeout();
|
|
1354
|
+
checkResultArrayLength(result, g);
|
|
1355
|
+
checkResultStringLength(result, g);
|
|
1317
1356
|
return result;
|
|
1318
1357
|
} catch (e) {
|
|
1319
1358
|
if (s) attachLocation(e, s, node.start, node.end);
|
|
@@ -1343,35 +1382,32 @@ async function pushCallArgumentAsync(args, node, env) {
|
|
|
1343
1382
|
async function evalAsyncArrayMethod(methodName, arr, predicate) {
|
|
1344
1383
|
switch (methodName) {
|
|
1345
1384
|
case "filter": {
|
|
1346
|
-
const
|
|
1347
|
-
const
|
|
1348
|
-
return { value:
|
|
1385
|
+
const out = [];
|
|
1386
|
+
for (const item of arr) if (await predicate(item)) out.push(item);
|
|
1387
|
+
return { value: out };
|
|
1349
1388
|
}
|
|
1350
1389
|
case "map": {
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
case "find": {
|
|
1355
|
-
const results = arr.map(predicate);
|
|
1356
|
-
const idx = (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).findIndex(Boolean);
|
|
1357
|
-
return { value: idx >= 0 ? arr[idx] : void 0 };
|
|
1358
|
-
}
|
|
1359
|
-
case "some": {
|
|
1360
|
-
const results = arr.map(predicate);
|
|
1361
|
-
return { value: (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).some(Boolean) };
|
|
1362
|
-
}
|
|
1363
|
-
case "every": {
|
|
1364
|
-
const results = arr.map(predicate);
|
|
1365
|
-
return { value: (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).every(Boolean) };
|
|
1366
|
-
}
|
|
1367
|
-
case "findIndex": {
|
|
1368
|
-
const results = arr.map(predicate);
|
|
1369
|
-
return { value: (results.some((r) => r instanceof Promise) ? await Promise.all(results) : results).findIndex(Boolean) };
|
|
1390
|
+
const out = [];
|
|
1391
|
+
for (const item of arr) out.push(await predicate(item));
|
|
1392
|
+
return { value: out };
|
|
1370
1393
|
}
|
|
1371
1394
|
case "flatMap": {
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1395
|
+
const out = [];
|
|
1396
|
+
for (const item of arr) out.push(await predicate(item));
|
|
1397
|
+
return { value: out.flat() };
|
|
1374
1398
|
}
|
|
1399
|
+
case "find":
|
|
1400
|
+
for (const item of arr) if (await predicate(item)) return { value: item };
|
|
1401
|
+
return { value: void 0 };
|
|
1402
|
+
case "findIndex":
|
|
1403
|
+
for (let i = 0; i < arr.length; i++) if (await predicate(arr[i])) return { value: i };
|
|
1404
|
+
return { value: -1 };
|
|
1405
|
+
case "some":
|
|
1406
|
+
for (const item of arr) if (await predicate(item)) return { value: true };
|
|
1407
|
+
return { value: false };
|
|
1408
|
+
case "every":
|
|
1409
|
+
for (const item of arr) if (!await predicate(item)) return { value: false };
|
|
1410
|
+
return { value: true };
|
|
1375
1411
|
default: return;
|
|
1376
1412
|
}
|
|
1377
1413
|
}
|
|
@@ -1429,15 +1465,19 @@ async function evalLambdaBodyAsync(node, item, env) {
|
|
|
1429
1465
|
const args = [];
|
|
1430
1466
|
for (const arg of node.args) if (arg.type === "SpreadElement") args.push(...expandSpreadValue(await evalNodeAsync(arg.argument, env), g.policy.maxArrayLength));
|
|
1431
1467
|
else args.push(await evalArgAsync(arg, env));
|
|
1432
|
-
validateMethodArgs(methodName, args);
|
|
1468
|
+
validateMethodArgs(obj, methodName, args, g);
|
|
1433
1469
|
if (Array.isArray(obj) && args.length === 1 && typeof args[0] === "function") {
|
|
1434
1470
|
const asyncResult = await evalAsyncArrayMethod(methodName, obj, args[0]);
|
|
1435
1471
|
if (asyncResult !== void 0) {
|
|
1436
1472
|
g.checkTimeout();
|
|
1473
|
+
checkResultArrayLength(asyncResult.value, g);
|
|
1437
1474
|
return asyncResult.value;
|
|
1438
1475
|
}
|
|
1439
1476
|
}
|
|
1440
|
-
|
|
1477
|
+
const result = await method.call(obj, ...args);
|
|
1478
|
+
checkResultArrayLength(result, g);
|
|
1479
|
+
checkResultStringLength(result, g);
|
|
1480
|
+
return result;
|
|
1441
1481
|
}
|
|
1442
1482
|
ownDepth = false;
|
|
1443
1483
|
g.exitDepth();
|
|
@@ -1614,6 +1654,28 @@ function createPluginRegistry() {
|
|
|
1614
1654
|
//#endregion
|
|
1615
1655
|
//#region src/index.ts
|
|
1616
1656
|
const DEFAULT_CACHE_SIZE = 256;
|
|
1657
|
+
/**
|
|
1658
|
+
* Validate options at construction so misconfiguration fails fast with a clear
|
|
1659
|
+
* error rather than silently producing surprising behavior (e.g. a negative
|
|
1660
|
+
* cacheSize quietly disabling the cache).
|
|
1661
|
+
*/
|
|
1662
|
+
function assertValidOptions(options) {
|
|
1663
|
+
const requireNonNegativeInt = (name, value) => {
|
|
1664
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < 0)) throw new RangeError(`bonsai: "${name}" must be a non-negative integer, received ${String(value)}`);
|
|
1665
|
+
};
|
|
1666
|
+
requireNonNegativeInt("cacheSize", options.cacheSize);
|
|
1667
|
+
requireNonNegativeInt("maxArrayLength", options.maxArrayLength);
|
|
1668
|
+
requireNonNegativeInt("maxStringLength", options.maxStringLength);
|
|
1669
|
+
if (options.maxDepth !== void 0 && (!Number.isInteger(options.maxDepth) || options.maxDepth < 1)) throw new RangeError(`bonsai: "maxDepth" must be a positive integer, received ${String(options.maxDepth)}`);
|
|
1670
|
+
if (options.timeout !== void 0 && (typeof options.timeout !== "number" || !Number.isFinite(options.timeout) || options.timeout < 0)) throw new RangeError(`bonsai: "timeout" must be a non-negative, finite number of milliseconds, received ${String(options.timeout)}`);
|
|
1671
|
+
const requireStringArray = (name, value) => {
|
|
1672
|
+
if (value === void 0) return;
|
|
1673
|
+
if (!Array.isArray(value)) throw new TypeError(`bonsai: "${name}" must be an array of strings`);
|
|
1674
|
+
if (!value.every((item) => typeof item === "string")) throw new TypeError(`bonsai: "${name}" must contain only strings`);
|
|
1675
|
+
};
|
|
1676
|
+
requireStringArray("allowedProperties", options.allowedProperties);
|
|
1677
|
+
requireStringArray("deniedProperties", options.deniedProperties);
|
|
1678
|
+
}
|
|
1617
1679
|
let sharedInstance;
|
|
1618
1680
|
/**
|
|
1619
1681
|
* Evaluate a single expression with default options. Uses a shared instance internally.
|
|
@@ -1647,6 +1709,7 @@ function evaluateExpression(expression, context) {
|
|
|
1647
1709
|
* ```
|
|
1648
1710
|
*/
|
|
1649
1711
|
function bonsai(options = {}) {
|
|
1712
|
+
assertValidOptions(options);
|
|
1650
1713
|
const registry = createPluginRegistry();
|
|
1651
1714
|
const cache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|
|
1652
1715
|
const astCache = new LRUCache(options.cacheSize ?? DEFAULT_CACHE_SIZE);
|