solve-engine 2.23.0 → 2.24.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.
- package/README.md +82 -32
- package/dist/{Configuration-BGQn-cJ8.d.cts → Configuration-DnzYPmoK.d.cts} +33 -0
- package/dist/{Configuration-BGQn-cJ8.d.ts → Configuration-DnzYPmoK.d.ts} +33 -0
- package/dist/{EngineError-DTk7I7hZ.d.cts → EngineError-D1kXsjIj.d.cts} +4 -0
- package/dist/{EngineError-DTk7I7hZ.d.ts → EngineError-D1kXsjIj.d.ts} +4 -0
- package/dist/{PackageCompatibility-BGXSFuL9.d.ts → PackageCompatibility-Bq82y0ZN.d.ts} +1 -1
- package/dist/{PackageCompatibility-BZCqaTOO.d.cts → PackageCompatibility-CeAPVHZr.d.cts} +1 -1
- package/dist/{PackageRegistry-rMGnTh7W.d.cts → PackageRegistry-B9-7fJGH.d.cts} +16 -11
- package/dist/{PackageRegistry-BRoVOYzg.d.ts → PackageRegistry-CDEgfhT_.d.ts} +16 -11
- package/dist/{Parselet-Bkbp9CKD.d.cts → Parselet-4oH5OuRt.d.cts} +1 -1
- package/dist/{Parselet-B-WyUtX4.d.ts → Parselet-BwDqKFfK.d.ts} +1 -1
- package/dist/{ScopeManager-gB9UengK.d.ts → ScopeManager-BQBhlDAu.d.ts} +38 -7
- package/dist/{ScopeManager-bCYewWVt.d.cts → ScopeManager-DrXB3Nvm.d.cts} +38 -7
- package/dist/{VMCheckpoints-DGar9Yg8.d.cts → VMCheckpoints-BK32PTl2.d.cts} +2 -2
- package/dist/{VMCheckpoints-BDRY1Kx8.d.ts → VMCheckpoints-C5jC92o3.d.ts} +2 -2
- package/dist/{Value-DCTqTSeP.d.cts → Value-Ds3Gy07C.d.cts} +1 -1
- package/dist/{Value-DCTqTSeP.d.ts → Value-Ds3Gy07C.d.ts} +1 -1
- package/dist/{WorkerError-DpZgKRks.d.ts → WorkerError-CVQbs6_D.d.ts} +1 -1
- package/dist/{WorkerError-DGBNM3gA.d.cts → WorkerError-DaNWQFp1.d.cts} +1 -1
- package/dist/chunk-2BXNZM3G.js +2 -0
- package/dist/chunk-2BXNZM3G.js.map +1 -0
- package/dist/chunk-2JSNWYYA.cjs +3 -0
- package/dist/chunk-2JSNWYYA.cjs.map +1 -0
- package/dist/{chunk-J7ABVGZH.cjs → chunk-2VE4OW4A.cjs} +2 -2
- package/dist/{chunk-J7ABVGZH.cjs.map → chunk-2VE4OW4A.cjs.map} +1 -1
- package/dist/{chunk-HMC7FJO7.js → chunk-3SHWTGTP.js} +2 -2
- package/dist/{chunk-HMC7FJO7.js.map → chunk-3SHWTGTP.js.map} +1 -1
- package/dist/{chunk-EN3CDYOS.cjs → chunk-5GL4SAVH.cjs} +2 -2
- package/dist/{chunk-EN3CDYOS.cjs.map → chunk-5GL4SAVH.cjs.map} +1 -1
- package/dist/{chunk-SGIDBZ4T.cjs → chunk-72N3ZRVE.cjs} +2 -2
- package/dist/chunk-72N3ZRVE.cjs.map +1 -0
- package/dist/{chunk-CHSO3DCS.js → chunk-7EP36NXE.js} +3 -3
- package/dist/{chunk-CHSO3DCS.js.map → chunk-7EP36NXE.js.map} +1 -1
- package/dist/chunk-A4JV7HRB.js +3 -0
- package/dist/chunk-A4JV7HRB.js.map +1 -0
- package/dist/chunk-AUOE7MFI.cjs +2 -0
- package/dist/chunk-AUOE7MFI.cjs.map +1 -0
- package/dist/chunk-BDF4VCQX.js +3 -0
- package/dist/chunk-BDF4VCQX.js.map +1 -0
- package/dist/{chunk-3SSMOVWB.cjs → chunk-CDNJZBM3.cjs} +2 -2
- package/dist/{chunk-3SSMOVWB.cjs.map → chunk-CDNJZBM3.cjs.map} +1 -1
- package/dist/{chunk-YAONCEAW.cjs → chunk-CMZSK6FY.cjs} +3 -3
- package/dist/{chunk-YAONCEAW.cjs.map → chunk-CMZSK6FY.cjs.map} +1 -1
- package/dist/chunk-D2Q2VFJ6.js +2 -0
- package/dist/chunk-D2Q2VFJ6.js.map +1 -0
- package/dist/{chunk-75JLWK4L.cjs → chunk-DA7M6H63.cjs} +2 -2
- package/dist/{chunk-75JLWK4L.cjs.map → chunk-DA7M6H63.cjs.map} +1 -1
- package/dist/chunk-ELQQKZN3.cjs +3 -0
- package/dist/{chunk-PIZQIQVM.js.map → chunk-ELQQKZN3.cjs.map} +1 -1
- package/dist/chunk-F7QIBC4B.cjs +3 -0
- package/dist/chunk-F7QIBC4B.cjs.map +1 -0
- package/dist/chunk-GP4H5OIT.js +3 -0
- package/dist/chunk-GP4H5OIT.js.map +1 -0
- package/dist/chunk-HQ7BKXG7.js +2 -0
- package/dist/chunk-HQ7BKXG7.js.map +1 -0
- package/dist/{chunk-KRXO3MOW.cjs → chunk-IHAZL4EF.cjs} +2 -2
- package/dist/chunk-IHAZL4EF.cjs.map +1 -0
- package/dist/{chunk-7E7NXKQ5.js → chunk-IJHL3LMH.js} +2 -2
- package/dist/chunk-IJHL3LMH.js.map +1 -0
- package/dist/{chunk-3GR46UOX.js → chunk-IPSOWFCA.js} +2 -2
- package/dist/{chunk-3GR46UOX.js.map → chunk-IPSOWFCA.js.map} +1 -1
- package/dist/chunk-LM6ZQUEU.js +2 -0
- package/dist/chunk-LM6ZQUEU.js.map +1 -0
- package/dist/{chunk-N37PUZJR.cjs → chunk-LTJV3VJE.cjs} +2 -2
- package/dist/{chunk-N37PUZJR.cjs.map → chunk-LTJV3VJE.cjs.map} +1 -1
- package/dist/chunk-MWWAKZOD.cjs +3 -0
- package/dist/chunk-MWWAKZOD.cjs.map +1 -0
- package/dist/chunk-NUF66H57.js +5 -0
- package/dist/chunk-NUF66H57.js.map +1 -0
- package/dist/{chunk-MRRMIBHE.js → chunk-OMHRBKAT.js} +2 -2
- package/dist/{chunk-MRRMIBHE.js.map → chunk-OMHRBKAT.js.map} +1 -1
- package/dist/{chunk-4ETS224G.js → chunk-PTQCHMYA.js} +2 -2
- package/dist/{chunk-4ETS224G.js.map → chunk-PTQCHMYA.js.map} +1 -1
- package/dist/{chunk-4DJJIUQF.js → chunk-PWWQZFAE.js} +2 -2
- package/dist/chunk-PWWQZFAE.js.map +1 -0
- package/dist/chunk-RE6AIU6Y.js +3 -0
- package/dist/chunk-RE6AIU6Y.js.map +1 -0
- package/dist/chunk-RECRD45C.cjs +5 -0
- package/dist/chunk-RECRD45C.cjs.map +1 -0
- package/dist/{chunk-O3BDXOQJ.js → chunk-RENO2AWO.js} +2 -2
- package/dist/{chunk-O3BDXOQJ.js.map → chunk-RENO2AWO.js.map} +1 -1
- package/dist/chunk-RMCN5WFO.cjs +2 -0
- package/dist/chunk-RMCN5WFO.cjs.map +1 -0
- package/dist/{chunk-TL545PXW.cjs → chunk-RN3ISTM3.cjs} +2 -2
- package/dist/{chunk-TL545PXW.cjs.map → chunk-RN3ISTM3.cjs.map} +1 -1
- package/dist/{chunk-JYLNQOPU.cjs → chunk-S46R5QZP.cjs} +2 -2
- package/dist/{chunk-JYLNQOPU.cjs.map → chunk-S46R5QZP.cjs.map} +1 -1
- package/dist/{chunk-USQ4KL6S.cjs → chunk-UPH22K2T.cjs} +2 -2
- package/dist/chunk-UPH22K2T.cjs.map +1 -0
- package/dist/{chunk-KEA5HRL3.js → chunk-UXR7JIPX.js} +2 -2
- package/dist/{chunk-KEA5HRL3.js.map → chunk-UXR7JIPX.js.map} +1 -1
- package/dist/chunk-UY3ID6JF.cjs +2 -0
- package/dist/chunk-UY3ID6JF.cjs.map +1 -0
- package/dist/{chunk-JVMINMAB.js → chunk-WFOQRPA6.js} +2 -2
- package/dist/{chunk-JVMINMAB.js.map → chunk-WFOQRPA6.js.map} +1 -1
- package/dist/{chunk-AF7HQN6I.js → chunk-XFJABR3B.js} +2 -2
- package/dist/chunk-XFJABR3B.js.map +1 -0
- package/dist/{chunk-LNZNJSRW.cjs → chunk-ZVPZQ66D.cjs} +2 -2
- package/dist/chunk-ZVPZQ66D.cjs.map +1 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +9 -9
- package/dist/engine.d.ts +9 -9
- package/dist/engine.js +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.d.cts +3 -3
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +1 -1
- package/dist/format.cjs +1 -1
- package/dist/format.d.cts +1 -1
- package/dist/format.d.ts +1 -1
- package/dist/format.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -9
- package/dist/index.d.ts +11 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/language.d.cts +8 -8
- package/dist/language.d.ts +8 -8
- package/dist/lexer.cjs +1 -1
- package/dist/lexer.js +1 -1
- package/dist/normalizer.cjs +1 -1
- package/dist/normalizer.d.cts +14 -8
- package/dist/normalizer.d.ts +14 -8
- package/dist/normalizer.js +1 -1
- package/dist/packages.cjs +1 -1
- package/dist/packages.d.cts +7 -7
- package/dist/packages.d.ts +7 -7
- package/dist/packages.js +1 -1
- package/dist/parser.cjs +1 -1
- package/dist/parser.d.cts +3 -3
- package/dist/parser.d.ts +3 -3
- package/dist/parser.js +1 -1
- package/dist/{pipeline-B4wf3M1h.d.cts → pipeline-69q2tsLE.d.cts} +1 -1
- package/dist/{pipeline-QIT4iD8f.d.ts → pipeline-DTqGLPsV.d.ts} +1 -1
- package/dist/resolvers.cjs +1 -1
- package/dist/resolvers.d.cts +18 -2
- package/dist/resolvers.d.ts +18 -2
- package/dist/resolvers.js +1 -1
- package/dist/testing.cjs +2 -2
- package/dist/testing.d.cts +8 -8
- package/dist/testing.d.ts +8 -8
- package/dist/testing.js +1 -1
- package/dist/uom.cjs +1 -1
- package/dist/uom.d.cts +1 -1
- package/dist/uom.d.ts +1 -1
- package/dist/uom.js +1 -1
- package/dist/vm.cjs +1 -1
- package/dist/vm.d.cts +7 -7
- package/dist/vm.d.ts +7 -7
- package/dist/vm.js +1 -1
- package/dist/worker.cjs +2 -2
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +31 -22
- package/dist/worker.d.ts +31 -22
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-42JXN3BH.js +0 -5
- package/dist/chunk-42JXN3BH.js.map +0 -1
- package/dist/chunk-4DJJIUQF.js.map +0 -1
- package/dist/chunk-6YM67DWH.cjs +0 -3
- package/dist/chunk-6YM67DWH.cjs.map +0 -1
- package/dist/chunk-7E7NXKQ5.js.map +0 -1
- package/dist/chunk-7SZBEE5R.cjs +0 -2
- package/dist/chunk-7SZBEE5R.cjs.map +0 -1
- package/dist/chunk-A5M4QZIK.js +0 -2
- package/dist/chunk-A5M4QZIK.js.map +0 -1
- package/dist/chunk-AF7HQN6I.js.map +0 -1
- package/dist/chunk-ALY4ZMYO.js +0 -2
- package/dist/chunk-ALY4ZMYO.js.map +0 -1
- package/dist/chunk-BRJ4RT5U.js +0 -2
- package/dist/chunk-BRJ4RT5U.js.map +0 -1
- package/dist/chunk-CO2BI6WL.cjs +0 -3
- package/dist/chunk-CO2BI6WL.cjs.map +0 -1
- package/dist/chunk-EQDT42JG.js +0 -3
- package/dist/chunk-EQDT42JG.js.map +0 -1
- package/dist/chunk-FMMT5C5H.cjs +0 -3
- package/dist/chunk-FMMT5C5H.cjs.map +0 -1
- package/dist/chunk-FP6Z6PYR.js +0 -2
- package/dist/chunk-FP6Z6PYR.js.map +0 -1
- package/dist/chunk-H3JXNH7X.cjs +0 -3
- package/dist/chunk-H3JXNH7X.cjs.map +0 -1
- package/dist/chunk-HZOLIL5T.js +0 -3
- package/dist/chunk-HZOLIL5T.js.map +0 -1
- package/dist/chunk-KRXO3MOW.cjs.map +0 -1
- package/dist/chunk-KVLJDOZ7.cjs +0 -5
- package/dist/chunk-KVLJDOZ7.cjs.map +0 -1
- package/dist/chunk-LNZNJSRW.cjs.map +0 -1
- package/dist/chunk-PIZQIQVM.js +0 -3
- package/dist/chunk-QTSDIDAS.js +0 -3
- package/dist/chunk-QTSDIDAS.js.map +0 -1
- package/dist/chunk-SGIDBZ4T.cjs.map +0 -1
- package/dist/chunk-UNKSCVIL.cjs +0 -2
- package/dist/chunk-UNKSCVIL.cjs.map +0 -1
- package/dist/chunk-USQ4KL6S.cjs.map +0 -1
- package/dist/chunk-XDXIPVZB.cjs +0 -2
- package/dist/chunk-XDXIPVZB.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -52,8 +52,14 @@ const value = engine.evaluateExpression("2 + 2 * 10");
|
|
|
52
52
|
console.log(value.toNumber()); // 22
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
Two kinds of failure, deliberately different. A line the parser cannot read
|
|
56
|
+
(`10 +`), or one that names a variable no line defined, throws an `EngineError`
|
|
57
|
+
(see `solve-engine/errors`), so wrap calls on untrusted input in a `try`/`catch`.
|
|
58
|
+
A line the engine can run but cannot answer (an impossible conversion, a rate it
|
|
59
|
+
has no data for, a live value still loading) comes back as a `Value` whose
|
|
60
|
+
`isError()` or `isPending()` says so, never as a throw, which is the right shape
|
|
61
|
+
for input being typed one character at a time. Check `isFault()` before
|
|
62
|
+
`toNumber()`: a faulted value reads as `0` through it.
|
|
57
63
|
|
|
58
64
|
For line-oriented input (e.g. a document made of multiple expressions, some referencing
|
|
59
65
|
variables defined on earlier lines), use `evaluateLine`/`parseDocument` instead, see the
|
|
@@ -104,13 +110,15 @@ they are:
|
|
|
104
110
|
|
|
105
111
|
| Subpath | Purpose |
|
|
106
112
|
|---|---|
|
|
107
|
-
| `solve-engine` | Start here
|
|
113
|
+
| `solve-engine` | Start here: `createEngine`, `ExpressionEngine`, `defineFunction`, `IEnginePackage`, and the result surface `Value`, `ValueType`, `formatValue`. |
|
|
108
114
|
| `solve-engine/engine` | `ExpressionEngine` and its supporting types (`Explanation`, `EngineSnapshot`, etc.) directly, without the package-registration wrapper. |
|
|
109
|
-
| `solve-engine/vm` | The bytecode VM: `Value`/`ValueType`, opcode dispatch
|
|
110
|
-
| `solve-engine/format` | Turning a `Value` into a display string (numbers, dates, units, vectors, ...). |
|
|
115
|
+
| `solve-engine/vm` | The bytecode VM: `Value`/`ValueType`, the value constructors, opcode dispatch. |
|
|
116
|
+
| `solve-engine/format` | Turning a `Value` into a display string (numbers, dates, units, vectors, ...) and the formatting settings. |
|
|
111
117
|
| `solve-engine/language` | Editor-agnostic language service: token categories, completions, highlighting. |
|
|
112
118
|
| `solve-engine/packages` | The built-in packages (arithmetic, datetime, time, dice, uom, currency, vector, conditionals, converters, mathphrases, ...). |
|
|
113
|
-
| `solve-engine/constants` | Engine configuration types and defaults (`EngineConfig`, `VMConfig`, ...). |
|
|
119
|
+
| `solve-engine/constants` | Engine configuration types and defaults (`EngineConfig`, `VMConfig`, `NetworkConfig`, ...). |
|
|
120
|
+
| `solve-engine/worker` | Off-main-thread evaluation: `createWorkerEngine`, `startWorkerRuntime`, the transports and the result DTOs. |
|
|
121
|
+
| `solve-engine/testing` | A test kit for package authors: `createTestEngine`, `expectExpression`, `expectPackage`. |
|
|
114
122
|
|
|
115
123
|
The following subpaths are **advanced-public**, everything a third-party package author
|
|
116
124
|
needs to extend the engine, but with a looser stability contract than the tier above (these
|
|
@@ -132,32 +140,70 @@ Anything not listed above (`telemetry`, `cache`, `diagnostics`, `types`, `worker
|
|
|
132
140
|
internal and not part of the package's public contract, it may change or disappear between
|
|
133
141
|
minor versions without notice.
|
|
134
142
|
|
|
143
|
+
## Adding a function
|
|
144
|
+
|
|
145
|
+
The shortest way to teach the engine a new name is `defineFunction`: declare the
|
|
146
|
+
name, the argument types and the return type, and get back a package that plugs
|
|
147
|
+
in beside the built-ins. No parser, no bytecode.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { createEngine, defineFunction } from "solve-engine";
|
|
151
|
+
|
|
152
|
+
const vat = defineFunction({
|
|
153
|
+
name: "vat",
|
|
154
|
+
args: [{ name: "amount", type: "number" }],
|
|
155
|
+
returns: "number",
|
|
156
|
+
call: (amount) => amount * 1.2,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const engine = createEngine({ extraPackages: [vat] });
|
|
160
|
+
engine.evaluateExpression("vat(100)").toNumber(); // 120
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Arguments are a fixed list of `number`, `string` or `boolean`, and `call` is
|
|
164
|
+
synchronous; a mismatched call produces a structured error naming the argument.
|
|
165
|
+
Anything beyond that shape (a unit-bearing argument, a phrase rather than a
|
|
166
|
+
call, live data) is a package proper.
|
|
167
|
+
|
|
135
168
|
## Authoring a package
|
|
136
169
|
|
|
137
|
-
A **package** (`IEnginePackage`) is a plain data descriptor bundling everything
|
|
138
|
-
extend the engine with a new domain:
|
|
139
|
-
|
|
140
|
-
[
|
|
141
|
-
|
|
170
|
+
A **package** (`IEnginePackage`) is a plain data descriptor bundling everything
|
|
171
|
+
needed to extend the engine with a new domain: token vocabulary, normaliser
|
|
172
|
+
rules, parselets, plugin functions, `as` converters, completions, and optional
|
|
173
|
+
async resolvers. The [package author guides](https://liamriddell.github.io/solve-engine/packages/authoring-a-package/)
|
|
174
|
+
walk through each extension point; [`IEnginePackage`](https://github.com/LiamRiddell/solve-engine/blob/main/packages/engine/src/api/PackageRegistry.ts)
|
|
175
|
+
carries the full field list with inline documentation.
|
|
142
176
|
|
|
143
|
-
Minimal shape:
|
|
177
|
+
Minimal shape, a keyword that calls a function:
|
|
144
178
|
|
|
145
179
|
```typescript
|
|
146
|
-
import { allocatePluginFunctionIndex } from "solve-engine/vm";
|
|
147
180
|
import type { IEnginePackage } from "solve-engine";
|
|
148
|
-
|
|
149
|
-
|
|
181
|
+
import type { PrefixParselet } from "solve-engine/parser";
|
|
182
|
+
import { stringValue } from "solve-engine/vm";
|
|
183
|
+
|
|
184
|
+
const reverseParselet: PrefixParselet = {
|
|
185
|
+
category: "Text",
|
|
186
|
+
parse(parser, _token, builder) {
|
|
187
|
+
parser.consume("LPAREN");
|
|
188
|
+
parser.parseExpression(0, builder);
|
|
189
|
+
parser.consume("RPAREN");
|
|
190
|
+
// By name; the engine assigns the index when the package registers.
|
|
191
|
+
builder.emitPluginCall("reverse", 1);
|
|
192
|
+
},
|
|
193
|
+
};
|
|
150
194
|
|
|
151
195
|
export const MY_PACKAGE: IEnginePackage = {
|
|
152
|
-
name: "
|
|
153
|
-
// engineVersion: "^0.
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
name: "my-package",
|
|
197
|
+
// engineVersion: "^2.0.0", // optional, see below
|
|
198
|
+
lexerVocabulary: { keywords: { reverse: "REVERSE_FN" } },
|
|
199
|
+
prefixParselets: { REVERSE_FN: reverseParselet },
|
|
200
|
+
pluginFunctions: { reverse: ([text]) => stringValue([...String(text.value)].reverse().join("")) },
|
|
156
201
|
};
|
|
157
202
|
```
|
|
158
203
|
|
|
159
|
-
Register it
|
|
160
|
-
|
|
204
|
+
Register it as one of the packages passed to the `ExpressionEngine` constructor
|
|
205
|
+
(or `createEngine`'s `extraPackages`), or at runtime via
|
|
206
|
+
`ExpressionEngine.registerPackage()` / `unregisterPackage()`.
|
|
161
207
|
|
|
162
208
|
### Declaring engine-version compatibility
|
|
163
209
|
|
|
@@ -210,17 +256,21 @@ published package, see `files` in `package.json`, only `dist/` ships):
|
|
|
210
256
|
|
|
211
257
|
## Known limitations
|
|
212
258
|
|
|
213
|
-
**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
259
|
+
**Exchange rates are shared across engines, by design.** Plugin functions, the
|
|
260
|
+
opcode registry and the lexer are owned per `ExpressionEngine`, so two engines
|
|
261
|
+
with different package sets do not interfere. The currency rate cache is the one
|
|
262
|
+
deliberate exception: rates are market data with a fifteen-minute freshness
|
|
263
|
+
window, and two engines in one process fetching the same pair separately could
|
|
264
|
+
disagree about it. A host that primes rates (`currencyExchangeService.primeRates`)
|
|
265
|
+
primes them for every engine in the process.
|
|
266
|
+
|
|
267
|
+
**A live value reaches the document only through the event stream.** When a
|
|
268
|
+
fetch lands, the engine does not push the new value at you; it emits a
|
|
269
|
+
`lines-updated` event on `engine.getEventStream()` naming the lines to
|
|
270
|
+
re-evaluate. A host that reads neither that stream nor sets
|
|
271
|
+
`AsyncResolutionBatcher.onLineResult` sees the line stay pending, and a warning
|
|
272
|
+
says so once. The [async guide](https://liamriddell.github.io/solve-engine/guide/async-and-live-data/)
|
|
273
|
+
shows the loop.
|
|
224
274
|
|
|
225
275
|
## Development
|
|
226
276
|
|
|
@@ -207,6 +207,37 @@ interface BackgroundRefreshConfig {
|
|
|
207
207
|
/** Master switch. When false, no timers run and every value refreshes only on re-evaluation. */
|
|
208
208
|
readonly enabled: boolean;
|
|
209
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Whether the engine may reach the network at all.
|
|
212
|
+
*
|
|
213
|
+
* On by default, which is the historic behaviour: a city name or a currency
|
|
214
|
+
* pair in a line fetches live data from a public endpoint as soon as the
|
|
215
|
+
* line evaluates. A host embedding the engine somewhere that must not make
|
|
216
|
+
* outbound requests (an offline document, a sandboxed evaluation, a tenant
|
|
217
|
+
* with egress rules) switches it off here and gets the same engine with no
|
|
218
|
+
* egress: every live-data form answers with a `NETWORK_DISABLED` error
|
|
219
|
+
* naming the setting, instead of a request.
|
|
220
|
+
*
|
|
221
|
+
* The gate closes before any request is made. It stops every async
|
|
222
|
+
* resolver a package registers (weather, currency, stocks, crypto,
|
|
223
|
+
* knowledge, and any host-supplied one) unless the resolver declares
|
|
224
|
+
* itself `local` (it reads engine state, never a network; the engine's own
|
|
225
|
+
* global-variable resolver is the one built-in case). It also refuses the
|
|
226
|
+
* result of a plugin function that returns a promise. That last case is a
|
|
227
|
+
* boundary rather than a guarantee: such a function has already run by the
|
|
228
|
+
* time the engine sees the promise, so a package that fetches inside a
|
|
229
|
+
* plugin function rather than through an async resolver may still have
|
|
230
|
+
* started its request. The documented shape for live data is the resolver
|
|
231
|
+
* (`createQueryResolver`), which this gate stops cold.
|
|
232
|
+
*
|
|
233
|
+
* Rates a host primes by hand (`currencyExchangeService.primeRates`) keep
|
|
234
|
+
* working with the network off, which is what an offline host with its own
|
|
235
|
+
* rate table wants.
|
|
236
|
+
*/
|
|
237
|
+
interface NetworkConfig {
|
|
238
|
+
/** Master switch. When false, no async resolver runs and no live value is fetched. */
|
|
239
|
+
readonly enabled: boolean;
|
|
240
|
+
}
|
|
210
241
|
/**
|
|
211
242
|
* Virtual Machine configuration.
|
|
212
243
|
* Controls the internal bytecode VM that executes compiled expressions.
|
|
@@ -321,6 +352,8 @@ interface EngineConfig {
|
|
|
321
352
|
readonly diagnostic: DiagnosticConfig;
|
|
322
353
|
/** Proactive background refresh of live async values */
|
|
323
354
|
readonly backgroundRefresh: BackgroundRefreshConfig;
|
|
355
|
+
/** Whether live data may be fetched at all. See {@link NetworkConfig}. */
|
|
356
|
+
readonly network: NetworkConfig;
|
|
324
357
|
}
|
|
325
358
|
/**
|
|
326
359
|
* A partial {@link EngineConfig} override, one section at a time.
|
|
@@ -207,6 +207,37 @@ interface BackgroundRefreshConfig {
|
|
|
207
207
|
/** Master switch. When false, no timers run and every value refreshes only on re-evaluation. */
|
|
208
208
|
readonly enabled: boolean;
|
|
209
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Whether the engine may reach the network at all.
|
|
212
|
+
*
|
|
213
|
+
* On by default, which is the historic behaviour: a city name or a currency
|
|
214
|
+
* pair in a line fetches live data from a public endpoint as soon as the
|
|
215
|
+
* line evaluates. A host embedding the engine somewhere that must not make
|
|
216
|
+
* outbound requests (an offline document, a sandboxed evaluation, a tenant
|
|
217
|
+
* with egress rules) switches it off here and gets the same engine with no
|
|
218
|
+
* egress: every live-data form answers with a `NETWORK_DISABLED` error
|
|
219
|
+
* naming the setting, instead of a request.
|
|
220
|
+
*
|
|
221
|
+
* The gate closes before any request is made. It stops every async
|
|
222
|
+
* resolver a package registers (weather, currency, stocks, crypto,
|
|
223
|
+
* knowledge, and any host-supplied one) unless the resolver declares
|
|
224
|
+
* itself `local` (it reads engine state, never a network; the engine's own
|
|
225
|
+
* global-variable resolver is the one built-in case). It also refuses the
|
|
226
|
+
* result of a plugin function that returns a promise. That last case is a
|
|
227
|
+
* boundary rather than a guarantee: such a function has already run by the
|
|
228
|
+
* time the engine sees the promise, so a package that fetches inside a
|
|
229
|
+
* plugin function rather than through an async resolver may still have
|
|
230
|
+
* started its request. The documented shape for live data is the resolver
|
|
231
|
+
* (`createQueryResolver`), which this gate stops cold.
|
|
232
|
+
*
|
|
233
|
+
* Rates a host primes by hand (`currencyExchangeService.primeRates`) keep
|
|
234
|
+
* working with the network off, which is what an offline host with its own
|
|
235
|
+
* rate table wants.
|
|
236
|
+
*/
|
|
237
|
+
interface NetworkConfig {
|
|
238
|
+
/** Master switch. When false, no async resolver runs and no live value is fetched. */
|
|
239
|
+
readonly enabled: boolean;
|
|
240
|
+
}
|
|
210
241
|
/**
|
|
211
242
|
* Virtual Machine configuration.
|
|
212
243
|
* Controls the internal bytecode VM that executes compiled expressions.
|
|
@@ -321,6 +352,8 @@ interface EngineConfig {
|
|
|
321
352
|
readonly diagnostic: DiagnosticConfig;
|
|
322
353
|
/** Proactive background refresh of live async values */
|
|
323
354
|
readonly backgroundRefresh: BackgroundRefreshConfig;
|
|
355
|
+
/** Whether live data may be fetched at all. See {@link NetworkConfig}. */
|
|
356
|
+
readonly network: NetworkConfig;
|
|
324
357
|
}
|
|
325
358
|
/**
|
|
326
359
|
* A partial {@link EngineConfig} override, one section at a time.
|
|
@@ -103,6 +103,8 @@ declare const CoreErrorCodes: {
|
|
|
103
103
|
readonly INTERNAL_MISSING_FUNCTION_BODY: "INTERNAL_MISSING_FUNCTION_BODY";
|
|
104
104
|
/** `MAP_INVOKE`/`REDUCE_INVOKE`'s anonymous-body-index lookup failing. Same class as `INTERNAL_MISSING_FUNCTION_BODY` above, for the `anonymousBodies` side-table instead of `userFunctionBodies`. */
|
|
105
105
|
readonly INTERNAL_MISSING_ANONYMOUS_BODY: "INTERNAL_MISSING_ANONYMOUS_BODY";
|
|
106
|
+
/** `CALL_BUILTIN` naming an index no builtin is registered at. It used to pop its arguments and push nothing, so the opcode after it read a neighbour's operand as its own; now an Error value on the stack, the shape `map`/`reduce` already used for the same fault. Bytecode-level, so unreachable from source without a compiler or snapshot fault. */
|
|
107
|
+
readonly UNKNOWN_BUILTIN_FUNCTION: "UNKNOWN_BUILTIN_FUNCTION";
|
|
106
108
|
/** An operand byte read past the end of the stream: the program ends in the middle of an instruction. */
|
|
107
109
|
readonly MALFORMED_BYTECODE_TRUNCATED: "MALFORMED_BYTECODE_TRUNCATED";
|
|
108
110
|
/** A constant-pool operand indexing a `numbers`/`strings` entry that does not exist, or that is not of the pool's type. */
|
|
@@ -164,6 +166,8 @@ declare const CoreErrorCodes: {
|
|
|
164
166
|
readonly UNKNOWN_SAVINGS_PERIOD: "UNKNOWN_SAVINGS_PERIOD";
|
|
165
167
|
/** Colon-separated numbers that are not a time any clock can show ("24:00", "9:60", "100:5"). Raised by the labeled-line fallback, which used to answer them with whatever stood after the colon. */
|
|
166
168
|
readonly INVALID_TIME_LITERAL: "INVALID_TIME_LITERAL";
|
|
169
|
+
/** A live-data form evaluated on an engine whose host switched the network off (`network.enabled: false`, see `constants/Configuration.ts`'s `NetworkConfig`). A recoverable Error value, raised by the VM for a currency conversion with no primed rate and for a plugin function that returned a promise, and by `createQueryResolver`'s plugin function when its preflight was skipped. Names the setting, so the reader knows it is policy rather than an outage. */
|
|
170
|
+
readonly NETWORK_DISABLED: "NETWORK_DISABLED";
|
|
167
171
|
/** `fromJSON()` handed an object that is not a snapshot at all, or whose serialised-shape version does not match this engine's reader. The versioning gate that refuses an incompatible snapshot clearly rather than restoring it wrongly. See `engine/EngineSnapshot.ts`'s `assertRestorable()`. */
|
|
168
172
|
readonly SNAPSHOT_VERSION_MISMATCH: "SNAPSHOT_VERSION_MISMATCH";
|
|
169
173
|
/** A snapshot with the right envelope but internally inconsistent contents (an unrecognised number sentinel, an unknown value tag). Distinct from a version mismatch: the format is right, the payload is not. */
|
|
@@ -103,6 +103,8 @@ declare const CoreErrorCodes: {
|
|
|
103
103
|
readonly INTERNAL_MISSING_FUNCTION_BODY: "INTERNAL_MISSING_FUNCTION_BODY";
|
|
104
104
|
/** `MAP_INVOKE`/`REDUCE_INVOKE`'s anonymous-body-index lookup failing. Same class as `INTERNAL_MISSING_FUNCTION_BODY` above, for the `anonymousBodies` side-table instead of `userFunctionBodies`. */
|
|
105
105
|
readonly INTERNAL_MISSING_ANONYMOUS_BODY: "INTERNAL_MISSING_ANONYMOUS_BODY";
|
|
106
|
+
/** `CALL_BUILTIN` naming an index no builtin is registered at. It used to pop its arguments and push nothing, so the opcode after it read a neighbour's operand as its own; now an Error value on the stack, the shape `map`/`reduce` already used for the same fault. Bytecode-level, so unreachable from source without a compiler or snapshot fault. */
|
|
107
|
+
readonly UNKNOWN_BUILTIN_FUNCTION: "UNKNOWN_BUILTIN_FUNCTION";
|
|
106
108
|
/** An operand byte read past the end of the stream: the program ends in the middle of an instruction. */
|
|
107
109
|
readonly MALFORMED_BYTECODE_TRUNCATED: "MALFORMED_BYTECODE_TRUNCATED";
|
|
108
110
|
/** A constant-pool operand indexing a `numbers`/`strings` entry that does not exist, or that is not of the pool's type. */
|
|
@@ -164,6 +166,8 @@ declare const CoreErrorCodes: {
|
|
|
164
166
|
readonly UNKNOWN_SAVINGS_PERIOD: "UNKNOWN_SAVINGS_PERIOD";
|
|
165
167
|
/** Colon-separated numbers that are not a time any clock can show ("24:00", "9:60", "100:5"). Raised by the labeled-line fallback, which used to answer them with whatever stood after the colon. */
|
|
166
168
|
readonly INVALID_TIME_LITERAL: "INVALID_TIME_LITERAL";
|
|
169
|
+
/** A live-data form evaluated on an engine whose host switched the network off (`network.enabled: false`, see `constants/Configuration.ts`'s `NetworkConfig`). A recoverable Error value, raised by the VM for a currency conversion with no primed rate and for a plugin function that returned a promise, and by `createQueryResolver`'s plugin function when its preflight was skipped. Names the setting, so the reader knows it is policy rather than an outage. */
|
|
170
|
+
readonly NETWORK_DISABLED: "NETWORK_DISABLED";
|
|
167
171
|
/** `fromJSON()` handed an object that is not a snapshot at all, or whose serialised-shape version does not match this engine's reader. The versioning gate that refuses an incompatible snapshot clearly rather than restoring it wrongly. See `engine/EngineSnapshot.ts`'s `assertRestorable()`. */
|
|
168
172
|
readonly SNAPSHOT_VERSION_MISMATCH: "SNAPSHOT_VERSION_MISMATCH";
|
|
169
173
|
/** A snapshot with the right envelope but internally inconsistent contents (an unrecognised number sentinel, an unknown value tag). Distinct from a version mismatch: the format is right, the payload is not. */
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-
|
|
2
|
-
import { V as Value,
|
|
1
|
+
import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-4oH5OuRt.cjs';
|
|
2
|
+
import { V as Value, a as ValueType } from './Value-Ds3Gy07C.cjs';
|
|
3
3
|
import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-CCHFDcgP.cjs';
|
|
4
4
|
import { IAsyncResolver } from './resolvers.cjs';
|
|
5
5
|
import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-MXaKLJ_m.cjs';
|
|
6
|
-
import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-
|
|
6
|
+
import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-DrXB3Nvm.cjs';
|
|
7
7
|
import { a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.cjs';
|
|
8
8
|
import { QueryClient } from '@tanstack/query-core';
|
|
9
|
-
import { E as EngineError } from './EngineError-
|
|
10
|
-
import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-
|
|
9
|
+
import { E as EngineError } from './EngineError-D1kXsjIj.cjs';
|
|
10
|
+
import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-69q2tsLE.cjs';
|
|
11
11
|
import { T as Token } from './Token-CbP_OutD.cjs';
|
|
12
|
-
import { E as EngineConfigOverride, a as EngineConfig } from './Configuration-
|
|
12
|
+
import { E as EngineConfigOverride, a as EngineConfig } from './Configuration-DnzYPmoK.cjs';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* One line's compiled bytecode, plus the variables it reads and writes.
|
|
@@ -728,9 +728,10 @@ declare class AsyncResolutionBatcher {
|
|
|
728
728
|
* Nullable rather than a constructor parameter because it is cleared by
|
|
729
729
|
* `clearAll()` and re-wired on re-subscribe, so it cannot be readonly. That
|
|
730
730
|
* makes it easy to miss, which is why {@link warnIfUnwired} exists: leaving
|
|
731
|
-
* it unset
|
|
732
|
-
*
|
|
733
|
-
*
|
|
731
|
+
* it unset, with nobody reading {@link getEventStream} either, means async
|
|
732
|
+
* values resolve into the cache and are never shown, with nothing to
|
|
733
|
+
* indicate why. A host that genuinely does not want async results should
|
|
734
|
+
* not register async resolvers at all.
|
|
734
735
|
*/
|
|
735
736
|
onLineResult: ((lineNumber: number, value: Value) => void) | null;
|
|
736
737
|
/**
|
|
@@ -742,11 +743,15 @@ declare class AsyncResolutionBatcher {
|
|
|
742
743
|
*/
|
|
743
744
|
private warnedAboutMissingHook;
|
|
744
745
|
/**
|
|
745
|
-
* Warn once if an async result resolved with
|
|
746
|
+
* Warn once if an async result resolved with nobody to receive it: no
|
|
747
|
+
* {@link onLineResult} wired and no reader on {@link getEventStream}.
|
|
746
748
|
*
|
|
747
749
|
* The failure this catches is silent by nature: the value arrives, the cache
|
|
748
750
|
* updates, and the line keeps showing pending forever. Without this a host
|
|
749
|
-
* author has no thread to pull on.
|
|
751
|
+
* author has no thread to pull on. A host reading the event stream is not
|
|
752
|
+
* in that position: the stream is the documented way to learn which lines
|
|
753
|
+
* to re-evaluate, and this used to warn at it anyway, telling it to set a
|
|
754
|
+
* hook it did not need.
|
|
750
755
|
*/
|
|
751
756
|
private warnIfUnwired;
|
|
752
757
|
/** High-water mark used when (re)creating the event stream. */
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-
|
|
2
|
-
import { V as Value,
|
|
1
|
+
import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from './Parselet-BwDqKFfK.js';
|
|
2
|
+
import { V as Value, a as ValueType } from './Value-Ds3Gy07C.js';
|
|
3
3
|
import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-CqagTewQ.js';
|
|
4
4
|
import { IAsyncResolver } from './resolvers.js';
|
|
5
5
|
import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-OTPS0Otq.js';
|
|
6
|
-
import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-
|
|
6
|
+
import { D as DependencyGraph, V as VM, a as DagSnapshot, b as EngineContext, S as ScopeManager, P as PluginFunctionHandler } from './ScopeManager-BQBhlDAu.js';
|
|
7
7
|
import { a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.js';
|
|
8
8
|
import { QueryClient } from '@tanstack/query-core';
|
|
9
|
-
import { E as EngineError } from './EngineError-
|
|
10
|
-
import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-
|
|
9
|
+
import { E as EngineError } from './EngineError-D1kXsjIj.js';
|
|
10
|
+
import { a as DiagnosticReportJSON, D as DiagnosticPipeline } from './pipeline-DTqGLPsV.js';
|
|
11
11
|
import { T as Token } from './Token-CbP_OutD.js';
|
|
12
|
-
import { E as EngineConfigOverride, a as EngineConfig } from './Configuration-
|
|
12
|
+
import { E as EngineConfigOverride, a as EngineConfig } from './Configuration-DnzYPmoK.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* One line's compiled bytecode, plus the variables it reads and writes.
|
|
@@ -728,9 +728,10 @@ declare class AsyncResolutionBatcher {
|
|
|
728
728
|
* Nullable rather than a constructor parameter because it is cleared by
|
|
729
729
|
* `clearAll()` and re-wired on re-subscribe, so it cannot be readonly. That
|
|
730
730
|
* makes it easy to miss, which is why {@link warnIfUnwired} exists: leaving
|
|
731
|
-
* it unset
|
|
732
|
-
*
|
|
733
|
-
*
|
|
731
|
+
* it unset, with nobody reading {@link getEventStream} either, means async
|
|
732
|
+
* values resolve into the cache and are never shown, with nothing to
|
|
733
|
+
* indicate why. A host that genuinely does not want async results should
|
|
734
|
+
* not register async resolvers at all.
|
|
734
735
|
*/
|
|
735
736
|
onLineResult: ((lineNumber: number, value: Value) => void) | null;
|
|
736
737
|
/**
|
|
@@ -742,11 +743,15 @@ declare class AsyncResolutionBatcher {
|
|
|
742
743
|
*/
|
|
743
744
|
private warnedAboutMissingHook;
|
|
744
745
|
/**
|
|
745
|
-
* Warn once if an async result resolved with
|
|
746
|
+
* Warn once if an async result resolved with nobody to receive it: no
|
|
747
|
+
* {@link onLineResult} wired and no reader on {@link getEventStream}.
|
|
746
748
|
*
|
|
747
749
|
* The failure this catches is silent by nature: the value arrives, the cache
|
|
748
750
|
* updates, and the line keeps showing pending forever. Without this a host
|
|
749
|
-
* author has no thread to pull on.
|
|
751
|
+
* author has no thread to pull on. A host reading the event stream is not
|
|
752
|
+
* in that position: the stream is the documented way to learn which lines
|
|
753
|
+
* to re-evaluate, and this used to warn at it anyway, telling it to set a
|
|
754
|
+
* hook it did not need.
|
|
750
755
|
*/
|
|
751
756
|
private warnIfUnwired;
|
|
752
757
|
/** High-water mark used when (re)creating the event stream. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { B as BytecodeBuilder } from './BytecodeBuilder-aqVa7Plx.cjs';
|
|
2
2
|
import { T as Token } from './Token-CbP_OutD.cjs';
|
|
3
|
-
import { D as DiagnosticPipeline } from './pipeline-
|
|
3
|
+
import { D as DiagnosticPipeline } from './pipeline-69q2tsLE.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Dual-keyed ParseletRegistry, accepts both string token types and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { B as BytecodeBuilder } from './BytecodeBuilder-aqVa7Plx.js';
|
|
2
2
|
import { T as Token } from './Token-CbP_OutD.js';
|
|
3
|
-
import { D as DiagnosticPipeline } from './pipeline-
|
|
3
|
+
import { D as DiagnosticPipeline } from './pipeline-DTqGLPsV.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Dual-keyed ParseletRegistry, accepts both string token types and
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { V as Value } from './Value-
|
|
1
|
+
import { V as Value } from './Value-Ds3Gy07C.js';
|
|
2
2
|
import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.js';
|
|
3
|
-
import { E as EngineError } from './EngineError-
|
|
4
|
-
import { D as DiagnosticPipeline } from './pipeline-
|
|
3
|
+
import { E as EngineError } from './EngineError-D1kXsjIj.js';
|
|
4
|
+
import { D as DiagnosticPipeline } from './pipeline-DTqGLPsV.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Create a new VM instance with the given opcode registry and configurable limits.
|
|
@@ -76,6 +76,13 @@ interface Bytecode {
|
|
|
76
76
|
interface LineExecutionContext {
|
|
77
77
|
/** 1-based current line number, or -1 when there is no real document (see class doc above). */
|
|
78
78
|
lineIndex: number;
|
|
79
|
+
/**
|
|
80
|
+
* Whether this engine may fetch live data (`network.enabled`). A plugin
|
|
81
|
+
* function that reads a resolver's cache uses it to say "live data is
|
|
82
|
+
* switched off" when the cache is empty, rather than the "not preflighted"
|
|
83
|
+
* message that describes a different fault. Absent means enabled.
|
|
84
|
+
*/
|
|
85
|
+
networkEnabled?: boolean;
|
|
79
86
|
/** Look up another line's cached result by 1-based line number. `undefined` = not evaluated yet (or out of range), distinct from a line that evaluated to an actual `undefined`-like Value, which can't happen (every Value type has a concrete representation). */
|
|
80
87
|
getLineResult?: (lineNumber: number) => Value | undefined;
|
|
81
88
|
/** Whether line `lineNumber` is a blank line or a `#` heading, the stopping condition for "total above"/"sum above"/"average above" aggregation. */
|
|
@@ -250,6 +257,17 @@ interface EngineContext {
|
|
|
250
257
|
* nowhere.
|
|
251
258
|
*/
|
|
252
259
|
readonly pluginFunctionOwners: Record<number, string>;
|
|
260
|
+
/**
|
|
261
|
+
* Whether this engine may fetch live data, from `network.enabled` in the
|
|
262
|
+
* engine's configuration.
|
|
263
|
+
*
|
|
264
|
+
* Lives on the context rather than only on the engine because the VM is
|
|
265
|
+
* where a plugin function's promise is first seen and where a currency
|
|
266
|
+
* conversion discovers it has no rate. Both need to answer "live data is
|
|
267
|
+
* switched off" rather than "no rate available" or "result discarded", and
|
|
268
|
+
* the context is the one object every VM already holds.
|
|
269
|
+
*/
|
|
270
|
+
readonly networkEnabled: boolean;
|
|
253
271
|
}
|
|
254
272
|
|
|
255
273
|
/**
|
|
@@ -356,15 +374,28 @@ declare class OpRegistry {
|
|
|
356
374
|
* and abort signal management for async cancellation.
|
|
357
375
|
*/
|
|
358
376
|
interface VM {
|
|
377
|
+
/**
|
|
378
|
+
* Push a value.
|
|
379
|
+
*
|
|
380
|
+
* @throws `STACK_LIMIT_EXCEEDED` (EXECUTION) when the stack is already at
|
|
381
|
+
* `maxStackDepth`. It used to drop the value silently, so a handler's
|
|
382
|
+
* result vanished and the next opcode read whatever lay beneath.
|
|
383
|
+
*/
|
|
359
384
|
push(value: Value): void;
|
|
385
|
+
/**
|
|
386
|
+
* Pop the top of the stack.
|
|
387
|
+
*
|
|
388
|
+
* @throws `STACK_UNDERFLOW` (INTERNAL) if the stack is empty. It used to
|
|
389
|
+
* answer with a plain `0`, which is indistinguishable from a real zero
|
|
390
|
+
* and turned a mismatched push/pop count into a wrong number.
|
|
391
|
+
*/
|
|
360
392
|
pop(): Value;
|
|
361
393
|
/**
|
|
362
394
|
* Pop the top of the stack as a number.
|
|
363
395
|
*
|
|
364
|
-
* @throws `STACK_UNDERFLOW` (INTERNAL) if the stack is empty
|
|
365
|
-
* `pop()
|
|
366
|
-
*
|
|
367
|
-
* one. Every Value converts, so there is no operand-type throw here.
|
|
396
|
+
* @throws `STACK_UNDERFLOW` (INTERNAL) if the stack is empty, exactly as
|
|
397
|
+
* `pop()` does. Every Value converts, so there is no operand-type throw
|
|
398
|
+
* here.
|
|
368
399
|
*/
|
|
369
400
|
popNumber(): number;
|
|
370
401
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { V as Value } from './Value-
|
|
1
|
+
import { V as Value } from './Value-Ds3Gy07C.cjs';
|
|
2
2
|
import { U as UserFunctionDef, A as AnonymousBodyDef, a as BytecodeProgram } from './BytecodeBuilder-aqVa7Plx.cjs';
|
|
3
|
-
import { E as EngineError } from './EngineError-
|
|
4
|
-
import { D as DiagnosticPipeline } from './pipeline-
|
|
3
|
+
import { E as EngineError } from './EngineError-D1kXsjIj.cjs';
|
|
4
|
+
import { D as DiagnosticPipeline } from './pipeline-69q2tsLE.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Create a new VM instance with the given opcode registry and configurable limits.
|
|
@@ -76,6 +76,13 @@ interface Bytecode {
|
|
|
76
76
|
interface LineExecutionContext {
|
|
77
77
|
/** 1-based current line number, or -1 when there is no real document (see class doc above). */
|
|
78
78
|
lineIndex: number;
|
|
79
|
+
/**
|
|
80
|
+
* Whether this engine may fetch live data (`network.enabled`). A plugin
|
|
81
|
+
* function that reads a resolver's cache uses it to say "live data is
|
|
82
|
+
* switched off" when the cache is empty, rather than the "not preflighted"
|
|
83
|
+
* message that describes a different fault. Absent means enabled.
|
|
84
|
+
*/
|
|
85
|
+
networkEnabled?: boolean;
|
|
79
86
|
/** Look up another line's cached result by 1-based line number. `undefined` = not evaluated yet (or out of range), distinct from a line that evaluated to an actual `undefined`-like Value, which can't happen (every Value type has a concrete representation). */
|
|
80
87
|
getLineResult?: (lineNumber: number) => Value | undefined;
|
|
81
88
|
/** Whether line `lineNumber` is a blank line or a `#` heading, the stopping condition for "total above"/"sum above"/"average above" aggregation. */
|
|
@@ -250,6 +257,17 @@ interface EngineContext {
|
|
|
250
257
|
* nowhere.
|
|
251
258
|
*/
|
|
252
259
|
readonly pluginFunctionOwners: Record<number, string>;
|
|
260
|
+
/**
|
|
261
|
+
* Whether this engine may fetch live data, from `network.enabled` in the
|
|
262
|
+
* engine's configuration.
|
|
263
|
+
*
|
|
264
|
+
* Lives on the context rather than only on the engine because the VM is
|
|
265
|
+
* where a plugin function's promise is first seen and where a currency
|
|
266
|
+
* conversion discovers it has no rate. Both need to answer "live data is
|
|
267
|
+
* switched off" rather than "no rate available" or "result discarded", and
|
|
268
|
+
* the context is the one object every VM already holds.
|
|
269
|
+
*/
|
|
270
|
+
readonly networkEnabled: boolean;
|
|
253
271
|
}
|
|
254
272
|
|
|
255
273
|
/**
|
|
@@ -356,15 +374,28 @@ declare class OpRegistry {
|
|
|
356
374
|
* and abort signal management for async cancellation.
|
|
357
375
|
*/
|
|
358
376
|
interface VM {
|
|
377
|
+
/**
|
|
378
|
+
* Push a value.
|
|
379
|
+
*
|
|
380
|
+
* @throws `STACK_LIMIT_EXCEEDED` (EXECUTION) when the stack is already at
|
|
381
|
+
* `maxStackDepth`. It used to drop the value silently, so a handler's
|
|
382
|
+
* result vanished and the next opcode read whatever lay beneath.
|
|
383
|
+
*/
|
|
359
384
|
push(value: Value): void;
|
|
385
|
+
/**
|
|
386
|
+
* Pop the top of the stack.
|
|
387
|
+
*
|
|
388
|
+
* @throws `STACK_UNDERFLOW` (INTERNAL) if the stack is empty. It used to
|
|
389
|
+
* answer with a plain `0`, which is indistinguishable from a real zero
|
|
390
|
+
* and turned a mismatched push/pop count into a wrong number.
|
|
391
|
+
*/
|
|
360
392
|
pop(): Value;
|
|
361
393
|
/**
|
|
362
394
|
* Pop the top of the stack as a number.
|
|
363
395
|
*
|
|
364
|
-
* @throws `STACK_UNDERFLOW` (INTERNAL) if the stack is empty
|
|
365
|
-
* `pop()
|
|
366
|
-
*
|
|
367
|
-
* one. Every Value converts, so there is no operand-type throw here.
|
|
396
|
+
* @throws `STACK_UNDERFLOW` (INTERNAL) if the stack is empty, exactly as
|
|
397
|
+
* `pop()` does. Every Value converts, so there is no operand-type throw
|
|
398
|
+
* here.
|
|
368
399
|
*/
|
|
369
400
|
popNumber(): number;
|
|
370
401
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { V as Value } from './Value-
|
|
2
|
-
import { V as VM } from './ScopeManager-
|
|
1
|
+
import { V as Value } from './Value-Ds3Gy07C.cjs';
|
|
2
|
+
import { V as VM } from './ScopeManager-DrXB3Nvm.cjs';
|
|
3
3
|
import { U as UserFunctionDef } from './BytecodeBuilder-aqVa7Plx.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { V as Value } from './Value-
|
|
2
|
-
import { V as VM } from './ScopeManager-
|
|
1
|
+
import { V as Value } from './Value-Ds3Gy07C.js';
|
|
2
|
+
import { V as VM } from './ScopeManager-BQBhlDAu.js';
|
|
3
3
|
import { U as UserFunctionDef } from './BytecodeBuilder-aqVa7Plx.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -533,4 +533,4 @@ declare function colVectorValue(data: readonly number[]): Value;
|
|
|
533
533
|
/** Create a Range value, a first-class integer range `min:max`, both bounds inclusive. */
|
|
534
534
|
declare function rangeValue(min: number, max: number): Value;
|
|
535
535
|
|
|
536
|
-
export { type ColourData as C, type IpCidrData as I, type MatrixData as M, type RangeData as R, type SplitData as S, Value as V,
|
|
536
|
+
export { type ColourData as C, type IpCidrData as I, type MatrixData as M, type RangeData as R, type SplitData as S, Value as V, ValueType as a, type MatrixEntry as b, bigIntValue as c, colVectorValue as d, rowVectorValue as e, type ChartData as f, type SymbolicNode as g, hexValue as h, type ColourFormat as i, matrixValue as m, numberValue as n, rangeValue as r, stringValue as s, uomValue as u };
|
|
@@ -533,4 +533,4 @@ declare function colVectorValue(data: readonly number[]): Value;
|
|
|
533
533
|
/** Create a Range value, a first-class integer range `min:max`, both bounds inclusive. */
|
|
534
534
|
declare function rangeValue(min: number, max: number): Value;
|
|
535
535
|
|
|
536
|
-
export { type ColourData as C, type IpCidrData as I, type MatrixData as M, type RangeData as R, type SplitData as S, Value as V,
|
|
536
|
+
export { type ColourData as C, type IpCidrData as I, type MatrixData as M, type RangeData as R, type SplitData as S, Value as V, ValueType as a, type MatrixEntry as b, bigIntValue as c, colVectorValue as d, rowVectorValue as e, type ChartData as f, type SymbolicNode as g, hexValue as h, type ColourFormat as i, matrixValue as m, numberValue as n, rangeValue as r, stringValue as s, uomValue as u };
|