solve-engine 2.15.0 → 2.17.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 (174) hide show
  1. package/dist/{BytecodeBuilder-CRYfrFfq.d.cts → BytecodeBuilder-aqVa7Plx.d.cts} +10 -0
  2. package/dist/{BytecodeBuilder-CRYfrFfq.d.ts → BytecodeBuilder-aqVa7Plx.d.ts} +10 -0
  3. package/dist/{EngineError-Cv5q4Rbv.d.cts → EngineError-DTk7I7hZ.d.cts} +24 -0
  4. package/dist/{EngineError-Cv5q4Rbv.d.ts → EngineError-DTk7I7hZ.d.ts} +24 -0
  5. package/dist/{PackageCompatibility-Cl7GF_Iu.d.ts → PackageCompatibility-BGXSFuL9.d.ts} +1 -1
  6. package/dist/{PackageCompatibility-C2IQZ_w3.d.cts → PackageCompatibility-BZCqaTOO.d.cts} +1 -1
  7. package/dist/{PackageRegistry-CK_JoX50.d.ts → PackageRegistry-BRoVOYzg.d.ts} +107 -5
  8. package/dist/{PackageRegistry-CZO1IbS-.d.cts → PackageRegistry-rMGnTh7W.d.cts} +107 -5
  9. package/dist/{Parselet-CK0bNO1l.d.ts → Parselet-B-WyUtX4.d.ts} +5 -1
  10. package/dist/{Parselet-DOGmj6N6.d.cts → Parselet-Bkbp9CKD.d.cts} +5 -1
  11. package/dist/{ScopeManager-BtqiTVjG.d.cts → ScopeManager-bCYewWVt.d.cts} +2 -2
  12. package/dist/{ScopeManager-C6c1WmJD.d.ts → ScopeManager-gB9UengK.d.ts} +2 -2
  13. package/dist/{TokenNormalizer-BfTHg1qt.d.cts → TokenNormalizer-MXaKLJ_m.d.cts} +182 -0
  14. package/dist/{TokenNormalizer-d7F1KQFs.d.ts → TokenNormalizer-OTPS0Otq.d.ts} +182 -0
  15. package/dist/{VMCheckpoints-DJdvpliw.d.ts → VMCheckpoints-BDRY1Kx8.d.ts} +2 -2
  16. package/dist/{VMCheckpoints-BFDlaGse.d.cts → VMCheckpoints-DGar9Yg8.d.cts} +2 -2
  17. package/dist/{WorkerError-FH7KXX4_.d.cts → WorkerError-DGBNM3gA.d.cts} +1 -1
  18. package/dist/{WorkerError-J3v3ix_z.d.ts → WorkerError-DpZgKRks.d.ts} +1 -1
  19. package/dist/{chunk-UZSPBFZN.js → chunk-2DPKJ2SM.js} +2 -2
  20. package/dist/{chunk-UZSPBFZN.js.map → chunk-2DPKJ2SM.js.map} +1 -1
  21. package/dist/{chunk-O3ALJLYC.js → chunk-3GR46UOX.js} +2 -2
  22. package/dist/{chunk-O3ALJLYC.js.map → chunk-3GR46UOX.js.map} +1 -1
  23. package/dist/{chunk-PREIL3IB.cjs → chunk-3SSMOVWB.cjs} +2 -2
  24. package/dist/{chunk-PREIL3IB.cjs.map → chunk-3SSMOVWB.cjs.map} +1 -1
  25. package/dist/{chunk-XK23K3EJ.js → chunk-4ETS224G.js} +2 -2
  26. package/dist/{chunk-XK23K3EJ.js.map → chunk-4ETS224G.js.map} +1 -1
  27. package/dist/{chunk-Q7HPPLMG.js → chunk-5FMYW44V.js} +2 -2
  28. package/dist/{chunk-Q7HPPLMG.js.map → chunk-5FMYW44V.js.map} +1 -1
  29. package/dist/{chunk-GWCBPITD.cjs → chunk-6YM67DWH.cjs} +3 -3
  30. package/dist/{chunk-GWCBPITD.cjs.map → chunk-6YM67DWH.cjs.map} +1 -1
  31. package/dist/{chunk-6U66HQKQ.cjs → chunk-75JLWK4L.cjs} +2 -2
  32. package/dist/{chunk-6U66HQKQ.cjs.map → chunk-75JLWK4L.cjs.map} +1 -1
  33. package/dist/chunk-A2RQUV5H.cjs +5 -0
  34. package/dist/chunk-A2RQUV5H.cjs.map +1 -0
  35. package/dist/{chunk-VJP4GGCW.cjs → chunk-BNCBB4H5.cjs} +2 -2
  36. package/dist/{chunk-VJP4GGCW.cjs.map → chunk-BNCBB4H5.cjs.map} +1 -1
  37. package/dist/{chunk-EEUA3OJ6.cjs → chunk-CEURQXSN.cjs} +2 -2
  38. package/dist/{chunk-EEUA3OJ6.cjs.map → chunk-CEURQXSN.cjs.map} +1 -1
  39. package/dist/chunk-CO2BI6WL.cjs +3 -0
  40. package/dist/chunk-CO2BI6WL.cjs.map +1 -0
  41. package/dist/chunk-EN3CDYOS.cjs +2 -0
  42. package/dist/chunk-EN3CDYOS.cjs.map +1 -0
  43. package/dist/{chunk-GWJJRH32.js → chunk-EQDT42JG.js} +3 -3
  44. package/dist/{chunk-GWJJRH32.js.map → chunk-EQDT42JG.js.map} +1 -1
  45. package/dist/chunk-G2MFTERZ.cjs +2 -0
  46. package/dist/chunk-G2MFTERZ.cjs.map +1 -0
  47. package/dist/chunk-G63TMUL2.js +2 -0
  48. package/dist/chunk-G63TMUL2.js.map +1 -0
  49. package/dist/{chunk-UUAFDZK6.cjs → chunk-H3JXNH7X.cjs} +3 -3
  50. package/dist/{chunk-UUAFDZK6.cjs.map → chunk-H3JXNH7X.cjs.map} +1 -1
  51. package/dist/{chunk-3Y56PSQD.cjs → chunk-HQ2PE6HY.cjs} +3 -3
  52. package/dist/{chunk-3Y56PSQD.cjs.map → chunk-HQ2PE6HY.cjs.map} +1 -1
  53. package/dist/chunk-IES365YJ.cjs +3 -0
  54. package/dist/chunk-IES365YJ.cjs.map +1 -0
  55. package/dist/{chunk-2XZCLDHJ.cjs → chunk-J7ABVGZH.cjs} +2 -2
  56. package/dist/{chunk-2XZCLDHJ.cjs.map → chunk-J7ABVGZH.cjs.map} +1 -1
  57. package/dist/chunk-JVMINMAB.js +2 -0
  58. package/dist/chunk-JVMINMAB.js.map +1 -0
  59. package/dist/chunk-JYLNQOPU.cjs +2 -0
  60. package/dist/chunk-JYLNQOPU.cjs.map +1 -0
  61. package/dist/{chunk-JOIQFDZQ.js → chunk-KEA5HRL3.js} +2 -2
  62. package/dist/{chunk-JOIQFDZQ.js.map → chunk-KEA5HRL3.js.map} +1 -1
  63. package/dist/{chunk-TVE2DNPU.js → chunk-LJFS3XHW.js} +2 -2
  64. package/dist/{chunk-TVE2DNPU.js.map → chunk-LJFS3XHW.js.map} +1 -1
  65. package/dist/{chunk-C5MP74ER.js → chunk-MRRMIBHE.js} +2 -2
  66. package/dist/{chunk-C5MP74ER.js.map → chunk-MRRMIBHE.js.map} +1 -1
  67. package/dist/chunk-N64ZK6CL.cjs +2 -0
  68. package/dist/{chunk-EEVUKZ5B.cjs.map → chunk-N64ZK6CL.cjs.map} +1 -1
  69. package/dist/chunk-O3BDXOQJ.js +2 -0
  70. package/dist/chunk-O3BDXOQJ.js.map +1 -0
  71. package/dist/chunk-PIZQIQVM.js +3 -0
  72. package/dist/chunk-PIZQIQVM.js.map +1 -0
  73. package/dist/chunk-PS5OA7QU.js +5 -0
  74. package/dist/chunk-PS5OA7QU.js.map +1 -0
  75. package/dist/{chunk-IPOU3JTA.js → chunk-QTSDIDAS.js} +3 -3
  76. package/dist/{chunk-IPOU3JTA.js.map → chunk-QTSDIDAS.js.map} +1 -1
  77. package/dist/chunk-RHE3LC4Q.js +2 -0
  78. package/dist/chunk-RHE3LC4Q.js.map +1 -0
  79. package/dist/{chunk-P4ETODMP.js → chunk-RLTH2H3U.js} +2 -2
  80. package/dist/{chunk-P4ETODMP.js.map → chunk-RLTH2H3U.js.map} +1 -1
  81. package/dist/{chunk-WOIJPV7Z.js → chunk-SEOIKHYK.js} +2 -2
  82. package/dist/{chunk-WOIJPV7Z.js.map → chunk-SEOIKHYK.js.map} +1 -1
  83. package/dist/{chunk-W5ELYJ4Z.cjs → chunk-TL545PXW.cjs} +2 -2
  84. package/dist/{chunk-W5ELYJ4Z.cjs.map → chunk-TL545PXW.cjs.map} +1 -1
  85. package/dist/{chunk-FAGFPVYK.cjs → chunk-TRDOYAKJ.cjs} +2 -2
  86. package/dist/{chunk-FAGFPVYK.cjs.map → chunk-TRDOYAKJ.cjs.map} +1 -1
  87. package/dist/chunk-WMISTHB2.cjs +2 -0
  88. package/dist/chunk-WMISTHB2.cjs.map +1 -0
  89. package/dist/{chunk-3PDDKWTJ.js → chunk-XOEZ7UQI.js} +3 -3
  90. package/dist/{chunk-3PDDKWTJ.js.map → chunk-XOEZ7UQI.js.map} +1 -1
  91. package/dist/{chunk-EXCFCHAT.cjs → chunk-XPJGDGJF.cjs} +2 -2
  92. package/dist/{chunk-EXCFCHAT.cjs.map → chunk-XPJGDGJF.cjs.map} +1 -1
  93. package/dist/chunk-YQV4WGXH.js +3 -0
  94. package/dist/chunk-YQV4WGXH.js.map +1 -0
  95. package/dist/constants.cjs +1 -1
  96. package/dist/constants.js +1 -1
  97. package/dist/engine.cjs +1 -1
  98. package/dist/engine.d.cts +8 -8
  99. package/dist/engine.d.ts +8 -8
  100. package/dist/engine.js +1 -1
  101. package/dist/errors.cjs +1 -1
  102. package/dist/errors.d.cts +3 -3
  103. package/dist/errors.d.ts +3 -3
  104. package/dist/errors.js +1 -1
  105. package/dist/format.cjs +1 -1
  106. package/dist/format.js +1 -1
  107. package/dist/index.cjs +1 -1
  108. package/dist/index.d.cts +8 -8
  109. package/dist/index.d.ts +8 -8
  110. package/dist/index.js +1 -1
  111. package/dist/language.d.cts +7 -7
  112. package/dist/language.d.ts +7 -7
  113. package/dist/lexer.cjs +1 -1
  114. package/dist/lexer.js +1 -1
  115. package/dist/normalizer.cjs +1 -1
  116. package/dist/normalizer.d.cts +2 -2
  117. package/dist/normalizer.d.ts +2 -2
  118. package/dist/normalizer.js +1 -1
  119. package/dist/packages.cjs +1 -1
  120. package/dist/packages.d.cts +6 -6
  121. package/dist/packages.d.ts +6 -6
  122. package/dist/packages.js +1 -1
  123. package/dist/parser.cjs +1 -1
  124. package/dist/parser.d.cts +2 -2
  125. package/dist/parser.d.ts +2 -2
  126. package/dist/parser.js +1 -1
  127. package/dist/resolvers.d.cts +1 -1
  128. package/dist/resolvers.d.ts +1 -1
  129. package/dist/testing.cjs +2 -2
  130. package/dist/testing.d.cts +7 -7
  131. package/dist/testing.d.ts +7 -7
  132. package/dist/testing.js +1 -1
  133. package/dist/uom.cjs +1 -1
  134. package/dist/uom.d.cts +1 -1
  135. package/dist/uom.d.ts +1 -1
  136. package/dist/uom.js +1 -1
  137. package/dist/vm.cjs +1 -1
  138. package/dist/vm.d.cts +5 -5
  139. package/dist/vm.d.ts +5 -5
  140. package/dist/vm.js +1 -1
  141. package/dist/worker.cjs +2 -2
  142. package/dist/worker.d.cts +7 -7
  143. package/dist/worker.d.ts +7 -7
  144. package/dist/worker.js +1 -1
  145. package/package.json +1 -1
  146. package/dist/chunk-4IIFFJTJ.js +0 -5
  147. package/dist/chunk-4IIFFJTJ.js.map +0 -1
  148. package/dist/chunk-5SDSIRPL.js +0 -2
  149. package/dist/chunk-5SDSIRPL.js.map +0 -1
  150. package/dist/chunk-AYTEPHRO.js +0 -3
  151. package/dist/chunk-AYTEPHRO.js.map +0 -1
  152. package/dist/chunk-CUDPOWXA.js +0 -2
  153. package/dist/chunk-CUDPOWXA.js.map +0 -1
  154. package/dist/chunk-EEVUKZ5B.cjs +0 -2
  155. package/dist/chunk-FPGYRFJY.cjs +0 -5
  156. package/dist/chunk-FPGYRFJY.cjs.map +0 -1
  157. package/dist/chunk-FVQWN5HZ.cjs +0 -2
  158. package/dist/chunk-FVQWN5HZ.cjs.map +0 -1
  159. package/dist/chunk-HDGARCI2.cjs +0 -2
  160. package/dist/chunk-HDGARCI2.cjs.map +0 -1
  161. package/dist/chunk-IMXSVHQK.cjs +0 -2
  162. package/dist/chunk-IMXSVHQK.cjs.map +0 -1
  163. package/dist/chunk-KKMLFYVW.cjs +0 -3
  164. package/dist/chunk-KKMLFYVW.cjs.map +0 -1
  165. package/dist/chunk-NAZ6PGLS.cjs +0 -3
  166. package/dist/chunk-NAZ6PGLS.cjs.map +0 -1
  167. package/dist/chunk-Q3PSTNSY.js +0 -2
  168. package/dist/chunk-Q3PSTNSY.js.map +0 -1
  169. package/dist/chunk-RC4M6HBX.cjs +0 -2
  170. package/dist/chunk-RC4M6HBX.cjs.map +0 -1
  171. package/dist/chunk-YAZ4DUJJ.js +0 -3
  172. package/dist/chunk-YAZ4DUJJ.js.map +0 -1
  173. package/dist/chunk-YFX53MZF.js +0 -2
  174. package/dist/chunk-YFX53MZF.js.map +0 -1
