solve-engine 2.32.0 → 2.34.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 (226) hide show
  1. package/dist/{EngineError-CqGtaT_v.d.cts → EngineError-Bm4OYmw8.d.cts} +6 -0
  2. package/dist/{EngineError-CqGtaT_v.d.ts → EngineError-Bm4OYmw8.d.ts} +6 -0
  3. package/dist/{Lexer-CqFjYoRA.d.ts → Lexer-DPn5dfCH.d.ts} +1 -13
  4. package/dist/{Lexer-sCI05tqO.d.cts → Lexer-DaWkCu84.d.cts} +1 -13
  5. package/dist/{PackageCompatibility-phvSFP4f.d.ts → PackageCompatibility-CkzZfoJ3.d.ts} +1 -1
  6. package/dist/{PackageCompatibility-Bn-T_xKW.d.cts → PackageCompatibility-E_5GtcQW.d.cts} +1 -1
  7. package/dist/{PackageRegistry-C78jVkI9.d.ts → PackageRegistry-DSqic8kz.d.ts} +132 -23
  8. package/dist/{PackageRegistry-B-C1duZw.d.cts → PackageRegistry-js47hr28.d.cts} +132 -23
  9. package/dist/{Parselet-Dgymjxzd.d.ts → Parselet-CQjcHOKw.d.ts} +13 -6
  10. package/dist/{Parselet-DF3864la.d.cts → Parselet-s1sRRn8H.d.cts} +13 -6
  11. package/dist/{TokenNormalizer-Pe3_390t.d.cts → TokenNormalizer-D382VSYh.d.cts} +4 -1
  12. package/dist/{TokenNormalizer-IF7nPRMp.d.ts → TokenNormalizer-DAzPp-j1.d.ts} +4 -1
  13. package/dist/{VMBuiltins-BkKzhj6C.d.cts → VMBuiltins-JCavlQUj.d.cts} +53 -18
  14. package/dist/{VMBuiltins-Du9y8sEo.d.ts → VMBuiltins-qgzA6qGi.d.ts} +53 -18
  15. package/dist/{VMCheckpoints-WdUQrdc9.d.cts → VMCheckpoints-BxPzmdu0.d.cts} +2 -2
  16. package/dist/{VMCheckpoints-gvy_yxYh.d.ts → VMCheckpoints-DjjE92mf.d.ts} +2 -2
  17. package/dist/{Value-BYHw-x7q.d.cts → Value-2TIKi-YL.d.cts} +19 -0
  18. package/dist/{Value-BYHw-x7q.d.ts → Value-2TIKi-YL.d.ts} +19 -0
  19. package/dist/{WorkerError-DTrl3Jag.d.ts → WorkerError-BaN8_eok.d.ts} +1 -1
  20. package/dist/{WorkerError-CRoM8Ju1.d.cts → WorkerError-CexHHmbb.d.cts} +1 -1
  21. package/dist/{chunk-IJE4AEDX.js → chunk-24OW3AAU.js} +3 -3
  22. package/dist/{chunk-IJE4AEDX.js.map → chunk-24OW3AAU.js.map} +1 -1
  23. package/dist/{chunk-CAJTDZ5B.cjs → chunk-2XOLAHW6.cjs} +2 -2
  24. package/dist/{chunk-CAJTDZ5B.cjs.map → chunk-2XOLAHW6.cjs.map} +1 -1
  25. package/dist/{chunk-AGCNMEP2.cjs → chunk-3MG7VIPL.cjs} +2 -2
  26. package/dist/{chunk-AGCNMEP2.cjs.map → chunk-3MG7VIPL.cjs.map} +1 -1
  27. package/dist/chunk-4ASM522I.js +2 -0
  28. package/dist/chunk-4ASM522I.js.map +1 -0
  29. package/dist/{chunk-KH4QXKLT.js → chunk-4MD5L23F.js} +2 -2
  30. package/dist/{chunk-KH4QXKLT.js.map → chunk-4MD5L23F.js.map} +1 -1
  31. package/dist/{chunk-IQVSCL2R.js → chunk-4XWZXP6I.js} +3 -3
  32. package/dist/{chunk-IQVSCL2R.js.map → chunk-4XWZXP6I.js.map} +1 -1
  33. package/dist/{chunk-RELMEWY6.js → chunk-6G2NES2I.js} +2 -2
  34. package/dist/chunk-6G2NES2I.js.map +1 -0
  35. package/dist/{chunk-CDE2ZUGG.js → chunk-6NL7RBAG.js} +2 -2
  36. package/dist/{chunk-CDE2ZUGG.js.map → chunk-6NL7RBAG.js.map} +1 -1
  37. package/dist/{chunk-SUAGBRQ7.cjs → chunk-6SYU4MH7.cjs} +2 -2
  38. package/dist/chunk-6SYU4MH7.cjs.map +1 -0
  39. package/dist/chunk-72J5V7SN.cjs +2 -0
  40. package/dist/chunk-72J5V7SN.cjs.map +1 -0
  41. package/dist/{chunk-TSJ4RQLZ.js → chunk-AENL3Z66.js} +2 -2
  42. package/dist/{chunk-TSJ4RQLZ.js.map → chunk-AENL3Z66.js.map} +1 -1
  43. package/dist/chunk-APYRNAU2.js +2 -0
  44. package/dist/chunk-APYRNAU2.js.map +1 -0
  45. package/dist/{chunk-AXTYQFIV.cjs → chunk-D7URPV5Z.cjs} +3 -3
  46. package/dist/{chunk-AXTYQFIV.cjs.map → chunk-D7URPV5Z.cjs.map} +1 -1
  47. package/dist/chunk-DB5ISONG.js +2 -0
  48. package/dist/chunk-DB5ISONG.js.map +1 -0
  49. package/dist/chunk-DGDVY6XI.js +3 -0
  50. package/dist/chunk-DGDVY6XI.js.map +1 -0
  51. package/dist/chunk-EOKHRS2T.js +2 -0
  52. package/dist/chunk-EOKHRS2T.js.map +1 -0
  53. package/dist/{chunk-NQFCXLEF.js → chunk-EOO6M65F.js} +2 -2
  54. package/dist/{chunk-NQFCXLEF.js.map → chunk-EOO6M65F.js.map} +1 -1
  55. package/dist/{chunk-A764BY7L.cjs → chunk-EQNKDTMX.cjs} +2 -2
  56. package/dist/{chunk-A764BY7L.cjs.map → chunk-EQNKDTMX.cjs.map} +1 -1
  57. package/dist/chunk-FESR6PZI.cjs +2 -0
  58. package/dist/chunk-FESR6PZI.cjs.map +1 -0
  59. package/dist/chunk-G2LCDR52.cjs +3 -0
  60. package/dist/chunk-G2LCDR52.cjs.map +1 -0
  61. package/dist/chunk-G5TZ6TDA.js +2 -0
  62. package/dist/chunk-G5TZ6TDA.js.map +1 -0
  63. package/dist/chunk-GDLYXRMA.cjs +2 -0
  64. package/dist/chunk-GDLYXRMA.cjs.map +1 -0
  65. package/dist/{chunk-7DJYS5PO.cjs → chunk-HSTGWWU2.cjs} +2 -2
  66. package/dist/{chunk-7DJYS5PO.cjs.map → chunk-HSTGWWU2.cjs.map} +1 -1
  67. package/dist/{chunk-OE2DBN4K.js → chunk-INMJ7ZXL.js} +2 -2
  68. package/dist/chunk-INMJ7ZXL.js.map +1 -0
  69. package/dist/chunk-ITH2RLRG.js +5 -0
  70. package/dist/chunk-ITH2RLRG.js.map +1 -0
  71. package/dist/{chunk-5AKT36UK.cjs → chunk-K3PZ66XR.cjs} +2 -2
  72. package/dist/{chunk-5AKT36UK.cjs.map → chunk-K3PZ66XR.cjs.map} +1 -1
  73. package/dist/chunk-LI6LYJDA.cjs +2 -0
  74. package/dist/chunk-LI6LYJDA.cjs.map +1 -0
  75. package/dist/{chunk-WQGHJAW2.cjs → chunk-M4UC2PB6.cjs} +2 -2
  76. package/dist/{chunk-WQGHJAW2.cjs.map → chunk-M4UC2PB6.cjs.map} +1 -1
  77. package/dist/chunk-O7HF345B.js +2 -0
  78. package/dist/chunk-O7HF345B.js.map +1 -0
  79. package/dist/{chunk-CM2WM5CM.cjs → chunk-OBZJKZ2F.cjs} +2 -2
  80. package/dist/{chunk-CM2WM5CM.cjs.map → chunk-OBZJKZ2F.cjs.map} +1 -1
  81. package/dist/{chunk-Q7TBBV62.cjs → chunk-P5YWBIIU.cjs} +2 -2
  82. package/dist/chunk-P5YWBIIU.cjs.map +1 -0
  83. package/dist/chunk-PICFFFJV.js +3 -0
  84. package/dist/chunk-PICFFFJV.js.map +1 -0
  85. package/dist/{chunk-P43SV4NJ.cjs → chunk-QOOTRUY7.cjs} +3 -3
  86. package/dist/{chunk-P43SV4NJ.cjs.map → chunk-QOOTRUY7.cjs.map} +1 -1
  87. package/dist/chunk-R3W53ZMP.cjs +2 -0
  88. package/dist/chunk-R3W53ZMP.cjs.map +1 -0
  89. package/dist/chunk-RMDCGYZM.js +2 -0
  90. package/dist/chunk-RMDCGYZM.js.map +1 -0
  91. package/dist/{chunk-CB3EXUM2.js → chunk-RU3KGJRA.js} +2 -2
  92. package/dist/chunk-RU3KGJRA.js.map +1 -0
  93. package/dist/chunk-S3SPDK6N.cjs +2 -0
  94. package/dist/chunk-S3SPDK6N.cjs.map +1 -0
  95. package/dist/chunk-SF5QVUEI.cjs +3 -0
  96. package/dist/chunk-SF5QVUEI.cjs.map +1 -0
  97. package/dist/{chunk-FXB5GCBT.js → chunk-TJKN466F.js} +2 -2
  98. package/dist/{chunk-FXB5GCBT.js.map → chunk-TJKN466F.js.map} +1 -1
  99. package/dist/{chunk-VLX7ZVK4.js → chunk-U5MESNRT.js} +2 -2
  100. package/dist/{chunk-VLX7ZVK4.js.map → chunk-U5MESNRT.js.map} +1 -1
  101. package/dist/chunk-WCVPXBCW.cjs +5 -0
  102. package/dist/chunk-WCVPXBCW.cjs.map +1 -0
  103. package/dist/{chunk-YWF6C2IO.cjs → chunk-WFT3XUSH.cjs} +2 -2
  104. package/dist/{chunk-YWF6C2IO.cjs.map → chunk-WFT3XUSH.cjs.map} +1 -1
  105. package/dist/{chunk-3ZGFAE3Q.js → chunk-X2GQAKL2.js} +2 -2
  106. package/dist/{chunk-3ZGFAE3Q.js.map → chunk-X2GQAKL2.js.map} +1 -1
  107. package/dist/{chunk-G3POUURF.js → chunk-YID47OPR.js} +2 -2
  108. package/dist/{chunk-G3POUURF.js.map → chunk-YID47OPR.js.map} +1 -1
  109. package/dist/chunk-YV4GDW5A.cjs +2 -0
  110. package/dist/chunk-YV4GDW5A.cjs.map +1 -0
  111. package/dist/chunk-ZSXQP7PD.cjs +2 -0
  112. package/dist/chunk-ZSXQP7PD.cjs.map +1 -0
  113. package/dist/constants.cjs +1 -1
  114. package/dist/constants.js +1 -1
  115. package/dist/engine.cjs +1 -1
  116. package/dist/engine.d.cts +10 -10
  117. package/dist/engine.d.ts +10 -10
  118. package/dist/engine.js +1 -1
  119. package/dist/engine.worker.cjs +1 -1
  120. package/dist/engine.worker.cjs.map +1 -1
  121. package/dist/engine.worker.js +1 -1
  122. package/dist/engine.worker.js.map +1 -1
  123. package/dist/errors.cjs +1 -1
  124. package/dist/errors.d.cts +3 -3
  125. package/dist/errors.d.ts +3 -3
  126. package/dist/errors.js +1 -1
  127. package/dist/format.cjs +1 -1
  128. package/dist/format.d.cts +1 -1
  129. package/dist/format.d.ts +1 -1
  130. package/dist/format.js +1 -1
  131. package/dist/index.cjs +1 -1
  132. package/dist/index.cjs.map +1 -1
  133. package/dist/index.d.cts +10 -10
  134. package/dist/index.d.ts +10 -10
  135. package/dist/index.js +1 -1
  136. package/dist/index.js.map +1 -1
  137. package/dist/language.d.cts +9 -9
  138. package/dist/language.d.ts +9 -9
  139. package/dist/lexer.cjs +1 -1
  140. package/dist/lexer.d.cts +3 -3
  141. package/dist/lexer.d.ts +3 -3
  142. package/dist/lexer.js +1 -1
  143. package/dist/normalizer.cjs +1 -1
  144. package/dist/normalizer.d.cts +2 -2
  145. package/dist/normalizer.d.ts +2 -2
  146. package/dist/normalizer.js +1 -1
  147. package/dist/packages.cjs +1 -1
  148. package/dist/packages.d.cts +8 -8
  149. package/dist/packages.d.ts +8 -8
  150. package/dist/packages.js +1 -1
  151. package/dist/parser.cjs +1 -1
  152. package/dist/parser.d.cts +3 -3
  153. package/dist/parser.d.ts +3 -3
  154. package/dist/parser.js +1 -1
  155. package/dist/{pipeline-BYkKNpql.d.cts → pipeline-Bum6y1B9.d.cts} +1 -1
  156. package/dist/{pipeline-Cl7KW8CB.d.ts → pipeline-DOo3KHaq.d.ts} +1 -1
  157. package/dist/resolvers.cjs +1 -1
  158. package/dist/resolvers.d.cts +69 -3
  159. package/dist/resolvers.d.ts +69 -3
  160. package/dist/resolvers.js +1 -1
  161. package/dist/temporal.cjs +1 -1
  162. package/dist/temporal.js +1 -1
  163. package/dist/testing.cjs +2 -2
  164. package/dist/testing.cjs.map +1 -1
  165. package/dist/testing.d.cts +9 -9
  166. package/dist/testing.d.ts +9 -9
  167. package/dist/testing.js +1 -1
  168. package/dist/testing.js.map +1 -1
  169. package/dist/uom.cjs +1 -1
  170. package/dist/uom.d.cts +35 -2
  171. package/dist/uom.d.ts +35 -2
  172. package/dist/uom.js +1 -1
  173. package/dist/vm.cjs +1 -1
  174. package/dist/vm.d.cts +6 -6
  175. package/dist/vm.d.ts +6 -6
  176. package/dist/vm.js +1 -1
  177. package/dist/worker.cjs +2 -2
  178. package/dist/worker.cjs.map +1 -1
  179. package/dist/worker.d.cts +9 -9
  180. package/dist/worker.d.ts +9 -9
  181. package/dist/worker.js +1 -1
  182. package/dist/worker.js.map +1 -1
  183. package/package.json +1 -1
  184. package/dist/chunk-4DR5RBHQ.cjs +0 -2
  185. package/dist/chunk-4DR5RBHQ.cjs.map +0 -1
  186. package/dist/chunk-4IC5OARK.js +0 -3
  187. package/dist/chunk-4IC5OARK.js.map +0 -1
  188. package/dist/chunk-7ODHPWQC.js +0 -5
  189. package/dist/chunk-7ODHPWQC.js.map +0 -1
  190. package/dist/chunk-A755LVPT.cjs +0 -2
  191. package/dist/chunk-A755LVPT.cjs.map +0 -1
  192. package/dist/chunk-AF6NDOIE.js +0 -2
  193. package/dist/chunk-AF6NDOIE.js.map +0 -1
  194. package/dist/chunk-B2NL65FI.js +0 -2
  195. package/dist/chunk-B2NL65FI.js.map +0 -1
  196. package/dist/chunk-CB3EXUM2.js.map +0 -1
  197. package/dist/chunk-DUFEONVN.cjs +0 -3
  198. package/dist/chunk-DUFEONVN.cjs.map +0 -1
  199. package/dist/chunk-IJHL3LMH.js +0 -2
  200. package/dist/chunk-IJHL3LMH.js.map +0 -1
  201. package/dist/chunk-LCYQQFUH.cjs +0 -2
  202. package/dist/chunk-LCYQQFUH.cjs.map +0 -1
  203. package/dist/chunk-LP3JYC3A.js +0 -2
  204. package/dist/chunk-LP3JYC3A.js.map +0 -1
  205. package/dist/chunk-OE2DBN4K.js.map +0 -1
  206. package/dist/chunk-OKKGPINQ.cjs +0 -5
  207. package/dist/chunk-OKKGPINQ.cjs.map +0 -1
  208. package/dist/chunk-OM7NOHJF.cjs +0 -2
  209. package/dist/chunk-OM7NOHJF.cjs.map +0 -1
  210. package/dist/chunk-PTXUWGRR.js +0 -3
  211. package/dist/chunk-PTXUWGRR.js.map +0 -1
  212. package/dist/chunk-Q7TBBV62.cjs.map +0 -1
  213. package/dist/chunk-RELMEWY6.js.map +0 -1
  214. package/dist/chunk-SFM4XFGJ.js +0 -2
  215. package/dist/chunk-SFM4XFGJ.js.map +0 -1
  216. package/dist/chunk-SRAU3BLV.cjs +0 -3
  217. package/dist/chunk-SRAU3BLV.cjs.map +0 -1
  218. package/dist/chunk-SUAGBRQ7.cjs.map +0 -1
  219. package/dist/chunk-UNKZ5XRC.cjs +0 -2
  220. package/dist/chunk-UNKZ5XRC.cjs.map +0 -1
  221. package/dist/chunk-WU7SBKVU.cjs +0 -2
  222. package/dist/chunk-WU7SBKVU.cjs.map +0 -1
  223. package/dist/chunk-X5K72MBV.js +0 -2
  224. package/dist/chunk-X5K72MBV.js.map +0 -1
  225. package/dist/chunk-ZVPZQ66D.cjs +0 -2
  226. package/dist/chunk-ZVPZQ66D.cjs.map +0 -1
