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
@@ -0,0 +1,271 @@
1
+ import { I as IEnginePackage, E as ExpressionEngine } from './PackageRegistry-BHWJP83F.cjs';
2
+ import { V as Value } from './Value-BUi1RA3S.cjs';
3
+ import { E as EngineError } from './EngineError-B61GS1jp.cjs';
4
+ import { C as CompatibilityReport } from './PackageCompatibility-B-7rK1TD.cjs';
5
+ import './Parselet-BHcgK9S7.cjs';
6
+ import './BytecodeBuilder-Bp9xeTmX.cjs';
7
+ import './Token-B1hdkedD.cjs';
8
+ import './pipeline-CtfJtPQc.cjs';
9
+ import './variables.cjs';
10
+ import './Lexer-BOs7euZe.cjs';
11
+ import './resolvers.cjs';
12
+ import '@tanstack/query-core';
13
+ import './TokenNormalizer-DRc1Js1V.cjs';
14
+ import './ScopeManager-8vf02dwj.cjs';
15
+ import './Configuration-C9W8tJv_.cjs';
16
+
17
+ /**
18
+ * A test kit for package authors, the supported way to test a package by the
19
+ * expressions it enables rather than by the opcodes it emits.
20
+ *
21
+ * Before this entry point a package author had two options, both bad: reach
22
+ * into engine internals, or assert on whatever bytecode a parselet happened to
23
+ * emit. The first is unstable across engine versions, the second pins the
24
+ * implementation instead of the behaviour, so a refactor that keeps every
25
+ * answer correct still breaks the tests. This module is a thin, dependency-free
26
+ * layer over the same public surface a host uses: it constructs an
27
+ * {@link ExpressionEngine} with the given packages and evaluates strings.
28
+ *
29
+ * It speaks in expressions. {@link expectExpression} evaluates a string and
30
+ * asserts on the result or the failure code; {@link expectPackage} asserts on
31
+ * the three mistakes a package actually makes (shadowing prose, colliding with
32
+ * another package's vocabulary, and declaring an `engineVersion` range that the
33
+ * running engine does not satisfy).
34
+ *
35
+ * Framework-agnostic on purpose. Nothing here imports jest, vitest or any
36
+ * runner: an assertion that fails throws an {@link ExpectationError}, and one
37
+ * that passes returns, so the kit drops into whatever runner the author already
38
+ * has (or into a plain script, or `node:assert`). Runtime dependency-free and
39
+ * side-effect free, so `solve-engine/testing` stays honest under the package's
40
+ * `"sideEffects": false` contract.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * import { createTestEngine, expectExpression } from "solve-engine/testing";
45
+ *
46
+ * const engine = createTestEngine([myPackage]);
47
+ * expectExpression(engine, "2 gp + 3 gp").toEqual(5, "gp");
48
+ * expectExpression(engine, "gp").toFailWith("UNDEFINED_VARIABLE");
49
+ * ```
50
+ */
51
+
52
+ /**
53
+ * The failure a matcher throws when an expectation is not met.
54
+ *
55
+ * A dedicated `Error` subclass rather than a bare `throw new Error`, so a
56
+ * caller (or a reporter) can tell a kit assertion failure apart from an
57
+ * unrelated exception with `instanceof`, and so the structured `expected` and
58
+ * `actual` fields survive alongside the human-readable message. This is NOT an
59
+ * {@link EngineError}: that type classifies pipeline-stage failures inside the
60
+ * engine, whereas this classifies a test-assertion mismatch, a different
61
+ * concern that would be miscategorised under any of `EngineError`'s categories.
62
+ */
63
+ declare class ExpectationError extends Error {
64
+ /** A short, machine-readable label for which expectation failed. */
65
+ readonly code: string;
66
+ /** What the matcher was told to expect, in plain words. */
67
+ readonly expected?: string;
68
+ /** What it found instead. */
69
+ readonly actual?: string;
70
+ constructor(init: {
71
+ code: string;
72
+ message: string;
73
+ expected?: string;
74
+ actual?: string;
75
+ });
76
+ }
77
+ /** Options for {@link createTestEngine}. */
78
+ interface TestEngineOptions {
79
+ /** Locale passed to the engine. Defaults to `"en"`. */
80
+ locale?: string;
81
+ /**
82
+ * Whether to load the engine's built-in packages (arithmetic, units, dates,
83
+ * and the rest) before the packages under test. Defaults to `true`, because
84
+ * almost every package builds on arithmetic and most authors want a
85
+ * realistic engine. Set `false` to test a package in isolation.
86
+ */
87
+ includeBuiltins?: boolean;
88
+ }
89
+ /**
90
+ * Build an {@link ExpressionEngine} loaded with the packages under test.
91
+ *
92
+ * The built-in packages load first (unless {@link TestEngineOptions.includeBuiltins}
93
+ * is `false`), then each package in `packages` is registered in order, exactly
94
+ * as a host would. Registration is honest: a package whose declared
95
+ * `engineVersion` the running engine does not satisfy, or whose lexer keyword
96
+ * collides with a built-in, throws here rather than being swallowed. That is
97
+ * deliberately different from passing packages straight to the
98
+ * `ExpressionEngine` constructor, which contains a bad package by logging and
99
+ * continuing, useful in production, wrong for a test that needs to know the
100
+ * package it is testing actually loaded.
101
+ *
102
+ * Call {@link ExpressionEngine.clear} when a test is finished with the engine
103
+ * if the test creates many, see the engine's own lifecycle note.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * const engine = createTestEngine([myPackage]);
108
+ * const engineOnly = createTestEngine([myPackage], { includeBuiltins: false });
109
+ * ```
110
+ */
111
+ declare function createTestEngine(packages?: IEnginePackage[], options?: TestEngineOptions): ExpressionEngine;
112
+ /**
113
+ * The normalised outcome of evaluating one expression, computed once when
114
+ * {@link expectExpression} runs and read by every matcher on the result.
115
+ *
116
+ * The engine surfaces a failure two different ways, and a package author should
117
+ * not have to know which: a parse/eval error is THROWN as an
118
+ * {@link EngineError} (with a `.code`), while a plugin-raised error is RETURNED
119
+ * as a {@link Value} of {@link ValueType.Error} (its `value` is the code, its
120
+ * `unit` is the message). Both collapse to the `"error"` status here. A value
121
+ * still resolving asynchronously is its own `"pending"` status, since the kit
122
+ * evaluates synchronously and cannot report a final number for it.
123
+ */
124
+ type Outcome = {
125
+ status: "value";
126
+ value: Value;
127
+ } | {
128
+ status: "pending";
129
+ value: Value;
130
+ } | {
131
+ status: "error";
132
+ code: string;
133
+ message: string;
134
+ source: EngineError | Value;
135
+ };
136
+ /**
137
+ * Assertions about the result of one evaluated expression, returned by
138
+ * {@link expectExpression}. Every matcher returns `this`, so assertions chain,
139
+ * and throws an {@link ExpectationError} when it fails.
140
+ */
141
+ declare class ExpressionAssertion {
142
+ private readonly expression;
143
+ private readonly outcome;
144
+ constructor(expression: string, outcome: Outcome);
145
+ /** The raw resolved {@link Value}, for an assertion the matchers do not cover. Throws if the expression failed. */
146
+ get value(): Value;
147
+ /**
148
+ * Assert the expression evaluated to a value (not an error, not a pending
149
+ * async result). Says nothing about what the value is.
150
+ */
151
+ toEvaluate(): this;
152
+ /**
153
+ * Assert the expression evaluated to `expected`, and, when `unit` is given,
154
+ * that the result carries exactly that unit. A number compares against the
155
+ * value's numeric magnitude within a small floating-point tolerance; a
156
+ * string compares against the value's own string; a boolean against its
157
+ * boolean reading. Omitting `unit` leaves the unit unchecked, so
158
+ * `toEqual(5)` passes for both `5` and `5 gp`.
159
+ */
160
+ toEqual(expected: number | string | boolean, unit?: string): this;
161
+ /** Assert the expression failed (a thrown engine error or a plugin-raised error value). Says nothing about the code. */
162
+ toBeError(): this;
163
+ /**
164
+ * Assert the expression failed with exactly `code`, the error catalog code
165
+ * (e.g. `"UNDEFINED_VARIABLE"`, or a package's own code). Reports the code
166
+ * it actually got when they differ, so a near-miss is obvious.
167
+ */
168
+ toFailWith(code: string): this;
169
+ /**
170
+ * Assert the expression returned a value still resolving asynchronously.
171
+ * The kit evaluates synchronously, so a package whose result comes from an
172
+ * async resolver reports pending on first evaluation, this is how a test
173
+ * confirms the async path was taken without resolving it.
174
+ */
175
+ toBePending(): this;
176
+ }
177
+ /**
178
+ * Evaluate `expression` on `engine` and return an assertion object.
179
+ *
180
+ * The expression is evaluated once, immediately, and every matcher reads that
181
+ * one outcome, so calling several matchers on the same result does not
182
+ * re-evaluate.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * expectExpression(engine, "2 gp + 3 gp").toEqual(5, "gp");
187
+ * expectExpression(engine, "gp").toFailWith("UNDEFINED_VARIABLE");
188
+ * ```
189
+ */
190
+ declare function expectExpression(engine: ExpressionEngine, expression: string): ExpressionAssertion;
191
+ /**
192
+ * A modest set of everyday English words a package's keywords should not claim.
193
+ *
194
+ * A starting point for {@link PackageAssertion.notToShadow}, not an exhaustive
195
+ * dictionary: it leans on the function words and common nouns/verbs a package
196
+ * is most tempted to grab as a trigger (`price`, `of`, `per`, `sum`, `total`).
197
+ * Pass your own list to check against the specific prose your package sits in.
198
+ */
199
+ declare const COMMON_PROSE_WORDS: readonly string[];
200
+ /** How strict {@link PackageAssertion.notToCollideWith} is about the conflicts it will accept. */
201
+ type CollisionStrictness = "error" | "warning" | "info";
202
+ /** One shadowed word found by {@link PackageAssertion.notToShadow}: the prose word and how the package claims it. */
203
+ interface ShadowedWord {
204
+ /** The prose word the package claims. */
205
+ word: string;
206
+ /** Which descriptor field claims it. */
207
+ via: "keyword" | "unit" | "operator" | "phrase";
208
+ /** The token type the word becomes, when the field maps to one. */
209
+ tokenType?: string;
210
+ }
211
+ /**
212
+ * Assertions about a package's declared descriptor, returned by
213
+ * {@link expectPackage}. These catch the three mistakes the issue calls out,
214
+ * before the package is ever registered. Every matcher returns `this` and
215
+ * throws an {@link ExpectationError} when it fails.
216
+ */
217
+ declare class PackageAssertion {
218
+ private readonly pkg;
219
+ constructor(pkg: IEnginePackage);
220
+ /**
221
+ * Assert none of the package's claimed words shadow a prose word.
222
+ *
223
+ * A trigger word that is also an ordinary word turns a line of prose into
224
+ * arithmetic, the single most common package mistake and the subject of the
225
+ * trigger-words guide. This checks the words the package declares
226
+ * (lexer keywords, units, operators, and single-word phrases) against
227
+ * `words` (defaulting to {@link COMMON_PROSE_WORDS}), case-insensitively.
228
+ *
229
+ * @returns the shadowed words, empty when the package is clean, for a test
230
+ * that wants to inspect rather than assert.
231
+ */
232
+ notToShadow(words?: readonly string[]): ShadowedWord[];
233
+ /**
234
+ * Assert the package does not collide with any of `others` in a way that
235
+ * would silently break one of them (two packages claiming the same lexer
236
+ * keyword, plugin-function index, async-resolver namespace, and so on).
237
+ *
238
+ * `strictness` sets which severities fail the assertion: `"error"` (the
239
+ * default) fails only on the collisions that always break something,
240
+ * `"warning"` also fails on the ones that silently pick a winner, `"info"`
241
+ * fails on cosmetic overlaps too.
242
+ *
243
+ * @returns the full compatibility report, so a test can inspect every
244
+ * conflict, including ones below the strictness threshold.
245
+ */
246
+ notToCollideWith(others: IEnginePackage[], strictness?: CollisionStrictness): CompatibilityReport;
247
+ /**
248
+ * Assert the package's declared `engineVersion` range is satisfied by the
249
+ * running engine (or by `engineVersion` when given).
250
+ *
251
+ * A package that declares a range and never checks it resolves is the third
252
+ * mistake the issue names: it looks fine until the one engine version it
253
+ * cannot run against, where the range either fails to match or turns out to
254
+ * be a malformed string. A package with no declared range passes (no
255
+ * constraint is always compatible).
256
+ */
257
+ toDeclareCompatibleEngineVersion(engineVersion?: string): this;
258
+ }
259
+ /**
260
+ * Begin an assertion about a package's declared descriptor.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * expectPackage(myPackage).notToShadow(["price", "in", "of"]);
265
+ * expectPackage(myPackage).notToCollideWith(BUILTIN_PACKAGES);
266
+ * expectPackage(myPackage).toDeclareCompatibleEngineVersion();
267
+ * ```
268
+ */
269
+ declare function expectPackage(pkg: IEnginePackage): PackageAssertion;
270
+
271
+ export { COMMON_PROSE_WORDS, type CollisionStrictness, ExpectationError, ExpressionAssertion, PackageAssertion, type ShadowedWord, type TestEngineOptions, createTestEngine, expectExpression, expectPackage };
@@ -0,0 +1,271 @@
1
+ import { I as IEnginePackage, E as ExpressionEngine } from './PackageRegistry-_8rDlvxI.js';
2
+ import { V as Value } from './Value-BUi1RA3S.js';
3
+ import { E as EngineError } from './EngineError-B61GS1jp.js';
4
+ import { C as CompatibilityReport } from './PackageCompatibility-Dh59eF-X.js';
5
+ import './Parselet-BBT8riYh.js';
6
+ import './BytecodeBuilder-Bp9xeTmX.js';
7
+ import './Token-B1hdkedD.js';
8
+ import './pipeline-DCd5M6Gk.js';
9
+ import './variables.js';
10
+ import './Lexer-CSI_lwbW.js';
11
+ import './resolvers.js';
12
+ import '@tanstack/query-core';
13
+ import './TokenNormalizer-C6VzZgHa.js';
14
+ import './ScopeManager-CxA24W5n.js';
15
+ import './Configuration-C9W8tJv_.js';
16
+
17
+ /**
18
+ * A test kit for package authors, the supported way to test a package by the
19
+ * expressions it enables rather than by the opcodes it emits.
20
+ *
21
+ * Before this entry point a package author had two options, both bad: reach
22
+ * into engine internals, or assert on whatever bytecode a parselet happened to
23
+ * emit. The first is unstable across engine versions, the second pins the
24
+ * implementation instead of the behaviour, so a refactor that keeps every
25
+ * answer correct still breaks the tests. This module is a thin, dependency-free
26
+ * layer over the same public surface a host uses: it constructs an
27
+ * {@link ExpressionEngine} with the given packages and evaluates strings.
28
+ *
29
+ * It speaks in expressions. {@link expectExpression} evaluates a string and
30
+ * asserts on the result or the failure code; {@link expectPackage} asserts on
31
+ * the three mistakes a package actually makes (shadowing prose, colliding with
32
+ * another package's vocabulary, and declaring an `engineVersion` range that the
33
+ * running engine does not satisfy).
34
+ *
35
+ * Framework-agnostic on purpose. Nothing here imports jest, vitest or any
36
+ * runner: an assertion that fails throws an {@link ExpectationError}, and one
37
+ * that passes returns, so the kit drops into whatever runner the author already
38
+ * has (or into a plain script, or `node:assert`). Runtime dependency-free and
39
+ * side-effect free, so `solve-engine/testing` stays honest under the package's
40
+ * `"sideEffects": false` contract.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * import { createTestEngine, expectExpression } from "solve-engine/testing";
45
+ *
46
+ * const engine = createTestEngine([myPackage]);
47
+ * expectExpression(engine, "2 gp + 3 gp").toEqual(5, "gp");
48
+ * expectExpression(engine, "gp").toFailWith("UNDEFINED_VARIABLE");
49
+ * ```
50
+ */
51
+
52
+ /**
53
+ * The failure a matcher throws when an expectation is not met.
54
+ *
55
+ * A dedicated `Error` subclass rather than a bare `throw new Error`, so a
56
+ * caller (or a reporter) can tell a kit assertion failure apart from an
57
+ * unrelated exception with `instanceof`, and so the structured `expected` and
58
+ * `actual` fields survive alongside the human-readable message. This is NOT an
59
+ * {@link EngineError}: that type classifies pipeline-stage failures inside the
60
+ * engine, whereas this classifies a test-assertion mismatch, a different
61
+ * concern that would be miscategorised under any of `EngineError`'s categories.
62
+ */
63
+ declare class ExpectationError extends Error {
64
+ /** A short, machine-readable label for which expectation failed. */
65
+ readonly code: string;
66
+ /** What the matcher was told to expect, in plain words. */
67
+ readonly expected?: string;
68
+ /** What it found instead. */
69
+ readonly actual?: string;
70
+ constructor(init: {
71
+ code: string;
72
+ message: string;
73
+ expected?: string;
74
+ actual?: string;
75
+ });
76
+ }
77
+ /** Options for {@link createTestEngine}. */
78
+ interface TestEngineOptions {
79
+ /** Locale passed to the engine. Defaults to `"en"`. */
80
+ locale?: string;
81
+ /**
82
+ * Whether to load the engine's built-in packages (arithmetic, units, dates,
83
+ * and the rest) before the packages under test. Defaults to `true`, because
84
+ * almost every package builds on arithmetic and most authors want a
85
+ * realistic engine. Set `false` to test a package in isolation.
86
+ */
87
+ includeBuiltins?: boolean;
88
+ }
89
+ /**
90
+ * Build an {@link ExpressionEngine} loaded with the packages under test.
91
+ *
92
+ * The built-in packages load first (unless {@link TestEngineOptions.includeBuiltins}
93
+ * is `false`), then each package in `packages` is registered in order, exactly
94
+ * as a host would. Registration is honest: a package whose declared
95
+ * `engineVersion` the running engine does not satisfy, or whose lexer keyword
96
+ * collides with a built-in, throws here rather than being swallowed. That is
97
+ * deliberately different from passing packages straight to the
98
+ * `ExpressionEngine` constructor, which contains a bad package by logging and
99
+ * continuing, useful in production, wrong for a test that needs to know the
100
+ * package it is testing actually loaded.
101
+ *
102
+ * Call {@link ExpressionEngine.clear} when a test is finished with the engine
103
+ * if the test creates many, see the engine's own lifecycle note.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * const engine = createTestEngine([myPackage]);
108
+ * const engineOnly = createTestEngine([myPackage], { includeBuiltins: false });
109
+ * ```
110
+ */
111
+ declare function createTestEngine(packages?: IEnginePackage[], options?: TestEngineOptions): ExpressionEngine;
112
+ /**
113
+ * The normalised outcome of evaluating one expression, computed once when
114
+ * {@link expectExpression} runs and read by every matcher on the result.
115
+ *
116
+ * The engine surfaces a failure two different ways, and a package author should
117
+ * not have to know which: a parse/eval error is THROWN as an
118
+ * {@link EngineError} (with a `.code`), while a plugin-raised error is RETURNED
119
+ * as a {@link Value} of {@link ValueType.Error} (its `value` is the code, its
120
+ * `unit` is the message). Both collapse to the `"error"` status here. A value
121
+ * still resolving asynchronously is its own `"pending"` status, since the kit
122
+ * evaluates synchronously and cannot report a final number for it.
123
+ */
124
+ type Outcome = {
125
+ status: "value";
126
+ value: Value;
127
+ } | {
128
+ status: "pending";
129
+ value: Value;
130
+ } | {
131
+ status: "error";
132
+ code: string;
133
+ message: string;
134
+ source: EngineError | Value;
135
+ };
136
+ /**
137
+ * Assertions about the result of one evaluated expression, returned by
138
+ * {@link expectExpression}. Every matcher returns `this`, so assertions chain,
139
+ * and throws an {@link ExpectationError} when it fails.
140
+ */
141
+ declare class ExpressionAssertion {
142
+ private readonly expression;
143
+ private readonly outcome;
144
+ constructor(expression: string, outcome: Outcome);
145
+ /** The raw resolved {@link Value}, for an assertion the matchers do not cover. Throws if the expression failed. */
146
+ get value(): Value;
147
+ /**
148
+ * Assert the expression evaluated to a value (not an error, not a pending
149
+ * async result). Says nothing about what the value is.
150
+ */
151
+ toEvaluate(): this;
152
+ /**
153
+ * Assert the expression evaluated to `expected`, and, when `unit` is given,
154
+ * that the result carries exactly that unit. A number compares against the
155
+ * value's numeric magnitude within a small floating-point tolerance; a
156
+ * string compares against the value's own string; a boolean against its
157
+ * boolean reading. Omitting `unit` leaves the unit unchecked, so
158
+ * `toEqual(5)` passes for both `5` and `5 gp`.
159
+ */
160
+ toEqual(expected: number | string | boolean, unit?: string): this;
161
+ /** Assert the expression failed (a thrown engine error or a plugin-raised error value). Says nothing about the code. */
162
+ toBeError(): this;
163
+ /**
164
+ * Assert the expression failed with exactly `code`, the error catalog code
165
+ * (e.g. `"UNDEFINED_VARIABLE"`, or a package's own code). Reports the code
166
+ * it actually got when they differ, so a near-miss is obvious.
167
+ */
168
+ toFailWith(code: string): this;
169
+ /**
170
+ * Assert the expression returned a value still resolving asynchronously.
171
+ * The kit evaluates synchronously, so a package whose result comes from an
172
+ * async resolver reports pending on first evaluation, this is how a test
173
+ * confirms the async path was taken without resolving it.
174
+ */
175
+ toBePending(): this;
176
+ }
177
+ /**
178
+ * Evaluate `expression` on `engine` and return an assertion object.
179
+ *
180
+ * The expression is evaluated once, immediately, and every matcher reads that
181
+ * one outcome, so calling several matchers on the same result does not
182
+ * re-evaluate.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * expectExpression(engine, "2 gp + 3 gp").toEqual(5, "gp");
187
+ * expectExpression(engine, "gp").toFailWith("UNDEFINED_VARIABLE");
188
+ * ```
189
+ */
190
+ declare function expectExpression(engine: ExpressionEngine, expression: string): ExpressionAssertion;
191
+ /**
192
+ * A modest set of everyday English words a package's keywords should not claim.
193
+ *
194
+ * A starting point for {@link PackageAssertion.notToShadow}, not an exhaustive
195
+ * dictionary: it leans on the function words and common nouns/verbs a package
196
+ * is most tempted to grab as a trigger (`price`, `of`, `per`, `sum`, `total`).
197
+ * Pass your own list to check against the specific prose your package sits in.
198
+ */
199
+ declare const COMMON_PROSE_WORDS: readonly string[];
200
+ /** How strict {@link PackageAssertion.notToCollideWith} is about the conflicts it will accept. */
201
+ type CollisionStrictness = "error" | "warning" | "info";
202
+ /** One shadowed word found by {@link PackageAssertion.notToShadow}: the prose word and how the package claims it. */
203
+ interface ShadowedWord {
204
+ /** The prose word the package claims. */
205
+ word: string;
206
+ /** Which descriptor field claims it. */
207
+ via: "keyword" | "unit" | "operator" | "phrase";
208
+ /** The token type the word becomes, when the field maps to one. */
209
+ tokenType?: string;
210
+ }
211
+ /**
212
+ * Assertions about a package's declared descriptor, returned by
213
+ * {@link expectPackage}. These catch the three mistakes the issue calls out,
214
+ * before the package is ever registered. Every matcher returns `this` and
215
+ * throws an {@link ExpectationError} when it fails.
216
+ */
217
+ declare class PackageAssertion {
218
+ private readonly pkg;
219
+ constructor(pkg: IEnginePackage);
220
+ /**
221
+ * Assert none of the package's claimed words shadow a prose word.
222
+ *
223
+ * A trigger word that is also an ordinary word turns a line of prose into
224
+ * arithmetic, the single most common package mistake and the subject of the
225
+ * trigger-words guide. This checks the words the package declares
226
+ * (lexer keywords, units, operators, and single-word phrases) against
227
+ * `words` (defaulting to {@link COMMON_PROSE_WORDS}), case-insensitively.
228
+ *
229
+ * @returns the shadowed words, empty when the package is clean, for a test
230
+ * that wants to inspect rather than assert.
231
+ */
232
+ notToShadow(words?: readonly string[]): ShadowedWord[];
233
+ /**
234
+ * Assert the package does not collide with any of `others` in a way that
235
+ * would silently break one of them (two packages claiming the same lexer
236
+ * keyword, plugin-function index, async-resolver namespace, and so on).
237
+ *
238
+ * `strictness` sets which severities fail the assertion: `"error"` (the
239
+ * default) fails only on the collisions that always break something,
240
+ * `"warning"` also fails on the ones that silently pick a winner, `"info"`
241
+ * fails on cosmetic overlaps too.
242
+ *
243
+ * @returns the full compatibility report, so a test can inspect every
244
+ * conflict, including ones below the strictness threshold.
245
+ */
246
+ notToCollideWith(others: IEnginePackage[], strictness?: CollisionStrictness): CompatibilityReport;
247
+ /**
248
+ * Assert the package's declared `engineVersion` range is satisfied by the
249
+ * running engine (or by `engineVersion` when given).
250
+ *
251
+ * A package that declares a range and never checks it resolves is the third
252
+ * mistake the issue names: it looks fine until the one engine version it
253
+ * cannot run against, where the range either fails to match or turns out to
254
+ * be a malformed string. A package with no declared range passes (no
255
+ * constraint is always compatible).
256
+ */
257
+ toDeclareCompatibleEngineVersion(engineVersion?: string): this;
258
+ }
259
+ /**
260
+ * Begin an assertion about a package's declared descriptor.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * expectPackage(myPackage).notToShadow(["price", "in", "of"]);
265
+ * expectPackage(myPackage).notToCollideWith(BUILTIN_PACKAGES);
266
+ * expectPackage(myPackage).toDeclareCompatibleEngineVersion();
267
+ * ```
268
+ */
269
+ declare function expectPackage(pkg: IEnginePackage): PackageAssertion;
270
+
271
+ export { COMMON_PROSE_WORDS, type CollisionStrictness, ExpectationError, ExpressionAssertion, PackageAssertion, type ShadowedWord, type TestEngineOptions, createTestEngine, expectExpression, expectPackage };