@@ -111,6 +111,16 @@ interface BytecodeProgram {
111
111
  opcodes: Uint8Array;
112
112
  numbers: Float64Array;
113
113
  strings: string[];
114
+ /**
115
+ * Numeric constants by opcode position, restored from a snapshot.
116
+ *
117
+ * Nothing in the compile path writes this: numbers are emitted inline into
118
+ * {@link numbers} instead. `build()` used to attach an empty Map to every
119
+ * program anyway, which was one allocation per compiled expression, around a
120
+ * tenth of parse-and-compile time, for a collection that was never read.
121
+ * It is left off now, and only {@link EngineSnapshot} sets it when restoring
122
+ * a program that carried one; every reader already guards on its absence.
123
+ */
114
124
  constants?: Map<number, number>;
115
125
  /**
116
126
  * Whether the program contains any async opcodes (CALL_PLUGIN, etc.).
@@ -111,6 +111,16 @@ interface BytecodeProgram {
111
111
  opcodes: Uint8Array;
112
112
  numbers: Float64Array;
113
113
  strings: string[];
114
+ /**
115
+ * Numeric constants by opcode position, restored from a snapshot.
116
+ *
117
+ * Nothing in the compile path writes this: numbers are emitted inline into
118
+ * {@link numbers} instead. `build()` used to attach an empty Map to every
119
+ * program anyway, which was one allocation per compiled expression, around a
120
+ * tenth of parse-and-compile time, for a collection that was never read.
121
+ * It is left off now, and only {@link EngineSnapshot} sets it when restoring
122
+ * a program that carried one; every reader already guards on its absence.
123
+ */
114
124
  constants?: Map<number, number>;
115
125
  /**
116
126
  * Whether the program contains any async opcodes (CALL_PLUGIN, etc.).
@@ -313,6 +313,30 @@ declare class EngineError extends Error {
313
313
  * interop with no build-config change needed.
314
314
  */
315
315
  readonly cause?: unknown;
316
+ /**
317
+ * Whether a recoverable error captures a JavaScript stack trace.
318
+ *
319
+ * Off, because a recoverable EngineError is a value rather than a fault. A
320
+ * line of prose in a notepad is not an expression, so parsing it fails, and
321
+ * that failure is the answer for that line rather than a bug to debug. The
322
+ * engine builds one such error per non-expression line.
323
+ *
324
+ * Capturing a stack is not cheap, and its cost grows with how deep the stack
325
+ * is when it happens. Measured through `parseDocument`, where the throw site
326
+ * sits about a dozen frames down, each capture cost around 62 microseconds
327
+ * and a 250-line document built 74 of them: a CPU profile put the
328
+ * constructor at 46% of the whole pipeline, more than lexing, normalising,
329
+ * parsing and executing put together.
330
+ *
331
+ * Turn it on to debug where a recoverable error is raised from. Errors that
332
+ * are NOT recoverable always capture, since those are the genuine faults.
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * EngineError.captureRecoverableStacks = true;
337
+ * ```
338
+ */
339
+ static captureRecoverableStacks: boolean;
316
340
  constructor(category: ErrorCategory, init: EngineErrorInit);
317
341
  /** `!recoverable`. See `EngineErrorInit.recoverable`'s doc comment for what this actually gates (message framing/telemetry, not whether evaluation continues). */
318
342
  isFatal(): boolean;
@@ -313,6 +313,30 @@ declare class EngineError extends Error {
313
313
  * interop with no build-config change needed.
314
314
  */
315
315
  readonly cause?: unknown;
316
+ /**
317
+ * Whether a recoverable error captures a JavaScript stack trace.
318
+ *
319
+ * Off, because a recoverable EngineError is a value rather than a fault. A
320
+ * line of prose in a notepad is not an expression, so parsing it fails, and
321
+ * that failure is the answer for that line rather than a bug to debug. The
322
+ * engine builds one such error per non-expression line.
323
+ *
324
+ * Capturing a stack is not cheap, and its cost grows with how deep the stack
325
+ * is when it happens. Measured through `parseDocument`, where the throw site
326
+ * sits about a dozen frames down, each capture cost around 62 microseconds
327
+ * and a 250-line document built 74 of them: a CPU profile put the
328
+ * constructor at 46% of the whole pipeline, more than lexing, normalising,
329
+ * parsing and executing put together.
330
+ *
331
+ * Turn it on to debug where a recoverable error is raised from. Errors that
332
+ * are NOT recoverable always capture, since those are the genuine faults.
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * EngineError.captureRecoverableStacks = true;
337
+ * ```
338
+ */
339
+ static captureRecoverableStacks: boolean;
316
340
  constructor(category: ErrorCategory, init: EngineErrorInit);
317
341
  /** `!recoverable`. See `EngineErrorInit.recoverable`'s doc comment for what this actually gates (message framing/telemetry, not whether evaluation continues). */
318
342
  isFatal(): boolean;
@@ -1,4 +1,4 @@
1
- import { I as IEnginePackage } from './PackageRegistry-CK_JoX50.js';
1
+ import { I as IEnginePackage } from './PackageRegistry-BRoVOYzg.js';
2
2
 
3
3
  /**
4
4
  * Package load-time compatibility checking, the "detect overlapping
@@ -1,4 +1,4 @@
1
- import { I as IEnginePackage } from './PackageRegistry-CZO1IbS-.cjs';
1
+ import { I as IEnginePackage } from './PackageRegistry-rMGnTh7W.cjs';
2
2
 
3
3
  /**
4
4
  * Package load-time compatibility checking, the "detect overlapping
@@ -1,12 +1,12 @@
1
- import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-CK0bNO1l.js';
1
+ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-B-WyUtX4.js';
2
2
  import { V as Value, b as ValueType } from './Value-DCTqTSeP.js';
3
3
  import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-CqagTewQ.js';
4
4
  import { IAsyncResolver } from './resolvers.js';
5
- import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-d7F1KQFs.js';
6
- import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-C6c1WmJD.js';
7
- import { a as BytecodeProgram } from './BytecodeBuilder-CRYfrFfq.js';
5
+ import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-OTPS0Otq.js';
6
+ import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-gB9UengK.js';
7
+ import { a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.js';
8
8
  import { QueryClient } from '@tanstack/query-core';
9
- import { E as EngineError } from './EngineError-Cv5q4Rbv.js';
9
+ import { E as EngineError } from './EngineError-DTk7I7hZ.js';
10
10
  import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-QIT4iD8f.js';
11
11
  import { T as Token } from './Token-CbP_OutD.js';
12
12
  import { E as EngineConfigOverride, a as EngineConfig } from './Configuration-BGQn-cJ8.js';
@@ -1191,6 +1191,32 @@ interface NormalizerOutput {
1191
1191
  }[];
1192
1192
  /** Post-normalization tokens ready for parsing */
1193
1193
  tokens: Token[];
1194
+ /**
1195
+ * Every registered rule with the shape it declared, in priority order.
1196
+ *
1197
+ * A rule that declares a shape is tried only where that shape can match; one
1198
+ * that declares none is tried at every position of every line. That
1199
+ * distinction is invisible from outside the engine and is the difference
1200
+ * between a package costing the documents that use it and costing all of
1201
+ * them, so the playground draws it.
1202
+ */
1203
+ ruleShapes?: {
1204
+ name: string;
1205
+ priority: number;
1206
+ shape: readonly {
1207
+ types?: readonly string[];
1208
+ values?: readonly string[];
1209
+ }[];
1210
+ unshapedReason?: string;
1211
+ indexedSlots: number;
1212
+ }[];
1213
+ /**
1214
+ * How many rules could fire at each position of the normalised stream.
1215
+ *
1216
+ * One entry per token. Mostly zeroes, which is the point of the index: a
1217
+ * position where nothing can match is rejected without calling a rule.
1218
+ */
1219
+ candidatesPerPosition?: number[];
1194
1220
  /**
1195
1221
  * All registered phrase → tokenType mappings from the PhraseTrie.
1196
1222
  * Populated by the engine at diagnostic stage build time so the
@@ -1478,6 +1504,18 @@ interface EngineOptions {
1478
1504
  config?: EngineConfigOverride;
1479
1505
  /** Turn on the diagnostic pipeline (per-stage timing and detail). Defaults to `false`. */
1480
1506
  diagnostics?: boolean;
1507
+ /**
1508
+ * Run a few throwaway expressions through the pipeline at construction, so
1509
+ * the first real one is not the one that pays for JIT warmup.
1510
+ *
1511
+ * Off by default, because it moves cost rather than removing it: a process
1512
+ * that evaluates one expression and exits pays for warming paths it never
1513
+ * reuses. Turn it on for anything interactive, where the first keystroke is
1514
+ * the one a person notices. See {@link ExpressionEngine.warmUp}.
1515
+ *
1516
+ * @default false
1517
+ */
1518
+ warmup?: boolean;
1481
1519
  }
1482
1520
  /**
1483
1521
  * Core expression evaluation engine, the top-level orchestrator.
@@ -1590,6 +1628,13 @@ declare class ExpressionEngine {
1590
1628
  *. See `api/PackageCompatibility.ts`'s module doc for why this exists.
1591
1629
  */
1592
1630
  private registeredPackages;
1631
+ /**
1632
+ * The merged `name -> fused token type` map every package's
1633
+ * {@link IEnginePackage.callFusions} feeds, read live by the single
1634
+ * {@link callFusionRule}. Kept in step with registration so one rule serves
1635
+ * every `name(` call word instead of one rule per package.
1636
+ */
1637
+ private callFusions;
1593
1638
  /**
1594
1639
  * Incremental index behind the package-compatibility check. Kept in step
1595
1640
  * with {@link registeredPackages} so each `registerPackage` costs O(that
@@ -1643,6 +1688,41 @@ declare class ExpressionEngine {
1643
1688
  * had set, rather than assuming it was `null`.
1644
1689
  */
1645
1690
  getDocumentModel(): DocumentModel | null;
1691
+ /**
1692
+ * A handful of expressions run through the pipeline to warm it, chosen to
1693
+ * cover the shapes the hot paths specialise on rather than to be
1694
+ * interesting.
1695
+ *
1696
+ * V8 runs a function interpreted until it has been called enough times to
1697
+ * be worth optimising, and it specialises on the types it has actually
1698
+ * seen. So the point is breadth, not volume: a number, a unit, a phrase, a
1699
+ * function call, a comparison and a string each drive a different branch of
1700
+ * the lexer, the normalizer and the VM. Warming only with `1 + 1` would
1701
+ * optimise those functions for integers and then deoptimise the moment a
1702
+ * real document mentioned kilograms, which is worse than not warming at all.
1703
+ */
1704
+ private static readonly WARMUP_EXPRESSIONS;
1705
+ /**
1706
+ * Run the pipeline over a few throwaway expressions so the first real one
1707
+ * is not the one that pays for JIT warmup.
1708
+ *
1709
+ * Nothing here reaches the engine's state: no variable is defined, no line
1710
+ * is registered, no bytecode is cached and no async resolver is consulted.
1711
+ * It lexes, normalises, parses, compiles and executes into a scratch
1712
+ * builder and then drops the result, which is enough to move the hot
1713
+ * functions past the interpreter and to build the normalizer's rule index.
1714
+ *
1715
+ * The cost lands on construction instead. That is the right trade for an
1716
+ * editor, where the first keystroke is the one a person notices, and the
1717
+ * wrong one for a process that evaluates a single expression and exits,
1718
+ * which is why {@link EngineOptions.warmup} exists rather than this being
1719
+ * unconditional.
1720
+ *
1721
+ * Failures are swallowed on purpose: a warmup expression that stops parsing
1722
+ * because a package changed is a warmup that did less good, not a reason to
1723
+ * refuse to construct an engine.
1724
+ */
1725
+ warmUp(): void;
1646
1726
  /**
1647
1727
  * Build the {@link LineExecutionContext} passed to `executeBytecode()`
1648
1728
  * for a given line. `lineNumber = -1` (the existing sentinel
@@ -3069,6 +3149,28 @@ interface IEnginePackage {
3069
3149
  * like implicit operator insertion.
3070
3150
  */
3071
3151
  normalizerRules?: NormalizerRule[];
3152
+ /**
3153
+ * Function-call words that fuse to a call token when immediately followed by
3154
+ * `(`, keyed by the lower-cased word and mapped to the token type to mint.
3155
+ * This is the declarative form of the common `name(` normalizer rule (the one
3156
+ * `base64(`, `sha256(`, `length(`, ... all hand-wrote): the engine merges every
3157
+ * package's `callFusions` into one shared map and runs a SINGLE rule for all of
3158
+ * them, rather than one rule per package tried at every identifier.
3159
+ *
3160
+ * The fused token carries the lower-cased word as its value (the call parselet
3161
+ * reads it to pick the function) and the original text as its raw value, and it
3162
+ * does not fire after a `:` (so `:base64 = ...` stays a variable). For anything
3163
+ * more than that plain shape (a different lookbehind, a deeper lookahead), write
3164
+ * a {@link normalizerRules} entry instead.
3165
+ *
3166
+ * @example
3167
+ * ```ts
3168
+ * callFusions: { base64: "BASE64_FN" }
3169
+ * // or, mapping many names to one token type:
3170
+ * callFusions: Object.fromEntries(Object.keys(FUNCS).map((n) => [n, "TEXT_CALL"]))
3171
+ * ```
3172
+ */
3173
+ callFusions?: Record<string, string>;
3072
3174
  /**
3073
3175
  * Semantic highlight categories for this package's custom token types
3074
3176
  * (introduced via {@link lexerVocabulary} or {@link normalizerRules}), the
@@ -1,12 +1,12 @@
1
- import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-DOGmj6N6.cjs';
1
+ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-Bkbp9CKD.cjs';
2
2
  import { V as Value, b as ValueType } from './Value-DCTqTSeP.cjs';
3
3
  import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-CCHFDcgP.cjs';
4
4
  import { IAsyncResolver } from './resolvers.cjs';
5
- import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-BfTHg1qt.cjs';
6
- import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-BtqiTVjG.cjs';
7
- import { a as BytecodeProgram } from './BytecodeBuilder-CRYfrFfq.cjs';
5
+ import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-MXaKLJ_m.cjs';
6
+ import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-bCYewWVt.cjs';
7
+ import { a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.cjs';
8
8
  import { QueryClient } from '@tanstack/query-core';
9
- import { E as EngineError } from './EngineError-Cv5q4Rbv.cjs';
9
+ import { E as EngineError } from './EngineError-DTk7I7hZ.cjs';
10
10
  import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-B4wf3M1h.cjs';
11
11
  import { T as Token } from './Token-CbP_OutD.cjs';
12
12
  import { E as EngineConfigOverride, a as EngineConfig } from './Configuration-BGQn-cJ8.cjs';
@@ -1191,6 +1191,32 @@ interface NormalizerOutput {
1191
1191
  }[];
1192
1192
  /** Post-normalization tokens ready for parsing */
1193
1193
  tokens: Token[];
1194
+ /**
1195
+ * Every registered rule with the shape it declared, in priority order.
1196
+ *
1197
+ * A rule that declares a shape is tried only where that shape can match; one
1198
+ * that declares none is tried at every position of every line. That
1199
+ * distinction is invisible from outside the engine and is the difference
1200
+ * between a package costing the documents that use it and costing all of
1201
+ * them, so the playground draws it.
1202
+ */
1203
+ ruleShapes?: {
1204
+ name: string;
1205
+ priority: number;
1206
+ shape: readonly {
1207
+ types?: readonly string[];
1208
+ values?: readonly string[];
1209
+ }[];
1210
+ unshapedReason?: string;
1211
+ indexedSlots: number;
1212
+ }[];
1213
+ /**
1214
+ * How many rules could fire at each position of the normalised stream.
1215
+ *
1216
+ * One entry per token. Mostly zeroes, which is the point of the index: a
1217
+ * position where nothing can match is rejected without calling a rule.
1218
+ */
1219
+ candidatesPerPosition?: number[];
1194
1220
  /**
1195
1221
  * All registered phrase → tokenType mappings from the PhraseTrie.
1196
1222
  * Populated by the engine at diagnostic stage build time so the
@@ -1478,6 +1504,18 @@ interface EngineOptions {
1478
1504
  config?: EngineConfigOverride;
1479
1505
  /** Turn on the diagnostic pipeline (per-stage timing and detail). Defaults to `false`. */
1480
1506
  diagnostics?: boolean;
1507
+ /**
1508
+ * Run a few throwaway expressions through the pipeline at construction, so
1509
+ * the first real one is not the one that pays for JIT warmup.
1510
+ *
1511
+ * Off by default, because it moves cost rather than removing it: a process
1512
+ * that evaluates one expression and exits pays for warming paths it never
1513
+ * reuses. Turn it on for anything interactive, where the first keystroke is
1514
+ * the one a person notices. See {@link ExpressionEngine.warmUp}.
1515
+ *
1516
+ * @default false
1517
+ */
1518
+ warmup?: boolean;
1481
1519
  }
