solve-engine 1.0.2 → 1.1.1

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 (237) hide show
  1. package/dist/{BytecodeBuilder-B0xskcv5.d.cts → BytecodeBuilder-Bp9xeTmX.d.cts} +4 -0
  2. package/dist/{BytecodeBuilder-B0xskcv5.d.ts → BytecodeBuilder-Bp9xeTmX.d.ts} +4 -0
  3. package/dist/{Configuration-B-G5gTRn.d.cts → Configuration-C9W8tJv_.d.cts} +53 -1
  4. package/dist/{Configuration-B-G5gTRn.d.ts → Configuration-C9W8tJv_.d.ts} +53 -1
  5. package/dist/{EngineError-LU7W7AgI.d.cts → EngineError-B61GS1jp.d.cts} +14 -0
  6. package/dist/{EngineError-LU7W7AgI.d.ts → EngineError-B61GS1jp.d.ts} +14 -0
  7. package/dist/FormattingSettings-CJHyxcYu.d.cts +27 -0
  8. package/dist/FormattingSettings-CJHyxcYu.d.ts +27 -0
  9. package/dist/{Lexer-DpjdaQ98.d.cts → Lexer-BOs7euZe.d.cts} +28 -1
  10. package/dist/{Lexer-CNmWxabg.d.ts → Lexer-CSI_lwbW.d.ts} +28 -1
  11. package/dist/PackageCompatibility-B-7rK1TD.d.cts +76 -0
  12. package/dist/PackageCompatibility-Dh59eF-X.d.ts +76 -0
  13. package/dist/{PackageRegistry-zuzqt51V.d.cts → PackageRegistry-BHWJP83F.d.cts} +449 -11
  14. package/dist/{PackageRegistry-ClIFXxAe.d.ts → PackageRegistry-_8rDlvxI.d.ts} +449 -11
  15. package/dist/{Parselet-BaySkMV3.d.ts → Parselet-BBT8riYh.d.ts} +12 -3
  16. package/dist/{Parselet-DuI1Pjiq.d.cts → Parselet-BHcgK9S7.d.cts} +12 -3
  17. package/dist/{ScopeManager-B6GzdhVG.d.cts → ScopeManager-8vf02dwj.d.cts} +56 -5
  18. package/dist/{ScopeManager-udv4Twwq.d.ts → ScopeManager-CxA24W5n.d.ts} +56 -5
  19. package/dist/{Token-BzG5G4ja.d.cts → Token-B1hdkedD.d.cts} +9 -0
  20. package/dist/{Token-BzG5G4ja.d.ts → Token-B1hdkedD.d.ts} +9 -0
  21. package/dist/{TokenNormalizer-t_GotBxr.d.ts → TokenNormalizer-C6VzZgHa.d.ts} +1 -1
  22. package/dist/{TokenNormalizer-DGVa24Q-.d.cts → TokenNormalizer-DRc1Js1V.d.cts} +1 -1
  23. package/dist/{VMCheckpoints-DwLjivM7.d.cts → VMCheckpoints-ELYqITdF.d.cts} +3 -3
  24. package/dist/{VMCheckpoints-BiaIlOOY.d.ts → VMCheckpoints-MK--EBH2.d.ts} +3 -3
  25. package/dist/{Value-CXJqDH9J.d.cts → Value-BUi1RA3S.d.cts} +124 -5
  26. package/dist/{Value-CXJqDH9J.d.ts → Value-BUi1RA3S.d.ts} +124 -5
  27. package/dist/WorkerError-_-RkoQ5P.d.ts +75 -0
  28. package/dist/WorkerError-gzmaopj2.d.cts +75 -0
  29. package/dist/{chunk-GQCOSXMG.js → chunk-267JPOTF.js} +43 -5
  30. package/dist/chunk-267JPOTF.js.map +1 -0
  31. package/dist/{chunk-JMXUNXQS.cjs → chunk-2TZKENDH.cjs} +51 -8
  32. package/dist/chunk-2TZKENDH.cjs.map +1 -0
  33. package/dist/chunk-3OWCDIPN.js +215 -0
  34. package/dist/chunk-3OWCDIPN.js.map +1 -0
  35. package/dist/{chunk-VB37OC6I.js → chunk-4D6NIHE2.js} +40 -3
  36. package/dist/chunk-4D6NIHE2.js.map +1 -0
  37. package/dist/{chunk-YXCTLWOH.js → chunk-4HQKTMWG.js} +1530 -57
  38. package/dist/chunk-4HQKTMWG.js.map +1 -0
  39. package/dist/{chunk-TY3TLZAW.cjs → chunk-536WPM2V.cjs} +18 -2
  40. package/dist/chunk-536WPM2V.cjs.map +1 -0
  41. package/dist/chunk-5HRB36DK.js +405 -0
  42. package/dist/chunk-5HRB36DK.js.map +1 -0
  43. package/dist/chunk-5ON7PUAZ.js +12 -0
  44. package/dist/chunk-5ON7PUAZ.js.map +1 -0
  45. package/dist/{chunk-KV7UW6T6.js → chunk-7KYSUDQO.js} +12 -5
  46. package/dist/chunk-7KYSUDQO.js.map +1 -0
  47. package/dist/{chunk-6WFMPTGB.cjs → chunk-7ZX6B7SY.cjs} +774 -528
  48. package/dist/chunk-7ZX6B7SY.cjs.map +1 -0
  49. package/dist/chunk-ALYRJ72W.cjs +434 -0
  50. package/dist/chunk-ALYRJ72W.cjs.map +1 -0
  51. package/dist/{chunk-5KMIY374.cjs → chunk-B4HBEFTB.cjs} +17 -2
  52. package/dist/chunk-B4HBEFTB.cjs.map +1 -0
  53. package/dist/chunk-B7NLZMQ3.cjs +74 -0
  54. package/dist/chunk-B7NLZMQ3.cjs.map +1 -0
  55. package/dist/{chunk-UQ3UIZJC.js → chunk-BLI4NIQY.js} +6 -2
  56. package/dist/chunk-BLI4NIQY.js.map +1 -0
  57. package/dist/{chunk-IP7ASJEW.js → chunk-BYJBUL7U.js} +5 -5
  58. package/dist/{chunk-IP7ASJEW.js.map → chunk-BYJBUL7U.js.map} +1 -1
  59. package/dist/{chunk-G535KJEG.js → chunk-CCBZZQAE.js} +2 -2
  60. package/dist/{chunk-G535KJEG.js.map → chunk-CCBZZQAE.js.map} +1 -1
  61. package/dist/chunk-CKQMXMHR.cjs +219 -0
  62. package/dist/chunk-CKQMXMHR.cjs.map +1 -0
  63. package/dist/{chunk-T556MJDZ.cjs → chunk-CUR2WLI4.cjs} +11 -11
  64. package/dist/{chunk-T556MJDZ.cjs.map → chunk-CUR2WLI4.cjs.map} +1 -1
  65. package/dist/{chunk-AA3KTWTX.js → chunk-DLEBSLF4.js} +4 -4
  66. package/dist/{chunk-AA3KTWTX.js.map → chunk-DLEBSLF4.js.map} +1 -1
  67. package/dist/{chunk-524F3ATQ.cjs → chunk-E23GZEWL.cjs} +15 -9
  68. package/dist/chunk-E23GZEWL.cjs.map +1 -0
  69. package/dist/{chunk-PFUESQTW.cjs → chunk-EJ3ILXX6.cjs} +48 -2
  70. package/dist/chunk-EJ3ILXX6.cjs.map +1 -0
  71. package/dist/{chunk-526PMQOA.js → chunk-ENKKJYD3.js} +10 -4
  72. package/dist/chunk-ENKKJYD3.js.map +1 -0
  73. package/dist/{chunk-5X2PTP6F.cjs → chunk-ENRIK36Q.cjs} +2 -12
  74. package/dist/chunk-ENRIK36Q.cjs.map +1 -0
  75. package/dist/{chunk-3D7V24DG.js → chunk-ERCOHGXD.js} +17 -2
  76. package/dist/chunk-ERCOHGXD.js.map +1 -0
  77. package/dist/{chunk-IF532O7C.js → chunk-FD5ZZHEU.js} +3 -12
  78. package/dist/chunk-FD5ZZHEU.js.map +1 -0
  79. package/dist/{chunk-7FSDNNNC.js → chunk-GLA4JXBO.js} +11 -5
  80. package/dist/chunk-GLA4JXBO.js.map +1 -0
  81. package/dist/{chunk-UM6BVY2S.cjs → chunk-HIQ5HSZL.cjs} +175 -43
  82. package/dist/chunk-HIQ5HSZL.cjs.map +1 -0
  83. package/dist/{chunk-HVQFNJKE.cjs → chunk-HVRVSI2Z.cjs} +104 -86
  84. package/dist/chunk-HVRVSI2Z.cjs.map +1 -0
  85. package/dist/{chunk-3LAEG75D.js → chunk-I4GAWIPW.js} +24 -6
  86. package/dist/chunk-I4GAWIPW.js.map +1 -0
  87. package/dist/{chunk-PA4VC73I.cjs → chunk-J45BCEZ4.cjs} +33 -27
  88. package/dist/chunk-J45BCEZ4.cjs.map +1 -0
  89. package/dist/{chunk-FQGX2PA2.js → chunk-JG6ZJ2WU.js} +128 -4
  90. package/dist/chunk-JG6ZJ2WU.js.map +1 -0
  91. package/dist/chunk-L2TE7PMO.cjs +14 -0
  92. package/dist/chunk-L2TE7PMO.cjs.map +1 -0
  93. package/dist/chunk-LHZ6VOA7.js +1764 -0
  94. package/dist/chunk-LHZ6VOA7.js.map +1 -0
  95. package/dist/{chunk-SESSWASV.cjs → chunk-MN3LHRGV.cjs} +1868 -393
  96. package/dist/chunk-MN3LHRGV.cjs.map +1 -0
  97. package/dist/{chunk-R3PY4G7J.js → chunk-MVTOCRV2.js} +48 -3
  98. package/dist/chunk-MVTOCRV2.js.map +1 -0
  99. package/dist/{chunk-FE23VSSA.cjs → chunk-N5NNW4VG.cjs} +6 -6
  100. package/dist/{chunk-FE23VSSA.cjs.map → chunk-N5NNW4VG.cjs.map} +1 -1
  101. package/dist/{chunk-Y7FT4IQT.js → chunk-NKW7LKYU.js} +266 -28
  102. package/dist/chunk-NKW7LKYU.js.map +1 -0
  103. package/dist/{chunk-O3ANBHSA.js → chunk-NNQ2TYDF.js} +123 -5
  104. package/dist/chunk-NNQ2TYDF.js.map +1 -0
  105. package/dist/{chunk-NG2JHZHE.js → chunk-NNZAEUDW.js} +1853 -2640
  106. package/dist/chunk-NNZAEUDW.js.map +1 -0
  107. package/dist/{chunk-5LI5EPGJ.cjs → chunk-QFTDTX6K.cjs} +40 -3
  108. package/dist/chunk-QFTDTX6K.cjs.map +1 -0
  109. package/dist/{chunk-HDP7VK3C.cjs → chunk-SRGQ72IR.cjs} +649 -349
  110. package/dist/chunk-SRGQ72IR.cjs.map +1 -0
  111. package/dist/{chunk-4B2CNWQU.cjs → chunk-SSV46KFA.cjs} +14 -7
  112. package/dist/chunk-SSV46KFA.cjs.map +1 -0
  113. package/dist/{chunk-HBVFFBRR.cjs → chunk-T6ZYQJ63.cjs} +1962 -2748
  114. package/dist/chunk-T6ZYQJ63.cjs.map +1 -0
  115. package/dist/{chunk-AJA6LUI7.js → chunk-UO6BUV6K.js} +18 -2
  116. package/dist/chunk-UO6BUV6K.js.map +1 -0
  117. package/dist/chunk-WWQEY7BV.js +67 -0
  118. package/dist/chunk-WWQEY7BV.js.map +1 -0
  119. package/dist/{chunk-5WVP4YHP.js → chunk-WXEHD6TT.js} +333 -33
  120. package/dist/chunk-WXEHD6TT.js.map +1 -0
  121. package/dist/{chunk-47LRVGOT.cjs → chunk-XA4CKRML.cjs} +2 -2
  122. package/dist/{chunk-47LRVGOT.cjs.map → chunk-XA4CKRML.cjs.map} +1 -1
  123. package/dist/{chunk-6KFYJ6TD.cjs → chunk-XIYYHA65.cjs} +6 -2
  124. package/dist/chunk-XIYYHA65.cjs.map +1 -0
  125. package/dist/chunk-YRIKITGF.cjs +1768 -0
  126. package/dist/chunk-YRIKITGF.cjs.map +1 -0
  127. package/dist/{chunk-TMA4RCEN.js → chunk-Z7XGGLI2.js} +3 -3
  128. package/dist/{chunk-TMA4RCEN.js.map → chunk-Z7XGGLI2.js.map} +1 -1
  129. package/dist/{chunk-R7YVBHCV.cjs → chunk-ZMW6NU2K.cjs} +197 -73
  130. package/dist/chunk-ZMW6NU2K.cjs.map +1 -0
  131. package/dist/{chunk-A2N2GFCG.cjs → chunk-ZVTWQLK4.cjs} +10 -10
  132. package/dist/{chunk-A2N2GFCG.cjs.map → chunk-ZVTWQLK4.cjs.map} +1 -1
  133. package/dist/constants.cjs +5 -5
  134. package/dist/constants.d.cts +1 -1
  135. package/dist/constants.d.ts +1 -1
  136. package/dist/constants.js +2 -2
  137. package/dist/engine.cjs +52 -35
  138. package/dist/engine.d.cts +13 -13
  139. package/dist/engine.d.ts +13 -13
  140. package/dist/engine.js +26 -21
  141. package/dist/errors.cjs +44 -19
  142. package/dist/errors.d.cts +3 -2
  143. package/dist/errors.d.ts +3 -2
  144. package/dist/errors.js +2 -1
  145. package/dist/format.cjs +19 -169
  146. package/dist/format.cjs.map +1 -1
  147. package/dist/format.d.cts +18 -26
  148. package/dist/format.d.ts +18 -26
  149. package/dist/format.js +7 -171
  150. package/dist/format.js.map +1 -1
  151. package/dist/index.cjs +250 -31
  152. package/dist/index.cjs.map +1 -1
  153. package/dist/index.d.cts +110 -74
  154. package/dist/index.d.ts +110 -74
  155. package/dist/index.js +228 -23
  156. package/dist/index.js.map +1 -1
  157. package/dist/language.cjs +9 -9
  158. package/dist/language.d.cts +12 -12
  159. package/dist/language.d.ts +12 -12
  160. package/dist/language.js +3 -3
  161. package/dist/lexer.cjs +16 -16
  162. package/dist/lexer.d.cts +3 -3
  163. package/dist/lexer.d.ts +3 -3
  164. package/dist/lexer.js +5 -5
  165. package/dist/normalizer.cjs +10 -10
  166. package/dist/normalizer.d.cts +3 -3
  167. package/dist/normalizer.d.ts +3 -3
  168. package/dist/normalizer.js +4 -4
  169. package/dist/packages.cjs +45 -36
  170. package/dist/packages.d.cts +135 -31
  171. package/dist/packages.d.ts +135 -31
  172. package/dist/packages.js +14 -13
  173. package/dist/parser.cjs +16 -16
  174. package/dist/parser.d.cts +6 -5
  175. package/dist/parser.d.ts +6 -5
  176. package/dist/parser.js +6 -6
  177. package/dist/{pipeline-BEb3hujr.d.cts → pipeline-CtfJtPQc.d.cts} +3 -3
  178. package/dist/{pipeline-B6k5lCB7.d.ts → pipeline-DCd5M6Gk.d.ts} +3 -3
  179. package/dist/resolvers.d.cts +3 -3
  180. package/dist/resolvers.d.ts +3 -3
  181. package/dist/testing.cjs +478 -0
  182. package/dist/testing.cjs.map +1 -0
  183. package/dist/testing.d.cts +271 -0
  184. package/dist/testing.d.ts +271 -0
  185. package/dist/testing.js +470 -0
  186. package/dist/testing.js.map +1 -0
  187. package/dist/uom.cjs +16 -16
  188. package/dist/uom.d.cts +3 -3
  189. package/dist/uom.d.ts +3 -3
  190. package/dist/uom.js +6 -6
  191. package/dist/utilities.cjs +6 -5
  192. package/dist/utilities.js +2 -1
  193. package/dist/vm.cjs +34 -34
  194. package/dist/vm.d.cts +8 -8
  195. package/dist/vm.d.ts +8 -8
  196. package/dist/vm.js +9 -9
  197. package/dist/worker.cjs +493 -0
  198. package/dist/worker.cjs.map +1 -0
  199. package/dist/worker.d.cts +509 -0
  200. package/dist/worker.d.ts +509 -0
  201. package/dist/worker.js +484 -0
  202. package/dist/worker.js.map +1 -0
  203. package/package.json +21 -1
  204. package/dist/chunk-3D7V24DG.js.map +0 -1
  205. package/dist/chunk-3LAEG75D.js.map +0 -1
  206. package/dist/chunk-4B2CNWQU.cjs.map +0 -1
  207. package/dist/chunk-524F3ATQ.cjs.map +0 -1
  208. package/dist/chunk-526PMQOA.js.map +0 -1
  209. package/dist/chunk-5KMIY374.cjs.map +0 -1
  210. package/dist/chunk-5LI5EPGJ.cjs.map +0 -1
  211. package/dist/chunk-5WVP4YHP.js.map +0 -1
  212. package/dist/chunk-5X2PTP6F.cjs.map +0 -1
  213. package/dist/chunk-6KFYJ6TD.cjs.map +0 -1
  214. package/dist/chunk-6WFMPTGB.cjs.map +0 -1
  215. package/dist/chunk-7FSDNNNC.js.map +0 -1
  216. package/dist/chunk-AJA6LUI7.js.map +0 -1
  217. package/dist/chunk-FQGX2PA2.js.map +0 -1
  218. package/dist/chunk-GQCOSXMG.js.map +0 -1
  219. package/dist/chunk-HBVFFBRR.cjs.map +0 -1
  220. package/dist/chunk-HDP7VK3C.cjs.map +0 -1
  221. package/dist/chunk-HVQFNJKE.cjs.map +0 -1
  222. package/dist/chunk-IF532O7C.js.map +0 -1
  223. package/dist/chunk-JMXUNXQS.cjs.map +0 -1
  224. package/dist/chunk-KV7UW6T6.js.map +0 -1
  225. package/dist/chunk-NG2JHZHE.js.map +0 -1
  226. package/dist/chunk-O3ANBHSA.js.map +0 -1
  227. package/dist/chunk-PA4VC73I.cjs.map +0 -1
  228. package/dist/chunk-PFUESQTW.cjs.map +0 -1
  229. package/dist/chunk-R3PY4G7J.js.map +0 -1
  230. package/dist/chunk-R7YVBHCV.cjs.map +0 -1
  231. package/dist/chunk-SESSWASV.cjs.map +0 -1
  232. package/dist/chunk-TY3TLZAW.cjs.map +0 -1
  233. package/dist/chunk-UM6BVY2S.cjs.map +0 -1
  234. package/dist/chunk-UQ3UIZJC.js.map +0 -1
  235. package/dist/chunk-VB37OC6I.js.map +0 -1
  236. package/dist/chunk-Y7FT4IQT.js.map +0 -1
  237. package/dist/chunk-YXCTLWOH.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { V as Value } from './Value-CXJqDH9J.cjs';
