@routier/core 0.6.0 → 0.7.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.
Files changed (52) hide show
  1. package/dist/assertions/index.cjs +19 -8
  2. package/dist/assertions/index.cjs.map +1 -1
  3. package/dist/assertions/index.d.ts +5 -1
  4. package/dist/assertions/index.js +21 -9
  5. package/dist/assertions/index.js.map +1 -1
  6. package/dist/collections/MemoryDataCollection.d.ts +10 -0
  7. package/dist/collections/index.cjs +29 -4
  8. package/dist/collections/index.cjs.map +1 -1
  9. package/dist/collections/index.js +29 -4
  10. package/dist/collections/index.js.map +1 -1
  11. package/dist/expressions/callSource.d.ts +41 -0
  12. package/dist/expressions/evaluate.d.ts +3 -0
  13. package/dist/expressions/fold.d.ts +7 -0
  14. package/dist/expressions/index.cjs +1754 -233
  15. package/dist/expressions/index.cjs.map +1 -1
  16. package/dist/expressions/index.d.ts +2 -0
  17. package/dist/expressions/index.js +1765 -234
  18. package/dist/expressions/index.js.map +1 -1
  19. package/dist/expressions/types.d.ts +45 -26
  20. package/dist/expressions/utils.d.ts +19 -1
  21. package/dist/index.cjs +2415 -364
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.js +2755 -684
  24. package/dist/index.js.map +1 -1
  25. package/dist/performance/index.cjs +6 -4
  26. package/dist/performance/index.cjs.map +1 -1
  27. package/dist/performance/index.js +6 -4
  28. package/dist/performance/index.js.map +1 -1
  29. package/dist/pipeline/index.cjs +6 -4
  30. package/dist/pipeline/index.cjs.map +1 -1
  31. package/dist/pipeline/index.js +6 -4
  32. package/dist/pipeline/index.js.map +1 -1
  33. package/dist/plugins/index.cjs +2309 -316
  34. package/dist/plugins/index.cjs.map +1 -1
  35. package/dist/plugins/index.js +2313 -311
  36. package/dist/plugins/index.js.map +1 -1
  37. package/dist/plugins/query/QueryOptionsCollection.d.ts +38 -10
  38. package/dist/plugins/query/describeFilter.d.ts +83 -0
  39. package/dist/plugins/query/explain.d.ts +71 -9
  40. package/dist/plugins/query/index.d.ts +1 -0
  41. package/dist/plugins/query/join.d.ts +4 -1
  42. package/dist/plugins/query/types.d.ts +36 -4
  43. package/dist/schema/PropertyInfo.d.ts +0 -1
  44. package/dist/schema/index.cjs +7 -14
  45. package/dist/schema/index.cjs.map +1 -1
  46. package/dist/schema/index.js +7 -14
  47. package/dist/schema/index.js.map +1 -1
  48. package/dist/utilities/index.cjs +242 -49
  49. package/dist/utilities/index.cjs.map +1 -1
  50. package/dist/utilities/index.js +242 -49
  51. package/dist/utilities/index.js.map +1 -1
  52. package/package.json +1 -1
@@ -4,6 +4,7 @@ __webpack_require__.d(__webpack_exports__, {
4
4
  Cv: () => (assertString),
5
5
  S6: () => (isValueExpression),
6
6
  e3: () => (isPropertyExpression),
7
+ fm: () => (isCallExpression),
7
8
  vg: () => (isOperatorExpression),
8
9
  xH: () => (isComparatorExpression)
9
10
  });