1482
1520
  /**
1483
1521
  * Core expression evaluation engine, the top-level orchestrator.
@@ -1590,6 +1628,13 @@ declare class ExpressionEngine {
1590
1628
  *. See `api/PackageCompatibility.ts`'s module doc for why this exists.
1591
1629
  */
1592
1630
  private registeredPackages;
1631
+ /**
1632
+ * The merged `name -> fused token type` map every package's
1633
+ * {@link IEnginePackage.callFusions} feeds, read live by the single
1634
+ * {@link callFusionRule}. Kept in step with registration so one rule serves
1635
+ * every `name(` call word instead of one rule per package.
1636
+ */
1637
+ private callFusions;
1593
1638
  /**
1594
1639
  * Incremental index behind the package-compatibility check. Kept in step
1595
1640
  * with {@link registeredPackages} so each `registerPackage` costs O(that
@@ -1643,6 +1688,41 @@ declare class ExpressionEngine {
1643
1688
  * had set, rather than assuming it was `null`.
1644
1689
  */
1645
1690
  getDocumentModel(): DocumentModel | null;
1691
+ /**
1692
+ * A handful of expressions run through the pipeline to warm it, chosen to
1693
+ * cover the shapes the hot paths specialise on rather than to be
1694
+ * interesting.
1695
+ *
1696
+ * V8 runs a function interpreted until it has been called enough times to
1697
+ * be worth optimising, and it specialises on the types it has actually
1698
+ * seen. So the point is breadth, not volume: a number, a unit, a phrase, a
1699
+ * function call, a comparison and a string each drive a different branch of
1700
+ * the lexer, the normalizer and the VM. Warming only with `1 + 1` would
1701
+ * optimise those functions for integers and then deoptimise the moment a
1702
+ * real document mentioned kilograms, which is worse than not warming at all.
1703
+ */
1704
+ private static readonly WARMUP_EXPRESSIONS;
1705
+ /**
1706
+ * Run the pipeline over a few throwaway expressions so the first real one
1707
+ * is not the one that pays for JIT warmup.
1708
+ *
1709
+ * Nothing here reaches the engine's state: no variable is defined, no line
1710
+ * is registered, no bytecode is cached and no async resolver is consulted.
1711
+ * It lexes, normalises, parses, compiles and executes into a scratch
1712
+ * builder and then drops the result, which is enough to move the hot
1713
+ * functions past the interpreter and to build the normalizer's rule index.
1714
+ *
1715
+ * The cost lands on construction instead. That is the right trade for an
1716
+ * editor, where the first keystroke is the one a person notices, and the
1717
+ * wrong one for a process that evaluates a single expression and exits,
1718
+ * which is why {@link EngineOptions.warmup} exists rather than this being
1719
+ * unconditional.
1720
+ *
1721
+ * Failures are swallowed on purpose: a warmup expression that stops parsing
1722
+ * because a package changed is a warmup that did less good, not a reason to
1723
+ * refuse to construct an engine.
1724
+ */
1725
+ warmUp(): void;
1646
1726
  /**
1647
1727
  * Build the {@link LineExecutionContext} passed to `executeBytecode()`
1648
1728
  * for a given line. `lineNumber = -1` (the existing sentinel
@@ -3069,6 +3149,28 @@ interface IEnginePackage {
3069
3149
  * like implicit operator insertion.
3070
3150
  */