2
- import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-B0xskcv5.cjs';
3
- import { E as EngineError } from './EngineError-LU7W7AgI.cjs';
4
- import { D as DiagnosticPipeline } from './pipeline-BEb3hujr.cjs';
1
+ import { V as Value } from './Value-BUi1RA3S.cjs';
2
+ import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-Bp9xeTmX.cjs';
3
+ import { E as EngineError } from './EngineError-B61GS1jp.cjs';
4
+ import { D as DiagnosticPipeline } from './pipeline-CtfJtPQc.cjs';
5
5
  import { VariableResolver } from './variables.cjs';
6
6
 
7
7
  /**
@@ -39,7 +39,7 @@ import { VariableResolver } from './variables.cjs';
39
39
  * -100).
40
40
  * @param context - Registries belonging to the engine that created this VM.
41
41
  */
42
- declare function createVM(registry: OpRegistry, maxStackDepth?: number, maxInstructions?: number, maxFunctionRecursionDepth?: number, maxCollectionSize?: number, maxAllocatedElements?: number, maxFunctionCalls?: number, maxDateOffsetYears?: number, minDateOffsetYears?: number, context?: EngineContext): VM;
42
+ declare function createVM(registry: OpRegistry, maxStackDepth?: number, maxInstructions?: number, maxFunctionRecursionDepth?: number, maxCollectionSize?: number, maxAllocatedElements?: number, maxFunctionCalls?: number, maxDateOffsetYears?: number, minDateOffsetYears?: number, context?: EngineContext, holidayPredicate?: (epochMs: number) => boolean): VM;
43
43
  /**
44
44
  * Compiled bytecode ready for VM execution.
45
45
  *
@@ -81,6 +81,44 @@ interface LineExecutionContext {
81
81
  getLineResult?: (lineNumber: number) => Value | undefined;
82
82
  /** Whether line `lineNumber` is a blank line or a `#` heading, the stopping condition for "total above"/"sum above"/"average above" aggregation. */
