@coldsmirk/abacus-core 0.4.1 → 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/dist/index.cjs CHANGED
@@ -1,5 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region src/engine/errors.ts
3
2
  var ExpressionError = class extends Error {
4
3
  expression;
5
4
  constructor(message, expression, cause) {
@@ -14,8 +13,42 @@ var ExpressionNotReadyError = class extends ExpressionError {
14
13
  this.name = "ExpressionNotReadyError";
15
14
  }
16
15
  };
17
- //#endregion
18
- //#region src/internal/predicates.ts
16
+ function isAscii(source) {
17
+ for (let index = 0; index < source.length; index += 1) if (source.codePointAt(index) > 127) return false;
18
+ return true;
19
+ }
20
+ function utf8ByteLength(codePoint) {
21
+ if (codePoint < 128) return 1;
22
+ if (codePoint < 2048) return 2;
23
+ return codePoint < 65536 ? 3 : 4;
24
+ }
25
+ function utf8OffsetConverter(source) {
26
+ if (isAscii(source)) return (byteOffset) => Math.max(0, Math.min(byteOffset, source.length));
27
+ const byteStarts = [];
28
+ const unitStarts = [];
29
+ let bytes = 0;
30
+ let units = 0;
31
+ for (const char of source) {
32
+ byteStarts.push(bytes);
33
+ unitStarts.push(units);
34
+ bytes += utf8ByteLength(char.codePointAt(0));
35
+ units += char.length;
36
+ }
37
+ byteStarts.push(bytes);
38
+ unitStarts.push(units);
39
+ return (byteOffset) => {
40
+ if (byteOffset <= 0) return 0;
41
+ if (byteOffset >= bytes) return source.length;
42
+ let low = 0;
43
+ let high = byteStarts.length - 1;
44
+ while (low < high) {
45
+ const middle = low + high + 1 >> 1;
46
+ if (byteStarts[middle] <= byteOffset) low = middle;
47
+ else high = middle - 1;
48
+ }
49
+ return unitStarts[low];
50
+ };
51
+ }
19
52
  function isUndefined(value) {
20
53
  return value === void 0;
21
54
  }
@@ -31,13 +64,13 @@ function isNullish(value) {
31
64
  function isRecord(value) {
32
65
  return typeof value === "object" && value !== null && !Array.isArray(value);
33
66
  }
34
- //#endregion
35
- //#region src/engine/loader.ts
36
67
  let enginePromise = null;
37
68
  let engineSync = null;
38
69
  let engineError = null;
39
70
  let configuredInput;
40
71
  let typeContextCache = null;
72
+ let loadEpoch = 0;
73
+ let lastLoad = null;
41
74
  function evaluateSafely(expression, run) {
42
75
  try {
43
76
  return run();
@@ -57,9 +90,13 @@ function configureEngine(options) {
57
90
  function loadEngine() {
58
91
  if (enginePromise) return enginePromise;
59
92
  engineError = null;
60
- enginePromise = (async () => {
93
+ const epoch = loadEpoch;
94
+ const predecessor = lastLoad;
95
+ const input = configuredInput;
96
+ const load = (async () => {
97
+ if (predecessor) await predecessor.catch(() => void 0);
61
98
  const zen = await import("@gorules/zen-engine-wasm");
62
- await zen.default(isUndefined(configuredInput) ? void 0 : { module_or_path: configuredInput });
99
+ await zen.default(isUndefined(input) ? void 0 : { module_or_path: input });
63
100
  const toVariableType = (type) => zen.VariableType.fromJson(type);
64
101
  const ensureTypeContext = (variables) => {
65
102
  if (typeContextCache && typeContextCache.variables === variables) return typeContextCache;
@@ -88,9 +125,19 @@ function loadEngine() {
88
125
  analyze: (variables, source, unary) => {
89
126
  const context = ensureTypeContext(variables);
90
127
  const rawSpans = unary ? context.handle.typeCheckUnary(source) : context.handle.typeCheck(source);
128
+ if (!Array.isArray(rawSpans)) return {
129
+ rootKind: context.rootKind,
130
+ spans: []
131
+ };
132
+ const toUtf16 = utf8OffsetConverter(source);
91
133
  return {
92
134
  rootKind: context.rootKind,
93
- spans: Array.isArray(rawSpans) ? rawSpans : []
135
+ spans: rawSpans.map((span) => {
136
+ return {
137
+ ...span,
138
+ span: [toUtf16(span.span[0]), toUtf16(span.span[1])]
139
+ };
140
+ })
94
141
  };
95
142
  },
96
143
  satisfies: (actual, expected) => {
@@ -108,12 +155,17 @@ function loadEngine() {
108
155
  },
109
156
  isReady: () => zen.isReady()
110
157
  });
111
- engineSync = engine;
158
+ if (epoch === loadEpoch) engineSync = engine;
112
159
  return engine;
113
- })().catch((error) => {
114
- enginePromise = null;
115
- engineError = new ExpressionError(loadFailureMessage(), void 0, error);
116
- throw engineError;
160
+ })();
161
+ lastLoad = load;
162
+ enginePromise = load.catch((error) => {
163
+ const failure = new ExpressionError(loadFailureMessage(), void 0, error);
164
+ if (epoch === loadEpoch) {
165
+ enginePromise = null;
166
+ engineError = failure;
167
+ }
168
+ throw failure;
117
169
  });
118
170
  return enginePromise;
119
171
  }
@@ -128,6 +180,7 @@ function getEngineSync() {
128
180
  return engineSync;
129
181
  }
130
182
  function resetEngine() {
183
+ loadEpoch += 1;
131
184
  typeContextCache?.handle.free();
132
185
  typeContextCache = null;
133
186
  enginePromise = null;
@@ -135,16 +188,178 @@ function resetEngine() {
135
188
  engineError = null;
136
189
  configuredInput = void 0;
137
190
  }
138
- //#endregion
139
- //#region src/internal/env.ts
140
191
  const isDev = detectDev();
141
192
  function detectDev() {
142
193
  if (typeof process === "undefined") return false;
143
194
  return process.env ? process.env.NODE_ENV !== "production" : false;
144
195
  }
145
- //#endregion
146
- //#region src/condition/subject.ts
196
+ const U64_MAX = 18446744073709551615n;
197
+ const I64_MIN_MAGNITUDE = 9223372036854775808n;
198
+ const ZEN_DECIMAL_MANTISSA_LIMIT = 2n ** 96n;
199
+ const ZEN_DECIMAL_MANTISSA_LIMIT_TEXT = String(ZEN_DECIMAL_MANTISSA_LIMIT);
200
+ const DECIMAL_SIGN_BIT = 2n ** 95n;
201
+ const EXCESS_PRECISION_LIMIT = 2n ** 52n;
202
+ const NUMBER_TEXT_PATTERN = /^(?<integer>\d+)(?:\.(?<fraction>\d+))?(?:e(?<exponent>[+-]\d+))?$/;
203
+ const UNSIGNED_INTEGER_TEXT_PATTERN = /^\d+$/;
204
+ function isZenUnsignedIntegerText(value) {
205
+ if (!UNSIGNED_INTEGER_TEXT_PATTERN.test(value)) return false;
206
+ const normalized = value.replace(/^0+(?=\d)/, "");
207
+ return normalized.length < ZEN_DECIMAL_MANTISSA_LIMIT_TEXT.length || normalized.length === ZEN_DECIMAL_MANTISSA_LIMIT_TEXT.length && normalized < ZEN_DECIMAL_MANTISSA_LIMIT_TEXT;
208
+ }
209
+ function powerOfTen(exponent) {
210
+ return Number(`1e${exponent}`);
211
+ }
212
+ function normalize(digits, exponent) {
213
+ let d = digits;
214
+ let e = exponent;
215
+ while (d !== 0n && d % 10n === 0n) {
216
+ d /= 10n;
217
+ e += 1;
218
+ }
219
+ return d === 0n ? {
220
+ digits: 0n,
221
+ exponent: 0
222
+ } : {
223
+ digits: d,
224
+ exponent: e
225
+ };
226
+ }
227
+ function parseAsSerdeJson(text) {
228
+ const match = NUMBER_TEXT_PATTERN.exec(text);
229
+ if (match?.groups === void 0) return null;
230
+ const { integer, fraction, exponent } = match.groups;
231
+ let significand = 0n;
232
+ let scale = 0;
233
+ let overflowed = false;
234
+ for (const digit of integer) {
235
+ if (!overflowed && significand * 10n + BigInt(digit) > U64_MAX) overflowed = true;
236
+ if (overflowed) scale += 1;
237
+ else significand = significand * 10n + BigInt(digit);
238
+ }
239
+ if (fraction === void 0 && exponent === void 0 && !overflowed) return {
240
+ kind: "int",
241
+ int: significand,
242
+ f64: 0
243
+ };
244
+ if (fraction !== void 0) for (const digit of fraction) {
245
+ if (overflowed || significand * 10n + BigInt(digit) > U64_MAX) break;
246
+ significand = significand * 10n + BigInt(digit);
247
+ scale -= 1;
248
+ }
249
+ if (exponent !== void 0) scale += Number(exponent);
250
+ let value = Number(significand);
251
+ let remaining = scale;
252
+ for (;;) {
253
+ if (Math.abs(remaining) <= 308) {
254
+ value = remaining >= 0 ? value * powerOfTen(remaining) : value / powerOfTen(-remaining);
255
+ break;
256
+ }
257
+ if (value === 0) break;
258
+ if (remaining >= 0) return null;
259
+ value /= 1e308;
260
+ remaining += 308;
261
+ }
262
+ return Number.isFinite(value) ? {
263
+ kind: "f64",
264
+ int: 0n,
265
+ f64: value
266
+ } : null;
267
+ }
268
+ function decimalFromF64(value) {
269
+ if (value === 0) return {
270
+ digits: 0n,
271
+ exponent: 0
272
+ };
273
+ const view = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(8));
274
+ view.setFloat64(0, value);
275
+ const raw = view.getBigUint64(0);
276
+ const biasedExponent = Number(raw >> 52n & 2047n);
277
+ const fractionBits = raw & 4503599627370495n;
278
+ let bits = biasedExponent === 0 ? fractionBits : fractionBits | 1n << 52n;
279
+ const exponent2 = (biasedExponent === 0 ? 1 : biasedExponent) - 1023 - 52;
280
+ let exponent5 = -exponent2;
281
+ let exponent10 = exponent2;
282
+ while (exponent5 > 0) if ((bits & 1n) === 0n) {
283
+ exponent10 += 1;
284
+ exponent5 -= 1;
285
+ bits >>= 1n;
286
+ } else {
287
+ exponent5 -= 1;
288
+ const timesFive = bits * 5n;
289
+ if (timesFive < ZEN_DECIMAL_MANTISSA_LIMIT) bits = timesFive;
290
+ else {
291
+ exponent10 += 1;
292
+ bits >>= 1n;
293
+ }
294
+ }
295
+ while (exponent5 < 0) if ((bits & DECIMAL_SIGN_BIT) === 0n) {
296
+ exponent10 -= 1;
297
+ exponent5 += 1;
298
+ bits <<= 1n;
299
+ } else if (exponent10 * 2 > -exponent5) return null;
300
+ else {
301
+ exponent5 += 1;
302
+ bits /= 5n;
303
+ }
304
+ while (exponent10 > 0) {
305
+ const timesTen = bits * 10n;
306
+ if (timesTen >= ZEN_DECIMAL_MANTISSA_LIMIT) return null;
307
+ bits = timesTen;
308
+ exponent10 -= 1;
309
+ }
310
+ while (exponent10 < -28) {
311
+ const remainder = bits % 10n;
312
+ bits /= 10n;
313
+ exponent10 += 1;
314
+ if (bits === 0n) exponent10 = 0;
315
+ else if (remainder >= 5n) bits += 1n;
316
+ }
317
+ while (exponent10 < 0 && bits >= EXCESS_PRECISION_LIMIT) {
318
+ const remainder = bits % 10n;
319
+ bits /= 10n;
320
+ exponent10 += 1;
321
+ if (remainder >= 5n) bits += 1n;
322
+ }
323
+ while (exponent10 < 0 && bits % 10n === 0n) {
324
+ bits /= 10n;
325
+ exponent10 += 1;
326
+ }
327
+ return {
328
+ digits: bits,
329
+ exponent: exponent10
330
+ };
331
+ }
332
+ function contextDecimal(value) {
333
+ const magnitude = Math.abs(value);
334
+ if (magnitude === 0) return {
335
+ digits: 0n,
336
+ exponent: 0
337
+ };
338
+ const parsed = parseAsSerdeJson(String(magnitude));
339
+ if (parsed === null) return null;
340
+ if (parsed.kind === "int") {
341
+ if (value >= 0 || parsed.int <= I64_MIN_MAGNITUDE) return normalize(parsed.int, 0);
342
+ const viaF64 = decimalFromF64(Number(parsed.int));
343
+ return viaF64 === null ? null : normalize(viaF64.digits, viaF64.exponent);
344
+ }
345
+ const converted = decimalFromF64(parsed.f64);
346
+ return converted === null ? null : normalize(converted.digits, converted.exponent);
347
+ }
348
+ function literalDecimal(value) {
349
+ const match = NUMBER_TEXT_PATTERN.exec(String(Math.abs(value)));
350
+ if (match?.groups === void 0) return null;
351
+ const { integer, fraction = "", exponent = "0" } = match.groups;
352
+ return normalize(BigInt(integer + fraction), Number(exponent) - fraction.length);
353
+ }
354
+ function isZenConsistentNumber(value) {
355
+ if (!Number.isFinite(value)) return false;
356
+ const literal = literalDecimal(value);
357
+ if (literal === null || literal.digits >= ZEN_DECIMAL_MANTISSA_LIMIT) return false;
358
+ const context = contextDecimal(value);
359
+ return context !== null && literal.digits === context.digits && literal.exponent === context.exponent;
360
+ }
147
361
  const SUBJECT_PATTERN = /^[A-Z_$][\w$]*(?:\.[A-Z_$][\w$]*|\[\d+\])*$/i;
362
+ const SUBJECT_INDEX_PATTERN = /\[(?<index>\d+)\]/g;
148
363
  const ZEN_RESERVED_WORDS = new Set([
149
364
  "and",
150
365
  "or",
@@ -155,17 +370,24 @@ const ZEN_RESERVED_WORDS = new Set([
155
370
  "null"
156
371
  ]);
157
372
  function isIdentifierPath(subject) {
158
- return SUBJECT_PATTERN.test(subject) && (subject.match(/[A-Z_$][\w$]*/gi) ?? []).every((segment) => !ZEN_RESERVED_WORDS.has(segment));
373
+ if (!SUBJECT_PATTERN.test(subject) || (subject.match(/[A-Z_$][\w$]*/gi) ?? []).some((segment) => ZEN_RESERVED_WORDS.has(segment))) return false;
374
+ for (const match of subject.matchAll(SUBJECT_INDEX_PATTERN)) if (match.groups?.index === void 0 || !isZenUnsignedIntegerText(match.groups.index)) return false;
375
+ return true;
376
+ }
377
+ function isZenRepresentableNumber(value) {
378
+ return isZenConsistentNumber(value);
159
379
  }
160
- //#endregion
161
- //#region src/condition/compile.ts
162
380
  function toZenLiteral(value) {
163
381
  if (isNullish(value)) return "null";
164
382
  if (typeof value === "number") {
165
- if (!Number.isFinite(value)) throw new ExpressionError(`Number ${String(value)} has no ZEN literal representation`);
383
+ if (!isZenRepresentableNumber(value)) throw new ExpressionError(`Number ${String(value)} has no faithful ZEN literal representation`);
384
+ return String(value);
385
+ }
386
+ if (typeof value === "bigint") {
387
+ if (value >= ZEN_DECIMAL_MANTISSA_LIMIT || -value >= ZEN_DECIMAL_MANTISSA_LIMIT) throw new ExpressionError(`Bigint ${String(value)} has no ZEN literal representation`);
166
388
  return String(value);
167
389
  }
168
- if (typeof value === "boolean" || typeof value === "bigint") return String(value);
390
+ if (typeof value === "boolean") return String(value);
169
391
  if (isString(value)) return encodeZenString(value);
170
392
  if (isArray(value)) return `[${value.map((item) => toZenLiteral(item)).join(", ")}]`;
171
393
  throw new ExpressionError(`Value of type "${typeof value}" has no ZEN literal representation`);
@@ -261,8 +483,6 @@ function reportEvaluationFailure(engine, expression, error) {
261
483
  async function selectBranch(branches, context) {
262
484
  return selectBranchWith(branches, context, await loadEngine());
263
485
  }
264
- //#endregion
265
- //#region src/condition/types.ts
266
486
  const CONDITION_OPERATORS = [
267
487
  "eq",
268
488
  "ne",
@@ -298,9 +518,83 @@ const CONDITION_OPERATOR_ARITIES = {
298
518
  function conditionOperatorArity(operator) {
299
519
  return CONDITION_OPERATOR_ARITIES[operator];
300
520
  }
301
- //#endregion
302
- //#region src/condition/compile-tree.ts
521
+ const MAX_CONDITION_TREE_DEPTH = 64;
522
+ const CONDITION_TREE_OPERATORS = CONDITION_OPERATORS;
523
+ const nodeIdRealm = Math.random().toString(36).slice(2, 7);
524
+ let nodeIdCounter = 0;
525
+ function newConditionNodeId() {
526
+ nodeIdCounter += 1;
527
+ return `cn-${nodeIdRealm}-${nodeIdCounter}`;
528
+ }
529
+ function emptyConditionGroup() {
530
+ return {
531
+ kind: "group",
532
+ id: newConditionNodeId(),
533
+ op: "and",
534
+ items: []
535
+ };
536
+ }
537
+ function assertConditionTreeStructure(tree) {
538
+ if (!isRecord(tree) || tree.kind !== "group") throw new ExpressionError("Condition tree root must be a group");
539
+ const ancestors = /* @__PURE__ */ new Set();
540
+ const stack = [{
541
+ group: tree,
542
+ depth: 0,
543
+ leaving: false
544
+ }];
545
+ while (stack.length > 0) {
546
+ const frame = stack.pop();
547
+ const { group } = frame;
548
+ if (frame.leaving) {
549
+ ancestors.delete(group);
550
+ continue;
551
+ }
552
+ if (frame.depth > 64) throw new ExpressionError(`Condition tree exceeds the maximum depth of 64`);
553
+ if (ancestors.has(group)) throw new ExpressionError("Condition tree contains a cycle");
554
+ if (group.op !== "and" && group.op !== "or") throw new ExpressionError(`Unsupported group operator: ${String(group.op)}`);
555
+ if (!isArray(group.items)) throw new ExpressionError("Condition tree group items must be an array");
556
+ ancestors.add(group);
557
+ stack.push({
558
+ ...frame,
559
+ leaving: true
560
+ });
561
+ for (let index = group.items.length - 1; index >= 0; index -= 1) {
562
+ const node = group.items[index];
563
+ if (!isRecord(node) || node.kind !== "group" && node.kind !== "rule") throw new ExpressionError("Condition tree contains an invalid node");
564
+ if (node.kind === "group") stack.push({
565
+ group: node,
566
+ depth: frame.depth + 1,
567
+ leaving: false
568
+ });
569
+ }
570
+ }
571
+ }
572
+ function withRuleId(rule) {
573
+ return rule.id === void 0 ? {
574
+ ...rule,
575
+ id: newConditionNodeId()
576
+ } : rule;
577
+ }
578
+ function withGroupIds(group) {
579
+ const items = group.items.map((item) => item.kind === "group" ? withGroupIds(item) : withRuleId(item));
580
+ if (group.id !== void 0 && items.every((item, index) => item === group.items[index])) return group;
581
+ return {
582
+ ...group,
583
+ id: group.id ?? newConditionNodeId(),
584
+ items
585
+ };
586
+ }
587
+ const normalizedTrees = /* @__PURE__ */ new WeakMap();
588
+ function ensureConditionNodeIds(tree) {
589
+ assertConditionTreeStructure(tree);
590
+ const cached = normalizedTrees.get(tree);
591
+ if (cached) return cached;
592
+ const normalized = withGroupIds(tree);
593
+ normalizedTrees.set(tree, normalized);
594
+ return normalized;
595
+ }
303
596
  function compileConditionTree(tree) {
597
+ assertConditionTreeStructure(tree);
304
598
  const normalized = normalizeNode(tree);
305
599
  return normalized === null ? "" : emitNode(normalized, true);
306
600
  }
@@ -317,9 +611,16 @@ function normalizeNode(node) {
317
611
  }
318
612
  function emitNode(node, topLevel) {
319
613
  if (node.kind === "rule") return compileRule(node);
320
- const joined = node.items.map((item) => emitNode(item, false)).join(node.op === "and" ? " and " : " or ");
614
+ const joined = node.items.map((item) => emitNode(item, false)).join(groupJoiner(node.op));
321
615
  return topLevel ? joined : `(${joined})`;
322
616
  }
617
+ function groupJoiner(op) {
618
+ switch (op) {
619
+ case "and": return " and ";
620
+ case "or": return " or ";
621
+ default: throw new ExpressionError(`Unsupported group operator: ${String(op)}`);
622
+ }
623
+ }
323
624
  function compileRule(rule) {
324
625
  if (!matchesOperatorArity(rule)) return null;
325
626
  return compileCondition({
@@ -342,8 +643,6 @@ function isConditionScalar(value) {
342
643
  function isScalarArray(value) {
343
644
  return isArray(value) && value.every((item) => isConditionScalar(item));
344
645
  }
345
- //#endregion
346
- //#region src/condition/lift-tree.ts
347
646
  const TWO_CHAR_PUNCTUATION = new Set([
348
647
  "==",
349
648
  "!=",
@@ -366,6 +665,10 @@ const IDENT_PART = /[\w$]/;
366
665
  const DIGIT = /\d/;
367
666
  const NUMBER_PATTERN = /^\d+(?:\.\d+)?(?:e[+-]?\d+)?/i;
368
667
  const INTEGER_PATTERN = /^\d+$/;
668
+ function parseCanonicalNumber(text) {
669
+ const value = Number(text);
670
+ return String(value) === text && isZenRepresentableNumber(value) ? value : null;
671
+ }
369
672
  const COMPARISON_OPERATORS = {
370
673
  "==": "eq",
371
674
  "!=": "ne",
@@ -379,7 +682,6 @@ const CALL_OPERATORS = {
379
682
  startsWith: "starts_with",
380
683
  endsWith: "ends_with"
381
684
  };
382
- const MAX_GROUP_DEPTH = 64;
383
685
  function liftConditionTree(expression) {
384
686
  const tokens = tokenize(expression);
385
687
  if (tokens === null || tokens.length === 0) return null;
@@ -425,8 +727,10 @@ function liftConditionTree(expression) {
425
727
  return token.value;
426
728
  }
427
729
  if (token.kind === "number") {
730
+ const value = parseCanonicalNumber(token.value);
731
+ if (value === null) return null;
428
732
  pos += 1;
429
- return Number(token.value);
733
+ return value;
430
734
  }
431
735
  if (token.kind === "ident") {
432
736
  if (token.value === "true") {
@@ -442,8 +746,10 @@ function liftConditionTree(expression) {
442
746
  if (token.kind === "punct" && token.value === "-") {
443
747
  const digits = peek(1);
444
748
  if (digits === void 0 || digits.kind !== "number") return null;
749
+ const value = parseCanonicalNumber(digits.value);
750
+ if (value === null || value === 0 || !isZenRepresentableNumber(-value)) return null;
445
751
  pos += 2;
446
- return -Number(digits.value);
752
+ return -value;
447
753
  }
448
754
  return null;
449
755
  }
@@ -472,6 +778,7 @@ function liftConditionTree(expression) {
472
778
  if (right === null || !consumePunct(")")) return null;
473
779
  return {
474
780
  kind: "rule",
781
+ id: newConditionNodeId(),
475
782
  left,
476
783
  operator,
477
784
  right
@@ -484,6 +791,7 @@ function liftConditionTree(expression) {
484
791
  if (right === null || !consumePunct(")")) return null;
485
792
  return {
486
793
  kind: "rule",
794
+ id: newConditionNodeId(),
487
795
  left,
488
796
  operator: "not_in",
489
797
  right
@@ -515,6 +823,7 @@ function liftConditionTree(expression) {
515
823
  pos += expected.length;
516
824
  return {
517
825
  kind: "rule",
826
+ id: newConditionNodeId(),
518
827
  left: read.path,
519
828
  operator
520
829
  };
@@ -550,6 +859,7 @@ function liftConditionTree(expression) {
550
859
  const right = parseArray();
551
860
  return right === null ? null : {
552
861
  kind: "rule",
862
+ id: newConditionNodeId(),
553
863
  left,
554
864
  operator: "in",
555
865
  right
@@ -562,6 +872,7 @@ function liftConditionTree(expression) {
562
872
  const right = parseLiteral();
563
873
  return right === null ? null : {
564
874
  kind: "rule",
875
+ id: newConditionNodeId(),
565
876
  left,
566
877
  operator,
567
878
  right
@@ -573,7 +884,7 @@ function liftConditionTree(expression) {
573
884
  const emptiness = tryEmptiness();
574
885
  if (emptiness !== null) return emptiness;
575
886
  if (isPunct(peek(), "(")) {
576
- if (depth >= MAX_GROUP_DEPTH) return null;
887
+ if (depth >= 64) return null;
577
888
  pos += 1;
578
889
  const inner = parseGroup(depth + 1);
579
890
  if (inner === null || !consumePunct(")")) return null;
@@ -603,6 +914,7 @@ function liftConditionTree(expression) {
603
914
  }
604
915
  return items.length === 1 ? items[0] : {
605
916
  kind: "group",
917
+ id: newConditionNodeId(),
606
918
  op: op ?? "and",
607
919
  items
608
920
  };
@@ -641,6 +953,7 @@ function readPath(tokens, from) {
641
953
  function asGroup(node) {
642
954
  return node.kind === "group" ? node : {
643
955
  kind: "group",
956
+ id: newConditionNodeId(),
644
957
  op: "and",
645
958
  items: [node]
646
959
  };
@@ -708,11 +1021,6 @@ function tokenize(input) {
708
1021
  }
709
1022
  return tokens;
710
1023
  }
711
- //#endregion
712
- //#region src/condition/tree-types.ts
713
- const CONDITION_TREE_OPERATORS = CONDITION_OPERATORS;
714
- //#endregion
715
- //#region src/engine/evaluate.ts
716
1024
  async function evaluate(expression, context) {
717
1025
  await loadEngine();
718
1026
  return evaluateSync(expression, context);
@@ -727,8 +1035,6 @@ function evaluateSync(expression, context) {
727
1035
  function evaluateUnarySync(expression, context) {
728
1036
  return getEngineSync().evaluateUnary(expression, context);
729
1037
  }
730
- //#endregion
731
- //#region src/engine/messages.ts
732
1038
  const COMPLETION_INFO_ZH = {
733
1039
  "Returns the length of variable": "返回变量的长度",
734
1040
  "Checks if variable contains a needle": "检查变量是否包含指定元素",
@@ -861,8 +1167,6 @@ function configureExpressionMessages({ locale, messages }) {
861
1167
  function getExpressionMessages() {
862
1168
  return activeMessages;
863
1169
  }
864
- //#endregion
865
- //#region src/engine/intellisense.ts
866
1170
  function parseOffset(text) {
867
1171
  const trimmed = text?.trim();
868
1172
  return trimmed ? Number(trimmed) : NaN;
@@ -881,7 +1185,9 @@ function normalizeDiagnostic(raw, source) {
881
1185
  if (raw === null || raw === void 0) return null;
882
1186
  const errorType = isRecord(raw) && typeof raw.type === "string" ? raw.type : void 0;
883
1187
  const message = isRecord(raw) && typeof raw.source === "string" ? raw.source : String(raw);
884
- const [from, to] = extractPosition(message) ?? [0, source.length];
1188
+ const bytePosition = extractPosition(message);
1189
+ const toUtf16 = utf8OffsetConverter(source);
1190
+ const [from, to] = bytePosition === null ? [0, source.length] : [toUtf16(bytePosition[0]), toUtf16(bytePosition[1])];
885
1191
  return {
886
1192
  from,
887
1193
  to,
@@ -933,8 +1239,6 @@ async function satisfiesType(actual, expected) {
933
1239
  function satisfiesTypeSync(actual, expected) {
934
1240
  return getEngineSync().satisfies(actual, expected);
935
1241
  }
936
- //#endregion
937
- //#region src/engine/template.ts
938
1242
  const HOLE_PATTERN = /\{\{(?<expression>[^{}]*)\}\}/g;
939
1243
  function parseTemplateHoles(source) {
940
1244
  const holes = [];
@@ -995,11 +1299,11 @@ async function getTemplateDiagnostics(source) {
995
1299
  await loadEngine();
996
1300
  return getTemplateDiagnosticsSync(source);
997
1301
  }
998
- //#endregion
999
1302
  exports.CONDITION_OPERATORS = CONDITION_OPERATORS;
1000
1303
  exports.CONDITION_TREE_OPERATORS = CONDITION_TREE_OPERATORS;
1001
1304
  exports.ExpressionError = ExpressionError;
1002
1305
  exports.ExpressionNotReadyError = ExpressionNotReadyError;
1306
+ exports.MAX_CONDITION_TREE_DEPTH = MAX_CONDITION_TREE_DEPTH;
1003
1307
  exports.analyzeTemplate = analyzeTemplate;
1004
1308
  exports.analyzeTemplateSync = analyzeTemplateSync;
1005
1309
  exports.analyzeTypes = analyzeTypes;
@@ -1011,7 +1315,9 @@ exports.compileGroup = compileGroup;
1011
1315
  exports.conditionOperatorArity = conditionOperatorArity;
1012
1316
  exports.configureEngine = configureEngine;
1013
1317
  exports.configureExpressionMessages = configureExpressionMessages;
1318
+ exports.emptyConditionGroup = emptyConditionGroup;
1014
1319
  exports.enMessages = enMessages;
1320
+ exports.ensureConditionNodeIds = ensureConditionNodeIds;
1015
1321
  exports.evaluate = evaluate;
1016
1322
  exports.evaluateSync = evaluateSync;
1017
1323
  exports.evaluateUnary = evaluateUnary;
@@ -1026,8 +1332,10 @@ exports.getExpressionMessages = getExpressionMessages;
1026
1332
  exports.getTemplateDiagnostics = getTemplateDiagnostics;
1027
1333
  exports.getTemplateDiagnosticsSync = getTemplateDiagnosticsSync;
1028
1334
  exports.isEngineReady = isEngineReady;
1335
+ exports.isZenRepresentableNumber = isZenRepresentableNumber;
1029
1336
  exports.liftConditionTree = liftConditionTree;
1030
1337
  exports.loadEngine = loadEngine;
1338
+ exports.newConditionNodeId = newConditionNodeId;
1031
1339
  exports.parseTemplateHoles = parseTemplateHoles;
1032
1340
  exports.registerExpressionLocale = registerExpressionLocale;
1033
1341
  exports.resetEngine = resetEngine;