@@ -77,6 +78,11 @@ function isObjectWithType(value) {
77
78
  */ function isValueExpression(value) {
78
79
  return isObjectWithType(value) && value.type === "value";
79
80
  }
81
+ /**
82
+ * Type guard: narrows `value` to `CallExpression` when it is an object with `type === "call"`.
83
+ */ function isCallExpression(value) {
84
+ return isObjectWithType(value) && value.type === "call";
85
+ }
80
86
  /**
81
87
  * Type guard: narrows `value` to `EmptyExpression` when it is an object with `type === "empty"`.
82
88
  */ function isEmptyExpression(value) {
@@ -89,62 +95,396 @@ function isObjectWithType(value) {
89
95
  }
90
96
 
91
97
 
98
+ },
99
+ 429(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
100
+ __webpack_require__.d(__webpack_exports__, {
101
+ N: () => (CALL_SOURCE),
102
+ a: () => (renderCallAsJs)
103
+ });
104
+ const CALL_SOURCE = {
105
+ "to-lower-case": {
106
+ form: "method",
107
+ name: "toLowerCase"
108
+ },
109
+ "to-upper-case": {
110
+ form: "method",
111
+ name: "toUpperCase"
112
+ },
113
+ "length": {
114
+ form: "property",
115
+ name: "length"
116
+ },
117
+ "trim": {
118
+ form: "method",
119
+ name: "trim"
120
+ },
121
+ "trim-start": {
122
+ form: "method",
123
+ name: "trimStart"
124
+ },
125
+ "trim-end": {
126
+ form: "method",
127
+ name: "trimEnd"
128
+ },
129
+ "index-of": {
130
+ form: "method",
131
+ name: "indexOf"
132
+ },
133
+ "substring": {
134
+ form: "method",
135
+ name: "substring"
136
+ },
137
+ "concat": {
138
+ form: "method",
139
+ name: "concat"
140
+ },
141
+ "replace": {
142
+ form: "method",
143
+ name: "replace"
144
+ },
145
+ "replace-all": {
146
+ form: "method",
147
+ name: "replaceAll"
148
+ },
149
+ "absolute": {
150
+ form: "function",
151
+ name: "Math.abs"
152
+ },
153
+ "floor": {
154
+ form: "function",
155
+ name: "Math.floor"
156
+ },
157
+ "ceiling": {
158
+ form: "function",
159
+ name: "Math.ceil"
160
+ },
161
+ "round": {
162
+ form: "function",
163
+ name: "Math.round"
164
+ },
165
+ "sign": {
166
+ form: "function",
167
+ name: "Math.sign"
168
+ },
169
+ "square-root": {
170
+ form: "function",
171
+ name: "Math.sqrt"
172
+ },
173
+ "add": {
174
+ form: "operator",
175
+ symbol: "+"
176
+ },
177
+ "subtract": {
178
+ form: "operator",
179
+ symbol: "-"
180
+ },
181
+ "multiply": {
182
+ form: "operator",
183
+ symbol: "*"
184
+ },
185
+ "divide": {
186
+ form: "operator",
187
+ symbol: "/"
188
+ },
189
+ "modulo": {
190
+ form: "operator",
191
+ symbol: "%"
192
+ },
193
+ "utc-year": {
194
+ form: "method",
195
+ name: "getUTCFullYear"
196
+ },
197
+ "utc-month": {
198
+ form: "method",
199
+ name: "getUTCMonth"
200
+ },
201
+ "utc-day-of-month": {
202
+ form: "method",
203
+ name: "getUTCDate"
204
+ },
205
+ "utc-day-of-week": {
206
+ form: "method",
207
+ name: "getUTCDay"
208
+ },
209
+ "utc-hour": {
210
+ form: "method",
211
+ name: "getUTCHours"
212
+ },
213
+ "utc-minute": {
214
+ form: "method",
215
+ name: "getUTCMinutes"
216
+ },
217
+ "utc-second": {
218
+ form: "method",
219
+ name: "getUTCSeconds"
220
+ },
221
+ "utc-millisecond": {
222
+ form: "method",
223
+ name: "getUTCMilliseconds"
224
+ },
225
+ "epoch-ms": {
226
+ form: "method",
227
+ name: "getTime"
228
+ },
229
+ "to-string": {
230
+ form: "function",
231
+ name: "String"
232
+ },
233
+ "to-number": {
234
+ form: "function",
235
+ name: "Number"
236
+ },
237
+ "to-boolean": {
238
+ form: "function",
239
+ name: "Boolean"
240
+ },
241
+ "type-of": {
242
+ form: "prefix",
243
+ keyword: "typeof"
244
+ },
245
+ "some": {
246
+ form: "method",
247
+ name: "some"
248
+ },
249
+ "every": {
250
+ form: "method",
251
+ name: "every"
252
+ },
253
+ // `Math.pow(a, b)` parses to the same call; `**` is the shorter of the two spellings
254
+ "power": {
255
+ form: "operator",
256
+ symbol: "**"
257
+ },
258
+ "bit-and": {
259
+ form: "operator",
260
+ symbol: "&"
261
+ },
262
+ "bit-or": {
263
+ form: "operator",
264
+ symbol: "|"
265
+ },
266
+ "bit-xor": {
267
+ form: "operator",
268
+ symbol: "^"
269
+ },
270
+ "shift-left": {
271
+ form: "operator",
272
+ symbol: "<<"
273
+ },
274
+ "shift-right": {
275
+ form: "operator",
276
+ symbol: ">>"
277
+ },
278
+ "shift-right-unsigned": {
279
+ form: "operator",
280
+ symbol: ">>>"
281
+ },
282
+ "bit-not": {
283
+ form: "prefix",
284
+ keyword: "~"
285
+ },
286
+ "coalesce": {
287
+ form: "operator",
288
+ symbol: "??"
289
+ },
290
+ "conditional": {
291
+ form: "conditional"
292
+ },
293
+ "matches": {
294
+ form: "regex-test"
295
+ }
296
+ };
297
+ /**
298
+ * A call rendered as the JavaScript that produced it, from operand and argument text already
299
+ * rendered by the caller.
300
+ *
301
+ * Takes strings so one implementation serves a live tree and a serialized one.
302
+ */ /**
303
+ * Thunked because rendering a side can record a parameter, and `regex-test` emits its argument
304
+ * before its operand — so the two orders have to agree.
305
+ */ const renderCallAsJs = (call, renderOperand, renderArgs)=>{
306
+ const source = CALL_SOURCE[call];
307
+ if (source == null) {
308
+ const operand = renderOperand();
309
+ return `${operand}.${call}(${renderArgs().join(", ")})`;
310
+ }
311
+ if (source.form === "property") {
312
+ return `${renderOperand()}.${source.name}`;
313
+ }
314
+ if (source.form === "regex-test") {
315
+ const pattern = renderArgs()[0] ?? "?";
316
+ return `${pattern}.test(${renderOperand()})`;
317
+ }
318
+ if (source.form === "method") {
319
+ const operand = renderOperand();
320
+ return `${operand}.${source.name}(${renderArgs().join(", ")})`;
321
+ }
322
+ if (source.form === "function") {
323
+ const operand = renderOperand();
324
+ return `${source.name}(${[
325
+ operand,
326
+ ...renderArgs()
327
+ ].join(", ")})`;
328
+ }
329
+ if (source.form === "prefix") {
330
+ // `~x`, not `~ x` — a bitwise complement is written tight, unlike `typeof`
331
+ const operand = renderOperand();
332
+ return source.keyword === "~" ? `${source.keyword}${operand}` : `${source.keyword} ${operand}`;
333
+ }
334
+ if (source.form === "conditional") {
335
+ const operand = renderOperand();
336
+ const args = renderArgs();
337
+ return `${operand} ? ${args[0] ?? "?"} : ${args[1] ?? "?"}`;
338
+ }
339
+ const operand = renderOperand();
340
+ return `${[
341
+ operand,
342
+ ...renderArgs()
343
+ ].join(` ${source.symbol} `)}`;
344
+ };
345
+
346
+
92
347
  },
93
348
  835(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
94
349
  __webpack_require__.d(__webpack_exports__, {
95
350
  t: () => (EXPRESSION_TYPES)
96
351
  });
97
- const EXPRESSION_TYPES = [
98
- "operator",
99
- "comparator",
100
- "property",
101
- "value",
102
- "empty",
103
- "not-parsable"
104
- ];
352
+ /**
353
+ * A `Record` rather than a list, so adding to `ExpressionType` without adding it here is a compile
354
+ * error. As a list it was not exhaustive, and `call` was silently missing from `isExpression`.
355
+ */ const EXPRESSION_TYPE_SET = {
356
+ "operator": true,
357
+ "comparator": true,
358
+ "property": true,
359
+ "value": true,
360
+ "call": true,
361
+ "empty": true,
362
+ "not-parsable": true
363
+ };
364
+ const EXPRESSION_TYPES = Object.keys(EXPRESSION_TYPE_SET);
105
365
 
106
366
 
107
367
  },
108
368
  379(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
109
369
  __webpack_require__.d(__webpack_exports__, {
110
370
  Vu: () => (toPredicate),
371
+ Vv: () => (operandValue),
111
372
  _3: () => (evaluate),
373
+ gm: () => (UNRESOLVED),
112
374
  wS: () => (toStrictPredicate)
113
375
  });
114
376
  /* import */ var _assertions__rspack_import_0 = __webpack_require__(126);
115
377
 
116
378
  /** Reads a property or literal operand, or `UNRESOLVED` when the node is not one. */ const UNRESOLVED = Symbol("unresolved");
117
- const applyTransformer = (value, transformer)=>{
118
- if (transformer == null) {
119
- return value;
120
- }
121
- // A transformer applied to an absent value has no answer, and inventing one ("" for a missing
122
- // string) is how a filter starts matching rows it should not.
379
+ const ARITHMETIC = {
380
+ "add": (left, right)=>left + right,
381
+ "subtract": (left, right)=>left - right,
382
+ "multiply": (left, right)=>left * right,
383
+ "divide": (left, right)=>left / right,
384
+ "modulo": (left, right)=>left % right,
385
+ "power": (left, right)=>left ** right,
386
+ "bit-and": (left, right)=>left & right,
387
+ "bit-or": (left, right)=>left | right,
388
+ "bit-xor": (left, right)=>left ^ right,
389
+ "shift-left": (left, right)=>left << right,
390
+ "shift-right": (left, right)=>left >> right,
391
+ "shift-right-unsigned": (left, right)=>left >>> right
392
+ };
393
+ const applyCall = (call, value, args)=>{
394
+ // Above the guard: a template renders null as "null" in JavaScript, so these two are total.
395
+ if (call === "to-string") {
396
+ return String(value);
397
+ }
398
+ if (call === "concat") {
399
+ return [
400
+ value,
401
+ ...args
402
+ ].map(String).join("");
403
+ }
404
+ // A call applied to an absent value has no answer, and inventing one ("" for a missing string)
405
+ // is how a filter starts matching rows it should not.
123
406
  if (value == null) {
124
407
  return UNRESOLVED;
125
408
  }
126
- if (transformer === "to-lower-case") {
127
- return typeof value === "string" ? value.toLowerCase() : UNRESOLVED;
128
- }
129
- if (transformer === "to-upper-case") {
130
- return typeof value === "string" ? value.toUpperCase() : UNRESOLVED;
409
+ if (call === "to-lower-case" || call === "to-upper-case") {
410
+ if (typeof value !== "string") {
411
+ return UNRESOLVED;
412
+ }
413
+ const lower = call === "to-lower-case";
414
+ if (args.length === 0 || args[0] == null) {
415
+ return lower ? value.toLowerCase() : value.toUpperCase();
416
+ }
417
+ if (typeof args[0] !== "string") {
418
+ return UNRESOLVED;
419
+ }
420
+ try {
421
+ // An explicit locale is deterministic; dropping it answers a different question in Turkish.
422
+ return lower ? value.toLocaleLowerCase(args[0]) : value.toLocaleUpperCase(args[0]);
423
+ } catch {
424
+ // An invalid language tag throws RangeError; no answer beats the host's default.
425
+ return UNRESOLVED;
426
+ }
131
427
  }
132
- if (transformer === "length") {
428
+ if (call === "length") {
133
429
  return typeof value === "string" || Array.isArray(value) ? value.length : UNRESOLVED;
134
430
  }
431
+ if (call === "bit-not") {
432
+ return typeof value === "number" ? ~value : UNRESOLVED;
433
+ }
434
+ if (call === "matches") {
435
+ if (typeof value !== "string" || !(args[0] instanceof RegExp)) {
436
+ return UNRESOLVED;
437
+ }
438
+ // `test` advances `lastIndex` on a global or sticky pattern, and the pattern is shared with
439
+ // the cached template, where a source evaluates fresh in JavaScript.
440
+ return args[0].global || args[0].sticky ? new RegExp(args[0].source, args[0].flags).test(value) : args[0].test(value);
441
+ }
442
+ const arithmetic = ARITHMETIC[call];
443
+ if (arithmetic != null) {
444
+ return typeof value === "number" && typeof args[0] === "number" ? arithmetic(value, args[0]) : UNRESOLVED;
445
+ }
135
446
  return UNRESOLVED;
136
447
  };
137
- const operand = (expression, row)=>{
448
+ const operandValue = (expression, row)=>{
138
449
  if (expression == null) {
139
450
  return UNRESOLVED;
140
451
  }
141
452
  if ((0,_assertions__rspack_import_0/* .isValueExpression */.S6)(expression)) {
142
- return applyTransformer(expression.value, expression.transformer);
453
+ return expression.value;
143
454
  }
144
455
  if ((0,_assertions__rspack_import_0/* .isPropertyExpression */.e3)(expression)) {
145
456
  // Through the PropertyInfo, so a nested path and a `from`-renamed segment resolve the same
146
457
  // way every other consumer of the tree resolves them.
147
- return applyTransformer(expression.property.getValue(row), expression.transformer);
458
+ return expression.property.getValue(row);
459
+ }
460
+ if ((0,_assertions__rspack_import_0/* .isCallExpression */.fm)(expression)) {
461
+ /**
462
+ * `??` and `? :` are the two calls whose whole job is to answer when something is absent, so
463
+ * they run before the guard that refuses an absent operand.
464
+ */ if (expression.call === "coalesce") {
465
+ const left = operandValue(expression.expression, row);
466
+ return left === UNRESOLVED || left == null ? operandValue(expression.arguments[0], row) : left;
467
+ }
468
+ if (expression.call === "conditional") {
469
+ const condition = evaluate(expression.expression, row);
470
+ if (condition === undefined) {
471
+ return UNRESOLVED;
472
+ }
473
+ return operandValue(expression.arguments[condition === true ? 0 : 1], row);
474
+ }
475
+ const inner = operandValue(expression.expression, row);
476
+ if (inner === UNRESOLVED) {
477
+ return UNRESOLVED;
478
+ }
479
+ const args = [];
480
+ for (const argument of expression.arguments){
481
+ const resolved = operandValue(argument, row);
482
+ if (resolved === UNRESOLVED) {
483
+ return UNRESOLVED;
484
+ }
485
+ args.push(resolved);
486
+ }
487
+ return applyCall(expression.call, inner, args);
148
488
  }
149
489
  return UNRESOLVED;
150
490
  };
@@ -229,8 +569,8 @@ const evaluateComparator = (comparator, left, right, strict)=>{
229
569
  return left === false && right === false ? false : undefined;
230
570
  }
231
571
  if ((0,_assertions__rspack_import_0/* .isComparatorExpression */.xH)(expression)) {
232
- const left = operand(expression.left, row);
233
- const right = operand(expression.right, row);
572
+ const left = operandValue(expression.left, row);
573
+ const right = operandValue(expression.right, row);
234
574
  if (left === UNRESOLVED || right === UNRESOLVED) {
235
575
  return undefined;
236
576
  }
@@ -268,6 +608,114 @@ const evaluateComparator = (comparator, left, right, strict)=>{
268
608
  };
269
609
 
270
610
 
611
+ },
612
+ 43(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
613
+ __webpack_require__.d(__webpack_exports__, {
614
+ F5: () => (foldConstantCalls),
615
+ Sv: () => (FOLDABLE),
616
+ br: () => (foldedOperandValue)
617
+ });
618
+ /* import */ var _assertions__rspack_import_0 = __webpack_require__(126);
619
+ /* import */ var _evaluate__rspack_import_3 = __webpack_require__(379);
620
+ /* import */ var _types__rspack_import_2 = __webpack_require__(27);
621
+ /* import */ var _utils__rspack_import_1 = __webpack_require__(63);
622
+
623
+
624
+
625
+
626
+ /** Calls fold may compute. Absent means a plugin declines it, so a new call is opt-in. */ const FOLDABLE = new Set([
627
+ "to-lower-case",
628
+ "to-upper-case",
629
+ "length",
630
+ "bit-not",
631
+ "matches",
632
+ "to-string",
633
+ "concat",
634
+ "add",
635
+ "subtract",
636
+ "multiply",
637
+ "divide",
638
+ "modulo",
639
+ "power",
640
+ "bit-and",
641
+ "bit-or",
642
+ "bit-xor",
643
+ "shift-left",
644
+ "shift-right",
645
+ "shift-right-unsigned",
646
+ "coalesce",
647
+ "conditional"
648
+ ]);
649
+ /** `String(value)` on an object is the host's rendering — a Date carries its timezone. */ const COERCES_TO_TEXT = new Set([
650
+ "to-string",
651
+ "concat"
652
+ ]);
653
+ const isFrozenPrimitive = (value)=>value == null || typeof value !== "object";
654
+ const readsAProperty = (expression)=>{
655
+ if ((0,_assertions__rspack_import_0/* .isPropertyExpression */.e3)(expression)) {
656
+ return true;
657
+ }
658
+ return (0,_utils__rspack_import_1/* .childrenOf */.LU)(expression).some(readsAProperty);
659
+ };
660
+ /** A `conditional` holds a condition where every other call holds a value. */ const isConstant = (call)=>{
661
+ if (!FOLDABLE.has(call.call) || !call.arguments.every(_assertions__rspack_import_0/* .isValueExpression */.S6)) {
662
+ return false;
663
+ }
664
+ if (COERCES_TO_TEXT.has(call.call) && [
665
+ call.expression,
666
+ ...call.arguments
667
+ ].some((operand)=>(0,_assertions__rspack_import_0/* .isValueExpression */.S6)(operand) && !isFrozenPrimitive(operand.value))) {
668
+ return false;
669
+ }
670
+ return call.call === "conditional" ? !readsAProperty(call.expression) : (0,_assertions__rspack_import_0/* .isValueExpression */.S6)(call.expression);
671
+ };
672
+ /** Computes every call whose operand and arguments are all literals. Runs after `bindExpression`. */ const foldConstantCalls = (expression)=>{
673
+ if ((0,_assertions__rspack_import_0/* .isCallExpression */.fm)(expression)) {
674
+ const folded = new _types__rspack_import_2/* .CallExpression */.DG({
675
+ call: expression.call,
676
+ expression: foldConstantCalls(expression.expression),
677
+ arguments: expression.arguments.map(foldConstantCalls)
678
+ });
679
+ if (!isConstant(folded)) {
680
+ return folded;
681
+ }
682
+ const value = (0,_evaluate__rspack_import_3/* .operandValue */.Vv)(folded, {});
683
+ return value === _evaluate__rspack_import_3/* .UNRESOLVED */.gm ? folded : new _types__rspack_import_2/* .ValueExpression */.Ko({
684
+ value
685
+ });
686
+ }
687
+ if ((0,_assertions__rspack_import_0/* .isComparatorExpression */.xH)(expression)) {
688
+ return new _types__rspack_import_2/* .ComparatorExpression */.bQ({
689
+ comparator: expression.comparator,
690
+ negated: expression.negated,
691
+ strict: expression.strict,
692
+ left: expression.left == null ? undefined : foldConstantCalls(expression.left),
693
+ right: expression.right == null ? undefined : foldConstantCalls(expression.right)
694
+ });
695
+ }
696
+ if ((0,_assertions__rspack_import_0/* .isOperatorExpression */.vg)(expression)) {
697
+ return new _types__rspack_import_2/* .OperatorExpression */.fw({
698
+ operator: expression.operator,
699
+ left: expression.left == null ? undefined : foldConstantCalls(expression.left),
700
+ right: expression.right == null ? undefined : foldConstantCalls(expression.right)
701
+ });
702
+ }
703
+ return expression;
704
+ };
705
+ /** The value a literal operand binds as once the calls on it are computed. Throws if it cannot. */ const foldedOperandValue = (operand, calls)=>{
706
+ if (calls.length === 0) {
707
+ return operand.value;
708
+ }
709
+ // `peelCalls` returns calls innermost first, so the last one evaluates the whole chain.
710
+ const outermost = calls[calls.length - 1];
711
+ const value = readsAProperty(outermost) ? _evaluate__rspack_import_3/* .UNRESOLVED */.gm : (0,_evaluate__rspack_import_3/* .operandValue */.Vv)(outermost, {});
712
+ if (value === _evaluate__rspack_import_3/* .UNRESOLVED */.gm) {
713
+ throw new Error(`'${calls.map((call)=>call.call).join("', '")}' cannot be computed on the literal ` + `'${String(operand.value)}'.`);
714
+ }
715
+ return value;
716
+ };
717
+
718
+
271
719
  },
272
720
  91(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
273
721
  __webpack_require__.d(__webpack_exports__, {
@@ -275,14 +723,18 @@ __webpack_require__.d(__webpack_exports__, {
275
723
  oH: () => (parseFragment),
276
724
  pg: () => (combineExpressions)
277
725
  });
278
- /* import */ var _utilities__rspack_import_3 = __webpack_require__(581);
726
+ /* import */ var _utilities__rspack_import_5 = __webpack_require__(581);
279
727
  /* import */ var _assertions__rspack_import_0 = __webpack_require__(126);
280
728
  /* import */ var _schema__rspack_import_2 = __webpack_require__(537);
729
+ /* import */ var _evaluate__rspack_import_3 = __webpack_require__(379);
730
+ /* import */ var _fold__rspack_import_4 = __webpack_require__(43);
281
731
  /* import */ var _types__rspack_import_1 = __webpack_require__(27);
282
732
 
283
733
 
284
734
 
285
735
 
736
+
737
+
286
738
  // Error message constants
287
739
  const ERROR_MESSAGES = {
288
740
  PROPERTY_NOT_FOUND: (path)=>`Error parsing query, could not find PropertyInfo for path: ${path}`,
@@ -328,8 +780,13 @@ const converters = {
328
780
  };
329
781
  // Longest first so multi-character punctuation wins over its prefixes
330
782
  const MULTI_CHARACTER_PUNCTUATION = [
783
+ ">>>",
331
784
  "===",
332
785
  "!==",
786
+ "**",
787
+ "<<",
788
+ ">>",
789
+ "??",
333
790
  "?.",
334
791
  "&&",
335
792
  "||",
@@ -366,9 +823,17 @@ const SINGLE_CHARACTER_PUNCTUATION = new Set([
366
823
  "?",
367
824
  ":",
368
825
  "&",
369
- "|"
826
+ "|",
827
+ "^",
828
+ "~"
370
829
  ]);
371
- const STRING_ESCAPES = {
830
+ /**
831
+ * A lookup table keyed by source text.
832
+ *
833
+ * Null-prototype: `TRANSFORM_METHODS["toString"]` otherwise returns `Object.prototype.toString`,
834
+ * which is truthy, and the parser reads a method it does not support as one it does.
835
+ */ const sourceKeyed = (entries)=>Object.assign(Object.create(null), entries);
836
+ const STRING_ESCAPES = sourceKeyed({
372
837
  "n": "\n",
373
838
  "r": "\r",
374
839
  "t": "\t",
@@ -376,6 +841,24 @@ const STRING_ESCAPES = {
376
841
  "f": "\f",
377
842
  "v": "\v",
378
843
  "0": "\0"
844
+ });
845
+ /**
846
+ * Whether a `/` here opens a regex rather than dividing.
847
+ *
848
+ * A regex cannot follow a value. Everything else — the start of the source, an operator, an opening
849
+ * bracket, a comma — is a position where only a regex makes sense.
850
+ */ const regexCanStartHere = (tokens)=>{
851
+ const previous = tokens[tokens.length - 1];
852
+ if (previous == null) {
853
+ return true;
854
+ }
855
+ if (previous.kind === "number" || previous.kind === "string" || previous.kind === "bigint" || previous.kind === "regex") {
856
+ return false;
857
+ }
858
+ if (previous.kind === "identifier") {
859
+ return false;
860
+ }
861
+ return previous.value !== ")" && previous.value !== "]";
379
862
  };
380
863
  const isIdentifierStart = (char)=>/[a-zA-Z_$]/.test(char);
381
864
  const isIdentifierPart = (char)=>/[a-zA-Z0-9_$]/.test(char);
@@ -425,6 +908,51 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
425
908
  i++;
426
909
  continue;
427
910
  }
911
+ /**
912
+ * A regex literal, told from division by what came before it.
913
+ *
914
+ * `/` after a value — a number, string, identifier, `)` or `]` — is division. Anywhere else
915
+ * it opens a regex. That is the same rule a JavaScript lexer uses, and it is why `x.a / 2`
916
+ * and `/^a/.test(x.a)` can share a character.
917
+ */ if (char === "/" && source[i + 1] !== "/" && source[i + 1] !== "*" && regexCanStartHere(tokens)) {
918
+ let value = "";
919
+ let inClass = false;
920
+ let j = i + 1;
921
+ while(j < source.length){
922
+ const current = source[j];
923
+ if (current === "\\") {
924
+ value += current + (source[j + 1] ?? "");
925
+ j += 2;
926
+ continue;
927
+ }
928
+ if (current === "[") {
929
+ inClass = true;
930
+ } else if (current === "]") {
931
+ inClass = false;
932
+ } else if (current === "/" && inClass === false) {
933
+ break;
934
+ } else if (current === "\n") {
935
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated regular expression"));
936
+ }
937
+ value += current;
938
+ j++;
939
+ }
940
+ if (j >= source.length) {
941
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated regular expression"));
942
+ }
943
+ j++;
944
+ let flags = "";
945
+ while(j < source.length && isIdentifierPart(source[j])){
946
+ flags += source[j];
947
+ j++;
948
+ }
949
+ i = j;
950
+ tokens.push({
951
+ kind: "regex",
952
+ value: `${value}\u0000${flags}`
953
+ });
954
+ continue;
955
+ }
428
956
  // Comments
429
957
  if (char === "/" && source[i + 1] === "/") {
430
958
  while(i < source.length && source[i] !== "\n"){
@@ -444,6 +972,8 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
444
972
  if (char === "'" || char === "\"" || char === "`") {
445
973
  const quote = char;
446
974
  let value = "";
975
+ const chunks = [];
976
+ const expressions = [];
447
977
  i++;
448
978
  while(i < source.length && source[i] !== quote){
449
979
  if (source[i] === "\\") {
@@ -458,8 +988,43 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
458
988
  i += 2;
459
989
  continue;
460
990
  }
461
- if (quote === "`" && source[i] === "$" && source[i + 1] === "{") {
462
- throw new Error(ERROR_MESSAGES.UNSUPPORTED("template literal interpolation"));
991
+ /**
992
+ * An interpolation. The literal so far becomes a chunk and the expression source is
993
+ * kept whole, to be parsed by its own stream — nesting means the inner source can
994
+ * hold anything, including another template.
995
+ */ if (quote === "`" && source[i] === "$" && source[i + 1] === "{") {
996
+ let depth = 1;
997
+ let expression = "";
998
+ let at = i + 2;
999
+ while(at < source.length && depth > 0){
1000
+ const current = source[at];
1001
+ if (current === "{") {
1002
+ depth++;
1003
+ } else if (current === "}") {
1004
+ depth--;
1005
+ if (depth === 0) {
1006
+ break;
1007
+ }
1008
+ } else if (current === "'" || current === '"' || current === "`") {
1009
+ const closing = current;
1010
+ expression += current;
1011
+ at++;
1012
+ while(at < source.length && source[at] !== closing){
1013
+ expression += source[at] === "\\" ? source[at] + (source[at + 1] ?? "") : source[at];
1014
+ at += source[at] === "\\" ? 2 : 1;
1015
+ }
1016
+ }
1017
+ expression += source[at];
1018
+ at++;
1019
+ }
1020
+ if (depth > 0) {
1021
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated template interpolation"));
1022
+ }
1023
+ chunks.push(value);
1024
+ expressions.push(expression);
1025
+ value = "";
1026
+ i = at + 1;
1027
+ continue;
463
1028
  }
464
1029
  value += source[i];
465
1030
  i++;
@@ -468,6 +1033,17 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
468
1033
  throw new Error(ERROR_MESSAGES.UNSUPPORTED("unterminated string literal"));
469
1034
  }
470
1035
  i++; // consume closing quote
1036
+ if (expressions.length > 0) {
1037
+ chunks.push(value);
1038
+ tokens.push({
1039
+ kind: "template",
1040
+ value: JSON.stringify({
1041
+ chunks,
1042
+ expressions
1043
+ })
1044
+ });
1045
+ continue;
1046
+ }
471
1047
  tokens.push({
472
1048
  kind: "string",
473
1049
  value
@@ -511,6 +1087,14 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
511
1087
  }
512
1088
  }
513
1089
  }
1090
+ if (source[i] === "n") {
1091
+ i++;
1092
+ tokens.push({
1093
+ kind: "bigint",
1094
+ value: value.replace(/_/g, "")
1095
+ });
1096
+ continue;
1097
+ }
514
1098
  tokens.push({
515
1099
  kind: "number",
516
1100
  value: value.replace(/_/g, "")
@@ -561,6 +1145,24 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
561
1145
  constructor(tokens){
562
1146
  this.tokens = tokens;
563
1147
  }
1148
+ /** Inserts tokens at the cursor, bracketed so they keep their own precedence. */ splice(tokens) {
1149
+ const bracketed = [
1150
+ {
1151
+ kind: "punctuation",
1152
+ value: "("
1153
+ },
1154
+ ...tokens,
1155
+ {
1156
+ kind: "punctuation",
1157
+ value: ")"
1158
+ }
1159
+ ];
1160
+ this.tokens = [
1161
+ ...this.tokens.slice(0, this.index),
1162
+ ...bracketed,
1163
+ ...this.tokens.slice(this.index)
1164
+ ];
1165
+ }
564
1166
  get isAtEnd() {
565
1167
  return this.index >= this.tokens.length;
566
1168
  }
@@ -575,10 +1177,108 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
575
1177
  this.index++;
576
1178
  return token;
577
1179
  }
1180
+ /** The tokens of one statement's value, through the `;` or block end that closes it. */ takeStatementTokens() {
1181
+ const tokens = [];
1182
+ let depth = 0;
1183
+ while(!this.isAtEnd){
1184
+ const token = this.peek();
1185
+ if (token.kind === "punctuation") {
1186
+ if (token.value === "(" || token.value === "[" || token.value === "{") {
1187
+ depth++;
1188
+ } else if (token.value === ")" || token.value === "]" || token.value === "}") {
1189
+ if (depth === 0) {
1190
+ break;
1191
+ }
1192
+ depth--;
1193
+ } else if (token.value === ";" && depth === 0) {
1194
+ this.next();
1195
+ break;
1196
+ }
1197
+ }
1198
+ tokens.push(this.next());
1199
+ }
1200
+ if (tokens.length === 0) {
1201
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("a declaration with no value"));
1202
+ }
1203
+ return tokens;
1204
+ }
578
1205
  isPunctuation(value, offset = 0) {
579
1206
  const token = this.peek(offset);
580
1207
  return token != null && token.kind === "punctuation" && token.value === value;
581
1208
  }
1209
+ /**
1210
+ * Whether the group starting here holds a value rather than a condition.
1211
+ *
1212
+ * `(a && b)` is a boolean sub-expression; `(x.name ?? '') === 'ada'` and `(x.name).length` are
1213
+ * values. Only the token after the matching bracket tells them apart, so the decision is made by
1214
+ * looking ahead rather than by parsing one way and catching the failure — a rewind on exception
1215
+ * would swallow a genuine syntax error inside the group and report it as something else.
1216
+ */ groupIsValue() {
1217
+ let depth = 0;
1218
+ let at = this.index;
1219
+ for(; at < this.tokens.length; at++){
1220
+ const token = this.tokens[at];
1221
+ if (token.kind !== "punctuation") {
1222
+ continue;
1223
+ }
1224
+ if (token.value === "(") {
1225
+ depth++;
1226
+ continue;
1227
+ }
1228
+ if (token.value === ")") {
1229
+ depth--;
1230
+ if (depth === 0) {
1231
+ break;
1232
+ }
1233
+ }
1234
+ }
1235
+ const after = this.tokens[at + 1];
1236
+ if (after == null || after.kind !== "punctuation") {
1237
+ return false;
1238
+ }
1239
+ return COMPARISON_OPERATORS[after.value] != null || after.value === "." || after.value === "?.";
1240
+ }
1241
+ /** Whether a `?` sits at the top level of what is left, so this is a conditional. */ holdsConditional() {
1242
+ let depth = 0;
1243
+ for(let at = this.index; at < this.tokens.length; at++){
1244
+ const token = this.tokens[at];
1245
+ if (token.kind !== "punctuation") {
1246
+ continue;
1247
+ }
1248
+ if (token.value === "(" || token.value === "[") {
1249
+ depth++;
1250
+ } else if (token.value === ")" || token.value === "]") {
1251
+ depth--;
1252
+ } else if (token.value === "?" && depth === 0) {
1253
+ return true;
1254
+ }
1255
+ }
1256
+ return false;
1257
+ }
1258
+ /** Whether the group starting here is `( … ? … : … )` rather than a plain value. */ groupHoldsConditional() {
1259
+ let depth = 0;
1260
+ for(let at = this.index; at < this.tokens.length; at++){
1261
+ const token = this.tokens[at];
1262
+ if (token.kind !== "punctuation") {
1263
+ continue;
1264
+ }
1265
+ if (token.value === "(") {
1266
+ depth++;
1267
+ continue;
1268
+ }
1269
+ if (token.value === ")") {
1270
+ depth--;
1271
+ if (depth === 0) {
1272
+ return false;
1273
+ }
1274
+ continue;
1275
+ }
1276
+ if (token.value === "?" && depth === 1) {
1277
+ return true;
1278
+ }
1279
+ }
1280
+ return false;
1281
+ }
582
1282
  matchPunctuation(value) {
583
1283
  if (this.isPunctuation(value)) {
584
1284
  this.index++;
@@ -592,12 +1292,101 @@ const isHexDigit = (char)=>isDigit(char) || char >= "a" && char <= "f" || char >
592
1292
  }
593
1293
  }
594
1294
  }
595
- const COMPARATOR_METHODS = {
1295
+ /**
1296
+ * Calls JavaScript binds LOOSER than a comparison.
1297
+ *
1298
+ * This grammar reads a comparison's operands as values, which puts these tighter than they belong:
1299
+ * `x.flags & 6 === 2` is `x.flags & (6 === 2)` in JavaScript and would be read here as
1300
+ * `(x.flags & 6) === 2`. The two answer differently, so an ungrouped one is refused rather than
1301
+ * reinterpreted — the filter then runs in memory against the caller's own function, which is right by
1302
+ * construction. Brackets say which was meant, and JavaScript itself makes an unbracketed `??` mix a
1303
+ * syntax error for the same reason.
1304
+ */ const LOOSER_THAN_COMPARISON = [
1305
+ "bit-and",
1306
+ "bit-or",
1307
+ "bit-xor",
1308
+ "coalesce"
1309
+ ];
1310
+ const needsBrackets = (operand)=>operand.kind === "arithmetic" && operand.grouped !== true && LOOSER_THAN_COMPARISON.includes(operand.call);
1311
+ /** JavaScript precedence: `*`, `/`, `%` bind tighter than `+` and `-`. */ const MULTIPLICATIVE_OPERATORS = sourceKeyed({
1312
+ "*": "multiply",
1313
+ "/": "divide",
1314
+ "%": "modulo"
1315
+ });
1316
+ const ADDITIVE_OPERATORS = sourceKeyed({
1317
+ "+": "add",
1318
+ "-": "subtract"
1319
+ });
1320
+ const SHIFT_OPERATORS = sourceKeyed({
1321
+ "<<": "shift-left",
1322
+ ">>": "shift-right",
1323
+ ">>>": "shift-right-unsigned"
1324
+ });
1325
+ const BITWISE_AND_OPERATORS = sourceKeyed({
1326
+ "&": "bit-and"
1327
+ });
1328
+ const BITWISE_XOR_OPERATORS = sourceKeyed({
1329
+ "^": "bit-xor"
1330
+ });
1331
+ const BITWISE_OR_OPERATORS = sourceKeyed({
1332
+ "|": "bit-or"
1333
+ });
1334
+ const COALESCE_OPERATORS = sourceKeyed({
1335
+ "??": "coalesce"
1336
+ });
1337
+ /** Whether a schema property is reachable in here, which decides which side of a comparison it is. */ const containsProperty = (operand)=>{
1338
+ if (operand.kind === "property") {
1339
+ return true;
1340
+ }
1341
+ if (operand.kind === "conditional") {
1342
+ // A comparison always names a schema property, so the condition alone settles it
1343
+ return true;
1344
+ }
1345
+ return operand.kind === "arithmetic" && (containsProperty(operand.left) || containsProperty(operand.right) || operand.extra != null && containsProperty(operand.extra));
1346
+ };
1347
+ const DECLARATION_KEYWORDS = new Set([
1348
+ "const",
1349
+ "let",
1350
+ "var"
1351
+ ]);
1352
+ /** An operand whose value only a row can supply. */ const UNKNOWN_UNTIL_ROW = Symbol("unknown until row");
1353
+ /** The empty argument slot of a unary call. Compared by identity, so a real `undefined` still counts. */ const NO_ARGUMENT = Object.freeze({
1354
+ kind: "value",
1355
+ value: undefined,
1356
+ transformer: null,
1357
+ locale: null
1358
+ });
1359
+ const noArgument = ()=>NO_ARGUMENT;
1360
+ /** A predicate no row satisfies. Never reaches a tree: no expression node means "match nothing". */ const NEVER = "never";
1361
+ const and = (left, right)=>{
1362
+ if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(left)) {
1363
+ return right;
1364
+ }
1365
+ if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(right)) {
1366
+ return left;
1367
+ }
1368
+ return new _types__rspack_import_1/* .OperatorExpression */.fw({
1369
+ operator: "&&",
1370
+ left,
1371
+ right
1372
+ });
1373
+ };
1374
+ const or = (left, right)=>{
1375
+ if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(left) || _types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(right)) {
1376
+ return _types__rspack_import_1/* .Expression.EMPTY */.r4.EMPTY;
1377
+ }
1378
+ return new _types__rspack_import_1/* .OperatorExpression */.fw({
1379
+ operator: "||",
1380
+ left,
1381
+ right
1382
+ });
1383
+ };
1384
+ const COMPARATOR_METHODS = sourceKeyed({
596
1385
  startsWith: "starts-with",
597
1386
  endsWith: "ends-with",
598
1387
  includes: "includes"
599
- };
600
- const TRANSFORM_METHODS = {
1388
+ });
1389
+ const TRANSFORM_METHODS = sourceKeyed({
601
1390
  toLowerCase: {
602
1391
  transformer: "to-lower-case",
603
1392
  locale: null
@@ -614,8 +1403,8 @@ const TRANSFORM_METHODS = {
614
1403
  transformer: "to-upper-case",
615
1404
  locale: "en-US"
616
1405
  }
617
- };
618
- const COMPARISON_OPERATORS = {
1406
+ });
1407
+ const COMPARISON_OPERATORS = sourceKeyed({
619
1408
  "==": {
620
1409
  comparator: "equals",
621
1410
  negated: false,
@@ -656,7 +1445,7 @@ const COMPARISON_OPERATORS = {
656
1445
  negated: false,
657
1446
  strict: false
658
1447
  }
659
- };
1448
+ });
660
1449
  const SWAPPED_COMPARATORS = {
661
1450
  "equals": "equals",
662
1451
  "greater-than": "less-than",
@@ -727,14 +1516,14 @@ const resolveParamPath = (paramsName, path, data)=>{
727
1516
  */ class ExpressionParser {
728
1517
  schema;
729
1518
  stream;
730
- entityName;
1519
+ scope;
731
1520
  paramsName;
732
1521
  params;
733
1522
  /** Set when a param value shaped the tree itself (e.g. x[p.name]) — such templates cannot be cached. */ structurallyDependsOnParams = false;
734
- constructor(schema, stream, entityName, paramsName, params){
1523
+ constructor(schema, stream, scope, paramsName, params){
735
1524
  this.schema = schema;
736
1525
  this.stream = stream;
737
- this.entityName = entityName;
1526
+ this.scope = scope;
738
1527
  this.paramsName = paramsName;
739
1528
  this.params = params;
740
1529
  }
@@ -745,6 +1534,180 @@ const resolveParamPath = (paramsName, path, data)=>{
745
1534
  }
746
1535
  return expression;
747
1536
  }
1537
+ parseBody() {
1538
+ if (!this.stream.isPunctuation("{")) {
1539
+ return this.parse();
1540
+ }
1541
+ const answer = this.parseBlock();
1542
+ if (!this.stream.isAtEnd) {
1543
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`unexpected token '${this.stream.peek()?.value}'`));
1544
+ }
1545
+ if (answer === NEVER) {
1546
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("a predicate no row can satisfy"));
1547
+ }
1548
+ return answer;
1549
+ }
1550
+ /** The expression a `{ … }` block answers with. */ parseBlock() {
1551
+ this.stream.expectPunctuation("{");
1552
+ const answer = this.parseStatements();
1553
+ this.stream.expectPunctuation("}");
1554
+ return answer;
1555
+ }
1556
+ /** Statements up to the one that returns. What follows a `return` is never read, as in JavaScript. */ parseStatements() {
1557
+ if (this.stream.isPunctuation("}") || this.stream.isAtEnd) {
1558
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("a block body that returns nothing"));
1559
+ }
1560
+ const keyword = this.stream.peek();
1561
+ if (keyword == null || keyword.kind !== "identifier") {
1562
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`a statement starting '${keyword?.value}'`));
1563
+ }
1564
+ if (DECLARATION_KEYWORDS.has(keyword.value)) {
1565
+ this.declare();
1566
+ return this.parseStatements();
1567
+ }
1568
+ if (keyword.value === "return") {
1569
+ this.stream.next();
1570
+ const answer = this.parseReturnedCondition();
1571
+ this.stream.matchPunctuation(";");
1572
+ return answer;
1573
+ }
1574
+ if (keyword.value === "if") {
1575
+ return this.parseIfStatement();
1576
+ }
1577
+ if (keyword.value === "switch") {
1578
+ return this.parseSwitchStatement();
1579
+ }
1580
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`the statement '${keyword.value}'`));
1581
+ }
1582
+ /**
1583
+ * Binds a `const`/`let`/`var` name to the tokens of its initializer — tokens rather than a parsed
1584
+ * expression, so the name works as an operand, an argument, or a call receiver alike.
1585
+ */ declare() {
1586
+ this.stream.next();
1587
+ const name = this.stream.next();
1588
+ if (name.kind !== "identifier") {
1589
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`the declaration '${name.value}'`));
1590
+ }
1591
+ this.stream.expectPunctuation("=");
1592
+ this.scope.set(name.value, {
1593
+ kind: "inlined",
1594
+ tokens: this.stream.takeStatementTokens()
1595
+ });
1596
+ }
1597
+ /** `return false` on its own, which no row satisfies, and every other returned condition. */ parseReturnedCondition() {
1598
+ const next = this.stream.peek();
1599
+ const after = this.stream.peek(1);
1600
+ const endsHere = after == null || after.kind === "punctuation" && (after.value === ";" || after.value === "}");
1601
+ if (next != null && next.kind === "identifier" && next.value === "false" && endsHere) {
1602
+ this.stream.next();
1603
+ return NEVER;
1604
+ }
1605
+ return this.parseOr();
1606
+ }
1607
+ parseIfStatement() {
1608
+ this.stream.next();
1609
+ this.stream.expectPunctuation("(");
1610
+ const condition = this.parseOr();
1611
+ this.stream.expectPunctuation(")");
1612
+ const whenTrue = this.parseBranch();
1613
+ if (this.stream.peek()?.value === "else") {
1614
+ this.stream.next();
1615
+ return this.either(condition, whenTrue, this.parseBranch());
1616
+ }
1617
+ // Without an `else`, the statements after the `if` are the other branch
1618
+ return this.either(condition, whenTrue, this.parseStatements());
1619
+ }
1620
+ /** One arm of an `if`: a block, or a single statement. */ parseBranch() {
1621
+ return this.stream.isPunctuation("{") ? this.parseBlock() : this.parseStatements();
1622
+ }
1623
+ /** A `switch` over one subject, as the disjunction of its cases. */ parseSwitchStatement() {
1624
+ this.stream.next();
1625
+ this.stream.expectPunctuation("(");
1626
+ const subject = this.parseValue();
1627
+ this.stream.expectPunctuation(")");
1628
+ this.stream.expectPunctuation("{");
1629
+ let matching = null;
1630
+ let pending = [];
1631
+ let everyLabel = [];
1632
+ let byDefault = null;
1633
+ let anyCaseBroke = false;
1634
+ while(!this.stream.matchPunctuation("}")){
1635
+ const label = this.stream.next();
1636
+ if (label.kind !== "identifier" || label.value !== "case" && label.value !== "default") {
1637
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`'${label.value}' inside a switch`));
1638
+ }
1639
+ if (label.value === "case") {
1640
+ const test = this.buildComparison(subject, COMPARISON_OPERATORS["==="], this.parseValue());
1641
+ pending.push(test);
1642
+ everyLabel.push(test);
1643
+ }
1644
+ this.stream.expectPunctuation(":");
1645
+ // `case 'a':` with no body of its own runs the next case's body
1646
+ if (this.stream.peek()?.value === "case" || this.stream.peek()?.value === "default") {
1647
+ continue;
1648
+ }
1649
+ if (this.stream.peek()?.value === "break") {
1650
+ this.stream.next();
1651
+ this.stream.matchPunctuation(";");
1652
+ anyCaseBroke = true;
1653
+ pending = [];
1654
+ continue;
1655
+ }
1656
+ const body = this.parseCaseBody();
1657
+ if (label.value === "default") {
1658
+ byDefault = body === NEVER ? null : body;
1659
+ continue;
1660
+ }
1661
+ if (body !== NEVER && pending.length > 0) {
1662
+ const reached = pending.reduce((left, right)=>or(left, right));
1663
+ const term = _types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(body) ? reached : and(reached, body);
1664
+ matching = matching == null ? term : or(matching, term);
1665
+ }
1666
+ pending = [];
1667
+ }
1668
+ // Falling out of the switch continues after it, so the statements there are the default too
1669
+ const afterSwitch = byDefault == null && !this.stream.isPunctuation("}") && !this.stream.isAtEnd ? this.parseStatements() : NEVER;
1670
+ if (afterSwitch !== NEVER) {
1671
+ // A `break` also continues after the switch, so its case would take that answer rather
1672
+ // than none — a distinction this rewrite cannot carry
1673
+ if (anyCaseBroke) {
1674
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("a switch that breaks and then falls into more statements"));
1675
+ }
1676
+ byDefault = afterSwitch;
1677
+ }
1678
+ // A `default` runs only when every case failed, wherever it was written
1679
+ if (byDefault != null) {
1680
+ const noCaseMatched = everyLabel.length === 0 ? byDefault : and(this.negateExpression(everyLabel.reduce((left, right)=>or(left, right))), byDefault);
1681
+ matching = matching == null ? noCaseMatched : or(matching, noCaseMatched);
1682
+ }
1683
+ return matching ?? NEVER;
1684
+ }
1685
+ /** One case body, and the `break` that may follow its `return`. */ parseCaseBody() {
1686
+ const answer = this.parseStatements();
1687
+ if (this.stream.peek()?.value === "break") {
1688
+ this.stream.next();
1689
+ this.stream.matchPunctuation(";");
1690
+ }
1691
+ return answer;
1692
+ }
1693
+ /**
1694
+ * The predicate an `if`/`else` answers: `(condition && whenTrue) || (!condition && whenFalse)`,
1695
+ * with each case below that form after a constant branch cancels out.
1696
+ */ either(condition, whenTrue, whenFalse) {
1697
+ if (whenTrue === NEVER) {
1698
+ return whenFalse === NEVER ? NEVER : and(this.negateExpression(condition), whenFalse);
1699
+ }
1700
+ if (whenFalse === NEVER) {
1701
+ return and(condition, whenTrue);
1702
+ }
1703
+ if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(whenTrue)) {
1704
+ return or(condition, whenFalse);
1705
+ }
1706
+ if (_types__rspack_import_1/* .Expression.isEmpty */.r4.isEmpty(whenFalse)) {
1707
+ return or(this.negateExpression(condition), whenTrue);
1708
+ }
1709
+ return or(and(condition, whenTrue), and(this.negateExpression(condition), whenFalse));
1710
+ }
748
1711
  // || binds loosest, so it sits at the root of the parse
749
1712
  parseOr() {
750
1713
  let left = this.parseAnd();
@@ -792,10 +1755,18 @@ const resolveParamPath = (paramsName, path, data)=>{
792
1755
  /**
793
1756
  * Applies `!` to an already-parsed expression: comparators flip their
794
1757
  * negated flag, compound expressions distribute via De Morgan's laws.
1758
+ *
1759
+ * Builds a new tree rather than flipping the flag in place, because an `if` uses its condition
1760
+ * twice — once negated — and a shared node would carry the flip into both branches.
795
1761
  */ negateExpression(expression) {
796
1762
  if (expression instanceof _types__rspack_import_1/* .ComparatorExpression */.bQ) {
797
- expression.negated = !expression.negated;
798
- return expression;
1763
+ return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
1764
+ comparator: expression.comparator,
1765
+ negated: !expression.negated,
1766
+ strict: expression.strict,
1767
+ left: expression.left,
1768
+ right: expression.right
1769
+ });
799
1770
  }
800
1771
  if (expression instanceof _types__rspack_import_1/* .OperatorExpression */.fw && expression.left != null && expression.right != null) {
801
1772
  return new _types__rspack_import_1/* .OperatorExpression */.fw({
@@ -807,25 +1778,125 @@ const resolveParamPath = (paramsName, path, data)=>{
807
1778
  throw new Error(ERROR_MESSAGES.UNSUPPORTED("'!' on this expression"));
808
1779
  }
809
1780
  parseComparison() {
810
- // Parenthesized group
811
- if (this.stream.matchPunctuation("(")) {
1781
+ /**
1782
+ * A parenthesised group is either a boolean sub-expression or a VALUE — `(a && b)` against
1783
+ * `(x.name ?? '') === 'ada'` — and which one it is is only known at the closing bracket, by
1784
+ * what follows. So the boolean reading is tried first and rewound if a comparator turns up.
1785
+ */ if (this.stream.isPunctuation("(") && this.stream.groupIsValue() === false) {
1786
+ this.stream.next();
812
1787
  const expression = this.parseOr();
813
1788
  this.stream.expectPunctuation(")");
814
- const trailing = this.stream.peek();
815
- if (trailing != null && trailing.kind === "punctuation" && COMPARISON_OPERATORS[trailing.value] != null) {
816
- throw new Error(ERROR_MESSAGES.UNSUPPORTED("comparison against a parenthesized expression"));
817
- }
818
1789
  return expression;
819
1790
  }
820
- const left = this.parseOperand();
1791
+ const left = this.parseValue();
821
1792
  const operatorToken = this.stream.peek();
822
1793
  if (operatorToken != null && operatorToken.kind === "punctuation" && COMPARISON_OPERATORS[operatorToken.value] != null) {
823
1794
  this.stream.next();
824
- const right = this.parseOperand();
1795
+ const right = this.parseValue();
825
1796
  return this.buildComparison(left, COMPARISON_OPERATORS[operatorToken.value], right);
826
1797
  }
827
1798
  return this.buildStandalone(left);
828
1799
  }
1800
+ /**
1801
+ * A value, at JavaScript's precedence.
1802
+ *
1803
+ * Lowest first: the conditional operator, then nullish coalescing, then the bitwise levels, then
1804
+ * the shifts, then the arithmetic. Comparison sits between the shifts and the bitwise levels in
1805
+ * JavaScript, but a comparison is a boolean and is handled by `parseComparison` above, so this
1806
+ * chain skips it — a bitwise operand here is always a value.
1807
+ */ /**
1808
+ * An operand from its own source, sharing this parser's schema and parameter names.
1809
+ *
1810
+ * A structural dependence found inside propagates outward: the template it belongs to cannot be
1811
+ * cached either.
1812
+ */ parseNested(source) {
1813
+ const nested = new ExpressionParser(this.schema, new TokenStream(tokenize(source)), this.scope, this.paramsName, this.params);
1814
+ const operand = nested.parseInterpolation();
1815
+ // Leftover tokens mean the interpolation held something this reads only part of. Silently
1816
+ // keeping the part it understood is the worst outcome available: `${x.age > 5 ? "a" : "b"}`
1817
+ // would become `x.age`, and the filter would answer a question nobody asked.
1818
+ if (nested.stream.isAtEnd === false) {
1819
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("an interpolation this parser reads only part of"));
1820
+ }
1821
+ if (nested.structurallyDependsOnParams === true) {
1822
+ this.structurallyDependsOnParams = true;
1823
+ }
1824
+ return operand;
1825
+ }
1826
+ /**
1827
+ * The whole of one `${…}`.
1828
+ *
1829
+ * A conditional is read here rather than in `parseValue`, because an interpolation is the one
1830
+ * place a conditional appears without brackets around it.
1831
+ */ parseInterpolation() {
1832
+ if (this.stream.holdsConditional()) {
1833
+ const condition = this.parseOr();
1834
+ this.stream.expectPunctuation("?");
1835
+ const whenTrue = this.parseValue();
1836
+ this.stream.expectPunctuation(":");
1837
+ const whenFalse = this.parseValue();
1838
+ return {
1839
+ kind: "conditional",
1840
+ condition,
1841
+ whenTrue,
1842
+ whenFalse
1843
+ };
1844
+ }
1845
+ return this.parseValue();
1846
+ }
1847
+ parseValue() {
1848
+ return this.parseCoalesce();
1849
+ }
1850
+ parseCoalesce() {
1851
+ return this.parseBinary(COALESCE_OPERATORS, ()=>this.parseBitwiseOr());
1852
+ }
1853
+ parseBitwiseOr() {
1854
+ return this.parseBinary(BITWISE_OR_OPERATORS, ()=>this.parseBitwiseXor());
1855
+ }
1856
+ parseBitwiseXor() {
1857
+ return this.parseBinary(BITWISE_XOR_OPERATORS, ()=>this.parseBitwiseAnd());
1858
+ }
1859
+ parseBitwiseAnd() {
1860
+ return this.parseBinary(BITWISE_AND_OPERATORS, ()=>this.parseShift());
1861
+ }
1862
+ parseShift() {
1863
+ return this.parseBinary(SHIFT_OPERATORS, ()=>this.parseAdditive());
1864
+ }
1865
+ parseAdditive() {
1866
+ return this.parseBinary(ADDITIVE_OPERATORS, ()=>this.parseMultiplicative());
1867
+ }
1868
+ parseMultiplicative() {
1869
+ return this.parseBinary(MULTIPLICATIVE_OPERATORS, ()=>this.parseExponent());
1870
+ }
1871
+ /** `**` is RIGHT-associative: `2 ** 3 ** 2` is 2 ** 9, not 8 ** 2. */ parseExponent() {
1872
+ const left = this.parseOperand();
1873
+ if (this.stream.isPunctuation("**") === false) {
1874
+ return left;
1875
+ }
1876
+ this.stream.next();
1877
+ return {
1878
+ kind: "arithmetic",
1879
+ call: "power",
1880
+ left,
1881
+ right: this.parseExponent()
1882
+ };
1883
+ }
1884
+ /** Left-associative, so `a - b - c` is `(a - b) - c` rather than `a - (b - c)`. */ parseBinary(operators, next) {
1885
+ let left = next();
1886
+ for(;;){
1887
+ const token = this.stream.peek();
1888
+ if (token == null || token.kind !== "punctuation" || operators[token.value] == null) {
1889
+ return left;
1890
+ }
1891
+ this.stream.next();
1892
+ left = {
1893
+ kind: "arithmetic",
1894
+ call: operators[token.value],
1895
+ left,
1896
+ right: next()
1897
+ };
1898
+ }
1899
+ }
829
1900
  parseOperand() {
830
1901
  const token = this.stream.peek();
831
1902
  if (token == null) {
@@ -849,6 +1920,131 @@ const resolveParamPath = (paramsName, path, data)=>{
849
1920
  locale: null
850
1921
  };
851
1922
  }
1923
+ // A parenthesised VALUE — `(x.price & 1)`, `(x.name ?? '')`. The boolean reading of a group
1924
+ // is handled in parseComparison; by the time an operand sees one it is arithmetic.
1925
+ if (token.kind === "punctuation" && token.value === "(") {
1926
+ const conditional = this.stream.groupHoldsConditional();
1927
+ this.stream.next();
1928
+ if (conditional === true) {
1929
+ const condition = this.parseOr();
1930
+ this.stream.expectPunctuation("?");
1931
+ const whenTrue = this.parseValue();
1932
+ this.stream.expectPunctuation(":");
1933
+ const whenFalse = this.parseValue();
1934
+ this.stream.expectPunctuation(")");
1935
+ return {
1936
+ kind: "conditional",
1937
+ condition,
1938
+ whenTrue,
1939
+ whenFalse
1940
+ };
1941
+ }
1942
+ const inner = this.parseValue();
1943
+ this.stream.expectPunctuation(")");
1944
+ const grouped = inner.kind === "arithmetic" ? {
1945
+ ...inner,
1946
+ grouped: true
1947
+ } : inner;
1948
+ return this.withGroupCall(grouped);
1949
+ }
1950
+ /**
1951
+ * A template with interpolation, folded into `concat`.
1952
+ *
1953
+ * Each `${…}` was kept as source by the tokenizer and is parsed by its own stream, so it can
1954
+ * hold anything an operand can — a property, a param, arithmetic, another template. Empty
1955
+ * chunks are dropped: `${a}${b}` is two operands, not two operands and three empty strings.
1956
+ */ if (token.kind === "template") {
1957
+ this.stream.next();
1958
+ const { chunks, expressions } = JSON.parse(token.value);
1959
+ const pieces = [];
1960
+ for(let at = 0; at < chunks.length; at++){
1961
+ if (chunks[at].length > 0) {
1962
+ pieces.push({
1963
+ kind: "value",
1964
+ value: chunks[at],
1965
+ transformer: null,
1966
+ locale: null
1967
+ });
1968
+ }
1969
+ if (at < expressions.length) {
1970
+ pieces.push(this.parseNested(expressions[at]));
1971
+ }
1972
+ }
1973
+ if (pieces.length === 0) {
1974
+ return {
1975
+ kind: "value",
1976
+ value: "",
1977
+ transformer: null,
1978
+ locale: null
1979
+ };
1980
+ }
1981
+ // One piece and no chunk means no concat to do the coercion, so the conversion has to be
1982
+ // explicit: `` `${x.age}` `` is the STRING "9", not the number 9.
1983
+ if (pieces.length === 1) {
1984
+ const only = pieces[0];
1985
+ const alreadyText = only.kind === "value" && typeof only.value === "string";
1986
+ return alreadyText ? only : {
1987
+ kind: "arithmetic",
1988
+ call: "to-string",
1989
+ left: only,
1990
+ right: noArgument()
1991
+ };
1992
+ }
1993
+ return pieces.reduce((left, right)=>({
1994
+ kind: "arithmetic",
1995
+ call: "concat",
1996
+ left,
1997
+ right
1998
+ }));
1999
+ }
2000
+ if (token.kind === "bigint") {
2001
+ this.stream.next();
2002
+ return {
2003
+ kind: "value",
2004
+ value: BigInt(token.value),
2005
+ transformer: null,
2006
+ locale: null
2007
+ };
2008
+ }
2009
+ if (token.kind === "regex") {
2010
+ this.stream.next();
2011
+ const [source, flags] = token.value.split("\u0000");
2012
+ const pattern = {
2013
+ kind: "value",
2014
+ value: new RegExp(source, flags),
2015
+ transformer: null,
2016
+ locale: null
2017
+ };
2018
+ // `/^a/.test(x.name)` — the pattern is the literal, the subject is the argument, and the
2019
+ // tree puts them the other way round: the property is what the call applies to.
2020
+ if (this.stream.isPunctuation(".")) {
2021
+ const method = this.stream.peek(1);
2022
+ if (method != null && method.kind === "identifier" && method.value === "test") {
2023
+ this.stream.next();
2024
+ this.stream.next();
2025
+ this.stream.expectPunctuation("(");
2026
+ const subject = this.parseValue();
2027
+ this.stream.expectPunctuation(")");
2028
+ return {
2029
+ kind: "arithmetic",
2030
+ call: "matches",
2031
+ left: subject,
2032
+ right: pattern
2033
+ };
2034
+ }
2035
+ }
2036
+ return pattern;
2037
+ }
2038
+ if (token.kind === "punctuation" && token.value === "~") {
2039
+ this.stream.next();
2040
+ // Unary, so the tree carries the operand and no argument
2041
+ return {
2042
+ kind: "arithmetic",
2043
+ call: "bit-not",
2044
+ left: this.parseOperand(),
2045
+ right: noArgument()
2046
+ };
2047
+ }
852
2048
  if (token.kind === "punctuation" && token.value === "-") {
853
2049
  this.stream.next();
854
2050
  const numberToken = this.stream.next();
@@ -906,6 +2102,9 @@ const resolveParamPath = (paramsName, path, data)=>{
906
2102
  if (argument.kind === "method-call") {
907
2103
  throw new Error(ERROR_MESSAGES.UNSUPPORTED("nested method call inside .includes()"));
908
2104
  }
2105
+ if (argument.kind === "arithmetic" || argument.kind === "conditional") {
2106
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic inside .includes()"));
2107
+ }
909
2108
  return {
910
2109
  kind: "method-call",
911
2110
  target: array,
@@ -951,16 +2150,17 @@ const resolveParamPath = (paramsName, path, data)=>{
951
2150
  locale: null
952
2151
  };
953
2152
  }
954
- if (root === this.entityName) {
955
- return this.parseChain({
956
- kind: "property",
957
- root
958
- });
959
- }
960
- if (this.paramsName != null && root === this.paramsName) {
2153
+ const binding = this.scope.get(root);
2154
+ if (binding != null) {
2155
+ if (binding.kind === "inlined") {
2156
+ this.stream.splice(binding.tokens);
2157
+ return this.parseOperand();
2158
+ }
961
2159
  return this.parseChain({
962
- kind: "param",
963
- root
2160
+ kind: binding.kind,
2161
+ path: [
2162
+ ...binding.path
2163
+ ]
964
2164
  });
965
2165
  }
966
2166
  // A bare variable from the outer scope — its value cannot be derived from source text
@@ -970,7 +2170,7 @@ const resolveParamPath = (paramsName, path, data)=>{
970
2170
  * Parses the segments after an entity/params root: dot access, bracket
971
2171
  * access, transform methods and comparator methods.
972
2172
  */ parseChain(options) {
973
- const path = [];
2173
+ const path = options.path;
974
2174
  let transformer = null;
975
2175
  let locale = null;
976
2176
  while(true){
@@ -996,6 +2196,9 @@ const resolveParamPath = (paramsName, path, data)=>{
996
2196
  if (argument.kind === "method-call") {
997
2197
  throw new Error(ERROR_MESSAGES.UNSUPPORTED(`nested method call inside .${method}()`));
998
2198
  }
2199
+ if (argument.kind === "arithmetic" || argument.kind === "conditional") {
2200
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`arithmetic inside .${method}()`));
2201
+ }
999
2202
  return {
1000
2203
  kind: "method-call",
1001
2204
  target: this.resolveChain(options.kind, path, transformer, locale),
@@ -1032,12 +2235,15 @@ const resolveParamPath = (paramsName, path, data)=>{
1032
2235
  // docs/mutation-backlog.md) — every mutation of this four-conjunct guard reroutes
1033
2236
  // bracket access between two paths that both collapse to NOT_PARSABLE; the
1034
2237
  // experiment recorded there aimed 30 tests at this line and killed none.
1035
- if (kind === "property" && token.kind === "identifier" && this.paramsName != null && token.value === this.paramsName) {
1036
- const paramPath = [];
2238
+ const binding = token.kind === "identifier" ? this.scope.get(token.value) : undefined;
2239
+ if (kind === "property" && binding != null && binding.kind === "param") {
2240
+ const paramPath = [
2241
+ ...binding.path
2242
+ ];
1037
2243
  while(this.stream.matchPunctuation(".") || this.stream.matchPunctuation("?.")){
1038
2244
  paramPath.push(this.stream.next().value);
1039
2245
  }
1040
- const resolved = resolveParamPath(this.paramsName, paramPath, this.params);
2246
+ const resolved = resolveParamPath(this.paramsName ?? token.value, paramPath, this.params);
1041
2247
  if (typeof resolved !== "string") {
1042
2248
  throw new ParamDependentParseError(ERROR_MESSAGES.PROPERTY_NOT_FOUND(paramPath.join(".")));
1043
2249
  }
@@ -1090,6 +2296,67 @@ const resolveParamPath = (paramsName, path, data)=>{
1090
2296
  locale
1091
2297
  };
1092
2298
  }
2299
+ /**
2300
+ * A call on a parenthesised value: `(x.name).toLowerCase()`, `(x.age + 1).length`. Any operand can
2301
+ * receive one here, unlike a property chain, which carries at most one transform.
2302
+ */ withGroupCall(operand) {
2303
+ let receiver = operand;
2304
+ while(this.stream.isPunctuation(".") || this.stream.isPunctuation("?.")){
2305
+ const segment = this.stream.peek(1);
2306
+ if (segment == null || segment.kind !== "identifier") {
2307
+ break;
2308
+ }
2309
+ if (segment.value === "length" && !this.stream.isPunctuation("(", 2)) {
2310
+ this.stream.next();
2311
+ this.stream.next();
2312
+ receiver = {
2313
+ kind: "arithmetic",
2314
+ call: "length",
2315
+ left: receiver,
2316
+ right: noArgument()
2317
+ };
2318
+ continue;
2319
+ }
2320
+ const transform = TRANSFORM_METHODS[segment.value];
2321
+ if (transform != null) {
2322
+ this.stream.next();
2323
+ this.stream.next();
2324
+ this.stream.expectPunctuation("(");
2325
+ this.stream.expectPunctuation(")");
2326
+ receiver = {
2327
+ kind: "arithmetic",
2328
+ call: transform.transformer,
2329
+ left: receiver,
2330
+ right: transform.locale == null ? noArgument() : {
2331
+ kind: "value",
2332
+ value: transform.locale,
2333
+ transformer: null,
2334
+ locale: null
2335
+ }
2336
+ };
2337
+ continue;
2338
+ }
2339
+ // A comparator method needs a property target, which only an ungrouped chain produces
2340
+ if (COMPARATOR_METHODS[segment.value] != null && receiver.kind === "property") {
2341
+ this.stream.next();
2342
+ this.stream.next();
2343
+ this.stream.expectPunctuation("(");
2344
+ const argument = this.parseOperand();
2345
+ this.stream.expectPunctuation(")");
2346
+ if (argument.kind !== "property" && argument.kind !== "value" && argument.kind !== "param") {
2347
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`'.${segment.value}()' on that argument`));
2348
+ }
2349
+ return {
2350
+ kind: "method-call",
2351
+ target: receiver,
2352
+ method: segment.value,
2353
+ argument
2354
+ };
2355
+ }
2356
+ break;
2357
+ }
2358
+ return receiver;
2359
+ }
1093
2360
  withValueTransformer(operand) {
1094
2361
  if (this.stream.isPunctuation(".")) {
1095
2362
  const method = this.stream.peek(1);
@@ -1123,12 +2390,22 @@ const resolveParamPath = (paramsName, path, data)=>{
1123
2390
  if (right.kind === "method-call") {
1124
2391
  throw new Error(ERROR_MESSAGES.UNSUPPORTED("method call on the right side of a comparison"));
1125
2392
  }
1126
- if (left.kind === "property" && right.kind === "property") {
1127
- // Casing transformers are only valid with string-matching comparators,
1128
- // which cannot produce a property-to-property comparison
1129
- if (left.transformer === "to-lower-case" || left.transformer === "to-upper-case" || right.transformer === "to-lower-case" || right.transformer === "to-upper-case") {
1130
- throw new Error(ERROR_MESSAGES.UNSUPPORTED("transform method outside of startsWith/endsWith/includes"));
2393
+ if (needsBrackets(left) || needsBrackets(right)) {
2394
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("a bitwise or nullish operator compared without brackets, which JavaScript reads the other way round"));
2395
+ }
2396
+ if (left.kind === "arithmetic" || right.kind === "arithmetic" || left.kind === "conditional" || right.kind === "conditional") {
2397
+ if (containsProperty(left) === false && containsProperty(right) === false) {
2398
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic that references no schema property"));
1131
2399
  }
2400
+ return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
2401
+ comparator: operator.comparator,
2402
+ negated: operator.negated,
2403
+ strict: operator.strict,
2404
+ left: this.createOperandExpression(left),
2405
+ right: this.createOperandExpression(right)
2406
+ });
2407
+ }
2408
+ if (left.kind === "property" && right.kind === "property") {
1132
2409
  return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
1133
2410
  comparator: operator.comparator,
1134
2411
  negated: operator.negated,
@@ -1137,18 +2414,63 @@ const resolveParamPath = (paramsName, path, data)=>{
1137
2414
  right: this.createPropertyExpression(right)
1138
2415
  });
1139
2416
  }
2417
+ // Only a loose comparison coerces. `===` records `strict` and honouring it is the point.
1140
2418
  if (left.kind === "property" && right.kind !== "property") {
1141
- return this.buildPropertyComparator(left, operator, right, /* applyConverter */ true);
2419
+ return this.buildPropertyComparator(left, operator, right, /* applyConverter */ !operator.strict);
1142
2420
  }
1143
2421
  if (right.kind === "property" && left.kind !== "property") {
1144
2422
  const swapped = {
1145
2423
  ...operator,
1146
2424
  comparator: SWAPPED_COMPARATORS[operator.comparator]
1147
2425
  };
1148
- return this.buildPropertyComparator(right, swapped, left, /* applyConverter */ true);
2426
+ return this.buildPropertyComparator(right, swapped, left, /* applyConverter */ !operator.strict);
2427
+ }
2428
+ const settled = this.settleConstantComparison(left, operator, right);
2429
+ if (settled != null) {
2430
+ return settled;
1149
2431
  }
1150
2432
  throw new Error(ERROR_MESSAGES.UNSUPPORTED("comparison requires a schema property on at least one side"));
1151
2433
  }
2434
+ /**
2435
+ * The answer a comparison of two constants gives, when that answer is `true`. The other answer
2436
+ * excludes every row, which has no expression node.
2437
+ */ settleConstantComparison(left, operator, right) {
2438
+ const leftValue = this.constantOf(left);
2439
+ const rightValue = this.constantOf(right);
2440
+ if (leftValue === UNKNOWN_UNTIL_ROW || rightValue === UNKNOWN_UNTIL_ROW) {
2441
+ return null;
2442
+ }
2443
+ const answer = (0,_evaluate__rspack_import_3/* .evaluate */._3)(new _types__rspack_import_1/* .ComparatorExpression */.bQ({
2444
+ comparator: operator.comparator,
2445
+ negated: operator.negated,
2446
+ strict: operator.strict,
2447
+ left: new _types__rspack_import_1/* .ValueExpression */.Ko({
2448
+ value: leftValue
2449
+ }),
2450
+ right: new _types__rspack_import_1/* .ValueExpression */.Ko({
2451
+ value: rightValue
2452
+ })
2453
+ }), {});
2454
+ if (answer === true) {
2455
+ return _types__rspack_import_1/* .Expression.EMPTY */.r4.EMPTY;
2456
+ }
2457
+ // Params decided this, so the refusal must not be cached against the source: the same filter
2458
+ // with other params can be a tautology.
2459
+ if (left.kind === "param" || right.kind === "param") {
2460
+ throw new ParamDependentParseError(ERROR_MESSAGES.UNSUPPORTED("a params comparison no row satisfies"));
2461
+ }
2462
+ return null;
2463
+ }
2464
+ /** The value an operand holds already, for the operands that do not depend on a row. */ constantOf(operand) {
2465
+ if (operand.kind === "value" && operand.transformer == null) {
2466
+ return operand.value;
2467
+ }
2468
+ if (operand.kind === "param" && operand.transformer == null) {
2469
+ this.structurallyDependsOnParams = true;
2470
+ return resolveParamPath(this.paramsName ?? "params", operand.path, this.params);
2471
+ }
2472
+ return UNKNOWN_UNTIL_ROW;
2473
+ }
1152
2474
  buildStandalone(operand) {
1153
2475
  if (operand.kind === "method-call") {
1154
2476
  return this.buildMethodComparator(operand);
@@ -1171,6 +2493,21 @@ const resolveParamPath = (paramsName, path, data)=>{
1171
2493
  locale: null
1172
2494
  }, /* applyConverter */ true);
1173
2495
  }
2496
+ // A boolean-valued call standing alone IS the predicate
2497
+ if (operand.kind === "arithmetic" && operand.call === "matches") {
2498
+ return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
2499
+ comparator: "equals",
2500
+ negated: false,
2501
+ strict: false,
2502
+ left: this.createOperandExpression(operand),
2503
+ right: new _types__rspack_import_1/* .ValueExpression */.Ko({
2504
+ value: true
2505
+ })
2506
+ });
2507
+ }
2508
+ if (operand.kind === "arithmetic" || operand.kind === "conditional") {
2509
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("arithmetic used as a condition rather than compared"));
2510
+ }
1174
2511
  // Constant `true` — a tautology, which parseAnd/parseOr simplify away
1175
2512
  if (operand.kind === "value" && operand.value === true && operand.transformer == null) {
1176
2513
  return _types__rspack_import_1/* .Expression.EMPTY */.r4.EMPTY;
@@ -1223,12 +2560,6 @@ const resolveParamPath = (paramsName, path, data)=>{
1223
2560
  right: this.createValueExpression(value, null, /* applyConverter */ false)
1224
2561
  });
1225
2562
  }
1226
- // Casing transformers on a property are only meaningful with string-matching
1227
- // comparators; on relational comparators the plugins would silently
1228
- // ignore them and return wrong data
1229
- if (property.transformer != null && !isStringMatch) {
1230
- throw new Error(ERROR_MESSAGES.UNSUPPORTED("transform method outside of startsWith/endsWith/includes"));
1231
- }
1232
2563
  return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
1233
2564
  comparator: operator.comparator,
1234
2565
  negated: operator.negated,
@@ -1237,31 +2568,60 @@ const resolveParamPath = (paramsName, path, data)=>{
1237
2568
  right: this.createValueExpression(value, property.property, applyConverter)
1238
2569
  });
1239
2570
  }
2571
+ /**
2572
+ * Any operand as an expression.
2573
+ *
2574
+ * Values inside arithmetic take no paired property: the result is a computed number, so the
2575
+ * property's serializer and type converter do not describe it — the same reason `.length` skips
2576
+ * them.
2577
+ */ createOperandExpression(operand) {
2578
+ if (operand.kind === "conditional") {
2579
+ return new _types__rspack_import_1/* .CallExpression */.DG({
2580
+ call: "conditional",
2581
+ expression: operand.condition,
2582
+ arguments: [
2583
+ this.createOperandExpression(operand.whenTrue),
2584
+ this.createOperandExpression(operand.whenFalse)
2585
+ ]
2586
+ });
2587
+ }
2588
+ if (operand.kind === "arithmetic") {
2589
+ return new _types__rspack_import_1/* .CallExpression */.DG({
2590
+ call: operand.call,
2591
+ expression: this.createOperandExpression(operand.left),
2592
+ arguments: operand.right === NO_ARGUMENT ? [] : operand.extra == null ? [
2593
+ this.createOperandExpression(operand.right)
2594
+ ] : [
2595
+ this.createOperandExpression(operand.right),
2596
+ this.createOperandExpression(operand.extra)
2597
+ ]
2598
+ });
2599
+ }
2600
+ if (operand.kind === "property") {
2601
+ return this.createPropertyExpression(operand);
2602
+ }
2603
+ if (operand.kind === "method-call") {
2604
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED("a method call inside arithmetic"));
2605
+ }
2606
+ return this.createValueExpression(operand, null, /* applyConverter */ false);
2607
+ }
1240
2608
  createPropertyExpression(operand) {
1241
- const expression = new _types__rspack_import_1/* .PropertyExpression */.ep({
2609
+ return asCall(new _types__rspack_import_1/* .PropertyExpression */.ep({
1242
2610
  property: operand.property
1243
- });
1244
- expression.transformer = operand.transformer;
1245
- expression.locale = operand.locale;
1246
- return expression;
2611
+ }), operand.transformer, operand.locale);
1247
2612
  }
1248
2613
  createValueExpression(operand, pairedProperty, applyConverter) {
1249
2614
  if (operand.kind === "param") {
1250
- const expression = new ParamReferenceExpression({
2615
+ return asCall(new ParamReferenceExpression({
1251
2616
  paramPath: operand.path,
1252
2617
  pairedProperty,
1253
2618
  applyConverter
1254
- });
1255
- expression.transformer = operand.transformer;
1256
- expression.locale = operand.locale;
1257
- return expression;
2619
+ }), operand.transformer, operand.locale);
1258
2620
  }
1259
2621
  const expression = new _types__rspack_import_1/* .ValueExpression */.Ko({
1260
2622
  value: resolvePairedValue(operand.value, pairedProperty, applyConverter)
1261
2623
  });
1262
- expression.transformer = operand.transformer;
1263
- expression.locale = operand.locale;
1264
- return expression;
2624
+ return asCall(expression, operand.transformer, operand.locale);
1265
2625
  }
1266
2626
  }
1267
2627
  // #endregion
@@ -1273,28 +2633,19 @@ const resolveParamPath = (paramsName, path, data)=>{
1273
2633
  */ const bindExpression = (expression, paramsName, params)=>{
1274
2634
  if (expression instanceof ParamReferenceExpression) {
1275
2635
  const raw = resolveParamPath(paramsName ?? "params", expression.paramPath, params);
1276
- const bound = new _types__rspack_import_1/* .ValueExpression */.Ko({
2636
+ return new _types__rspack_import_1/* .ValueExpression */.Ko({
1277
2637
  value: resolvePairedValue(raw, expression.pairedProperty, expression.applyConverter)
1278
2638
  });
1279
- bound.transformer = expression.transformer;
1280
- bound.locale = expression.locale;
1281
- return bound;
1282
2639
  }
1283
2640
  if (expression instanceof _types__rspack_import_1/* .ValueExpression */.Ko) {
1284
- const clone = new _types__rspack_import_1/* .ValueExpression */.Ko({
2641
+ return new _types__rspack_import_1/* .ValueExpression */.Ko({
1285
2642
  value: expression.value
1286
2643
  });
1287
- clone.transformer = expression.transformer;
1288
- clone.locale = expression.locale;
1289
- return clone;
1290
2644
  }
1291
2645
  if (expression instanceof _types__rspack_import_1/* .PropertyExpression */.ep) {
1292
- const clone = new _types__rspack_import_1/* .PropertyExpression */.ep({
2646
+ return new _types__rspack_import_1/* .PropertyExpression */.ep({
1293
2647
  property: expression.property
1294
2648
  });
1295
- clone.transformer = expression.transformer;
1296
- clone.locale = expression.locale;
1297
- return clone;
1298
2649
  }
1299
2650
  if (expression instanceof _types__rspack_import_1/* .ComparatorExpression */.bQ) {
1300
2651
  return new _types__rspack_import_1/* .ComparatorExpression */.bQ({
@@ -1312,8 +2663,99 @@ const resolveParamPath = (paramsName, path, data)=>{
1312
2663
  right: expression.right ? bindExpression(expression.right, paramsName, params) : undefined
1313
2664
  });
1314
2665
  }
2666
+ if (expression instanceof _types__rspack_import_1/* .CallExpression */.DG) {
2667
+ return new _types__rspack_import_1/* .CallExpression */.DG({
2668
+ call: expression.call,
2669
+ expression: bindExpression(expression.expression, paramsName, params),
2670
+ arguments: expression.arguments.map((argument)=>bindExpression(argument, paramsName, params))
2671
+ });
2672
+ }
1315
2673
  return expression;
1316
2674
  };
2675
+ /**
2676
+ * Wraps an operand in the call a transform method named, if there was one.
2677
+ *
2678
+ * `Transformer` and `Call` share these three names, so the transform IS the call name. A locale
2679
+ * becomes the call's first argument, which is where it belongs — it qualifies the casing, not the
2680
+ * property.
2681
+ */ const asCall = (inner, transformer, locale)=>{
2682
+ if (transformer == null) {
2683
+ return inner;
2684
+ }
2685
+ return new _types__rspack_import_1/* .CallExpression */.DG({
2686
+ call: transformer,
2687
+ expression: inner,
2688
+ arguments: locale == null ? [] : [
2689
+ new _types__rspack_import_1/* .ValueExpression */.Ko({
2690
+ value: locale
2691
+ })
2692
+ ]
2693
+ });
2694
+ };
2695
+ /** Binds every name a destructuring pattern introduces to the path it reads. */ const bindPattern = (stream, kind, path, scope)=>{
2696
+ if (!stream.matchPunctuation("{")) {
2697
+ const name = stream.next();
2698
+ if (name.kind !== "identifier") {
2699
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`parameter '${name.value}'`));
2700
+ }
2701
+ scope.set(name.value, {
2702
+ kind,
2703
+ path
2704
+ });
2705
+ return;
2706
+ }
2707
+ while(!stream.matchPunctuation("}")){
2708
+ const key = stream.next();
2709
+ if (key.kind !== "identifier") {
2710
+ throw new Error(ERROR_MESSAGES.UNSUPPORTED(`destructured key '${key.value}'`));
2711
+ }
2712
+ if (stream.matchPunctuation(":")) {
2713
+ bindPattern(stream, kind, [
2714
+ ...path,
2715
+ key.value
2716
+ ], scope);
2717
+ } else {
2718
+ scope.set(key.value, {
2719
+ kind,
2720
+ path: [
2721
+ ...path,
2722
+ key.value
2723
+ ]
2724
+ });
2725
+ }
2726
+ if (!stream.matchPunctuation(",")) {
2727
+ stream.expectPunctuation("}");
2728
+ return;
2729
+ }
2730
+ }
2731
+ };
2732
+ /** Reads a filter's parameter list — the entity alone, or the `[entity, params]` pair — into a scope. */ const buildScope = (parameterNames, hasParams)=>{
2733
+ const stream = new TokenStream(tokenize(parameterNames));
2734
+ const scope = new Map();
2735
+ if (!stream.matchPunctuation("[")) {
2736
+ bindPattern(stream, "property", [], scope);
2737
+ return {
2738
+ scope,
2739
+ paramsName: null
2740
+ };
2741
+ }
2742
+ bindPattern(stream, "property", [], scope);
2743
+ if (hasParams && stream.matchPunctuation(",") && !stream.isPunctuation("]")) {
2744
+ bindPattern(stream, "param", [], scope);
2745
+ }
2746
+ return {
2747
+ scope,
2748
+ paramsName: wholeParamsName(scope)
2749
+ };
2750
+ };
2751
+ /** The name the whole params object was given, when it was not destructured. Error messages only. */ const wholeParamsName = (scope)=>{
2752
+ for (const [name, binding] of scope){
2753
+ if (binding.kind === "param" && binding.path.length === 0) {
2754
+ return name;
2755
+ }
2756
+ }
2757
+ return null;
2758
+ };
1317
2759
  /**
1318
2760
  * Splits stringified filter source into parameter names and the expression
1319
2761
  * body, unwrapping single-return block bodies.
@@ -1343,33 +2785,12 @@ const resolveParamPath = (paramsName, path, data)=>{
1343
2785
  parameterNames = parameterNames.slice(1, -1).trim();
1344
2786
  }
1345
2787
  }
1346
- let entityName;
1347
- let paramsName = null;
1348
- if (parameterNames.startsWith("[") && parameterNames.endsWith("]")) {
1349
- const destructured = parameterNames.slice(1, -1).split(",").map((w)=>w.trim());
1350
- entityName = destructured[0];
1351
- if (hasParams) {
1352
- paramsName = destructured[1] ?? null;
1353
- }
1354
- } else {
1355
- entityName = parameterNames;
1356
- }
1357
- if (entityName == null || entityName.length === 0) {
2788
+ if (parameterNames.length === 0) {
1358
2789
  throw new Error("Invalid Function");
1359
2790
  }
1360
- // Unwrap a single-return block body: { return <expression>; }
1361
- if (body.startsWith("{")) {
1362
- const inner = body.slice(1, body.lastIndexOf("}")).trim();
1363
- if (!inner.startsWith("return")) {
1364
- throw new Error(ERROR_MESSAGES.UNSUPPORTED("block body without a single return statement"));
1365
- }
1366
- body = inner.slice("return".length).trim();
1367
- if (body.endsWith(";")) {
1368
- body = body.slice(0, -1).trim();
1369
- }
1370
- }
2791
+ const { scope, paramsName } = buildScope(parameterNames, hasParams);
1371
2792
  return {
1372
- entityName,
2793
+ scope,
1373
2794
  paramsName,
1374
2795
  body
1375
2796
  };
@@ -1437,17 +2858,27 @@ const combineExpressions = (...expressions)=>{
1437
2858
  */ const parseFragment = (schema, body, rootName)=>{
1438
2859
  try {
1439
2860
  const stream = new TokenStream(tokenize(body));
1440
- const parser = new ExpressionParser(schema, stream, rootName, null, undefined);
1441
- return parser.parse();
2861
+ const scope = new Map([
2862
+ [
2863
+ rootName,
2864
+ {
2865
+ kind: "property",
2866
+ path: []
2867
+ }
2868
+ ]
2869
+ ]);
2870
+ const parser = new ExpressionParser(schema, stream, scope, null, undefined);
2871
+ return (0,_fold__rspack_import_4/* .foldConstantCalls */.F5)(parser.parse());
1442
2872
  } catch {
1443
2873
  // The failure is expected and informative — see above — so it is not logged. A caller that
1444
2874
  // parses one conjunct against two schemas would otherwise warn on every successful split.
1445
2875
  return _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE;
1446
2876
  }
1447
2877
  };
2878
+ /** What the parser refused, from a throw that may not be an `Error`. */ const refusalOf = (error)=>error instanceof Error ? error.message : String(error);
1448
2879
  const toExpression = (schema, fn, params)=>{
1449
2880
  const stringifiedFunction = fn.toString();
1450
- const warn = (error)=>_utilities__rspack_import_3/* .logger.warn */.vF.warn("Error parsing expression", {
2881
+ const warn = (error)=>_utilities__rspack_import_5/* .logger.warn */.vF.warn("Error parsing expression", {
1451
2882
  error,
1452
2883
  collectionName: schema.collectionName,
1453
2884
  params,
@@ -1455,16 +2886,17 @@ const toExpression = (schema, fn, params)=>{
1455
2886
  });
1456
2887
  const cached = getCachedTemplate(schema, stringifiedFunction);
1457
2888
  if (cached != null) {
1458
- // A cached failure — the warning was already logged when it was discovered
2889
+ // A cached failure — the warning was already logged when it was discovered. The template
2890
+ // carries what was refused, and `.explain()` is usually called once the cache is warm.
1459
2891
  if (_types__rspack_import_1/* .Expression.isNotParsable */.r4.isNotParsable(cached.template)) {
1460
- return _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE;
2892
+ return cached.template;
1461
2893
  }
1462
2894
  try {
1463
- return bindExpression(cached.template, cached.paramsName, params);
2895
+ return (0,_fold__rspack_import_4/* .foldConstantCalls */.F5)(bindExpression(cached.template, cached.paramsName, params));
1464
2896
  } catch (error) {
1465
2897
  // Binding failures are param-dependent by nature — never cached
1466
2898
  warn(error);
1467
- return _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE;
2899
+ return _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
1468
2900
  }
1469
2901
  }
1470
2902
  let paramsName = null;
@@ -1473,22 +2905,23 @@ const toExpression = (schema, fn, params)=>{
1473
2905
  try {
1474
2906
  const shape = resolveFunctionShape(stringifiedFunction, params != null);
1475
2907
  const stream = new TokenStream(tokenize(shape.body));
1476
- const parser = new ExpressionParser(schema, stream, shape.entityName, shape.paramsName, params);
2908
+ const parser = new ExpressionParser(schema, stream, shape.scope, shape.paramsName, params);
1477
2909
  paramsName = shape.paramsName;
1478
- template = parser.parse();
2910
+ template = parser.parseBody();
1479
2911
  structurallyDependsOnParams = parser.structurallyDependsOnParams;
1480
2912
  } catch (error) {
1481
2913
  // Cache the failure so a hot query on an unsupported filter doesn't
1482
2914
  // re-parse and re-warn on every execution. Param-dependent failures are
1483
2915
  // exempt: the same source can succeed with different params.
2916
+ const refused = _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
1484
2917
  if (!(error instanceof ParamDependentParseError)) {
1485
2918
  setCachedTemplate(schema, stringifiedFunction, {
1486
- template: _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE,
2919
+ template: refused,
1487
2920
  paramsName: null
1488
2921
  });
1489
2922
  }
1490
2923
  warn(error);
1491
- return _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE;
2924
+ return refused;
1492
2925
  }
1493
2926
  // Templates whose structure was resolved from param values are only
1494
2927
  // valid for this exact params object — parse those fresh every time
@@ -1499,10 +2932,10 @@ const toExpression = (schema, fn, params)=>{
1499
2932
  });
1500
2933
  }
1501
2934
  try {
1502
- return bindExpression(template, paramsName, params);
2935
+ return (0,_fold__rspack_import_4/* .foldConstantCalls */.F5)(bindExpression(template, paramsName, params));
1503
2936
  } catch (error) {
1504
2937
  warn(error);
1505
- return _types__rspack_import_1/* .Expression.NOT_PARSABLE */.r4.NOT_PARSABLE;
2938
+ return _types__rspack_import_1/* .Expression.notParsable */.r4.notParsable(refusalOf(error));
1506
2939
  }
1507
2940
  };
1508
2941
 
@@ -1510,6 +2943,7 @@ const toExpression = (schema, fn, params)=>{
1510
2943
  },
1511
2944
  27(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1512
2945
  __webpack_require__.d(__webpack_exports__, {
2946
+ DG: () => (CallExpression),
1513
2947
  Ko: () => (ValueExpression),
1514
2948
  SC: () => (NotParsableExpression),
1515
2949
  Sm: () => (EmptyExpression),
@@ -1521,59 +2955,69 @@ __webpack_require__.d(__webpack_exports__, {
1521
2955
  const valueToJson = (value)=>{
1522
2956
  if (value === undefined) {
1523
2957
  return {
1524
- k: "undefined"
2958
+ undefined: true
1525
2959
  };
1526
2960
  }
1527
2961
  if (value === null) {
1528
- return {
1529
- k: "raw",
1530
- v: null
1531
- };
2962
+ return null;
1532
2963
  }
1533
2964
  if (value instanceof Date) {
1534
2965
  // ISO rather than epoch millis: it survives a human reading the payload, and an invalid
1535
2966
  // Date has no ISO form — so it is caught here rather than becoming a silent `null`.
1536
2967
  return {
1537
- k: "date",
1538
- v: value.toISOString()
2968
+ date: value.toISOString()
1539
2969
  };
1540
2970
  }
1541
2971
  if (Array.isArray(value)) {
1542
- return {
1543
- k: "array",
1544
- v: value.map(valueToJson)
1545
- };
2972
+ return value.map(valueToJson);
1546
2973
  }
1547
2974
  if (typeof value === "number" && Number.isFinite(value) === false) {
1548
2975
  // `JSON.stringify` turns all three of these into `null`, which would compare as a different
1549
2976
  // value entirely rather than failing.
1550
2977
  return {
1551
- k: "number",
1552
- v: Number.isNaN(value) ? "NaN" : value > 0 ? "Infinity" : "-Infinity"
2978
+ number: Number.isNaN(value) ? "NaN" : value > 0 ? "Infinity" : "-Infinity"
1553
2979
  };
1554
2980
  }
1555
2981
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2982
+ return value;
2983
+ }
2984
+ if (value instanceof RegExp) {
1556
2985
  return {
1557
- k: "raw",
1558
- v: value
2986
+ regex: {
2987
+ source: value.source,
2988
+ flags: value.flags
2989
+ }
2990
+ };
2991
+ }
2992
+ // `JSON.stringify` throws outright on a bigint rather than losing it quietly, so this is the one
2993
+ // tag that turns a crash into a value
2994
+ if (typeof value === "bigint") {
2995
+ return {
2996
+ bigint: value.toString()
1559
2997
  };
1560
2998
  }
1561
2999
  throw new Error(`Cannot serialize this filter value: only strings, numbers, booleans, null, undefined, Dates and arrays of those can cross a wire. ` + `Received: ${Object.prototype.toString.call(value)}`);
1562
3000
  };
1563
3001
  const valueFromJson = (value)=>{
1564
- if (value.k === "undefined") {
1565
- return undefined;
3002
+ if (value === null || typeof value !== "object") {
3003
+ return value;
3004
+ }
3005
+ if (Array.isArray(value)) {
3006
+ return value.map(valueFromJson);
1566
3007
  }
1567
- if (value.k === "date") {
1568
- return new Date(value.v);
3008
+ if ("date" in value) {
3009
+ return new Date(value.date);
1569
3010
  }
1570
- if (value.k === "array") {
1571
- return value.v.map(valueFromJson);
3011
+ if ("undefined" in value) {
3012
+ return undefined;
3013
+ }
3014
+ if ("regex" in value) {
3015
+ return new RegExp(value.regex.source, value.regex.flags);
1572
3016
  }
1573
- if (value.k === "number") {
1574
- return value.v === "NaN" ? Number.NaN : value.v === "Infinity" ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
3017
+ if ("bigint" in value) {
3018
+ return BigInt(value.bigint);
1575
3019
  }
1576
- return value.v;
3020
+ return value.number === "NaN" ? Number.NaN : value.number === "Infinity" ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
1577
3021
  };
1578
3022
  /**
1579
3023
  * The base class for all expression types.
@@ -1590,6 +3034,9 @@ const valueFromJson = (value)=>{
1590
3034
  static get NOT_PARSABLE() {
1591
3035
  return new NotParsableExpression();
1592
3036
  }
3037
+ /** `NOT_PARSABLE`, carrying what the parser refused. */ static notParsable(reason) {
3038
+ return new NotParsableExpression(reason);
3039
+ }
1593
3040
  static isEmpty(expression) {
1594
3041
  return expression.type === "empty" || expression instanceof EmptyExpression;
1595
3042
  }
@@ -1606,7 +3053,7 @@ const valueFromJson = (value)=>{
1606
3053
  *
1607
3054
  * ## Why it is this small
1608
3055
  *
1609
- * Of the six node types a bound tree can contain, exactly one holds anything JSON cannot carry:
3056
+ * Of the seven node types a bound tree can contain, exactly one holds anything JSON cannot carry:
1610
3057
  * `PropertyExpression`, whose live `PropertyInfo` has functions, a parent chain and caches. It
1611
3058
  * reduces to a property PATH — `PropertyInfo.id` IS the dotted path, and `getProperty` is keyed by
1612
3059
  * exactly that — so rebinding is one lookup.
@@ -1621,7 +3068,7 @@ const valueFromJson = (value)=>{
1621
3068
  if (expression.type === "operator") {
1622
3069
  const operator = expression;
1623
3070
  return {
1624
- t: "operator",
3071
+ type: "operator",
1625
3072
  operator: operator.operator,
1626
3073
  ...operator.left != null && {
1627
3074
  left: Expression.toJson(operator.left)
@@ -1634,7 +3081,7 @@ const valueFromJson = (value)=>{
1634
3081
  if (expression.type === "comparator") {
1635
3082
  const comparator = expression;
1636
3083
  return {
1637
- t: "comparator",
3084
+ type: "comparator",
1638
3085
  comparator: comparator.comparator,
1639
3086
  negated: comparator.negated,
1640
3087
  strict: comparator.strict,
@@ -1646,29 +3093,41 @@ const valueFromJson = (value)=>{
1646
3093
  }
1647
3094
  };
1648
3095
  }
3096
+ if (expression.type === "call") {
3097
+ const call = expression;
3098
+ return {
3099
+ type: "call",
3100
+ call: call.call,
3101
+ expression: Expression.toJson(call.expression),
3102
+ arguments: call.arguments.map(Expression.toJson)
3103
+ };
3104
+ }
1649
3105
  if (expression.type === "property") {
1650
3106
  const property = expression;
1651
3107
  return {
1652
- t: "property",
3108
+ type: "property",
1653
3109
  // The dotted path, which is exactly the key `getProperty` is looking up
1654
- path: property.property.id,
1655
- transformer: property.transformer,
1656
- locale: property.locale
3110
+ path: property.property.id
1657
3111
  };
1658
3112
  }
1659
3113
  if (expression.type === "value") {
1660
3114
  const value = expression;
1661
3115
  return {
1662
- t: "value",
1663
- value: valueToJson(value.value),
1664
- transformer: value.transformer,
1665
- locale: value.locale
3116
+ type: "value",
3117
+ value: valueToJson(value.value)
3118
+ };
3119
+ }
3120
+ if (expression.type === "empty") {
3121
+ return {
3122
+ type: "empty"
1666
3123
  };
1667
3124
  }
1668
- return expression.type === "empty" ? {
1669
- t: "empty"
3125
+ const reason = expression.reason;
3126
+ return reason == null ? {
3127
+ type: "not-parsable"
1670
3128
  } : {
1671
- t: "not-parsable"
3129
+ type: "not-parsable",
3130
+ reason
1672
3131
  };
1673
3132
  }
1674
3133
  /**
@@ -1684,14 +3143,14 @@ const valueFromJson = (value)=>{
1684
3143
  * failure here worse than an error.
1685
3144
  */ static fromJson(json, schema) {
1686
3145
  const child = (node)=>node == null ? undefined : Expression.fromJson(node, schema);
1687
- if (json.t === "operator") {
3146
+ if (json.type === "operator") {
1688
3147
  return new OperatorExpression({
1689
3148
  operator: json.operator,
1690
3149
  left: child(json.left),
1691
3150
  right: child(json.right)
1692
3151
  });
1693
3152
  }
1694
- if (json.t === "comparator") {
3153
+ if (json.type === "comparator") {
1695
3154
  return new ComparatorExpression({
1696
3155
  comparator: json.comparator,
1697
3156
  negated: json.negated,
@@ -1700,27 +3159,34 @@ const valueFromJson = (value)=>{
1700
3159
  right: child(json.right)
1701
3160
  });
1702
3161
  }
1703
- if (json.t === "property") {
3162
+ if (json.type === "call") {
3163
+ if (json.expression == null) {
3164
+ throw new Error(`Cannot deserialize a filter: a '${json.call}' call carries no operand. ` + `Collection: ${schema.collectionName}.`);
3165
+ }
3166
+ return new CallExpression({
3167
+ call: json.call,
3168
+ expression: Expression.fromJson(json.expression, schema),
3169
+ arguments: (json.arguments ?? []).map((argument)=>Expression.fromJson(argument, schema))
3170
+ });
3171
+ }
3172
+ if (json.type === "property") {
1704
3173
  const property = schema.getProperty(json.path);
1705
3174
  if (property == null) {
1706
3175
  throw new Error(`Cannot deserialize a filter: this schema does not declare the property it names. ` + `Property: ${json.path}, Collection: ${schema.collectionName}. ` + `The two sides disagree about the shape of the data, so the filter cannot be applied.`);
1707
3176
  }
1708
- const rebuilt = new PropertyExpression({
3177
+ return new PropertyExpression({
1709
3178
  property
1710
3179
  });
1711
- rebuilt.transformer = json.transformer;
1712
- rebuilt.locale = json.locale;
1713
- return rebuilt;
1714
3180
  }
1715
- if (json.t === "value") {
1716
- const rebuilt = new ValueExpression({
3181
+ if (json.type === "value") {
3182
+ return new ValueExpression({
1717
3183
  value: valueFromJson(json.value)
1718
3184
  });
1719
- rebuilt.transformer = json.transformer;
1720
- rebuilt.locale = json.locale;
1721
- return rebuilt;
1722
3185
  }
1723
- return json.t === "empty" ? Expression.EMPTY : Expression.NOT_PARSABLE;
3186
+ if (json.type === "empty") {
3187
+ return Expression.EMPTY;
3188
+ }
3189
+ return json.reason == null ? Expression.NOT_PARSABLE : Expression.notParsable(json.reason);
1724
3190
  }
1725
3191
  }
1726
3192
  class EmptyExpression extends Expression {
@@ -1728,6 +3194,11 @@ class EmptyExpression extends Expression {
1728
3194
  }
1729
3195
  class NotParsableExpression extends Expression {
1730
3196
  type = "not-parsable";
3197
+ /** What the parser refused, when it knows. `.explain()` prints it beside the source. */ reason;
3198
+ constructor(reason){
3199
+ super();
3200
+ this.reason = reason;
3201
+ }
1731
3202
  }
1732
3203
  /**
1733
3204
  * A class representing a comparison operation (e.g., equals, greater-than).
@@ -1758,20 +3229,28 @@ class NotParsableExpression extends Expression {
1758
3229
  */ class PropertyExpression extends Expression {
1759
3230
  /** The type of the expression (always 'property'). */ type = "property";
1760
3231
  /** The property info for the path. */ property;
1761
- transformer = null;
1762
- locale = null;
1763
3232
  constructor(options){
1764
3233
  super();
1765
3234
  this.property = options.property;
1766
3235
  }
1767
3236
  }
3237
+ class CallExpression extends Expression {
3238
+ type = "call";
3239
+ call;
3240
+ expression;
3241
+ /** Empty for a unary call. */ arguments;
3242
+ constructor(options){
3243
+ super();
3244
+ this.call = options.call;
3245
+ this.expression = options.expression;
3246
+ this.arguments = options.arguments ?? [];
3247
+ }
3248
+ }
1768
3249
  /**
1769
3250
  * A class representing a literal value.
1770
3251
  */ class ValueExpression extends Expression {
1771
3252
  /** The type of the expression (always 'value'). */ type = "value";
1772
3253
  /** The literal value. */ value;
1773
- transformer = null;
1774
- locale = null;
1775
3254
  constructor(options){
1776
3255
  super();
1777
3256
  this.value = options.value;
@@ -1782,9 +3261,45 @@ class NotParsableExpression extends Expression {
1782
3261
  },
1783
3262
  63(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1784
3263
  __webpack_require__.d(__webpack_exports__, {
1785
- j: () => (forEach),
1786
- o: () => (getProperties)
3264
+ CC: () => (peelCalls),
3265
+ LU: () => (childrenOf),
3266
+ jJ: () => (forEach),
3267
+ oY: () => (getProperties)
1787
3268
  });
3269
+ /**
3270
+ * Separates an operand from the calls applied to it.
3271
+ *
3272
+ * `null` when there is no operand beneath the calls. Every consumer needs this to decide whether a
3273
+ * comparator side is a property or a value, so it lives here rather than in each translator.
3274
+ */ function peelCalls(expression) {
3275
+ const calls = [];
3276
+ let current = expression;
3277
+ while(current != null && current.type === "call"){
3278
+ calls.unshift(current);
3279
+ current = current.expression;
3280
+ }
3281
+ return current == null ? null : {
3282
+ operand: current,
3283
+ calls
3284
+ };
3285
+ }
3286
+ function childrenOf(expression) {
3287
+ if (expression.type === "call") {
3288
+ const call = expression;
3289
+ return [
3290
+ call.expression,
3291
+ ...call.arguments ?? []
3292
+ ].filter((child)=>child != null);
3293
+ }
3294
+ const children = [];
3295
+ if (expression.left != null) {
3296
+ children.push(expression.left);
3297
+ }
3298
+ if (expression.right != null) {
3299
+ children.push(expression.right);
3300
+ }
3301
+ return children;
3302
+ }
1788
3303
  /**
1789
3304
  * Extracts all properties referenced in an expression
1790
3305
  * @param expression The expression to analyze
@@ -1796,12 +3311,8 @@ __webpack_require__.d(__webpack_exports__, {
1796
3311
  if (expr.type === "property") {
1797
3312
  properties.push(expr.property);
1798
3313
  }
1799
- // Traverse left and right expressions if they exist
1800
- if (expr.left) {
1801
- traverse(expr.left);
1802
- }
1803
- if (expr.right) {
1804
- traverse(expr.right);
3314
+ for (const child of childrenOf(expr)){
3315
+ traverse(child);
1805
3316
  }
1806
3317
  }
1807
3318
  traverse(expression);
@@ -1814,14 +3325,8 @@ function forEach(expression, callback) {
1814
3325
  if (!callback(expr)) {
1815
3326
  return false;
1816
3327
  }
1817
- // Traverse left and right expressions if they exist
1818
- if (expr.left) {
1819
- if (!traverse(expr.left)) {
1820
- return false;
1821
- }
1822
- }
1823
- if (expr.right) {
1824
- if (!traverse(expr.right)) {
3328
+ for (const child of childrenOf(expr)){
3329
+ if (!traverse(child)) {
1825
3330
  return false;
1826
3331
  }
1827
3332
  }
@@ -1941,12 +3446,14 @@ const isLogLevel = (value)=>typeof value === 'string' && LOG_LEVELS.includes(val
1941
3446
  const debug = process.env.DEBUG;
1942
3447
  if (debug === 'routier' || debug === '*') return 'debug';
1943
3448
  const env = "production"?.toLowerCase();
1944
- // `test` is deliberately absent. It used to be here, which meant no test suite anywhere
1945
- // could run Routier quietly. Opt in with DEBUG=routier or ROUTIER_LOG_LEVEL when a test
1946
- // needs the output.
1947
3449
  if (env === 'dev' || env === 'development') return 'debug';
1948
3450
  }
1949
- return 'silent';
3451
+ // Warnings are on unless something turns them off.
3452
+ //
3453
+ // Routier warns when a query returns correct rows a slower way than it could, or when a filter
3454
+ // compares types that can never match. Both are the caller's to act on, and a default of
3455
+ // `silent` meant the only people who ever saw them were the ones who already knew to look.
3456
+ return 'warn';
1950
3457
  };
1951
3458
  let level = resolveLevel();
1952
3459
  let rank = RANK[level];
@@ -2038,28 +3545,42 @@ var __webpack_exports__ = {};
2038
3545
  // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
2039
3546
  (() => {
2040
3547
  __webpack_require__.d(__webpack_exports__, {
2041
- Ko: () => (/* reexport safe */ _types__rspack_import_2.Ko),
2042
- MY: () => (/* reexport safe */ _parser__rspack_import_1.MY),
2043
- SC: () => (/* reexport safe */ _types__rspack_import_2.SC),
2044
- Sm: () => (/* reexport safe */ _types__rspack_import_2.Sm),
2045
- Vu: () => (/* reexport safe */ _evaluate__rspack_import_0.Vu),
2046
- _3: () => (/* reexport safe */ _evaluate__rspack_import_0._3),
2047
- bQ: () => (/* reexport safe */ _types__rspack_import_2.bQ),
2048
- ep: () => (/* reexport safe */ _types__rspack_import_2.ep),
2049
- fw: () => (/* reexport safe */ _types__rspack_import_2.fw),
2050
- jJ: () => (/* reexport safe */ _utils__rspack_import_3.j),
2051
- oH: () => (/* reexport safe */ _parser__rspack_import_1.oH),
2052
- oY: () => (/* reexport safe */ _utils__rspack_import_3.o),
2053
- pg: () => (/* reexport safe */ _parser__rspack_import_1.pg),
2054
- r4: () => (/* reexport safe */ _types__rspack_import_2.r4),
2055
- tA: () => (/* reexport safe */ _constants__rspack_import_4.t),
2056
- wS: () => (/* reexport safe */ _evaluate__rspack_import_0.wS)
3548
+ CC: () => (/* reexport safe */ _utils__rspack_import_5.CC),
3549
+ DG: () => (/* reexport safe */ _types__rspack_import_4.DG),
3550
+ F5: () => (/* reexport safe */ _fold__rspack_import_2.F5),
3551
+ Ko: () => (/* reexport safe */ _types__rspack_import_4.Ko),
3552
+ LU: () => (/* reexport safe */ _utils__rspack_import_5.LU),
3553
+ MY: () => (/* reexport safe */ _parser__rspack_import_3.MY),
3554
+ Nb: () => (/* reexport safe */ _callSource__rspack_import_0.N),
3555
+ SC: () => (/* reexport safe */ _types__rspack_import_4.SC),
3556
+ Sm: () => (/* reexport safe */ _types__rspack_import_4.Sm),
3557
+ Sv: () => (/* reexport safe */ _fold__rspack_import_2.Sv),
3558
+ Vu: () => (/* reexport safe */ _evaluate__rspack_import_1.Vu),
3559
+ Vv: () => (/* reexport safe */ _evaluate__rspack_import_1.Vv),
3560
+ _3: () => (/* reexport safe */ _evaluate__rspack_import_1._3),
3561
+ ax: () => (/* reexport safe */ _callSource__rspack_import_0.a),
3562
+ bQ: () => (/* reexport safe */ _types__rspack_import_4.bQ),
3563
+ br: () => (/* reexport safe */ _fold__rspack_import_2.br),
3564
+ ep: () => (/* reexport safe */ _types__rspack_import_4.ep),
3565
+ fw: () => (/* reexport safe */ _types__rspack_import_4.fw),
3566
+ gm: () => (/* reexport safe */ _evaluate__rspack_import_1.gm),
3567
+ jJ: () => (/* reexport safe */ _utils__rspack_import_5.jJ),
3568
+ oH: () => (/* reexport safe */ _parser__rspack_import_3.oH),
3569
+ oY: () => (/* reexport safe */ _utils__rspack_import_5.oY),
3570
+ pg: () => (/* reexport safe */ _parser__rspack_import_3.pg),
3571
+ r4: () => (/* reexport safe */ _types__rspack_import_4.r4),
3572
+ tA: () => (/* reexport safe */ _constants__rspack_import_6.t),
3573
+ wS: () => (/* reexport safe */ _evaluate__rspack_import_1.wS)
2057
3574
  });
2058
- /* import */ var _evaluate__rspack_import_0 = __webpack_require__(379);
2059
- /* import */ var _parser__rspack_import_1 = __webpack_require__(91);
2060
- /* import */ var _types__rspack_import_2 = __webpack_require__(27);
2061
- /* import */ var _utils__rspack_import_3 = __webpack_require__(63);
2062
- /* import */ var _constants__rspack_import_4 = __webpack_require__(835);
3575
+ /* import */ var _callSource__rspack_import_0 = __webpack_require__(429);
3576
+ /* import */ var _evaluate__rspack_import_1 = __webpack_require__(379);
3577
+ /* import */ var _fold__rspack_import_2 = __webpack_require__(43);
3578
+ /* import */ var _parser__rspack_import_3 = __webpack_require__(91);
3579
+ /* import */ var _types__rspack_import_4 = __webpack_require__(27);
3580
+ /* import */ var _utils__rspack_import_5 = __webpack_require__(63);
3581
+ /* import */ var _constants__rspack_import_6 = __webpack_require__(835);
3582
+
3583
+
2063
3584
 
2064
3585
 
2065
3586
 
@@ -2068,22 +3589,32 @@ __webpack_require__.d(__webpack_exports__, {
2068
3589
 
2069
3590
  })();
2070
3591
 
3592
+ var __webpack_exports__CALL_SOURCE = __webpack_exports__.Nb;
3593
+ var __webpack_exports__CallExpression = __webpack_exports__.DG;
2071
3594
  var __webpack_exports__ComparatorExpression = __webpack_exports__.bQ;
2072
3595
  var __webpack_exports__EXPRESSION_TYPES = __webpack_exports__.tA;
2073
3596
  var __webpack_exports__EmptyExpression = __webpack_exports__.Sm;
2074
3597
  var __webpack_exports__Expression = __webpack_exports__.r4;
3598
+ var __webpack_exports__FOLDABLE = __webpack_exports__.Sv;
2075
3599
  var __webpack_exports__NotParsableExpression = __webpack_exports__.SC;
2076
3600
  var __webpack_exports__OperatorExpression = __webpack_exports__.fw;
2077
3601
  var __webpack_exports__PropertyExpression = __webpack_exports__.ep;
3602
+ var __webpack_exports__UNRESOLVED = __webpack_exports__.gm;
2078
3603
  var __webpack_exports__ValueExpression = __webpack_exports__.Ko;
3604
+ var __webpack_exports__childrenOf = __webpack_exports__.LU;
2079
3605
  var __webpack_exports__combineExpressions = __webpack_exports__.pg;
2080
3606
  var __webpack_exports__evaluate = __webpack_exports__._3;
3607
+ var __webpack_exports__foldConstantCalls = __webpack_exports__.F5;
3608
+ var __webpack_exports__foldedOperandValue = __webpack_exports__.br;
2081
3609
  var __webpack_exports__forEach = __webpack_exports__.jJ;
2082
3610
  var __webpack_exports__getProperties = __webpack_exports__.oY;
3611
+ var __webpack_exports__operandValue = __webpack_exports__.Vv;
2083
3612
  var __webpack_exports__parseFragment = __webpack_exports__.oH;
3613
+ var __webpack_exports__peelCalls = __webpack_exports__.CC;
3614
+ var __webpack_exports__renderCallAsJs = __webpack_exports__.ax;
2084
3615
  var __webpack_exports__toExpression = __webpack_exports__.MY;
2085
3616
  var __webpack_exports__toPredicate = __webpack_exports__.Vu;
2086
3617
  var __webpack_exports__toStrictPredicate = __webpack_exports__.wS;
2087
- export { __webpack_exports__ComparatorExpression as ComparatorExpression, __webpack_exports__EXPRESSION_TYPES as EXPRESSION_TYPES, __webpack_exports__EmptyExpression as EmptyExpression, __webpack_exports__Expression as Expression, __webpack_exports__NotParsableExpression as NotParsableExpression, __webpack_exports__OperatorExpression as OperatorExpression, __webpack_exports__PropertyExpression as PropertyExpression, __webpack_exports__ValueExpression as ValueExpression, __webpack_exports__combineExpressions as combineExpressions, __webpack_exports__evaluate as evaluate, __webpack_exports__forEach as forEach, __webpack_exports__getProperties as getProperties, __webpack_exports__parseFragment as parseFragment, __webpack_exports__toExpression as toExpression, __webpack_exports__toPredicate as toPredicate, __webpack_exports__toStrictPredicate as toStrictPredicate };
3618
+ export { __webpack_exports__CALL_SOURCE as CALL_SOURCE, __webpack_exports__CallExpression as CallExpression, __webpack_exports__ComparatorExpression as ComparatorExpression, __webpack_exports__EXPRESSION_TYPES as EXPRESSION_TYPES, __webpack_exports__EmptyExpression as EmptyExpression, __webpack_exports__Expression as Expression, __webpack_exports__FOLDABLE as FOLDABLE, __webpack_exports__NotParsableExpression as NotParsableExpression, __webpack_exports__OperatorExpression as OperatorExpression, __webpack_exports__PropertyExpression as PropertyExpression, __webpack_exports__UNRESOLVED as UNRESOLVED, __webpack_exports__ValueExpression as ValueExpression, __webpack_exports__childrenOf as childrenOf, __webpack_exports__combineExpressions as combineExpressions, __webpack_exports__evaluate as evaluate, __webpack_exports__foldConstantCalls as foldConstantCalls, __webpack_exports__foldedOperandValue as foldedOperandValue, __webpack_exports__forEach as forEach, __webpack_exports__getProperties as getProperties, __webpack_exports__operandValue as operandValue, __webpack_exports__parseFragment as parseFragment, __webpack_exports__peelCalls as peelCalls, __webpack_exports__renderCallAsJs as renderCallAsJs, __webpack_exports__toExpression as toExpression, __webpack_exports__toPredicate as toPredicate, __webpack_exports__toStrictPredicate as toStrictPredicate };
2088
3619
 
2089
3620
  //# sourceMappingURL=index.js.map