83
83
  isLineBoundary?: (lineNumber: number) => boolean;
84
+ /**
85
+ * The variables another line's expression reads, by 1-based line number, or
86
+ * `undefined` when the line has no evaluated expression (forward reference,
87
+ * out of range, or markdown). Goal seek (`packages/goalseek/`) uses it to
88
+ * refuse up front when the variable it was asked to vary is one the target
89
+ * line never reads, rather than searching a relationship that cannot move.
90
+ */
91
+ getLineReads?: (lineNumber: number) => string[] | undefined;
92
+ /**
93
+ * Re-evaluate another line's already-compiled expression with `variable`
94
+ * bound to `bound` for that one evaluation, without disturbing the
95
+ * document's own value for it. This is the primitive goal seek
96
+ * (`packages/goalseek/`) drives: binding a numeric candidate probes the
97
+ * relationship, binding a symbolic placeholder (with `symbolicTolerant`)
98
+ * reads it back in closed form. Returns an error Value when there is no
99
+ * document, the line is not a plain expression ready to run, or its
100
+ * re-evaluation itself faults. The binding is a call frame, so it shadows
101
+ * the document's value exactly the way a function parameter does and is
102
+ * gone the moment the probe returns.
103
+ */
104
+ evaluateLineWithBinding?: (lineNumber: number, variable: string, bound: Value, symbolicTolerant: boolean) => Value;
105
+ /**
106
+ * The hard ceiling on goal seek's bisection steps, from
107
+ * `config.vm.maxGoalSeekIterations`. Carried on the context so the search,
108
+ * which runs as a plugin function with no other view of engine config, is
109
+ * bounded by the host's configured limit rather than a hardcoded one.
110
+ */
111
+ goalSeekMaxIterations?: number;
112
+ /**
113
+ * The RAW markdown text of line `lineNumber` (1-based), or `undefined`
114
+ * when there is no real document or the line is out of range. Distinct
115
+ * from `getLineResult`, which returns a line's evaluated Value: a
116
+ * markdown table's rows are skipped by the evaluator and hold no result,
117
+ * so reading a column as data has to go back to the source text. Backs
118
+ * the tables package (`packages/tables/`), which walks upward from the
119
+ * current line to find the nearest table and read one of its columns.
120
+ */
121
+ getLineText?: (lineNumber: number) => string | undefined;
84
122
  }