3071
3151
  normalizerRules?: NormalizerRule[];
3152
+ /**
3153
+ * Function-call words that fuse to a call token when immediately followed by
3154
+ * `(`, keyed by the lower-cased word and mapped to the token type to mint.
3155
+ * This is the declarative form of the common `name(` normalizer rule (the one
3156
+ * `base64(`, `sha256(`, `length(`, ... all hand-wrote): the engine merges every
3157
+ * package's `callFusions` into one shared map and runs a SINGLE rule for all of
3158
+ * them, rather than one rule per package tried at every identifier.
3159
+ *
3160
+ * The fused token carries the lower-cased word as its value (the call parselet
3161
+ * reads it to pick the function) and the original text as its raw value, and it
3162
+ * does not fire after a `:` (so `:base64 = ...` stays a variable). For anything
3163
+ * more than that plain shape (a different lookbehind, a deeper lookahead), write
3164
+ * a {@link normalizerRules} entry instead.
3165
+ *
3166
+ * @example
3167
+ * ```ts
3168
+ * callFusions: { base64: "BASE64_FN" }
3169
+ * // or, mapping many names to one token type:
3170
+ * callFusions: Object.fromEntries(Object.keys(FUNCS).map((n) => [n, "TEXT_CALL"]))
3171
+ * ```
3172
+ */
3173
+ callFusions?: Record<string, string>;
3072
3174
  /**
3073
3175
  * Semantic highlight categories for this package's custom token types
3074
3176
  * (introduced via {@link lexerVocabulary} or {@link normalizerRules}), the
@@ -1,4 +1,4 @@
1
- import { B as BytecodeBuilder } from './BytecodeBuilder-CRYfrFfq.js';
1
+ import { B as BytecodeBuilder } from './BytecodeBuilder-aqVa7Plx.js';
2
2
  import { T as Token } from './Token-CbP_OutD.js';
3
3
  import { D as DiagnosticPipeline } from './pipeline-QIT4iD8f.js';
4
4
 
@@ -149,6 +149,10 @@ declare class PrecedenceParser {
149
149
  private diagnosticPipeline;
150
150
  private currentExpression;
151
151
  private localeCode;
152
+ /** The locale's decimal separator, cached from {@link localeCode}. */
153
+ private readonly decimalSeparator;
154
+ /** The locale's thousands separator, cached from {@link localeCode}. */
155
+ private readonly thousandsSeparator;
152
156
  /**
153
157
  * The binding power the current infix parselet is being invoked at, i.e. the
154
158
  * `minBp` of the expression it sits inside. Set immediately before each Tier-2
@@ -1,4 +1,4 @@
1
- import { B as BytecodeBuilder } from './BytecodeBuilder-CRYfrFfq.cjs';
1
+ import { B as BytecodeBuilder } from './BytecodeBuilder-aqVa7Plx.cjs';
2
2
  import { T as Token } from './Token-CbP_OutD.cjs';
3
3
  import { D as DiagnosticPipeline } from './pipeline-B4wf3M1h.cjs';
4
4
 
@@ -149,6 +149,10 @@ declare class PrecedenceParser {
149
149
  private diagnosticPipeline;
150
150
  private currentExpression;
151
151
  private localeCode;
152
+ /** The locale's decimal separator, cached from {@link localeCode}. */
153
+ private readonly decimalSeparator;
154
+ /** The locale's thousands separator, cached from {@link localeCode}. */
155
+ private readonly thousandsSeparator;
152
156
  /**
153
157
  * The binding power the current infix parselet is being invoked at, i.e. the
154
158
  * `minBp` of the expression it sits inside. Set immediately before each Tier-2
@@ -1,6 +1,6 @@
1
1
  import { V as Value } from './Value-DCTqTSeP.cjs';
2
- import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-CRYfrFfq.cjs';
3
- import { E as EngineError } from './EngineError-Cv5q4Rbv.cjs';
2
+ import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.cjs';
3
+ import { E as EngineError } from './EngineError-DTk7I7hZ.cjs';
4
4
  import { D as DiagnosticPipeline } from './pipeline-B4wf3M1h.cjs';
5
5
 
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import { V as Value } from './Value-DCTqTSeP.js';
2
- import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-CRYfrFfq.js';
3
- import { E as EngineError } from './EngineError-Cv5q4Rbv.js';
2
+ import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.js';
3
+ import { E as EngineError } from './EngineError-DTk7I7hZ.js';
4
4
  import { D as DiagnosticPipeline } from './pipeline-QIT4iD8f.js';
5
5
 
6
6
  /**