@@ -119,6 +119,8 @@ declare const CoreErrorCodes: {
119
119
  readonly MALFORMED_BYTECODE_BIGINT_LITERAL: "MALFORMED_BYTECODE_BIGINT_LITERAL";
120
120
  /** `executeBytecode()` called with something that is not a runnable program at all. Checked before the destructure that used to throw outside the function's own try/catch. */
121
121
  readonly MALFORMED_BYTECODE_PROGRAM: "MALFORMED_BYTECODE_PROGRAM";
122
+ /** An opcode the dispatch switch has no arm for, refused at the instruction that carries it (the `offset` in its context). The switch used to have no default arm, so an unknown opcode ran as a no-op and the program failed some instructions later with a STACK_UNDERFLOW naming the wrong instruction. Covers the two enum members no arm handles (`PUSH_VARIABLE`, `RETURN`) and the dynamic range an `OpRegistry` once claimed. See `vm/VM.ts`'s default arm. */
123
+ readonly MALFORMED_BYTECODE_UNKNOWN_OPCODE: "MALFORMED_BYTECODE_UNKNOWN_OPCODE";
122
124
  /** A coefficient grew past `RATIONAL_MAX_BITS`, e.g. repeated exact elimination multiplying denominators together. */
123
125
  readonly SYMBOLIC_RATIONAL_OVERFLOW: "SYMBOLIC_RATIONAL_OVERFLOW";
124
126
  /** `NaN` or `±Infinity` reaching a symbolic expression, neither of which has an exact rational value. */
@@ -156,6 +158,8 @@ declare const CoreErrorCodes: {
156
158
  /** A document with more lines than `performance.maxDocumentLines`. The per-line limits above bound what one line may ask for and say nothing about how many lines there are; two hundred thousand of `1 + 1` exhausted the heap on the line records alone. Recoverable. */
157
159
  readonly DOCUMENT_TOO_LARGE: "DOCUMENT_TOO_LARGE";
158
160
  readonly NORMALIZED_TOKEN_LIMIT_EXCEEDED: "NORMALIZED_TOKEN_LIMIT_EXCEEDED";
161
+ /** The normaliser was still changing the token stream after its pass budget (`maxPasses`, 100 by default): a rule chain that never settles. Reported rather than returning whatever the last pass left. Recoverable. */
162
+ readonly NORMALIZER_PASS_LIMIT_EXCEEDED: "NORMALIZER_PASS_LIMIT_EXCEEDED";
159
163
  /** `"=>"` with nothing before it, needs an expression or variable name to solve/simplify. */
160
164
  readonly THEREFORE_REQUIRES_EXPRESSION: "THEREFORE_REQUIRES_EXPRESSION";
161
165
  /** A `"=>"`-triggered expression called an async plugin (weather/stocks/currency). Same v1 scope restriction as user-function/map-reduce bodies. */
@@ -193,6 +197,8 @@ declare const CoreErrorCodes: {
193
197
  /** A `resize` was written without one of its parts: the dimensions, the `to`, the size, or the side it names. Recoverable. */
194
198
  readonly RESIZE_EXPECTED_SHAPE: "RESIZE_EXPECTED_SHAPE";
195
199
  readonly PLUGIN_OPERATOR_COLLISION: "PLUGIN_OPERATOR_COLLISION";
200
+ /** A package registered an operator the scanner cannot read: not exactly two characters, or a first character the scanner does not class as an operator. It used to register and never fire. Recoverable. */
201
+ readonly PLUGIN_OPERATOR_UNSUPPORTED: "PLUGIN_OPERATOR_UNSUPPORTED";
196
202
  readonly PLUGIN_KEYWORD_COLLISION: "PLUGIN_KEYWORD_COLLISION";
197
203
  readonly PLUGIN_UNIT_COLLISION: "PLUGIN_UNIT_COLLISION";
198
204
  /** A package's declared `IEnginePackage.engineVersion` semver range doesn't satisfy the running engine's ENGINE_VERSION. See api/EngineVersionCompatibility.ts. */
@@ -119,6 +119,8 @@ declare const CoreErrorCodes: {
119
119
  readonly MALFORMED_BYTECODE_BIGINT_LITERAL: "MALFORMED_BYTECODE_BIGINT_LITERAL";
120
120
  /** `executeBytecode()` called with something that is not a runnable program at all. Checked before the destructure that used to throw outside the function's own try/catch. */
121
121
  readonly MALFORMED_BYTECODE_PROGRAM: "MALFORMED_BYTECODE_PROGRAM";
122
+ /** An opcode the dispatch switch has no arm for, refused at the instruction that carries it (the `offset` in its context). The switch used to have no default arm, so an unknown opcode ran as a no-op and the program failed some instructions later with a STACK_UNDERFLOW naming the wrong instruction. Covers the two enum members no arm handles (`PUSH_VARIABLE`, `RETURN`) and the dynamic range an `OpRegistry` once claimed. See `vm/VM.ts`'s default arm. */
123
+ readonly MALFORMED_BYTECODE_UNKNOWN_OPCODE: "MALFORMED_BYTECODE_UNKNOWN_OPCODE";
122
124
  /** A coefficient grew past `RATIONAL_MAX_BITS`, e.g. repeated exact elimination multiplying denominators together. */
123
125
  readonly SYMBOLIC_RATIONAL_OVERFLOW: "SYMBOLIC_RATIONAL_OVERFLOW";
124
126
  /** `NaN` or `±Infinity` reaching a symbolic expression, neither of which has an exact rational value. */
@@ -156,6 +158,8 @@ declare const CoreErrorCodes: {
156
158
  /** A document with more lines than `performance.maxDocumentLines`. The per-line limits above bound what one line may ask for and say nothing about how many lines there are; two hundred thousand of `1 + 1` exhausted the heap on the line records alone. Recoverable. */
157
159
  readonly DOCUMENT_TOO_LARGE: "DOCUMENT_TOO_LARGE";
158
160
  readonly NORMALIZED_TOKEN_LIMIT_EXCEEDED: "NORMALIZED_TOKEN_LIMIT_EXCEEDED";
161
+ /** The normaliser was still changing the token stream after its pass budget (`maxPasses`, 100 by default): a rule chain that never settles. Reported rather than returning whatever the last pass left. Recoverable. */
162
+ readonly NORMALIZER_PASS_LIMIT_EXCEEDED: "NORMALIZER_PASS_LIMIT_EXCEEDED";
159
163
  /** `"=>"` with nothing before it, needs an expression or variable name to solve/simplify. */
160
164
  readonly THEREFORE_REQUIRES_EXPRESSION: "THEREFORE_REQUIRES_EXPRESSION";
161
165
  /** A `"=>"`-triggered expression called an async plugin (weather/stocks/currency). Same v1 scope restriction as user-function/map-reduce bodies. */
@@ -193,6 +197,8 @@ declare const CoreErrorCodes: {
193
197
  /** A `resize` was written without one of its parts: the dimensions, the `to`, the size, or the side it names. Recoverable. */
194
198
  readonly RESIZE_EXPECTED_SHAPE: "RESIZE_EXPECTED_SHAPE";
195
199
  readonly PLUGIN_OPERATOR_COLLISION: "PLUGIN_OPERATOR_COLLISION";
200
+ /** A package registered an operator the scanner cannot read: not exactly two characters, or a first character the scanner does not class as an operator. It used to register and never fire. Recoverable. */
201
+ readonly PLUGIN_OPERATOR_UNSUPPORTED: "PLUGIN_OPERATOR_UNSUPPORTED";
196
202
  readonly PLUGIN_KEYWORD_COLLISION: "PLUGIN_KEYWORD_COLLISION";
197
203
  readonly PLUGIN_UNIT_COLLISION: "PLUGIN_UNIT_COLLISION";
198
204
  /** A package's declared `IEnginePackage.engineVersion` semver range doesn't satisfy the running engine's ENGINE_VERSION. See api/EngineVersionCompatibility.ts. */
@@ -1,5 +1,5 @@
1
1
  import { T as Token } from './Token-D8f7yaz1.js';
2
- import { E as EngineError } from './EngineError-CqGtaT_v.js';
2
+ import { E as EngineError } from './EngineError-Bm4OYmw8.js';
3
3
 
4
4
  /** Trie node for multi-word phrase matching. */
5
5
  interface PhraseNode {
@@ -451,18 +451,6 @@ declare class ExpressionLexer {
451
451
  * which is the other half of why they are stripped here rather than later.
452
452
  */
453
453
  private tokenizeString;
454
- /**
455
- * L1 expression gating: quickly determine if a line contains any
456
- * characters that indicate an expression (digits, operators, currency,
457
- * backticks, parentheses, etc.).
458
- *
459
- * Pure prose lines (e.g., "The quick brown fox jumps over the lazy dog")
460
- * return false and can be skipped without full tokenization (L2).
461
- *
462
- * This is a fast character-by-character scan that stops at the first
463
- * expression indicator. Called once per line in classifyFromPositions().
464
- */
465
- static hasExpressionIndicators(input: string, start: number, end: number): boolean;
466
454
  /**
467
455
  * Classify a line by its character positions within this.input.
468
456
  * Reads directly from this.input using start/end boundaries.
@@ -1,5 +1,5 @@
1
1
  import { T as Token } from './Token-D8f7yaz1.cjs';
2
- import { E as EngineError } from './EngineError-CqGtaT_v.cjs';
2
+ import { E as EngineError } from './EngineError-Bm4OYmw8.cjs';
3
3
 
4
4
  /** Trie node for multi-word phrase matching. */
5
5
  interface PhraseNode {
@@ -451,18 +451,6 @@ declare class ExpressionLexer {
451
451
  * which is the other half of why they are stripped here rather than later.
452
452
  */
453
453
  private tokenizeString;
454
- /**
455
- * L1 expression gating: quickly determine if a line contains any
456
- * characters that indicate an expression (digits, operators, currency,
457
- * backticks, parentheses, etc.).
458
- *
459
- * Pure prose lines (e.g., "The quick brown fox jumps over the lazy dog")
460
- * return false and can be skipped without full tokenization (L2).
461
- *
462
- * This is a fast character-by-character scan that stops at the first
463
- * expression indicator. Called once per line in classifyFromPositions().
464
- */
465
- static hasExpressionIndicators(input: string, start: number, end: number): boolean;
466
454
  /**
467
455
  * Classify a line by its character positions within this.input.
468
456
  * Reads directly from this.input using start/end boundaries.
@@ -1,4 +1,4 @@
1
- import { I as IEnginePackage } from './PackageRegistry-C78jVkI9.js';
1
+ import { I as IEnginePackage } from './PackageRegistry-DSqic8kz.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-B-C1duZw.cjs';
1
+ import { I as IEnginePackage } from './PackageRegistry-js47hr28.cjs';
2
2
 
3
3
  /**
4
4
  * Package load-time compatibility checking, the "detect overlapping
@@ -1,14 +1,14 @@
1
- import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-Dgymjxzd.js';
2
- import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-CqFjYoRA.js';
1
+ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-CQjcHOKw.js';
2
+ import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-DPn5dfCH.js';
3
3
  import { IAsyncResolver } from './resolvers.js';
4
- import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-IF7nPRMp.js';
5
- import { D as DependencyGraph, V as VM, f as DagSnapshot, g as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-Du9y8sEo.js';
6
- import { V as Value, b as ValueType, D as DatetimeGrain } from './Value-BYHw-x7q.js';
4
+ import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-DAzPp-j1.js';
5
+ import { D as DependencyGraph, V as VM, f as DagSnapshot, g as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-qgzA6qGi.js';
6
+ import { V as Value, b as ValueType, D as DatetimeGrain } from './Value-2TIKi-YL.js';
7
7
  import { a as BytecodeProgram } from './BytecodeBuilder-DSWKZi4f.js';
8
8
  import { C as CalendarBackend } from './CalendarBackend-MdS3Bb4P.js';
9
9
  import { QueryClient } from '@tanstack/query-core';
10
- import { E as EngineError } from './EngineError-CqGtaT_v.js';
11
- import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-Cl7KW8CB.js';
10
+ import { E as EngineError } from './EngineError-Bm4OYmw8.js';
11
+ import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-DOo3KHaq.js';
12
12
  import { T as Token } from './Token-D8f7yaz1.js';
13
13
  import { D as DateAmbiguity, E as EngineConfigOverride, a as EngineConfig } from './Configuration-BxriK9Kw.js';
14
14
 
@@ -714,7 +714,15 @@ interface BatchEntry {
714
714
  * `highWaterMark` of 64 events to limit the internal buffer size.
715
715
  */
716
716
  declare class AsyncResolutionBatcher {
717
+ /**
718
+ * Resolutions waiting for the next flush, keyed by `packageId:queryKey`
719
+ * so a repeat within the tick is refused in constant time. This used to
720
+ * be an array scanned on every add, which made a tick that resolves
721
+ * thousands of keys quadratic.
722
+ */
717
723
  private pending;
724
+ /** How many adds this tick were refused as repeats of a key already pending. Reset on flush. */
725
+ private collapsed;
718
726
  private scheduled;
719
727
  /** Set to true by clearPending(), flush() checks this to abort stale work. */
720
728
  private cleared;
@@ -822,7 +830,11 @@ declare class AsyncResolutionBatcher {
822
830
  getEventStream(): ReadableStream<AsyncResolutionEvent>;
823
831
  /** Number of resolutions currently queued for the next flush. */
824
832
  get pendingCount(): number;
825
- /** Number of pending entries collapsed by (packageId, queryKey) deduplication. */
833
+ /**
834
+ * Number of resolutions this tick collapsed into one already pending for
835
+ * the same (packageId, queryKey). Counted as they are refused: the batch
836
+ * itself never holds a repeat, so recounting it here always gave zero.
837
+ */
826
838
  get dedupCount(): number;
827
839
  /**
828
840
  * Whether the internal event stream currently has an active reader.
@@ -1539,7 +1551,12 @@ interface AsyncPreflightOutput {
1539
1551
  pendingQueryKey?: string;
1540
1552
  /** Number of registered async resolvers */
1541
1553
  resolverCount: number;
1542
- /** Whether the preflight guard was skipped (no async opcodes, no resolvers) */
1554
+ /**
1555
+ * Whether the preflight was skipped because no registered resolver could
1556
+ * intercept this program: it calls no plugin function and contains none of
1557
+ * the opcodes a resolver declared it watches (see
1558
+ * `IAsyncResolver.watchedOpcodes`).
1559
+ */
1543
1560
  skippedGuard: boolean;
1544
1561
  }
1545
1562
  /** Stage 12: Stack VM execution of the compiled bytecode program. */
@@ -1943,6 +1960,22 @@ declare class ExpressionEngine {
1943
1960
  */
1944
1961
  private batchScanResults;
1945
1962
  private batchParsedLines;
1963
+ /**
1964
+ * The context handed to every line of the current pass, its line number
1965
+ * set by mutation before each execution. The closures inside read only
1966
+ * the document model, or the batch pass's scan and results array, and
1967
+ * those are fixed for the length of a pass, so one object serves the
1968
+ * pass: {@link makeLineContext} rebuilds it the moment any of the three
1969
+ * changes, and a batch pass drops it as it ends so a finished pass's scan
1970
+ * is not retained. The goal-seek re-entry builds its own instead (see
1971
+ * {@link evaluateLineWithBinding}): it runs inside another line's
1972
+ * evaluation, and moving the shared line number under that line would
1973
+ * misdirect every cross-line read it makes afterwards.
1974
+ */
1975
+ private lineContext;
1976
+ private lineContextDoc;
1977
+ private lineContextScan;
1978
+ private lineContextParsed;
1946
1979
  /**
1947
1980
  * Called once by `ThreeTierEvaluator`'s constructor. Not part of the
1948
1981
  * public evaluate-a-document contract, purely internal wiring so
@@ -1992,14 +2025,27 @@ declare class ExpressionEngine {
1992
2025
  */
1993
2026
  warmUp(): void;
1994
2027
  /**
1995
- * Build the {@link LineExecutionContext} passed to `executeBytecode()`
1996
- * for a given line. `lineNumber = -1` (the existing sentinel
2028
+ * The {@link LineExecutionContext} passed to `executeBytecode()` for a
2029
+ * given line. `lineNumber = -1` (the existing sentinel
1997
2030
  * `evaluateExpression()`/`evaluateLine(-1, ...)` already use for "no
1998
2031
  * real document") naturally produces a context with both closures
1999
2032
  * `undefined`, a cross-line plugin function must check for that itself
2000
2033
  * and return a clear error, never assume line 0 exists.
2034
+ *
2035
+ * One object serves a whole pass, its line number set by mutation here:
2036
+ * the closures read only the document model, or the batch pass's scan
2037
+ * and results, and those are fixed for the length of a pass, so the
2038
+ * context is rebuilt only when one of them changes. See
2039
+ * {@link lineContext} for the re-entrant case that must not share it.
2001
2040
  */
2002
2041
  private makeLineContext;
2042
+ /**
2043
+ * Allocate a context over the given cross-line sources. Every ordinary
2044
+ * line goes through {@link makeLineContext}, which keeps one of these per
2045
+ * pass; this is the allocation itself, called directly where a fresh
2046
+ * object is required.
2047
+ */
2048
+ private buildLineContext;
2003
2049
  /**
2004
2050
  * Re-evaluate line `targetLine`'s compiled expression with `variable` bound
2005
2051
  * to `bound` for that one run, the primitive goal seek drives while
@@ -2035,9 +2081,9 @@ declare class ExpressionEngine {
2035
2081
  * is aborted, causing all in-flight async work (fetches, preflight checks,
2036
2082
  * batcher flushes) to be canceled atomically.
2037
2083
  *
2038
- * executeAndStore() and executeRaw() link their local AbortControllers to
2039
- * this signal so that when the keystroke changes, all per-evaluation controllers
2040
- * are aborted together.
2084
+ * {@link armCancellation} links each per-evaluation AbortController to
2085
+ * this signal, for a line that can have work in flight, so that when the
2086
+ * keystroke changes, all of them are aborted together.
2041
2087
  */
2042
2088
  private keystrokeSignal;
2043
2089
  /**
@@ -2056,6 +2102,13 @@ declare class ExpressionEngine {
2056
2102
  * its results as current; the manager's own timers are what stop the work.
2057
2103
  */
2058
2104
  private readonly backgroundRefreshSignal;
2105
+ /**
2106
+ * The signal a line no resolver could intercept executes under. Never
2107
+ * aborted: such a line has no in-flight work for a keystroke to cancel,
2108
+ * so it shares this one signal rather than allocating a controller and
2109
+ * linking a listener nothing could fire. See {@link armCancellation}.
2110
+ */
2111
+ private readonly inertSignal;
2059
2112
  /** Post-lexer token normalizer for phrase fusion, implicit multiply, etc. */
2060
2113
  private normalizer;
2061
2114
  /**
@@ -2109,7 +2162,8 @@ declare class ExpressionEngine {
2109
2162
  /** Drop every cached program, its front half and every remembered parse failure. Every invalidation goes through here so the three cannot drift. */
2110
2163
  private clearCompiledCache;
2111
2164
  /**
2112
- * Insert into the bytecode cache, evicting the oldest entry when full.
2165
+ * Insert into the bytecode cache, evicting the least recently used entry
2166
+ * when full.
2113
2167
  *
2114
2168
  * Bug fix (found during release hardening): this used to check against
2115
2169
  * a hardcoded `BYTECODE_CACHE_MAX_ENTRIES = 2000` constant that never
@@ -2120,8 +2174,40 @@ declare class ExpressionEngine {
2120
2174
  * the bytecode-cache benefit on re-evaluation regardless of config.
2121
2175
  */
2122
2176
  private cacheBytecode;
2123
- /** Remember a parse failure, evicting the oldest when full: bounded like the program cache. */
2177
+ /**
2178
+ * Mark a cached program as just used, so eviction passes over it.
2179
+ *
2180
+ * Re-inserting the key moves it to the end of the Map's insertion order,
2181
+ * which makes the first key the least recently used, and that is where
2182
+ * {@link cacheBytecode} evicts from. Insertion order alone evicted the
2183
+ * earliest compiled line however hot it was: a document re-evaluated on
2184
+ * every keystroke touches all of its lines, and each one-off expression
2185
+ * (an inline solve, a classification probe of a prose line) pushed one
2186
+ * of them out once the cap was reached. About 45 ns per hit, measured.
2187
+ * The front half is not moved: it is keyed identically, evicted with the
2188
+ * same victim, and its own order is never consulted.
2189
+ *
2190
+ * Only once the cache is full, which is the whole point: recency decides
2191
+ * who gets evicted, and nothing is evicted below the cap, so under it the
2192
+ * delete and re-insert buy nothing and cost on the hottest path there is.
2193
+ * The 45 ns measured for one hit is not what it costs in a document, where
2194
+ * every keystroke touches every line: a variable chain measured 1.17 ms
2195
+ * before this guard and 2.10 ms after the LRU went in, in the same CI run
2196
+ * as its own merge base. A Map holds a deleted key's slot until it
2197
+ * compacts, so a delete-and-add on every hit pays for that repeatedly
2198
+ * rather than once.
2199
+ *
2200
+ * The trade this leaves: the first eviction after the cache fills reads
2201
+ * insertion order rather than true recency, because nothing before the cap
2202
+ * was ordered. Every hit after that re-inserts, so the order is accurate
2203
+ * from the second eviction on, and the first victim is at worst the same
2204
+ * one insertion order would have chosen anyway.
2205
+ */
2206
+ private touchCompiled;
2207
+ /** Remember a parse failure, evicting the least recently used when full: bounded like the program cache. */
2124
2208
  private rememberFailedParse;
2209
+ /** Mark a remembered parse failure as just used; the same move, and the same guard, as {@link touchCompiled}. */
2210
+ private touchFailedParse;
2125
2211
  private pluginFunctionIndexByName;
2126
2212
  private builderPool;
2127
2213
  private builderPoolIndex;
@@ -2269,6 +2355,29 @@ declare class ExpressionEngine {
2269
2355
  * Extracted common pattern from 8 call sites.
2270
2356
  */
2271
2357
  private storeLineResult;
2358
+ /**
2359
+ * Whether `program` can have work in flight: it calls a plugin function
2360
+ * (the VM hands its signal to the pending result), or a registered
2361
+ * resolver watches an opcode it contains. Everything else is a plain
2362
+ * line, which skips the async preflight and the cancellation state.
2363
+ */
2364
+ private mayResolve;
2365
+ /**
2366
+ * Point the VM at the signal `program` executes under.
2367
+ *
2368
+ * A line that can have work in flight gets a fresh controller linked to
2369
+ * the keystroke signal (One AbortController Per Keystroke): when the user
2370
+ * types again the keystroke aborts, which aborts this controller, which
2371
+ * cancels the fetch. A plain line gets the shared {@link inertSignal} and
2372
+ * links nothing, since there is nothing a keystroke could cancel; on a
2373
+ * large document the controller and the two listener changes per line
2374
+ * were most of what a cached line cost.
2375
+ *
2376
+ * @returns The unlink step, to run once the evaluation settles
2377
+ * synchronously (a pending result keeps its link so a later keystroke
2378
+ * still cancels it), or null when nothing was linked.
2379
+ */
2380
+ private armCancellation;
2272
2381
  /**
2273
2382
  * Execute bytecode and handle the result.
2274
2383
  *
@@ -2665,13 +2774,13 @@ declare class ExpressionEngine {
2665
2774
  /**
2666
2775
  * Shared async-resolver preflight check, run before VM execution.
2667
2776
  *
2668
- * O(1) guard: skips the O(n) resolver scan when the bytecode has no
2669
- * async opcodes AND no resolvers are registered. Either condition alone
2670
- * is enough to warrant a preflight scan:
2671
- * - program.hasAsync: bytecode contains CALL_PLUGIN (async VM path)
2672
- * - resolverRegistry.size > 0: resolvers may intercept any expression
2673
- * For purely sync expressions (e.g., `2 + 2`) with no resolvers, this is
2674
- * an O(1) fast-path that bypasses the resolver scan entirely.
2777
+ * Skipped outright for a line no registered resolver could intercept
2778
+ * (see {@link mayResolve}): no resolver scan, no controller, no keystroke
2779
+ * listener. With every built-in package loaded that used to be no line at
2780
+ * all, since the old gate asked only whether any resolver was registered,
2781
+ * so `2 + 2` paid for seven scans and two listener changes on every
2782
+ * keystroke. A plugin call (`program.hasAsync`) always takes the full
2783
+ * pass, which the VM's pending path relies on.
2675
2784
  *
2676
2785
  * If any registered resolver says "data not ready", registers a DAG
2677
2786
  * data-source dependency, stores a Pending result, and fires async
@@ -1,14 +1,14 @@
1
- import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-DF3864la.cjs';
2
- import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-sCI05tqO.cjs';
1
+ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-s1sRRn8H.cjs';
2
+ import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-DaWkCu84.cjs';
3
3
  import { IAsyncResolver } from './resolvers.cjs';
4
- import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-Pe3_390t.cjs';
5
- import { D as DependencyGraph, V as VM, f as DagSnapshot, g as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-BkKzhj6C.cjs';
6
- import { V as Value, b as ValueType, D as DatetimeGrain } from './Value-BYHw-x7q.cjs';
4
+ import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-D382VSYh.cjs';
5
+ import { D as DependencyGraph, V as VM, f as DagSnapshot, g as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-JCavlQUj.cjs';
6
+ import { V as Value, b as ValueType, D as DatetimeGrain } from './Value-2TIKi-YL.cjs';
7
7
  import { a as BytecodeProgram } from './BytecodeBuilder-DSWKZi4f.cjs';
8
8
  import { C as CalendarBackend } from './CalendarBackend-MdS3Bb4P.cjs';
9
9
  import { QueryClient } from '@tanstack/query-core';
10
- import { E as EngineError } from './EngineError-CqGtaT_v.cjs';
11
- import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-BYkKNpql.cjs';
10
+ import { E as EngineError } from './EngineError-Bm4OYmw8.cjs';
11
+ import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-Bum6y1B9.cjs';
12
12
  import { T as Token } from './Token-D8f7yaz1.cjs';
13
13
  import { D as DateAmbiguity, E as EngineConfigOverride, a as EngineConfig } from './Configuration-BxriK9Kw.cjs';
14
14
 
@@ -714,7 +714,15 @@ interface BatchEntry {
714
714
  * `highWaterMark` of 64 events to limit the internal buffer size.
715
715
  */
716
716
  declare class AsyncResolutionBatcher {
717
+ /**
718
+ * Resolutions waiting for the next flush, keyed by `packageId:queryKey`
719
+ * so a repeat within the tick is refused in constant time. This used to
720
+ * be an array scanned on every add, which made a tick that resolves
721
+ * thousands of keys quadratic.
722
+ */
717
723
  private pending;
724
+ /** How many adds this tick were refused as repeats of a key already pending. Reset on flush. */
725
+ private collapsed;
718
726
  private scheduled;
719
727
  /** Set to true by clearPending(), flush() checks this to abort stale work. */
720
728
  private cleared;
@@ -822,7 +830,11 @@ declare class AsyncResolutionBatcher {
822
830
  getEventStream(): ReadableStream<AsyncResolutionEvent>;
823
831
  /** Number of resolutions currently queued for the next flush. */
824
832
  get pendingCount(): number;
825
- /** Number of pending entries collapsed by (packageId, queryKey) deduplication. */
833
+ /**
834
+ * Number of resolutions this tick collapsed into one already pending for
835
+ * the same (packageId, queryKey). Counted as they are refused: the batch
836
+ * itself never holds a repeat, so recounting it here always gave zero.
837
+ */
826
838
  get dedupCount(): number;
827
839
  /**
828
840
  * Whether the internal event stream currently has an active reader.
@@ -1539,7 +1551,12 @@ interface AsyncPreflightOutput {
1539
1551
  pendingQueryKey?: string;
1540
1552
  /** Number of registered async resolvers */
1541
1553
  resolverCount: number;
1542
- /** Whether the preflight guard was skipped (no async opcodes, no resolvers) */
1554
+ /**
1555
+ * Whether the preflight was skipped because no registered resolver could
1556
+ * intercept this program: it calls no plugin function and contains none of
1557
+ * the opcodes a resolver declared it watches (see
1558
+ * `IAsyncResolver.watchedOpcodes`).
1559
+ */
1543
1560
  skippedGuard: boolean;
1544
1561
  }
1545
1562
  /** Stage 12: Stack VM execution of the compiled bytecode program. */
@@ -1943,6 +1960,22 @@ declare class ExpressionEngine {
1943
1960
  */
1944
1961
  private batchScanResults;
1945
1962
  private batchParsedLines;
1963
+ /**
1964
+ * The context handed to every line of the current pass, its line number
1965
+ * set by mutation before each execution. The closures inside read only
1966
+ * the document model, or the batch pass's scan and results array, and
1967
+ * those are fixed for the length of a pass, so one object serves the
1968
+ * pass: {@link makeLineContext} rebuilds it the moment any of the three
1969
+ * changes, and a batch pass drops it as it ends so a finished pass's scan
1970
+ * is not retained. The goal-seek re-entry builds its own instead (see
1971
+ * {@link evaluateLineWithBinding}): it runs inside another line's
1972
+ * evaluation, and moving the shared line number under that line would
1973
+ * misdirect every cross-line read it makes afterwards.
1974
+ */
1975
+ private lineContext;
1976
+ private lineContextDoc;
1977
+ private lineContextScan;
1978
+ private lineContextParsed;
1946
1979
  /**
1947
1980
  * Called once by `ThreeTierEvaluator`'s constructor. Not part of the
1948
1981
  * public evaluate-a-document contract, purely internal wiring so
@@ -1992,14 +2025,27 @@ declare class ExpressionEngine {
1992
2025
  */
1993
2026
  warmUp(): void;
1994
2027
  /**
1995
- * Build the {@link LineExecutionContext} passed to `executeBytecode()`
1996
- * for a given line. `lineNumber = -1` (the existing sentinel
2028
+ * The {@link LineExecutionContext} passed to `executeBytecode()` for a
2029
+ * given line. `lineNumber = -1` (the existing sentinel
1997
2030
  * `evaluateExpression()`/`evaluateLine(-1, ...)` already use for "no
1998
2031
  * real document") naturally produces a context with both closures
1999
2032
  * `undefined`, a cross-line plugin function must check for that itself
2000
2033
  * and return a clear error, never assume line 0 exists.
2034
+ *
2035
+ * One object serves a whole pass, its line number set by mutation here:
2036
+ * the closures read only the document model, or the batch pass's scan
2037
+ * and results, and those are fixed for the length of a pass, so the
2038
+ * context is rebuilt only when one of them changes. See
2039
+ * {@link lineContext} for the re-entrant case that must not share it.
2001
2040
  */
2002
2041
  private makeLineContext;
2042
+ /**
2043
+ * Allocate a context over the given cross-line sources. Every ordinary
2044
+ * line goes through {@link makeLineContext}, which keeps one of these per
2045
+ * pass; this is the allocation itself, called directly where a fresh
2046
+ * object is required.
2047
+ */
2048
+ private buildLineContext;
2003
2049
  /**
2004
2050
  * Re-evaluate line `targetLine`'s compiled expression with `variable` bound
2005
2051
  * to `bound` for that one run, the primitive goal seek drives while
@@ -2035,9 +2081,9 @@ declare class ExpressionEngine {
2035
2081
  * is aborted, causing all in-flight async work (fetches, preflight checks,
2036
2082
  * batcher flushes) to be canceled atomically.
2037
2083
  *
2038
- * executeAndStore() and executeRaw() link their local AbortControllers to
2039
- * this signal so that when the keystroke changes, all per-evaluation controllers
2040
- * are aborted together.
2084
+ * {@link armCancellation} links each per-evaluation AbortController to
2085
+ * this signal, for a line that can have work in flight, so that when the
2086
+ * keystroke changes, all of them are aborted together.
2041
2087
  */
2042
2088
  private keystrokeSignal;
2043
2089
  /**
@@ -2056,6 +2102,13 @@ declare class ExpressionEngine {
2056
2102
  * its results as current; the manager's own timers are what stop the work.
2057
2103
  */
2058
2104
  private readonly backgroundRefreshSignal;
2105
+ /**
2106
+ * The signal a line no resolver could intercept executes under. Never
2107
+ * aborted: such a line has no in-flight work for a keystroke to cancel,
2108
+ * so it shares this one signal rather than allocating a controller and
2109
+ * linking a listener nothing could fire. See {@link armCancellation}.
2110
+ */
2111
+ private readonly inertSignal;
2059
2112
  /** Post-lexer token normalizer for phrase fusion, implicit multiply, etc. */
2060
2113
  private normalizer;
2061
2114
  /**
@@ -2109,7 +2162,8 @@ declare class ExpressionEngine {
2109
2162
  /** Drop every cached program, its front half and every remembered parse failure. Every invalidation goes through here so the three cannot drift. */
2110
2163
  private clearCompiledCache;
2111
2164
  /**
2112
- * Insert into the bytecode cache, evicting the oldest entry when full.
2165
+ * Insert into the bytecode cache, evicting the least recently used entry
2166
+ * when full.
2113
2167
  *
2114
2168
  * Bug fix (found during release hardening): this used to check against
2115
2169
  * a hardcoded `BYTECODE_CACHE_MAX_ENTRIES = 2000` constant that never
@@ -2120,8 +2174,40 @@ declare class ExpressionEngine {
2120
2174
  * the bytecode-cache benefit on re-evaluation regardless of config.
2121
2175
  */
2122
2176
  private cacheBytecode;
2123
- /** Remember a parse failure, evicting the oldest when full: bounded like the program cache. */
2177
+ /**
2178
+ * Mark a cached program as just used, so eviction passes over it.
2179
+ *
2180
+ * Re-inserting the key moves it to the end of the Map's insertion order,
2181
+ * which makes the first key the least recently used, and that is where
2182
+ * {@link cacheBytecode} evicts from. Insertion order alone evicted the
2183
+ * earliest compiled line however hot it was: a document re-evaluated on
2184
+ * every keystroke touches all of its lines, and each one-off expression
2185
+ * (an inline solve, a classification probe of a prose line) pushed one
2186
+ * of them out once the cap was reached. About 45 ns per hit, measured.
2187
+ * The front half is not moved: it is keyed identically, evicted with the
2188
+ * same victim, and its own order is never consulted.
2189
+ *
2190
+ * Only once the cache is full, which is the whole point: recency decides
2191
+ * who gets evicted, and nothing is evicted below the cap, so under it the
2192
+ * delete and re-insert buy nothing and cost on the hottest path there is.
2193
+ * The 45 ns measured for one hit is not what it costs in a document, where
2194
+ * every keystroke touches every line: a variable chain measured 1.17 ms
2195
+ * before this guard and 2.10 ms after the LRU went in, in the same CI run
2196
+ * as its own merge base. A Map holds a deleted key's slot until it
2197
+ * compacts, so a delete-and-add on every hit pays for that repeatedly
2198
+ * rather than once.
2199
+ *
2200
+ * The trade this leaves: the first eviction after the cache fills reads
2201
+ * insertion order rather than true recency, because nothing before the cap
2202
+ * was ordered. Every hit after that re-inserts, so the order is accurate
2203
+ * from the second eviction on, and the first victim is at worst the same
2204
+ * one insertion order would have chosen anyway.
2205
+ */
2206
+ private touchCompiled;
2207
+ /** Remember a parse failure, evicting the least recently used when full: bounded like the program cache. */
2124
2208
  private rememberFailedParse;
2209
+ /** Mark a remembered parse failure as just used; the same move, and the same guard, as {@link touchCompiled}. */
2210
+ private touchFailedParse;
2125
2211
  private pluginFunctionIndexByName;
2126
2212
  private builderPool;
2127
2213
  private builderPoolIndex;
@@ -2269,6 +2355,29 @@ declare class ExpressionEngine {
2269
2355
  * Extracted common pattern from 8 call sites.
2270
2356
  */
2271
2357
  private storeLineResult;
2358
+ /**
2359
+ * Whether `program` can have work in flight: it calls a plugin function
2360
+ * (the VM hands its signal to the pending result), or a registered
2361
+ * resolver watches an opcode it contains. Everything else is a plain
2362
+ * line, which skips the async preflight and the cancellation state.
2363
+ */
2364
+ private mayResolve;
2365
+ /**
2366
+ * Point the VM at the signal `program` executes under.
2367
+ *
2368
+ * A line that can have work in flight gets a fresh controller linked to
2369
+ * the keystroke signal (One AbortController Per Keystroke): when the user
2370
+ * types again the keystroke aborts, which aborts this controller, which
2371
+ * cancels the fetch. A plain line gets the shared {@link inertSignal} and
2372
+ * links nothing, since there is nothing a keystroke could cancel; on a
2373
+ * large document the controller and the two listener changes per line
2374
+ * were most of what a cached line cost.
2375
+ *
2376
+ * @returns The unlink step, to run once the evaluation settles
2377
+ * synchronously (a pending result keeps its link so a later keystroke
2378
+ * still cancels it), or null when nothing was linked.
2379
+ */
2380
+ private armCancellation;
2272
2381
  /**
2273
2382
  * Execute bytecode and handle the result.
2274
2383
  *
@@ -2665,13 +2774,13 @@ declare class ExpressionEngine {
2665
2774
  /**
2666
2775
  * Shared async-resolver preflight check, run before VM execution.
2667
2776
  *
2668
- * O(1) guard: skips the O(n) resolver scan when the bytecode has no
2669
- * async opcodes AND no resolvers are registered. Either condition alone
2670
- * is enough to warrant a preflight scan:
2671
- * - program.hasAsync: bytecode contains CALL_PLUGIN (async VM path)
2672
- * - resolverRegistry.size > 0: resolvers may intercept any expression
2673
- * For purely sync expressions (e.g., `2 + 2`) with no resolvers, this is
2674
- * an O(1) fast-path that bypasses the resolver scan entirely.
2777
+ * Skipped outright for a line no registered resolver could intercept
2778
+ * (see {@link mayResolve}): no resolver scan, no controller, no keystroke
2779
+ * listener. With every built-in package loaded that used to be no line at
2780
+ * all, since the old gate asked only whether any resolver was registered,
2781
+ * so `2 + 2` paid for seven scans and two listener changes on every
2782
+ * keystroke. A plugin call (`program.hasAsync`) always takes the full
2783
+ * pass, which the VM's pending path relies on.
2675
2784
  *
2676
2785
  * If any registered resolver says "data not ready", registers a DAG
2677
2786
  * data-source dependency, stores a Pending result, and fires async