85
123
  /**
86
124
  * Discriminated union returned by {@link executeBytecode}.
@@ -389,6 +427,17 @@ interface VM {
389
427
  defineUserFunction(name: string, params: string[], program: BytecodeProgram): void;
390
428
  getUserFunction(name: string): UserFunctionDef | undefined;
391
429
  hasUserFunction(name: string): boolean;
430
+ /**
431
+ * Every session-scoped variable currently defined, as `[name, value]` pairs,
432
+ * for snapshotting the VM's state (see `engine/EngineSnapshot.ts`). The
433
+ * returned array is a fresh copy, so mutating it does not touch the store,
434
+ * and it reads the flat document-variable table only, never a transient
435
+ * user-function call frame (those are call-scoped and gone by the time any
436
+ * snapshot is taken).
437
+ */
438
+ getVariableEntries(): [string, Value][];
439
+ /** Every user-defined function currently defined, as a fresh array copy, for snapshotting (see {@link getVariableEntries}). */
440
+ getUserFunctionDefs(): UserFunctionDef[];
392
441
  /** Register (or redefine) a bare equation (`a*x = rhs`), keyed by its free variable. See {@link EquationDef}. */
393
442
  defineEquation(variable: string, factorNames: string[], rhsProgram: BytecodeProgram): void;
394
443
  getEquation(variable: string): EquationDef | undefined;
@@ -431,6 +480,8 @@ interface VM {
431
480
  getMaxDateOffsetYears(): number;
432
481
  /** The same bound backwards, as a negative number of years. */
433
482
  getMinDateOffsetYears(): number;
483
+ /** Whether the host's public-holiday calendar marks `epochMs` a holiday, for working-day arithmetic. Always `false` when no calendar is configured (weekends-only). Set from `constants/Configuration.ts`'s `date.holidays`; see `vm/HolidayCalendar.ts`. */
484
+ isHoliday(epochMs: number): boolean;
434
485
  getInstructionCount(): number;
435
486
  incrementInstructions(n: number): void;
436
487
  /** Active AbortSignal for the current expression evaluation. Checked before cache writes. */
@@ -1,7 +1,7 @@
1
- import { V as Value } from './Value-CXJqDH9J.js';
2
- import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-B0xskcv5.js';
3
- import { E as EngineError } from './EngineError-LU7W7AgI.js';
4
- import { D as DiagnosticPipeline } from './pipeline-B6k5lCB7.js';
1
+ import { V as Value } from './Value-BUi1RA3S.js';
2
+ import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-Bp9xeTmX.js';
3
+ import { E as EngineError } from './EngineError-B61GS1jp.js';
4
+ import { D as DiagnosticPipeline } from './pipeline-DCd5M6Gk.js';
5
5
  import { VariableResolver } from './variables.js';
6
6
 
7
7
  /**
@@ -39,7 +39,7 @@ import { VariableResolver } from './variables.js';
39
39
  * -100).
40
40
  * @param context - Registries belonging to the engine that created this VM.
41
41
  */
42
- declare function createVM(registry: OpRegistry, maxStackDepth?: number, maxInstructions?: number, maxFunctionRecursionDepth?: number, maxCollectionSize?: number, maxAllocatedElements?: number, maxFunctionCalls?: number, maxDateOffsetYears?: number, minDateOffsetYears?: number, context?: EngineContext): VM;
42
+ declare function createVM(registry: OpRegistry, maxStackDepth?: number, maxInstructions?: number, maxFunctionRecursionDepth?: number, maxCollectionSize?: number, maxAllocatedElements?: number, maxFunctionCalls?: number, maxDateOffsetYears?: number, minDateOffsetYears?: number, context?: EngineContext, holidayPredicate?: (epochMs: number) => boolean): VM;
43
43
  /**
44
44
  * Compiled bytecode ready for VM execution.
45
45
  *
@@ -81,6 +81,44 @@ interface LineExecutionContext {
81
81
  getLineResult?: (lineNumber: number) => Value | undefined;
82
82
  /** Whether line `lineNumber` is a blank line or a `#` heading, the stopping condition for "total above"/"sum above"/"average above" aggregation. */
83
83
  isLineBoundary?: (lineNumber: number) => boolean;
84
+ /**
85
+ * The variables another line's expression reads, by 1-based line number, or
86
+ * `undefined` when the line has no evaluated expression (forward reference,
87
+ * out of range, or markdown). Goal seek (`packages/goalseek/`) uses it to
88
+ * refuse up front when the variable it was asked to vary is one the target
89
+ * line never reads, rather than searching a relationship that cannot move.
90
+ */
91
+ getLineReads?: (lineNumber: number) => string[] | undefined;
92
+ /**
93
+ * Re-evaluate another line's already-compiled expression with `variable`
94
+ * bound to `bound` for that one evaluation, without disturbing the
95
+ * document's own value for it. This is the primitive goal seek
96
+ * (`packages/goalseek/`) drives: binding a numeric candidate probes the
97
+ * relationship, binding a symbolic placeholder (with `symbolicTolerant`)
98
+ * reads it back in closed form. Returns an error Value when there is no
99
+ * document, the line is not a plain expression ready to run, or its
100
+ * re-evaluation itself faults. The binding is a call frame, so it shadows
101
+ * the document's value exactly the way a function parameter does and is
102
+ * gone the moment the probe returns.
103
+ */
104
+ evaluateLineWithBinding?: (lineNumber: number, variable: string, bound: Value, symbolicTolerant: boolean) => Value;
105
+ /**
106
+ * The hard ceiling on goal seek's bisection steps, from
107
+ * `config.vm.maxGoalSeekIterations`. Carried on the context so the search,
108
+ * which runs as a plugin function with no other view of engine config, is
109
+ * bounded by the host's configured limit rather than a hardcoded one.
110
+ */
111
+ goalSeekMaxIterations?: number;
112
+ /**
113
+ * The RAW markdown text of line `lineNumber` (1-based), or `undefined`
114
+ * when there is no real document or the line is out of range. Distinct
115
+ * from `getLineResult`, which returns a line's evaluated Value: a
116
+ * markdown table's rows are skipped by the evaluator and hold no result,
117
+ * so reading a column as data has to go back to the source text. Backs
118
+ * the tables package (`packages/tables/`), which walks upward from the
119
+ * current line to find the nearest table and read one of its columns.
120
+ */
121
+ getLineText?: (lineNumber: number) => string | undefined;
84
122
  }
85
123
  /**
86
124
  * Discriminated union returned by {@link executeBytecode}.
@@ -389,6 +427,17 @@ interface VM {
389
427
  defineUserFunction(name: string, params: string[], program: BytecodeProgram): void;
390
428
  getUserFunction(name: string): UserFunctionDef | undefined;
391
429
  hasUserFunction(name: string): boolean;
430
+ /**
431
+ * Every session-scoped variable currently defined, as `[name, value]` pairs,
432
+ * for snapshotting the VM's state (see `engine/EngineSnapshot.ts`). The
433
+ * returned array is a fresh copy, so mutating it does not touch the store,
434
+ * and it reads the flat document-variable table only, never a transient
435
+ * user-function call frame (those are call-scoped and gone by the time any
436
+ * snapshot is taken).
437
+ */
438
+ getVariableEntries(): [string, Value][];
439
+ /** Every user-defined function currently defined, as a fresh array copy, for snapshotting (see {@link getVariableEntries}). */
440
+ getUserFunctionDefs(): UserFunctionDef[];
392
441
  /** Register (or redefine) a bare equation (`a*x = rhs`), keyed by its free variable. See {@link EquationDef}. */
393
442
  defineEquation(variable: string, factorNames: string[], rhsProgram: BytecodeProgram): void;
394
443
  getEquation(variable: string): EquationDef | undefined;
@@ -431,6 +480,8 @@ interface VM {
431
480
  getMaxDateOffsetYears(): number;
432
481
  /** The same bound backwards, as a negative number of years. */
433
482
  getMinDateOffsetYears(): number;
483
+ /** Whether the host's public-holiday calendar marks `epochMs` a holiday, for working-day arithmetic. Always `false` when no calendar is configured (weekends-only). Set from `constants/Configuration.ts`'s `date.holidays`; see `vm/HolidayCalendar.ts`. */
484
+ isHoliday(epochMs: number): boolean;
434
485
  getInstructionCount(): number;
435
486
  incrementInstructions(n: number): void;
436
487
  /** Active AbortSignal for the current expression evaluation. Checked before cache writes. */
@@ -48,6 +48,7 @@ declare const TokenTypes: {
48
48
  readonly MINUS: "MINUS";
49
49
  readonly STAR: "STAR";
50
50
  readonly SLASH: "SLASH";
51
+ readonly PLUS_MINUS: "PLUS_MINUS";
51
52
  readonly CARET: "CARET";
52
53
  readonly PERCENT: "PERCENT";
53
54
  readonly LSHIFT: "LSHIFT";
@@ -163,6 +164,8 @@ declare const TokenTypes: {
163
164
  readonly IS: "IS";
164
165
  readonly PCT_ON: "PCT_ON";
165
166
  readonly PCT_OFF: "PCT_OFF";
167
+ readonly PCT_UP: "PCT_UP";
168
+ readonly PCT_DOWN: "PCT_DOWN";
166
169
  readonly OF_WHAT: "OF_WHAT";
167
170
  readonly OFF_WHAT: "OFF_WHAT";
168
171
  readonly ON_WHAT: "ON_WHAT";
@@ -192,6 +195,8 @@ declare const TokenTypes: {
192
195
  readonly BACKTICK_OPEN: "BACKTICK_OPEN";
193
196
  readonly INLINE_SOLVE_START: "INLINE_SOLVE_START";
194
197
  readonly COMMENT: "COMMENT";
198
+ readonly HEX_COLOUR: "HEX_COLOUR";
199
+ readonly COLOUR_CALL: "COLOUR_CALL";
195
200
  readonly VEC2: "VEC2";
196
201
  readonly VEC3: "VEC3";
197
202
  readonly VEC4: "VEC4";
@@ -241,6 +246,9 @@ declare const TokenTypes: {
241
246
  readonly AT_RATE: "AT_RATE";
242
247
  readonly WEEK_IN: "WEEK_IN";
243
248
  readonly BETWEEN_UNIT: "BETWEEN_UNIT";
249
+ readonly WORKDAYS_AFTER: "WORKDAYS_AFTER";
250
+ readonly WORKDAYS_BEFORE: "WORKDAYS_BEFORE";
251
+ readonly WORKDAYS_BETWEEN: "WORKDAYS_BETWEEN";
244
252
  readonly IS_WEEKEND: "IS_WEEKEND";
245
253
  readonly IS_WORKDAY: "IS_WORKDAY";
246
254
  readonly VIDEO_TIMECODE: "VIDEO_TIMECODE";
@@ -254,6 +262,7 @@ declare const TokenTypes: {
254
262
  readonly TOTAL_ABOVE: "TOTAL_ABOVE";
255
263
  readonly SUM_ABOVE: "SUM_ABOVE";
256
264
  readonly AVERAGE_ABOVE: "AVERAGE_ABOVE";
265
+ readonly GOAL_SEEK: "GOAL_SEEK";
257
266
  };
258
267
  /**
259
268
  * A token's kind, as a string.
@@ -48,6 +48,7 @@ declare const TokenTypes: {
48
48
  readonly MINUS: "MINUS";
49
49
  readonly STAR: "STAR";
50
50
  readonly SLASH: "SLASH";
51
+ readonly PLUS_MINUS: "PLUS_MINUS";
51
52
  readonly CARET: "CARET";
52
53
  readonly PERCENT: "PERCENT";
53
54
  readonly LSHIFT: "LSHIFT";
@@ -163,6 +164,8 @@ declare const TokenTypes: {
163
164
  readonly IS: "IS";
164
165
  readonly PCT_ON: "PCT_ON";
165
166
  readonly PCT_OFF: "PCT_OFF";
167
+ readonly PCT_UP: "PCT_UP";
168
+ readonly PCT_DOWN: "PCT_DOWN";
166
169
  readonly OF_WHAT: "OF_WHAT";
167
170
  readonly OFF_WHAT: "OFF_WHAT";
168
171
  readonly ON_WHAT: "ON_WHAT";
@@ -192,6 +195,8 @@ declare const TokenTypes: {
192
195
  readonly BACKTICK_OPEN: "BACKTICK_OPEN";
193
196
  readonly INLINE_SOLVE_START: "INLINE_SOLVE_START";
194
197
  readonly COMMENT: "COMMENT";
198
+ readonly HEX_COLOUR: "HEX_COLOUR";
199
+ readonly COLOUR_CALL: "COLOUR_CALL";
195
200
  readonly VEC2: "VEC2";
196
201
  readonly VEC3: "VEC3";
197
202
  readonly VEC4: "VEC4";
@@ -241,6 +246,9 @@ declare const TokenTypes: {
241
246
  readonly AT_RATE: "AT_RATE";
242
247
  readonly WEEK_IN: "WEEK_IN";
243
248
  readonly BETWEEN_UNIT: "BETWEEN_UNIT";
249
+ readonly WORKDAYS_AFTER: "WORKDAYS_AFTER";
250
+ readonly WORKDAYS_BEFORE: "WORKDAYS_BEFORE";
251
+ readonly WORKDAYS_BETWEEN: "WORKDAYS_BETWEEN";
244
252
  readonly IS_WEEKEND: "IS_WEEKEND";
245
253
  readonly IS_WORKDAY: "IS_WORKDAY";
246
254
  readonly VIDEO_TIMECODE: "VIDEO_TIMECODE";
@@ -254,6 +262,7 @@ declare const TokenTypes: {
254
262
  readonly TOTAL_ABOVE: "TOTAL_ABOVE";
255
263
  readonly SUM_ABOVE: "SUM_ABOVE";
256
264
  readonly AVERAGE_ABOVE: "AVERAGE_ABOVE";
265
+ readonly GOAL_SEEK: "GOAL_SEEK";
257
266
  };
258
267
  /**
259
268
  * A token's kind, as a string.
@@ -1,4 +1,4 @@
1
- import { T as Token } from './Token-BzG5G4ja.js';
1
+ import { T as Token } from './Token-B1hdkedD.js';
2
2
 
3
3
  /**
4
4
  * NormalizerRule, pluggable token normalization rule for the
@@ -1,4 +1,4 @@
1
- import { T as Token } from './Token-BzG5G4ja.cjs';
1
+ import { T as Token } from './Token-B1hdkedD.cjs';
2
2
 
3
3
  /**
4
4
  * NormalizerRule, pluggable token normalization rule for the
@@ -1,6 +1,6 @@
1
- import { V as Value } from './Value-CXJqDH9J.cjs';
2
- import { V as VM } from './ScopeManager-B6GzdhVG.cjs';
3
- import { U as UserFunctionDef } from './BytecodeBuilder-B0xskcv5.cjs';
1
+ import { V as Value } from './Value-BUi1RA3S.cjs';
2
+ import { V as VM } from './ScopeManager-8vf02dwj.cjs';
3
+ import { U as UserFunctionDef } from './BytecodeBuilder-Bp9xeTmX.cjs';
4
4
 
5
5
  /**
6
6
  * A point-in-time snapshot of VM variable state.
@@ -1,6 +1,6 @@
1
- import { V as Value } from './Value-CXJqDH9J.js';
2
- import { V as VM } from './ScopeManager-udv4Twwq.js';
3
- import { U as UserFunctionDef } from './BytecodeBuilder-B0xskcv5.js';
1
+ import { V as Value } from './Value-BUi1RA3S.js';
2
+ import { V as VM } from './ScopeManager-CxA24W5n.js';
3
+ import { U as UserFunctionDef } from './BytecodeBuilder-Bp9xeTmX.js';
4
4
 
5
5
  /**
6
6
  * A point-in-time snapshot of VM variable state.
@@ -136,6 +136,28 @@ type SymbolicNode = {
136
136
  args: readonly SymbolicNode[];
137
137
  };
138
138
 
139
+ /**
140
+ * An exact base-ten number, held as an integer coefficient and a scale.
141
+ *
142
+ * The value is `coef * 10^(-scale)`, so `Decimal(30n, 2)` is exactly `0.30` and
143
+ * `Decimal(1005n, 3)` is exactly `1.005`. `scale` is a non-negative integer
144
+ * count of fractional digits, the sign lives on `coef`, and zero is
145
+ * `Decimal(0n, s)` for any `s`.
146
+ *
147
+ * This is the representation money is carried in so that two prices a user
148
+ * typed add and multiply without the binary-floating-point error a double
149
+ * introduces (`0.1 + 0.2` is `0.30000000000000004` as a double, `0.30` here).
150
+ * It is deliberately dependency-free: a bigint coefficient plus an integer
151
+ * scale needs nothing the runtime does not already have, which keeps the
152
+ * engine's single-runtime-dependency contract intact.
153
+ */
154
+ interface DecimalData {
155
+ /** The integer coefficient, carrying the sign. */
156
+ readonly coef: bigint;
157
+ /** The number of fractional digits, a non-negative integer. */
158
+ readonly scale: number;
159
+ }
160
+
139
161
  /**
140
162
  * A single matrix cell. `boolean` covers element-wise comparison results
141
163
  * (`[1,6;3,8] < [5,2;7,4]` produces a Matrix of booleans, not numbers). A
@@ -169,6 +191,33 @@ interface RangeData {
169
191
  readonly min: number;
170
192
  readonly max: number;
171
193
  }
194
+ /**
195
+ * How a colour was authored, and therefore how it should display. It never
196
+ * changes the channels: a colour is always stored as canonical sRGB (`r`,`g`,`b`
197
+ * integers 0-255, `a` in 0-1), and `format` only decides whether `formatValue`
198
+ * renders it as `#rrggbb`, `rgb(...)`, `hsl(...)` or a named keyword.
199
+ */
200
+ type ColourFormat = "hex" | "rgb" | "rgba" | "hsl" | "hsla" | "named";
201
+ /**
202
+ * A colour value. Canonical channels are sRGB (`r`,`g`,`b` are integers 0-255,
203
+ * `a` is 0-1); HSL is never stored, it is derived on demand for display and for
204
+ * hue/saturation/lightness operations, then re-quantised back to RGBA. A
205
+ * `lighten` followed by an equal `darken` returns to within one rounding step of
206
+ * the original (integer channels re-quantise each way), and does not drift on
207
+ * repetition, rather than storing both HSL and RGB and letting them disagree.
208
+ * `format`
209
+ * records the authored/display form; `name` carries the CSS keyword only when
210
+ * `format === "named"` (e.g. `"rebeccapurple"`). Lives in a {@link Value}'s
211
+ * `value` slot exactly as {@link MatrixData}/{@link RangeData} do.
212
+ */
213
+ interface ColourData {
214
+ readonly r: number;
215
+ readonly g: number;
216
+ readonly b: number;
217
+ readonly a: number;
218
+ readonly format: ColourFormat;
219
+ readonly name?: string;
220
+ }
172
221
  /**
173
222
  * Discriminated union tag for {@link Value} objects.
174
223
  *
@@ -198,7 +247,9 @@ declare enum ValueType {
198
247
  /** Async result pending resolution. Value stores the queryKey string. */
199
248
  Pending = 12,
200
249
  /** Plugin-raised error propagated through the DAG. Value stores error code, unit stores message. */
201
- Error = 13
250
+ Error = 13,
251
+ /** A colour (hex/rgb/hsl/named). Value is {@link ColourData}. */
252
+ Colour = 14
202
253
  }
203
254
  /**
204
255
  * Universal runtime value for the solve-js VM.
@@ -214,16 +265,81 @@ declare enum ValueType {
214
265
  declare class Value {
215
266
  private _cachedNumber;
216
267
  type: ValueType;
217
- value: number | bigint | string | boolean | MatrixData | RangeData | SymbolicNode;
268
+ value: number | bigint | string | boolean | MatrixData | RangeData | ColourData | SymbolicNode;
218
269
  unit?: string;
219
270
  /** Set by async resolvers when a fetch timed out, the result is a fallback (typically 0). */
220
271
  timedOut?: boolean;
221
- constructor(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | SymbolicNode, unit?: string);
272
+ /**
273
+ * The exact base-ten value this Value stands for, when it has one.
274
+ *
275
+ * A sidecar rather than a replacement for `value`: money and decimal-point
276
+ * literals set it to a {@link DecimalData} so that same-currency arithmetic
277
+ * and display can be exact ("$0.10 + $0.20" is "$0.30", not
278
+ * "$0.30000000000000004"), while `value` stays the nearest double so every
279
+ * existing consumer that reads `.value` or `toNumber()` is unchanged. The
280
+ * plain Number-times-Number fast paths deliberately ignore it, which is why
281
+ * a bare "0.1 + 0.2" still answers the double it always did: exactness is
282
+ * carried only where a unit-bearing operand asks for it. Cleared by
283
+ * {@link recycle} so a reused arena Value never inherits a stale exact.
284
+ */
285
+ exact?: DecimalData;
286
+ /**
287
+ * The exact rational value this Value stands for, when it has one.
288
+ *
289
+ * The second sidecar, the same shape as {@link exact} and for the same
290
+ * reason: a fraction has no exact base-ten form (`1/3` is not any decimal),
291
+ * so exact fraction arithmetic needs a numerator/denominator pair rather
292
+ * than a coefficient and a scale. Integer division seeds it ("1/3" carries
293
+ * the {@link Rational} 1/3), and `+`, `-`, `*`, `/` between rational-bearing
294
+ * numbers keep it reduced, so "1/49 * 49" is exactly 1 and "5/6 - 1/6 - 1/6
295
+ * - 1/6 - 1/6 - 1/6" is exactly 0 rather than the 1.6e-16 the doubles drift
296
+ * to. `value` still holds the nearest double, recomputed from the exact
297
+ * rational so accumulation error never creeps in, which is why the default
298
+ * display and every `.value`/`toNumber()` reader are unchanged. Only a
299
+ * fraction written with "/" carries it, so a decimal literal ("0.1") and a
300
+ * transcendental result ("sqrt(2)") stay the plain doubles they were.
301
+ * Cleared by {@link recycle} alongside {@link exact}.
302
+ */
303
+ rational?: Rational;
304
+ /**
305
+ * The one-sigma uncertainty (standard error) this value carries, when it
306
+ * has one.
307
+ *
308
+ * The third sidecar, the same shape as {@link exact} and {@link rational}
309
+ * and for the same reason: a measurement written `12.3 ± 0.5` is still the
310
+ * number 12.3 everywhere it is read as one, so the type stays
311
+ * {@link ValueType.Number} and `value` stays the center, while this non
312
+ * negative field carries the tolerance. The `±` (or ASCII `+/-`) operator
313
+ * seeds it, and `+`, `-`, `*`, `/` propagate it in quadrature for
314
+ * independent errors (see `vm/VMConversion.ts`'s `uncertainOp`). Everything
315
+ * else (a comparison, a transcendental function, a unit conversion) reads
316
+ * the center through `toNumber()` and drops the tolerance, which is why a
317
+ * value with no uncertainty behaves exactly as a plain number always did.
318
+ * Cleared by {@link recycle} alongside the other two sidecars.
319
+ */
320
+ uncertainty?: number;
321
+ /**
322
+ * The number of decimal places this value should DISPLAY at, when it has been
323
+ * given an explicit precision.
324
+ *
325
+ * A display sidecar, not a value one: `value` is unchanged, so every
326
+ * `.value`/`toNumber()` reader and all arithmetic behave exactly as before,
327
+ * and a value with no `decimalPlaces` formats the way it always did (the
328
+ * global two-place default with trailing zeros trimmed). It is set only by an
329
+ * explicit precision request, `<x> to N dp` and `round(x, N)`, so that
330
+ * `3.14159 to 4 dp` shows `3.1416` and `1.5 to 2 dp` shows `1.50` rather than
331
+ * the value being rounded but then displayed at the default two places. It is
332
+ * NOT propagated through arithmetic (a later `+ 1` re-decides precision),
333
+ * which is why nothing that did not ask for a precision is affected. Cleared
334
+ * by {@link recycle} alongside the other sidecars.
335
+ */
336
+ decimalPlaces?: number;
337
+ constructor(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | ColourData | SymbolicNode, unit?: string);
222
338
  /**
223
339
  * Phase 5.3: Reset all fields for arena reuse.
224
340
  * Called by ValueArena.acquire(), zero allocation, just field assignment.
225
341
  */
226
- recycle(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | SymbolicNode, unit?: string): void;
342
+ recycle(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | ColourData | SymbolicNode, unit?: string): void;
227
343
  isNumber(): this is Value & {
228
344
  value: number;
229
345
  };
@@ -244,6 +360,9 @@ declare class Value {
244
360
  isRange(): this is Value & {
245
361
  value: RangeData;
246
362
  };
363
+ isColour(): this is Value & {
364
+ value: ColourData;
365
+ };
247
366
  isSymbolic(): this is Value & {
248
367
  value: SymbolicNode;
249
368
  };
@@ -295,4 +414,4 @@ declare function colVectorValue(data: readonly number[]): Value;
295
414
  /** Create a Range value, a first-class integer range `min:max`, both bounds inclusive. */
296
415
  declare function rangeValue(min: number, max: number): Value;
297
416
 
298
- export { type MatrixData as M, type RangeData as R, type SymbolicNode as S, Value as V, type MatrixEntry as a, ValueType as b, bigIntValue as c, colVectorValue as d, rowVectorValue as e, hexValue as h, matrixValue as m, numberValue as n, rangeValue as r, stringValue as s, uomValue as u };
417
+ export { type ColourData as C, type MatrixData as M, type RangeData as R, type SymbolicNode as S, Value as V, type MatrixEntry as a, ValueType as b, bigIntValue as c, colVectorValue as d, rowVectorValue as e, type ColourFormat as f, hexValue as h, matrixValue as m, numberValue as n, rangeValue as r, stringValue as s, uomValue as u };
@@ -136,6 +136,28 @@ type SymbolicNode = {
136
136
  args: readonly SymbolicNode[];
137
137
  };
138
138
 
139
+ /**
140
+ * An exact base-ten number, held as an integer coefficient and a scale.
141
+ *
142
+ * The value is `coef * 10^(-scale)`, so `Decimal(30n, 2)` is exactly `0.30` and
143
+ * `Decimal(1005n, 3)` is exactly `1.005`. `scale` is a non-negative integer
144
+ * count of fractional digits, the sign lives on `coef`, and zero is
145
+ * `Decimal(0n, s)` for any `s`.
146
+ *
147
+ * This is the representation money is carried in so that two prices a user
148
+ * typed add and multiply without the binary-floating-point error a double
149
+ * introduces (`0.1 + 0.2` is `0.30000000000000004` as a double, `0.30` here).
150
+ * It is deliberately dependency-free: a bigint coefficient plus an integer
151
+ * scale needs nothing the runtime does not already have, which keeps the
152
+ * engine's single-runtime-dependency contract intact.
153
+ */
154
+ interface DecimalData {
155
+ /** The integer coefficient, carrying the sign. */
156
+ readonly coef: bigint;
157
+ /** The number of fractional digits, a non-negative integer. */
158
+ readonly scale: number;
159
+ }
160
+
139
161
  /**
140
162
  * A single matrix cell. `boolean` covers element-wise comparison results
141
163
  * (`[1,6;3,8] < [5,2;7,4]` produces a Matrix of booleans, not numbers). A
@@ -169,6 +191,33 @@ interface RangeData {
169
191
  readonly min: number;
170
192
  readonly max: number;
171
193
  }
194
+ /**
195
+ * How a colour was authored, and therefore how it should display. It never
196
+ * changes the channels: a colour is always stored as canonical sRGB (`r`,`g`,`b`
197
+ * integers 0-255, `a` in 0-1), and `format` only decides whether `formatValue`
198
+ * renders it as `#rrggbb`, `rgb(...)`, `hsl(...)` or a named keyword.
199
+ */
200
+ type ColourFormat = "hex" | "rgb" | "rgba" | "hsl" | "hsla" | "named";
201
+ /**
202
+ * A colour value. Canonical channels are sRGB (`r`,`g`,`b` are integers 0-255,
203
+ * `a` is 0-1); HSL is never stored, it is derived on demand for display and for
204
+ * hue/saturation/lightness operations, then re-quantised back to RGBA. A
205
+ * `lighten` followed by an equal `darken` returns to within one rounding step of
206
+ * the original (integer channels re-quantise each way), and does not drift on
207
+ * repetition, rather than storing both HSL and RGB and letting them disagree.
208
+ * `format`
209
+ * records the authored/display form; `name` carries the CSS keyword only when
210
+ * `format === "named"` (e.g. `"rebeccapurple"`). Lives in a {@link Value}'s
211
+ * `value` slot exactly as {@link MatrixData}/{@link RangeData} do.
212
+ */
213
+ interface ColourData {
214
+ readonly r: number;
215
+ readonly g: number;
216
+ readonly b: number;
217
+ readonly a: number;
218
+ readonly format: ColourFormat;
219
+ readonly name?: string;
220
+ }
172
221
  /**
173
222
  * Discriminated union tag for {@link Value} objects.
174
223
  *
@@ -198,7 +247,9 @@ declare enum ValueType {
198
247
  /** Async result pending resolution. Value stores the queryKey string. */
199
248
  Pending = 12,
200
249
  /** Plugin-raised error propagated through the DAG. Value stores error code, unit stores message. */
201
- Error = 13
250
+ Error = 13,
251
+ /** A colour (hex/rgb/hsl/named). Value is {@link ColourData}. */
252
+ Colour = 14
202
253
  }
203
254
  /**
204
255
  * Universal runtime value for the solve-js VM.
@@ -214,16 +265,81 @@ declare enum ValueType {
214
265
  declare class Value {
215
266
  private _cachedNumber;
216
267
  type: ValueType;
217
- value: number | bigint | string | boolean | MatrixData | RangeData | SymbolicNode;
268
+ value: number | bigint | string | boolean | MatrixData | RangeData | ColourData | SymbolicNode;
218
269
  unit?: string;
219
270
  /** Set by async resolvers when a fetch timed out, the result is a fallback (typically 0). */
220
271
  timedOut?: boolean;
221
- constructor(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | SymbolicNode, unit?: string);
272
+ /**
273
+ * The exact base-ten value this Value stands for, when it has one.
274
+ *
275
+ * A sidecar rather than a replacement for `value`: money and decimal-point
276
+ * literals set it to a {@link DecimalData} so that same-currency arithmetic
277
+ * and display can be exact ("$0.10 + $0.20" is "$0.30", not
278
+ * "$0.30000000000000004"), while `value` stays the nearest double so every
279
+ * existing consumer that reads `.value` or `toNumber()` is unchanged. The
280
+ * plain Number-times-Number fast paths deliberately ignore it, which is why
281
+ * a bare "0.1 + 0.2" still answers the double it always did: exactness is
282
+ * carried only where a unit-bearing operand asks for it. Cleared by
283
+ * {@link recycle} so a reused arena Value never inherits a stale exact.
284
+ */
285
+ exact?: DecimalData;
286
+ /**
287
+ * The exact rational value this Value stands for, when it has one.
288
+ *
289
+ * The second sidecar, the same shape as {@link exact} and for the same
290
+ * reason: a fraction has no exact base-ten form (`1/3` is not any decimal),
291
+ * so exact fraction arithmetic needs a numerator/denominator pair rather
292
+ * than a coefficient and a scale. Integer division seeds it ("1/3" carries
293
+ * the {@link Rational} 1/3), and `+`, `-`, `*`, `/` between rational-bearing
294
+ * numbers keep it reduced, so "1/49 * 49" is exactly 1 and "5/6 - 1/6 - 1/6
295
+ * - 1/6 - 1/6 - 1/6" is exactly 0 rather than the 1.6e-16 the doubles drift
296
+ * to. `value` still holds the nearest double, recomputed from the exact
297
+ * rational so accumulation error never creeps in, which is why the default
298
+ * display and every `.value`/`toNumber()` reader are unchanged. Only a
299
+ * fraction written with "/" carries it, so a decimal literal ("0.1") and a
300
+ * transcendental result ("sqrt(2)") stay the plain doubles they were.
301
+ * Cleared by {@link recycle} alongside {@link exact}.
302
+ */
303
+ rational?: Rational;
304
+ /**
305
+ * The one-sigma uncertainty (standard error) this value carries, when it
306
+ * has one.
307
+ *
308
+ * The third sidecar, the same shape as {@link exact} and {@link rational}
309
+ * and for the same reason: a measurement written `12.3 ± 0.5` is still the
310
+ * number 12.3 everywhere it is read as one, so the type stays
311
+ * {@link ValueType.Number} and `value` stays the center, while this non
312
+ * negative field carries the tolerance. The `±` (or ASCII `+/-`) operator
313
+ * seeds it, and `+`, `-`, `*`, `/` propagate it in quadrature for
314
+ * independent errors (see `vm/VMConversion.ts`'s `uncertainOp`). Everything
315
+ * else (a comparison, a transcendental function, a unit conversion) reads
316
+ * the center through `toNumber()` and drops the tolerance, which is why a
317
+ * value with no uncertainty behaves exactly as a plain number always did.
318
+ * Cleared by {@link recycle} alongside the other two sidecars.
319
+ */
320
+ uncertainty?: number;
321
+ /**
322
+ * The number of decimal places this value should DISPLAY at, when it has been
323
+ * given an explicit precision.
324
+ *
325
+ * A display sidecar, not a value one: `value` is unchanged, so every
326
+ * `.value`/`toNumber()` reader and all arithmetic behave exactly as before,
327
+ * and a value with no `decimalPlaces` formats the way it always did (the
328
+ * global two-place default with trailing zeros trimmed). It is set only by an
329
+ * explicit precision request, `<x> to N dp` and `round(x, N)`, so that
330
+ * `3.14159 to 4 dp` shows `3.1416` and `1.5 to 2 dp` shows `1.50` rather than
331
+ * the value being rounded but then displayed at the default two places. It is
332
+ * NOT propagated through arithmetic (a later `+ 1` re-decides precision),
333
+ * which is why nothing that did not ask for a precision is affected. Cleared
334
+ * by {@link recycle} alongside the other sidecars.
335
+ */
336
+ decimalPlaces?: number;
337
+ constructor(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | ColourData | SymbolicNode, unit?: string);
222
338
  /**
223
339
  * Phase 5.3: Reset all fields for arena reuse.
224
340
  * Called by ValueArena.acquire(), zero allocation, just field assignment.
225
341
  */
226
- recycle(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | SymbolicNode, unit?: string): void;
342
+ recycle(type: ValueType, value: number | bigint | string | boolean | MatrixData | RangeData | ColourData | SymbolicNode, unit?: string): void;
227
343
  isNumber(): this is Value & {
228
344
  value: number;
229
345
  };
@@ -244,6 +360,9 @@ declare class Value {
244
360
  isRange(): this is Value & {
245
361
  value: RangeData;
246
362
  };
363
+ isColour(): this is Value & {
364
+ value: ColourData;
365
+ };
247
366
  isSymbolic(): this is Value & {
248
367
  value: SymbolicNode;
249
368
  };
@@ -295,4 +414,4 @@ declare function colVectorValue(data: readonly number[]): Value;
295
414
  /** Create a Range value, a first-class integer range `min:max`, both bounds inclusive. */
296
415
  declare function rangeValue(min: number, max: number): Value;
297
416
 
298
- export { type MatrixData as M, type RangeData as R, type SymbolicNode as S, Value as V, type MatrixEntry as a, ValueType as b, bigIntValue as c, colVectorValue as d, rowVectorValue as e, hexValue as h, matrixValue as m, numberValue as n, rangeValue as r, stringValue as s, uomValue as u };
417
+ export { type ColourData as C, type MatrixData as M, type RangeData as R, type SymbolicNode as S, Value as V, type MatrixEntry as a, ValueType as b, bigIntValue as c, colVectorValue as d, rowVectorValue as e, type ColourFormat as f, hexValue as h, matrixValue as m, numberValue as n, rangeValue as r, stringValue as s, uomValue as u };