solve-engine 1.0.0-beta.7 → 1.0.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.
- package/dist/{BytecodeBuilder-3egMyuDi.d.cts → BytecodeBuilder-B0xskcv5.d.cts} +2 -1
- package/dist/{BytecodeBuilder-3egMyuDi.d.ts → BytecodeBuilder-B0xskcv5.d.ts} +2 -1
- package/dist/{Configuration-fVpd-HC-.d.cts → Configuration-B-G5gTRn.d.cts} +107 -29
- package/dist/{Configuration-fVpd-HC-.d.ts → Configuration-B-G5gTRn.d.ts} +107 -29
- package/dist/{EngineError-DvPSnhZi.d.cts → EngineError-LU7W7AgI.d.cts} +96 -16
- package/dist/{EngineError-DvPSnhZi.d.ts → EngineError-LU7W7AgI.d.ts} +96 -16
- package/dist/{Lexer-W9MBOP0V.d.ts → Lexer-BN5mt30n.d.ts} +16 -0
- package/dist/{Lexer-Cfm79Dak.d.cts → Lexer-D78E2yDW.d.cts} +16 -0
- package/dist/{PackageRegistry-pHtKythi.d.cts → PackageRegistry-B37mRvvz.d.cts} +69 -13
- package/dist/{PackageRegistry-CjDt-Jy_.d.ts → PackageRegistry-DCUEzbQt.d.ts} +69 -13
- package/dist/{Parselet-DEdF9I7n.d.ts → Parselet-BaySkMV3.d.ts} +37 -5
- package/dist/{Parselet-Cu0bLSis.d.cts → Parselet-DuI1Pjiq.d.cts} +37 -5
- package/dist/{ScopeManager-CKfQtl7l.d.cts → ScopeManager-B6GzdhVG.d.cts} +75 -6
- package/dist/{ScopeManager-B5k0nk05.d.ts → ScopeManager-udv4Twwq.d.ts} +75 -6
- package/dist/{VMCheckpoints-BmDwIXgK.d.ts → VMCheckpoints-BiaIlOOY.d.ts} +4 -4
- package/dist/{VMCheckpoints-CqFuLwrT.d.cts → VMCheckpoints-DwLjivM7.d.cts} +4 -4
- package/dist/{Value-KtAQNHur.d.cts → Value-CXJqDH9J.d.cts} +5 -2
- package/dist/{Value-KtAQNHur.d.ts → Value-CXJqDH9J.d.ts} +5 -2
- package/dist/{chunk-35IWNZPJ.cjs → chunk-2XLQPQKI.cjs} +58 -27
- package/dist/chunk-2XLQPQKI.cjs.map +1 -0
- package/dist/{chunk-CSG5XVNJ.cjs → chunk-3AFRJYP4.cjs} +556 -414
- package/dist/chunk-3AFRJYP4.cjs.map +1 -0
- package/dist/chunk-3BOK2LDK.js +17 -0
- package/dist/chunk-3BOK2LDK.js.map +1 -0
- package/dist/{chunk-6RDSQURE.js → chunk-3D7V24DG.js} +57 -6
- package/dist/chunk-3D7V24DG.js.map +1 -0
- package/dist/{chunk-MEOHSQEH.js → chunk-3LAEG75D.js} +47 -15
- package/dist/chunk-3LAEG75D.js.map +1 -0
- package/dist/{chunk-4AVD7NZW.cjs → chunk-4B2CNWQU.cjs} +13 -7
- package/dist/chunk-4B2CNWQU.cjs.map +1 -0
- package/dist/{chunk-6GCKCWLB.js → chunk-526PMQOA.js} +126 -69
- package/dist/chunk-526PMQOA.js.map +1 -0
- package/dist/{chunk-2CS6OMZK.js → chunk-5F4C26RU.js} +8 -5
- package/dist/chunk-5F4C26RU.js.map +1 -0
- package/dist/{chunk-M4N5GA5Z.cjs → chunk-5KMIY374.cjs} +57 -6
- package/dist/chunk-5KMIY374.cjs.map +1 -0
- package/dist/{chunk-R24DI24X.cjs → chunk-5LI5EPGJ.cjs} +6 -5
- package/dist/chunk-5LI5EPGJ.cjs.map +1 -0
- package/dist/{chunk-BAMZT3GL.js → chunk-5WVP4YHP.js} +939 -134
- package/dist/chunk-5WVP4YHP.js.map +1 -0
- package/dist/{chunk-MZOVP3WI.cjs → chunk-6KFYJ6TD.cjs} +3 -2
- package/dist/chunk-6KFYJ6TD.cjs.map +1 -0
- package/dist/{chunk-BXKJLM5R.cjs → chunk-6WFMPTGB.cjs} +972 -636
- package/dist/chunk-6WFMPTGB.cjs.map +1 -0
- package/dist/{chunk-2MV4HBKC.cjs → chunk-A2N2GFCG.cjs} +10 -10
- package/dist/{chunk-2MV4HBKC.cjs.map → chunk-A2N2GFCG.cjs.map} +1 -1
- package/dist/chunk-ADBA3XBE.cjs +20 -0
- package/dist/chunk-ADBA3XBE.cjs.map +1 -0
- package/dist/{chunk-MBNQVDVC.cjs → chunk-AMOJYLG2.cjs} +16 -10
- package/dist/chunk-AMOJYLG2.cjs.map +1 -0
- package/dist/{chunk-CLVQBF5C.cjs → chunk-AQNHHJMJ.cjs} +5 -5
- package/dist/{chunk-CLVQBF5C.cjs.map → chunk-AQNHHJMJ.cjs.map} +1 -1
- package/dist/{chunk-V6HA6JJZ.cjs → chunk-B7TLZABL.cjs} +242 -138
- package/dist/chunk-B7TLZABL.cjs.map +1 -0
- package/dist/{chunk-AHCLWAM5.cjs → chunk-BHTNFEGZ.cjs} +13 -13
- package/dist/chunk-BHTNFEGZ.cjs.map +1 -0
- package/dist/{chunk-BVAR5J2F.js → chunk-GQCOSXMG.js} +117 -6
- package/dist/chunk-GQCOSXMG.js.map +1 -0
- package/dist/{chunk-QDMPOAI7.cjs → chunk-HDP7VK3C.cjs} +1118 -313
- package/dist/chunk-HDP7VK3C.cjs.map +1 -0
- package/dist/{chunk-GVWHCL2N.js → chunk-HMOISHXR.js} +217 -75
- package/dist/chunk-HMOISHXR.js.map +1 -0
- package/dist/{chunk-MTX2KVU7.cjs → chunk-HVQFNJKE.cjs} +122 -90
- package/dist/chunk-HVQFNJKE.cjs.map +1 -0
- package/dist/{chunk-OC6LTXYS.cjs → chunk-JMXUNXQS.cjs} +126 -7
- package/dist/chunk-JMXUNXQS.cjs.map +1 -0
- package/dist/{chunk-WWOLFXTX.cjs → chunk-KBSXGXPM.cjs} +10 -7
- package/dist/chunk-KBSXGXPM.cjs.map +1 -0
- package/dist/{chunk-YU2CUNFO.js → chunk-KV7UW6T6.js} +11 -5
- package/dist/chunk-KV7UW6T6.js.map +1 -0
- package/dist/{chunk-CCJPSHGI.cjs → chunk-N7FNVFXW.cjs} +4 -12
- package/dist/chunk-N7FNVFXW.cjs.map +1 -0
- package/dist/{chunk-CYFK5SY2.js → chunk-O3ANBHSA.js} +169 -39
- package/dist/chunk-O3ANBHSA.js.map +1 -0
- package/dist/{chunk-NZFKROS7.cjs → chunk-PA4VC73I.cjs} +130 -72
- package/dist/chunk-PA4VC73I.cjs.map +1 -0
- package/dist/{chunk-NJTXJ5AG.cjs → chunk-PFUESQTW.cjs} +18 -18
- package/dist/{chunk-NJTXJ5AG.cjs.map → chunk-PFUESQTW.cjs.map} +1 -1
- package/dist/{chunk-SFQWJMKT.js → chunk-QY25VWBF.js} +51 -12
- package/dist/chunk-QY25VWBF.js.map +1 -0
- package/dist/{chunk-WC5FFSHB.js → chunk-R3PY4G7J.js} +3 -3
- package/dist/{chunk-WC5FFSHB.js.map → chunk-R3PY4G7J.js.map} +1 -1
- package/dist/{chunk-M5E34VG5.js → chunk-SBUFNJVE.js} +3 -3
- package/dist/{chunk-M5E34VG5.js.map → chunk-SBUFNJVE.js.map} +1 -1
- package/dist/{chunk-P33SBYGG.js → chunk-TBN7DEHO.js} +54 -23
- package/dist/chunk-TBN7DEHO.js.map +1 -0
- package/dist/{chunk-UKPGSAZW.js → chunk-TMA4RCEN.js} +3 -3
- package/dist/{chunk-UKPGSAZW.js.map → chunk-TMA4RCEN.js.map} +1 -1
- package/dist/{chunk-DUEZFTK2.js → chunk-UKQHXPGW.js} +4 -12
- package/dist/chunk-UKQHXPGW.js.map +1 -0
- package/dist/{chunk-4CVLFLOB.cjs → chunk-UM6BVY2S.cjs} +201 -69
- package/dist/chunk-UM6BVY2S.cjs.map +1 -0
- package/dist/{chunk-75JP4BZX.js → chunk-UQ3UIZJC.js} +3 -2
- package/dist/chunk-UQ3UIZJC.js.map +1 -0
- package/dist/{chunk-PCPX42KL.js → chunk-V4XG5QF6.js} +16 -10
- package/dist/chunk-V4XG5QF6.js.map +1 -0
- package/dist/{chunk-P75775WC.js → chunk-V5PYO44Y.js} +145 -41
- package/dist/chunk-V5PYO44Y.js.map +1 -0
- package/dist/{chunk-GPPLSM2Z.js → chunk-VB37OC6I.js} +6 -5
- package/dist/chunk-VB37OC6I.js.map +1 -0
- package/dist/{chunk-MVBEL57Q.js → chunk-Y7FT4IQT.js} +500 -170
- package/dist/chunk-Y7FT4IQT.js.map +1 -0
- package/dist/{chunk-OFXOTECC.js → chunk-YPPPYLSR.js} +7 -7
- package/dist/chunk-YPPPYLSR.js.map +1 -0
- package/dist/{chunk-M4F66R4O.cjs → chunk-YWQ6V4ZN.cjs} +121 -82
- package/dist/chunk-YWQ6V4ZN.cjs.map +1 -0
- package/dist/constants.cjs +5 -5
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +2 -2
- package/dist/engine.cjs +31 -31
- package/dist/engine.d.cts +11 -11
- package/dist/engine.d.ts +11 -11
- package/dist/engine.js +21 -21
- package/dist/errors.cjs +19 -19
- package/dist/errors.d.cts +2 -2
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +1 -1
- package/dist/format.cjs +29 -22
- package/dist/format.cjs.map +1 -1
- package/dist/format.d.cts +1 -2
- package/dist/format.d.ts +1 -2
- package/dist/format.js +23 -16
- package/dist/format.js.map +1 -1
- package/dist/index.cjs +31 -31
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +22 -22
- package/dist/language.cjs +8 -8
- package/dist/language.cjs.map +1 -1
- package/dist/language.d.cts +10 -10
- package/dist/language.d.ts +10 -10
- package/dist/language.js +6 -6
- package/dist/language.js.map +1 -1
- package/dist/lexer.cjs +18 -18
- package/dist/lexer.d.cts +2 -2
- package/dist/lexer.d.ts +2 -2
- package/dist/lexer.js +6 -6
- package/dist/normalizer.cjs +12 -12
- package/dist/normalizer.js +6 -6
- package/dist/packages.cjs +39 -39
- package/dist/packages.d.cts +9 -9
- package/dist/packages.d.ts +9 -9
- package/dist/packages.js +16 -16
- package/dist/parser.cjs +15 -15
- package/dist/parser.d.cts +23 -9
- package/dist/parser.d.ts +23 -9
- package/dist/parser.js +5 -5
- package/dist/{pipeline-CrsRPmsv.d.ts → pipeline-B6k5lCB7.d.ts} +1 -1
- package/dist/{pipeline-DPUQt7yL.d.cts → pipeline-BEb3hujr.d.cts} +1 -1
- package/dist/resolvers.d.cts +2 -2
- package/dist/resolvers.d.ts +2 -2
- package/dist/uom.cjs +17 -17
- package/dist/uom.d.cts +2 -2
- package/dist/uom.d.ts +2 -2
- package/dist/uom.js +7 -7
- package/dist/utilities.cjs +8 -4
- package/dist/utilities.d.cts +19 -3
- package/dist/utilities.d.ts +19 -3
- package/dist/utilities.js +2 -2
- package/dist/vm.cjs +43 -31
- package/dist/vm.d.cts +105 -9
- package/dist/vm.d.ts +105 -9
- package/dist/vm.js +9 -9
- package/package.json +1 -1
- package/dist/chunk-2CS6OMZK.js.map +0 -1
- package/dist/chunk-35IWNZPJ.cjs.map +0 -1
- package/dist/chunk-4AVD7NZW.cjs.map +0 -1
- package/dist/chunk-4CVLFLOB.cjs.map +0 -1
- package/dist/chunk-6GCKCWLB.js.map +0 -1
- package/dist/chunk-6RDSQURE.js.map +0 -1
- package/dist/chunk-75JP4BZX.js.map +0 -1
- package/dist/chunk-AHCLWAM5.cjs.map +0 -1
- package/dist/chunk-BAMZT3GL.js.map +0 -1
- package/dist/chunk-BVAR5J2F.js.map +0 -1
- package/dist/chunk-BXKJLM5R.cjs.map +0 -1
- package/dist/chunk-CCJPSHGI.cjs.map +0 -1
- package/dist/chunk-CSG5XVNJ.cjs.map +0 -1
- package/dist/chunk-CYFK5SY2.js.map +0 -1
- package/dist/chunk-DUEZFTK2.js.map +0 -1
- package/dist/chunk-GPPLSM2Z.js.map +0 -1
- package/dist/chunk-GVWHCL2N.js.map +0 -1
- package/dist/chunk-M4F66R4O.cjs.map +0 -1
- package/dist/chunk-M4N5GA5Z.cjs.map +0 -1
- package/dist/chunk-MBNQVDVC.cjs.map +0 -1
- package/dist/chunk-MEOHSQEH.js.map +0 -1
- package/dist/chunk-MTX2KVU7.cjs.map +0 -1
- package/dist/chunk-MVBEL57Q.js.map +0 -1
- package/dist/chunk-MZOVP3WI.cjs.map +0 -1
- package/dist/chunk-N5YFR4UP.cjs +0 -10
- package/dist/chunk-N5YFR4UP.cjs.map +0 -1
- package/dist/chunk-NZFKROS7.cjs.map +0 -1
- package/dist/chunk-OC6LTXYS.cjs.map +0 -1
- package/dist/chunk-OFXOTECC.js.map +0 -1
- package/dist/chunk-P33SBYGG.js.map +0 -1
- package/dist/chunk-P75775WC.js.map +0 -1
- package/dist/chunk-PCPX42KL.js.map +0 -1
- package/dist/chunk-PFSDGGYL.js +0 -8
- package/dist/chunk-PFSDGGYL.js.map +0 -1
- package/dist/chunk-QDMPOAI7.cjs.map +0 -1
- package/dist/chunk-R24DI24X.cjs.map +0 -1
- package/dist/chunk-SFQWJMKT.js.map +0 -1
- package/dist/chunk-V6HA6JJZ.cjs.map +0 -1
- package/dist/chunk-WWOLFXTX.cjs.map +0 -1
- package/dist/chunk-YU2CUNFO.js.map +0 -1
|
@@ -87,7 +87,8 @@ declare enum OpCode {
|
|
|
87
87
|
MAT_SLICE = 155,// (matrix, rowRange, colRange already on stack) -> sub-matrix via two Range values
|
|
88
88
|
RANGE_NEW = 156,// (min, max already on stack) -> push a Range value `min:max`
|
|
89
89
|
MAP_INVOKE = 157,// map(...) — see parser/BytecodeBuilder.ts's `anonymousBodies` side-table
|
|
90
|
-
REDUCE_INVOKE = 158
|
|
90
|
+
REDUCE_INVOKE = 158,// reduce(...) — same anonymousBodies mechanism as MAP_INVOKE
|
|
91
|
+
BIND_UNKNOWN = 159
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* Gets the name of an OpCode as a string.
|
|
@@ -87,7 +87,8 @@ declare enum OpCode {
|
|
|
87
87
|
MAT_SLICE = 155,// (matrix, rowRange, colRange already on stack) -> sub-matrix via two Range values
|
|
88
88
|
RANGE_NEW = 156,// (min, max already on stack) -> push a Range value `min:max`
|
|
89
89
|
MAP_INVOKE = 157,// map(...) — see parser/BytecodeBuilder.ts's `anonymousBodies` side-table
|
|
90
|
-
REDUCE_INVOKE = 158
|
|
90
|
+
REDUCE_INVOKE = 158,// reduce(...) — same anonymousBodies mechanism as MAP_INVOKE
|
|
91
|
+
BIND_UNKNOWN = 159
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* Gets the name of an OpCode as a string.
|
|
@@ -27,29 +27,30 @@
|
|
|
27
27
|
* (e.g., `today + 20 days`, `last monday`).
|
|
28
28
|
*/
|
|
29
29
|
interface DateConfig {
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/**
|
|
31
|
+
* How far forward a date offset whose COST grows with the offset may reach,
|
|
32
|
+
* in years. Enforced by `vm/VM.ts`'s `addBusinessDays()`.
|
|
33
|
+
*
|
|
34
|
+
* Bounds the walk, not the calendar. Every other date offset in the engine
|
|
35
|
+
* is arithmetic on a Date field, so `today + 100000 days` costs exactly what
|
|
36
|
+
* one day costs and needs no ceiling; workdays are the one offset that has
|
|
37
|
+
* to step day by day, because which days are skipped depends on where each
|
|
38
|
+
* step lands. `today + 100000000 workdays` therefore froze the host for
|
|
39
|
+
* thirteen seconds inside a single ADD opcode (where `vm.maxInstructions`
|
|
40
|
+
* cannot see it) and then answered "Invalid Date", and a trillion never
|
|
41
|
+
* returned at all.
|
|
42
|
+
*
|
|
43
|
+
* Release hardening: this field was declared, documented as a "safety
|
|
44
|
+
* limit", and read nowhere, so it bounded nothing. A limit a host can
|
|
45
|
+
* configure and the engine ignores is worse than no limit, because it reads
|
|
46
|
+
* as protection that is not there.
|
|
47
|
+
*/
|
|
33
48
|
readonly maxOffsetYears: number;
|
|
34
|
-
/**
|
|
49
|
+
/** How far BACK the same walk may reach, in years, as a negative number. See {@link maxOffsetYears}. */
|
|
35
50
|
readonly minOffsetYears: number;
|
|
36
51
|
/** Default date string format for display (moment.js format string) */
|
|
37
52
|
readonly defaultFormat: string;
|
|
38
53
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Dice-related configuration.
|
|
41
|
-
* Controls dice expression evaluation (e.g., `roll(1, 100)`, `3d6`).
|
|
42
|
-
*/
|
|
43
|
-
interface DiceConfig {
|
|
44
|
-
/** Default number of sides on a die when not specified */
|
|
45
|
-
readonly defaultSides: number;
|
|
46
|
-
/** Maximum allowed sides per die (prevents excessive allocation) */
|
|
47
|
-
readonly maxSides: number;
|
|
48
|
-
/** Maximum number of dice in a single roll expression */
|
|
49
|
-
readonly maxDice: number;
|
|
50
|
-
/** Default number of dice when not specified */
|
|
51
|
-
readonly defaultDice: number;
|
|
52
|
-
}
|
|
53
54
|
/**
|
|
54
55
|
* Performance-related configuration.
|
|
55
56
|
* Controls caching, timeouts, and processing limits to prevent runaway
|
|
@@ -67,12 +68,23 @@ interface PerformanceConfig {
|
|
|
67
68
|
* size limit of its own.
|
|
68
69
|
*/
|
|
69
70
|
readonly defaultCacheSize: number;
|
|
70
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Maximum lines a document may have for the engine to process it in one
|
|
73
|
+
* pass. Enforced by `ExpressionEngine.parseDocument()` and by
|
|
74
|
+
* `engine/DocumentModel.ts`'s `setDocument()`, the two entry points that
|
|
75
|
+
* take a whole document.
|
|
76
|
+
*
|
|
77
|
+
* A document costs memory per line whatever each line says: a parsed line
|
|
78
|
+
* record, a cache entry, a dependency-graph node. Two hundred thousand
|
|
79
|
+
* lines of `1 + 1` therefore exhausted the heap and aborted the process
|
|
80
|
+
* before any per-line limit had anything to object to, which is the same
|
|
81
|
+
* shape of hole as an unbounded expression and needs the same kind of
|
|
82
|
+
* ceiling. Refusing the document names the limit; a host that genuinely has
|
|
83
|
+
* a larger one raises this field.
|
|
84
|
+
*
|
|
85
|
+
* Release hardening: declared and read nowhere until now.
|
|
86
|
+
*/
|
|
71
87
|
readonly maxDocumentLines: number;
|
|
72
|
-
/** Maximum time (ms) allowed for parsing a single expression before timeout */
|
|
73
|
-
readonly parseTimeoutMs: number;
|
|
74
|
-
/** Maximum time (ms) allowed for executing a single expression before timeout */
|
|
75
|
-
readonly executionTimeoutMs: number;
|
|
76
88
|
}
|
|
77
89
|
/**
|
|
78
90
|
* Validation / safety-limit configuration.
|
|
@@ -131,6 +143,60 @@ interface VMConfig {
|
|
|
131
143
|
readonly maxStackDepth: number;
|
|
132
144
|
/** Maximum opcodes executed per expression, halts runaway infinite loops */
|
|
133
145
|
readonly maxInstructions: number;
|
|
146
|
+
/**
|
|
147
|
+
* Maximum elements a collection may be expanded to before `map`/`reduce`/
|
|
148
|
+
* `sum`/`prod` will iterate it.
|
|
149
|
+
*
|
|
150
|
+
* A Range is stored as its two bounds and costs nothing until something
|
|
151
|
+
* materializes it, at which point it becomes one Value per element. Twenty
|
|
152
|
+
* characters (`sum(x, 1:100000000)`) therefore asked for a hundred million
|
|
153
|
+
* of them, and neither `maxInstructions` nor `maxStackDepth` could see it:
|
|
154
|
+
* the expansion happens inside a single opcode, so the instruction counter
|
|
155
|
+
* is never consulted while it runs, and the elements never reach the value
|
|
156
|
+
* stack. V8 aborted the whole process with "Reached heap limit", which a
|
|
157
|
+
* host embedding the engine cannot catch.
|
|
158
|
+
*/
|
|
159
|
+
readonly maxCollectionSize: number;
|
|
160
|
+
/**
|
|
161
|
+
* Maximum elements (collection Values, matrix cells) one evaluation may
|
|
162
|
+
* materialize in total.
|
|
163
|
+
*
|
|
164
|
+
* `maxCollectionSize` above bounds a single collection. This bounds the sum
|
|
165
|
+
* of everything an expression asks for, which is a different question and
|
|
166
|
+
* the one that actually protects the host: two collections that are
|
|
167
|
+
* individually legal are legal together, and an operation whose result is
|
|
168
|
+
* the PRODUCT of two legal operands is bounded by neither of them. A matrix
|
|
169
|
+
* multiply is exactly that shape, so three lines within every other limit
|
|
170
|
+
* (`:a = map(1*x, 0:20000)`, `:b = transpose(a)`, `b * a`) asked for four
|
|
171
|
+
* hundred million cells and aborted the process.
|
|
172
|
+
*
|
|
173
|
+
* Counted in elements rather than bytes, because a count is what a call
|
|
174
|
+
* site has before it allocates. An element is 8 bytes as a numeric matrix
|
|
175
|
+
* cell and closer to a hundred as a full Value, so the default is worth
|
|
176
|
+
* roughly 16 MB of matrix or 200 MB of expanded collection: far past any
|
|
177
|
+
* document and far short of what an editor cannot survive.
|
|
178
|
+
*/
|
|
179
|
+
readonly maxAllocatedElements: number;
|
|
180
|
+
/**
|
|
181
|
+
* Maximum user-defined-function calls one evaluation may make in TOTAL,
|
|
182
|
+
* however deeply or widely they nest.
|
|
183
|
+
*
|
|
184
|
+
* `maxFunctionRecursionDepth` bounds how DEEP calls nest and cannot see
|
|
185
|
+
* how MANY there are, and those are different numbers. Twenty-two lines of
|
|
186
|
+
* `f(n)(v) = f(n-1)(v) + f(n-1)(v)` reach a depth of only 22 against a
|
|
187
|
+
* limit of 50, and make 2,097,152 calls doing it: a fatal heap abort in
|
|
188
|
+
* under a second. `maxInstructions` cannot bound it either, because
|
|
189
|
+
* `executeBytecode()` re-enters itself per call and each reentrant call
|
|
190
|
+
* gets its OWN instruction count, so recursion refreshes its allowance on
|
|
191
|
+
* the way in. This is the tally that does not refresh; see
|
|
192
|
+
* `vm/AllocationBudget.ts`, which holds it for the same reason it holds
|
|
193
|
+
* the element tally.
|
|
194
|
+
*
|
|
195
|
+
* Counted in calls rather than in the instructions they run, because the
|
|
196
|
+
* call is the thing that multiplies: every call allocates a frame, its
|
|
197
|
+
* arguments and its result whatever its body says.
|
|
198
|
+
*/
|
|
199
|
+
readonly maxFunctionCalls: number;
|
|
134
200
|
}
|
|
135
201
|
/**
|
|
136
202
|
* Complete engine configuration.
|
|
@@ -148,15 +214,13 @@ interface VMConfig {
|
|
|
148
214
|
* maxExpressionLength: 1000,
|
|
149
215
|
* maxComplexity: 200,
|
|
150
216
|
* },
|
|
151
|
-
* // date,
|
|
217
|
+
* // date, performance, vm, worker, diagnostic all use defaults
|
|
152
218
|
* });
|
|
153
219
|
* ```
|
|
154
220
|
*/
|
|
155
221
|
interface EngineConfig {
|
|
156
222
|
/** Date/time expression evaluation bounds and formatting */
|
|
157
223
|
readonly date: DateConfig;
|
|
158
|
-
/** Dice roll expression controls */
|
|
159
|
-
readonly dice: DiceConfig;
|
|
160
224
|
/** Performance budgets and cache sizing */
|
|
161
225
|
readonly performance: PerformanceConfig;
|
|
162
226
|
/** Safety limits for expression complexity */
|
|
@@ -200,7 +264,12 @@ declare class ConfigManager {
|
|
|
200
264
|
*/
|
|
201
265
|
set<T>(path: string, value: T): void;
|
|
202
266
|
/**
|
|
203
|
-
* Get complete configuration
|
|
267
|
+
* Get complete configuration.
|
|
268
|
+
*
|
|
269
|
+
* A detached copy, section objects included. A top-level spread would hand
|
|
270
|
+
* the caller this manager's own section objects, so `getConfig().vm.x = 1`
|
|
271
|
+
* would be an undeclared back door into {@link set}, bypassing its path
|
|
272
|
+
* validation and reaching every later {@link get}.
|
|
204
273
|
*/
|
|
205
274
|
getConfig(): EngineConfig;
|
|
206
275
|
/**
|
|
@@ -208,7 +277,16 @@ declare class ConfigManager {
|
|
|
208
277
|
*/
|
|
209
278
|
update(config: Partial<EngineConfig>): void;
|
|
210
279
|
/**
|
|
211
|
-
* Reset to default configuration
|
|
280
|
+
* Reset to default configuration.
|
|
281
|
+
*
|
|
282
|
+
* Goes through {@link mergeEngineConfig} for the same reason the constructor
|
|
283
|
+
* does. A top-level `{ ...DEFAULT_CONFIG }` copies the six section
|
|
284
|
+
* references, not the sections, so after a reset this manager's
|
|
285
|
+
* `performance` object WAS `DEFAULT_CONFIG.performance` and the next
|
|
286
|
+
* `set('performance.x', ...)` wrote into the module constant. That constant
|
|
287
|
+
* is what every `ExpressionEngine` is built from, so one manager's reset
|
|
288
|
+
* could change the cache size, instruction ceiling or allocation budget of
|
|
289
|
+
* every engine constructed later in the process.
|
|
212
290
|
*/
|
|
213
291
|
reset(): void;
|
|
214
292
|
/**
|
|
@@ -225,4 +303,4 @@ interface ValidationResult {
|
|
|
225
303
|
warnings?: string[];
|
|
226
304
|
}
|
|
227
305
|
|
|
228
|
-
export { ConfigManager as C, DEFAULT_CONFIG as D, type EngineConfig as E, type PerformanceConfig as P, type VMConfig as V, type WorkerConfig as W, type DateConfig as a, type DiagnosticConfig as b, type
|
|
306
|
+
export { ConfigManager as C, DEFAULT_CONFIG as D, type EngineConfig as E, type PerformanceConfig as P, type VMConfig as V, type WorkerConfig as W, type DateConfig as a, type DiagnosticConfig as b, type ValidationConfig as c, type ValidationResult as d };
|
|
@@ -27,29 +27,30 @@
|
|
|
27
27
|
* (e.g., `today + 20 days`, `last monday`).
|
|
28
28
|
*/
|
|
29
29
|
interface DateConfig {
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/**
|
|
31
|
+
* How far forward a date offset whose COST grows with the offset may reach,
|
|
32
|
+
* in years. Enforced by `vm/VM.ts`'s `addBusinessDays()`.
|
|
33
|
+
*
|
|
34
|
+
* Bounds the walk, not the calendar. Every other date offset in the engine
|
|
35
|
+
* is arithmetic on a Date field, so `today + 100000 days` costs exactly what
|
|
36
|
+
* one day costs and needs no ceiling; workdays are the one offset that has
|
|
37
|
+
* to step day by day, because which days are skipped depends on where each
|
|
38
|
+
* step lands. `today + 100000000 workdays` therefore froze the host for
|
|
39
|
+
* thirteen seconds inside a single ADD opcode (where `vm.maxInstructions`
|
|
40
|
+
* cannot see it) and then answered "Invalid Date", and a trillion never
|
|
41
|
+
* returned at all.
|
|
42
|
+
*
|
|
43
|
+
* Release hardening: this field was declared, documented as a "safety
|
|
44
|
+
* limit", and read nowhere, so it bounded nothing. A limit a host can
|
|
45
|
+
* configure and the engine ignores is worse than no limit, because it reads
|
|
46
|
+
* as protection that is not there.
|
|
47
|
+
*/
|
|
33
48
|
readonly maxOffsetYears: number;
|
|
34
|
-
/**
|
|
49
|
+
/** How far BACK the same walk may reach, in years, as a negative number. See {@link maxOffsetYears}. */
|
|
35
50
|
readonly minOffsetYears: number;
|
|
36
51
|
/** Default date string format for display (moment.js format string) */
|
|
37
52
|
readonly defaultFormat: string;
|
|
38
53
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Dice-related configuration.
|
|
41
|
-
* Controls dice expression evaluation (e.g., `roll(1, 100)`, `3d6`).
|
|
42
|
-
*/
|
|
43
|
-
interface DiceConfig {
|
|
44
|
-
/** Default number of sides on a die when not specified */
|
|
45
|
-
readonly defaultSides: number;
|
|
46
|
-
/** Maximum allowed sides per die (prevents excessive allocation) */
|
|
47
|
-
readonly maxSides: number;
|
|
48
|
-
/** Maximum number of dice in a single roll expression */
|
|
49
|
-
readonly maxDice: number;
|
|
50
|
-
/** Default number of dice when not specified */
|
|
51
|
-
readonly defaultDice: number;
|
|
52
|
-
}
|
|
53
54
|
/**
|
|
54
55
|
* Performance-related configuration.
|
|
55
56
|
* Controls caching, timeouts, and processing limits to prevent runaway
|
|
@@ -67,12 +68,23 @@ interface PerformanceConfig {
|
|
|
67
68
|
* size limit of its own.
|
|
68
69
|
*/
|
|
69
70
|
readonly defaultCacheSize: number;
|
|
70
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Maximum lines a document may have for the engine to process it in one
|
|
73
|
+
* pass. Enforced by `ExpressionEngine.parseDocument()` and by
|
|
74
|
+
* `engine/DocumentModel.ts`'s `setDocument()`, the two entry points that
|
|
75
|
+
* take a whole document.
|
|
76
|
+
*
|
|
77
|
+
* A document costs memory per line whatever each line says: a parsed line
|
|
78
|
+
* record, a cache entry, a dependency-graph node. Two hundred thousand
|
|
79
|
+
* lines of `1 + 1` therefore exhausted the heap and aborted the process
|
|
80
|
+
* before any per-line limit had anything to object to, which is the same
|
|
81
|
+
* shape of hole as an unbounded expression and needs the same kind of
|
|
82
|
+
* ceiling. Refusing the document names the limit; a host that genuinely has
|
|
83
|
+
* a larger one raises this field.
|
|
84
|
+
*
|
|
85
|
+
* Release hardening: declared and read nowhere until now.
|
|
86
|
+
*/
|
|
71
87
|
readonly maxDocumentLines: number;
|
|
72
|
-
/** Maximum time (ms) allowed for parsing a single expression before timeout */
|
|
73
|
-
readonly parseTimeoutMs: number;
|
|
74
|
-
/** Maximum time (ms) allowed for executing a single expression before timeout */
|
|
75
|
-
readonly executionTimeoutMs: number;
|
|
76
88
|
}
|
|
77
89
|
/**
|
|
78
90
|
* Validation / safety-limit configuration.
|
|
@@ -131,6 +143,60 @@ interface VMConfig {
|
|
|
131
143
|
readonly maxStackDepth: number;
|
|
132
144
|
/** Maximum opcodes executed per expression, halts runaway infinite loops */
|
|
133
145
|
readonly maxInstructions: number;
|
|
146
|
+
/**
|
|
147
|
+
* Maximum elements a collection may be expanded to before `map`/`reduce`/
|
|
148
|
+
* `sum`/`prod` will iterate it.
|
|
149
|
+
*
|
|
150
|
+
* A Range is stored as its two bounds and costs nothing until something
|
|
151
|
+
* materializes it, at which point it becomes one Value per element. Twenty
|
|
152
|
+
* characters (`sum(x, 1:100000000)`) therefore asked for a hundred million
|
|
153
|
+
* of them, and neither `maxInstructions` nor `maxStackDepth` could see it:
|
|
154
|
+
* the expansion happens inside a single opcode, so the instruction counter
|
|
155
|
+
* is never consulted while it runs, and the elements never reach the value
|
|
156
|
+
* stack. V8 aborted the whole process with "Reached heap limit", which a
|
|
157
|
+
* host embedding the engine cannot catch.
|
|
158
|
+
*/
|
|
159
|
+
readonly maxCollectionSize: number;
|
|
160
|
+
/**
|
|
161
|
+
* Maximum elements (collection Values, matrix cells) one evaluation may
|
|
162
|
+
* materialize in total.
|
|
163
|
+
*
|
|
164
|
+
* `maxCollectionSize` above bounds a single collection. This bounds the sum
|
|
165
|
+
* of everything an expression asks for, which is a different question and
|
|
166
|
+
* the one that actually protects the host: two collections that are
|
|
167
|
+
* individually legal are legal together, and an operation whose result is
|
|
168
|
+
* the PRODUCT of two legal operands is bounded by neither of them. A matrix
|
|
169
|
+
* multiply is exactly that shape, so three lines within every other limit
|
|
170
|
+
* (`:a = map(1*x, 0:20000)`, `:b = transpose(a)`, `b * a`) asked for four
|
|
171
|
+
* hundred million cells and aborted the process.
|
|
172
|
+
*
|
|
173
|
+
* Counted in elements rather than bytes, because a count is what a call
|
|
174
|
+
* site has before it allocates. An element is 8 bytes as a numeric matrix
|
|
175
|
+
* cell and closer to a hundred as a full Value, so the default is worth
|
|
176
|
+
* roughly 16 MB of matrix or 200 MB of expanded collection: far past any
|
|
177
|
+
* document and far short of what an editor cannot survive.
|
|
178
|
+
*/
|
|
179
|
+
readonly maxAllocatedElements: number;
|
|
180
|
+
/**
|
|
181
|
+
* Maximum user-defined-function calls one evaluation may make in TOTAL,
|
|
182
|
+
* however deeply or widely they nest.
|
|
183
|
+
*
|
|
184
|
+
* `maxFunctionRecursionDepth` bounds how DEEP calls nest and cannot see
|
|
185
|
+
* how MANY there are, and those are different numbers. Twenty-two lines of
|
|
186
|
+
* `f(n)(v) = f(n-1)(v) + f(n-1)(v)` reach a depth of only 22 against a
|
|
187
|
+
* limit of 50, and make 2,097,152 calls doing it: a fatal heap abort in
|
|
188
|
+
* under a second. `maxInstructions` cannot bound it either, because
|
|
189
|
+
* `executeBytecode()` re-enters itself per call and each reentrant call
|
|
190
|
+
* gets its OWN instruction count, so recursion refreshes its allowance on
|
|
191
|
+
* the way in. This is the tally that does not refresh; see
|
|
192
|
+
* `vm/AllocationBudget.ts`, which holds it for the same reason it holds
|
|
193
|
+
* the element tally.
|
|
194
|
+
*
|
|
195
|
+
* Counted in calls rather than in the instructions they run, because the
|
|
196
|
+
* call is the thing that multiplies: every call allocates a frame, its
|
|
197
|
+
* arguments and its result whatever its body says.
|
|
198
|
+
*/
|
|
199
|
+
readonly maxFunctionCalls: number;
|
|
134
200
|
}
|
|
135
201
|
/**
|
|
136
202
|
* Complete engine configuration.
|
|
@@ -148,15 +214,13 @@ interface VMConfig {
|
|
|
148
214
|
* maxExpressionLength: 1000,
|
|
149
215
|
* maxComplexity: 200,
|
|
150
216
|
* },
|
|
151
|
-
* // date,
|
|
217
|
+
* // date, performance, vm, worker, diagnostic all use defaults
|
|
152
218
|
* });
|
|
153
219
|
* ```
|
|
154
220
|
*/
|
|
155
221
|
interface EngineConfig {
|
|
156
222
|
/** Date/time expression evaluation bounds and formatting */
|
|
157
223
|
readonly date: DateConfig;
|
|
158
|
-
/** Dice roll expression controls */
|
|
159
|
-
readonly dice: DiceConfig;
|
|
160
224
|
/** Performance budgets and cache sizing */
|
|
161
225
|
readonly performance: PerformanceConfig;
|
|
162
226
|
/** Safety limits for expression complexity */
|
|
@@ -200,7 +264,12 @@ declare class ConfigManager {
|
|
|
200
264
|
*/
|
|
201
265
|
set<T>(path: string, value: T): void;
|
|
202
266
|
/**
|
|
203
|
-
* Get complete configuration
|
|
267
|
+
* Get complete configuration.
|
|
268
|
+
*
|
|
269
|
+
* A detached copy, section objects included. A top-level spread would hand
|
|
270
|
+
* the caller this manager's own section objects, so `getConfig().vm.x = 1`
|
|
271
|
+
* would be an undeclared back door into {@link set}, bypassing its path
|
|
272
|
+
* validation and reaching every later {@link get}.
|
|
204
273
|
*/
|
|
205
274
|
getConfig(): EngineConfig;
|
|
206
275
|
/**
|
|
@@ -208,7 +277,16 @@ declare class ConfigManager {
|
|
|
208
277
|
*/
|
|
209
278
|
update(config: Partial<EngineConfig>): void;
|
|
210
279
|
/**
|
|
211
|
-
* Reset to default configuration
|
|
280
|
+
* Reset to default configuration.
|
|
281
|
+
*
|
|
282
|
+
* Goes through {@link mergeEngineConfig} for the same reason the constructor
|
|
283
|
+
* does. A top-level `{ ...DEFAULT_CONFIG }` copies the six section
|
|
284
|
+
* references, not the sections, so after a reset this manager's
|
|
285
|
+
* `performance` object WAS `DEFAULT_CONFIG.performance` and the next
|
|
286
|
+
* `set('performance.x', ...)` wrote into the module constant. That constant
|
|
287
|
+
* is what every `ExpressionEngine` is built from, so one manager's reset
|
|
288
|
+
* could change the cache size, instruction ceiling or allocation budget of
|
|
289
|
+
* every engine constructed later in the process.
|
|
212
290
|
*/
|
|
213
291
|
reset(): void;
|
|
214
292
|
/**
|
|
@@ -225,4 +303,4 @@ interface ValidationResult {
|
|
|
225
303
|
warnings?: string[];
|
|
226
304
|
}
|
|
227
305
|
|
|
228
|
-
export { ConfigManager as C, DEFAULT_CONFIG as D, type EngineConfig as E, type PerformanceConfig as P, type VMConfig as V, type WorkerConfig as W, type DateConfig as a, type DiagnosticConfig as b, type
|
|
306
|
+
export { ConfigManager as C, DEFAULT_CONFIG as D, type EngineConfig as E, type PerformanceConfig as P, type VMConfig as V, type WorkerConfig as W, type DateConfig as a, type DiagnosticConfig as b, type ValidationConfig as c, type ValidationResult as d };
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
*/
|
|
29
29
|
declare const CoreErrorCodes: {
|
|
30
30
|
readonly INVALID_NUMBER_LITERAL: "INVALID_NUMBER_LITERAL";
|
|
31
|
+
/** A `"` that never meets its closing partner (`lexer/ExpressionLexer.ts`'s `tokenizeString()`). Previously the tokenizer ran off the end of the input and returned what it had, so `"abc` lexed to an ordinary String and an unterminated literal was indistinguishable from a terminated one. */
|
|
32
|
+
readonly UNTERMINATED_STRING: "UNTERMINATED_STRING";
|
|
31
33
|
readonly NO_PREFIX_PARSELET: "NO_PREFIX_PARSELET";
|
|
32
34
|
readonly UNEXPECTED_END_OF_INPUT: "UNEXPECTED_END_OF_INPUT";
|
|
33
35
|
readonly UNEXPECTED_TOKEN_TYPE: "UNEXPECTED_TOKEN_TYPE";
|
|
@@ -53,7 +55,9 @@ declare const CoreErrorCodes: {
|
|
|
53
55
|
readonly EVALUATION_ERROR: "EVALUATION_ERROR";
|
|
54
56
|
readonly INSTRUCTION_LIMIT_EXCEEDED: "INSTRUCTION_LIMIT_EXCEEDED";
|
|
55
57
|
readonly STACK_LIMIT_EXCEEDED: "STACK_LIMIT_EXCEEDED";
|
|
56
|
-
/**
|
|
58
|
+
/** One evaluation asking for more elements (collection Values, matrix cells) than `vm.maxAllocatedElements` allows. The counter the two limits above cannot be: both are checked between opcodes, so neither can see what a single opcode allocates inside a loop of its own. See `vm/AllocationBudget.ts`. Recoverable, since it describes this expression rather than the engine. */
|
|
59
|
+
readonly ALLOCATION_LIMIT_EXCEEDED: "ALLOCATION_LIMIT_EXCEEDED";
|
|
60
|
+
/** New this phase. See `VM.ts`'s `safePop()`: a stack-underflow (corrupted bytecode, a buggy plugin) is now a controlled EngineError instead of a raw TypeError. Category INTERNAL, since it is an engine or plugin fault rather than a typed line, but recoverable: it happened on one line and the engine is still usable. */
|
|
57
61
|
readonly STACK_UNDERFLOW: "STACK_UNDERFLOW";
|
|
58
62
|
readonly UNDEFINED_VARIABLE: "UNDEFINED_VARIABLE";
|
|
59
63
|
/** New this phase, the Tier-2/`LOAD_GLOBAL_VAR` hardening: a global variable read before its async preflight resolved, surfaced as a controlled error instead of pushing `undefined`. */
|
|
@@ -64,15 +68,43 @@ declare const CoreErrorCodes: {
|
|
|
64
68
|
/** User-defined-function call/definition errors (`CALL_USER_FUNCTION`/`DEFINE_USER_FUNCTION` opcodes), calling a name with no matching definition, calling with the wrong argument count, and the deliberate v1 restriction that a function body can't itself contain async work. */
|
|
65
69
|
readonly UNDEFINED_FUNCTION: "UNDEFINED_FUNCTION";
|
|
66
70
|
readonly FUNCTION_ARITY_MISMATCH: "FUNCTION_ARITY_MISMATCH";
|
|
71
|
+
/** The same mistake against a BUILT-IN rather than a user-defined function (`sqrt()`, `atan2(1)`, `sqrt(1,2,3)`). Separate from `FUNCTION_ARITY_MISMATCH` above because the two are raised by different opcodes and a host may want to word them differently. Checked at the `CALL_BUILTIN` dispatch against `vm/VMBuiltinArity.ts`; recoverable, since it is a typo in the line, not an engine fault. */
|
|
72
|
+
readonly BUILTIN_ARITY_MISMATCH: "BUILTIN_ARITY_MISMATCH";
|
|
67
73
|
readonly USER_FUNCTION_ASYNC_UNSUPPORTED: "USER_FUNCTION_ASYNC_UNSUPPORTED";
|
|
68
74
|
/** A map/reduce transform body (inline expression or user-defined function) calling an async plugin. Same v1 scope restriction as `USER_FUNCTION_ASYNC_UNSUPPORTED` above, enforced both at parse time (`MAP_REDUCE_TRANSFORM_MUST_BE_SYNCHRONOUS`, packages/mapreduce/) and as a defense-in-depth runtime backstop here. */
|
|
69
75
|
readonly MAP_REDUCE_ASYNC_UNSUPPORTED: "MAP_REDUCE_ASYNC_UNSUPPORTED";
|
|
76
|
+
/** An algebra verb's expression argument (`BIND_UNKNOWN`'s body) calling an async plugin. Same v1 scope restriction as the two above, and likewise refused at parse time first (`SYMBOLIC_ARGUMENT_MUST_BE_SYNCHRONOUS`, packages/symbolic/). */
|
|
77
|
+
readonly SYMBOLIC_ASYNC_UNSUPPORTED: "SYMBOLIC_ASYNC_UNSUPPORTED";
|
|
70
78
|
/** `pushCallFrame()`'s recursion guard, a nested `CALL_USER_FUNCTION` re-enters `executeBytecode()`, so `maxInstructions` alone can't catch e.g. `f(x) = f(x)`; this is the dedicated backstop. `recoverable: true` (the default for `.execution()`), ordinary user-written infinite recursion, not an engine bug; the guard exists precisely so it surfaces as a clear error instead of overflowing the native call stack uncatchably. */
|
|
71
79
|
readonly FUNCTION_RECURSION_LIMIT_EXCEEDED: "FUNCTION_RECURSION_LIMIT_EXCEEDED";
|
|
80
|
+
/** The companion to the code above, and the half it could never see: how MANY user-defined-function calls one evaluation makes, rather than how deeply they nest. A twenty-two-line doubling chain nests twenty-two deep (legal) and makes two million calls (a fatal heap abort). Counted in `vm/AllocationBudget.ts`, because the tally has to survive `executeBytecode()` re-entering itself. Recoverable. */
|
|
81
|
+
readonly FUNCTION_CALL_LIMIT_EXCEEDED: "FUNCTION_CALL_LIMIT_EXCEEDED";
|
|
82
|
+
/** A `<date> + N workdays` offset outside `date.maxOffsetYears`/`minOffsetYears`. Workdays are the one date offset that walks the calendar a day at a time, so the one whose cost is the offset; every other one moves a Date field once. Recoverable. */
|
|
83
|
+
readonly DATE_OFFSET_LIMIT_EXCEEDED: "DATE_OFFSET_LIMIT_EXCEEDED";
|
|
84
|
+
/** A `<<`/`>>` with a bigint operand whose exact result would pass `MAX_EXACT_SHIFT_BITS`, whichever operand is the bigint. Both spellings refuse as of 1.0.0: a bigint on the left used to fall through to `x * 2^n` in doubles and report a 19,870-digit integer as Infinity. See `vm/VM.ts`'s `bigIntShift()`. Recoverable. */
|
|
85
|
+
readonly BIGINT_SHIFT_LIMIT_EXCEEDED: "BIGINT_SHIFT_LIMIT_EXCEEDED";
|
|
86
|
+
/** The same ceiling for `^`, the operator it was written for: `2n ^ 100000` asks for the same 100,001-bit integer `1n << 100000` does, so the two spellings answer the same way. Also new in 1.0.0, and for the same reason: this used to fall through to the double path and answer Infinity. A fractional or negative exponent (`4n ^ 0.5`, `2n ^ -1`) has no exact answer to bound and still uses the double path. See `vm/VM.ts`'s `MAX_EXACT_POW_BITS`. Recoverable. */
|
|
87
|
+
readonly BIGINT_POW_LIMIT_EXCEEDED: "BIGINT_POW_LIMIT_EXCEEDED";
|
|
88
|
+
/** An operand with no whole-number form (a fraction, an infinity, a NaN) meeting a bigint: `1n + 0.5`, `1n & 1.5`, `5n/pi`. `BigInt()` answers those with a raw RangeError, which the VM relabelled UNEXPECTED_ERROR, so a typo in the line was reported as an engine fault. Recoverable; see `vm/VMConversion.ts`'s `toBigIntOperand()`. */
|
|
89
|
+
readonly BIGINT_INEXACT_OPERAND: "BIGINT_INEXACT_OPERAND";
|
|
90
|
+
/** `10n / 0n` and `10n mod 0n`. Deliberately NOT what `1 / 0` does, which is Infinity: a bigint division is exact integer division (`7n / 2n` is 3n), and integer division by zero has no answer, exactly as it has none in C, Java, Python or JavaScript's own BigInt. Previously V8's own RangeError, relabelled UNEXPECTED_ERROR. Recoverable; see `vm/VMConversion.ts`'s `bigIntDivisionByZero()`. */
|
|
91
|
+
readonly BIGINT_DIVISION_BY_ZERO: "BIGINT_DIVISION_BY_ZERO";
|
|
72
92
|
/** `DEFINE_USER_FUNCTION`'s body-index lookup failing, a compiler/VM invariant violation (the opcode stream referenced a `userFunctionBodies` slot that doesn't exist), never a user-input error. */
|
|
73
93
|
readonly INTERNAL_MISSING_FUNCTION_BODY: "INTERNAL_MISSING_FUNCTION_BODY";
|
|
74
94
|
/** `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`. */
|
|
75
95
|
readonly INTERNAL_MISSING_ANONYMOUS_BODY: "INTERNAL_MISSING_ANONYMOUS_BODY";
|
|
96
|
+
/** An operand byte read past the end of the stream: the program ends in the middle of an instruction. */
|
|
97
|
+
readonly MALFORMED_BYTECODE_TRUNCATED: "MALFORMED_BYTECODE_TRUNCATED";
|
|
98
|
+
/** A constant-pool operand indexing a `numbers`/`strings` entry that does not exist, or that is not of the pool's type. */
|
|
99
|
+
readonly MALFORMED_BYTECODE_CONSTANT_INDEX: "MALFORMED_BYTECODE_CONSTANT_INDEX";
|
|
100
|
+
/** A unit or converter name read off the value stack that is not a string. Distinct from the pool case above: the operand is a Value another opcode pushed, not a pool entry. */
|
|
101
|
+
readonly MALFORMED_BYTECODE_OPERAND_TYPE: "MALFORMED_BYTECODE_OPERAND_TYPE";
|
|
102
|
+
/** `MAP_INVOKE`/`REDUCE_INVOKE` carrying a body kind other than 0/1/2. Used to fall through every arm with no body and recurse into `executeBytecode(undefined)`. */
|
|
103
|
+
readonly MALFORMED_BYTECODE_BODY_KIND: "MALFORMED_BYTECODE_BODY_KIND";
|
|
104
|
+
/** A `PUSH_BIGINT` pool entry that is not a whole number, e.g. `"1.000"`. `BigInt()` answers that with a raw SyntaxError. */
|
|
105
|
+
readonly MALFORMED_BYTECODE_BIGINT_LITERAL: "MALFORMED_BYTECODE_BIGINT_LITERAL";
|
|
106
|
+
/** `executeBytecode()` called with something that is not a runnable program at all. Checked before the destructure that used to throw outside the function's own try/catch. */
|
|
107
|
+
readonly MALFORMED_BYTECODE_PROGRAM: "MALFORMED_BYTECODE_PROGRAM";
|
|
76
108
|
/** A coefficient grew past `RATIONAL_MAX_BITS`, e.g. repeated exact elimination multiplying denominators together. */
|
|
77
109
|
readonly SYMBOLIC_RATIONAL_OVERFLOW: "SYMBOLIC_RATIONAL_OVERFLOW";
|
|
78
110
|
/** `NaN` or `±Infinity` reaching a symbolic expression, neither of which has an exact rational value. */
|
|
@@ -87,6 +119,8 @@ declare const CoreErrorCodes: {
|
|
|
87
119
|
readonly SYMBOLIC_FACTOR_LIMIT_EXCEEDED: "SYMBOLIC_FACTOR_LIMIT_EXCEEDED";
|
|
88
120
|
/** An equation outside what the solver attempts: not a polynomial, above the degree ceiling, or non-linear in the unknown while another unknown is present. */
|
|
89
121
|
readonly SYMBOLIC_SOLVE_UNSUPPORTED: "SYMBOLIC_SOLVE_UNSUPPORTED";
|
|
122
|
+
/** Some but not all of an equation's roots were found. Reported rather than returned, because a partial list of roots looks exactly like a complete one. */
|
|
123
|
+
readonly SYMBOLIC_SOLVE_INCOMPLETE: "SYMBOLIC_SOLVE_INCOMPLETE";
|
|
90
124
|
/** `solve`'s second argument not being a bare name. */
|
|
91
125
|
readonly SOLVE_REQUIRES_VARIABLE_NAME: "SOLVE_REQUIRES_VARIABLE_NAME";
|
|
92
126
|
/** A derivative order outside 0..`DERIVATIVE_MAX_ORDER`. */
|
|
@@ -105,11 +139,15 @@ declare const CoreErrorCodes: {
|
|
|
105
139
|
readonly INTERNAL_RATIONAL_PARSE: "INTERNAL_RATIONAL_PARSE";
|
|
106
140
|
readonly EXPRESSION_TOO_LONG: "EXPRESSION_TOO_LONG";
|
|
107
141
|
readonly EXPRESSION_TOO_COMPLEX: "EXPRESSION_TOO_COMPLEX";
|
|
142
|
+
/** A document with more lines than `performance.maxDocumentLines`. The per-line limits above bound what one line may ask for and say nothing about how many lines there are; two hundred thousand of `1 + 1` exhausted the heap on the line records alone. Recoverable. */
|
|
143
|
+
readonly DOCUMENT_TOO_LARGE: "DOCUMENT_TOO_LARGE";
|
|
108
144
|
readonly NORMALIZED_TOKEN_LIMIT_EXCEEDED: "NORMALIZED_TOKEN_LIMIT_EXCEEDED";
|
|
109
145
|
/** `"=>"` with nothing before it, needs an expression or variable name to solve/simplify. */
|
|
110
146
|
readonly THEREFORE_REQUIRES_EXPRESSION: "THEREFORE_REQUIRES_EXPRESSION";
|
|
111
147
|
/** A `"=>"`-triggered expression called an async plugin (weather/stocks/currency). Same v1 scope restriction as user-function/map-reduce bodies. */
|
|
112
148
|
readonly THEREFORE_ASYNC_UNSUPPORTED: "THEREFORE_ASYNC_UNSUPPORTED";
|
|
149
|
+
/** 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. */
|
|
150
|
+
readonly INVALID_TIME_LITERAL: "INVALID_TIME_LITERAL";
|
|
113
151
|
readonly CONFIG_PATH_NOT_FOUND: "CONFIG_PATH_NOT_FOUND";
|
|
114
152
|
readonly INVALID_CONFIG_PATH: "INVALID_CONFIG_PATH";
|
|
115
153
|
readonly CONFIG_SECTION_NOT_FOUND: "CONFIG_SECTION_NOT_FOUND";
|
|
@@ -192,20 +230,29 @@ interface EngineErrorInit {
|
|
|
192
230
|
/** An actionable, worked-example fix, e.g. `e.g. "weather in London"`. Mirrors this codebase's best existing messages (`WEATHER_EXPECTED_CITY`, `AS_CONVERTER_EXPECTED_NAME`). */
|
|
193
231
|
suggestion?: string;
|
|
194
232
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
233
|
+
* Whether this engine instance is still usable. `true` for everything that
|
|
234
|
+
* went wrong on ONE line, whether the line's fault (bad syntax, an unknown
|
|
235
|
+
* variable, a safety limit exceeded), the environment's (an external API
|
|
236
|
+
* down), or the engine's own (corrupted bytecode, a stack underflow from a
|
|
237
|
+
* buggy plugin). `false` is for the far rarer case where there is no working
|
|
238
|
+
* engine to go on with: a configuration or package-registration failure,
|
|
239
|
+
* which is what `ErrorFactory.config()` is for and the only factory method
|
|
240
|
+
* that still defaults to it.
|
|
202
241
|
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
242
|
+
* The category answers a different question, and the two used to be answered
|
|
243
|
+
* as one. Category INTERNAL says whose fault this is, the engine's, worth
|
|
244
|
+
* reporting as a bug. `recoverable` says whether the host may carry on, and
|
|
245
|
+
* after an internal slip on one line it may:
|
|
246
|
+
* `__tests__/hardening/RobustnessEngineLifecycle.spec.ts` alternates a
|
|
247
|
+
* throwing line with a good one five hundred times and every answer stays
|
|
248
|
+
* correct. Reporting that as `isFatal()` told a host the opposite, and a host
|
|
249
|
+
* that honours the name would tear a document down over one bad line.
|
|
250
|
+
*
|
|
251
|
+
* This has never gated "does evaluation of the rest of the document
|
|
252
|
+
* continue"; with this engine's per-line containment it always does, even for
|
|
253
|
+
* a `recoverable: false` error (see `ARCHITECTURE.md`'s async-batcher/Tier-2
|
|
254
|
+
* hardening notes). It gates what a host is TOLD. See
|
|
255
|
+
* {@link EngineError.isFatal}.
|
|
209
256
|
*/
|
|
210
257
|
recoverable?: boolean;
|
|
211
258
|
/** Character-offset span into the source expression, when available. Not yet threaded through every call site, populate opportunistically, don't block on retrofitting every existing throw site. */
|
|
@@ -281,10 +328,23 @@ declare class ErrorFactory {
|
|
|
281
328
|
static execution(init: EngineErrorInit): EngineError;
|
|
282
329
|
static external(code: string, message: string, context?: Record<string, unknown>): EngineError;
|
|
283
330
|
static external(init: EngineErrorInit): EngineError;
|
|
284
|
-
/**
|
|
331
|
+
/**
|
|
332
|
+
* Reserve for genuine engine-internal invariant violations (corrupted
|
|
333
|
+
* bytecode, a stack underflow from a buggy plugin, an "impossible" state),
|
|
334
|
+
* not for user-input errors: the category is a bug report.
|
|
335
|
+
*
|
|
336
|
+
* Defaults `recoverable: true` all the same, because every one of these sites
|
|
337
|
+
* is an invariant that failed on ONE line and none of them leaves the engine
|
|
338
|
+
* unusable. This used to default to `false`, which conflated "the engine's
|
|
339
|
+
* fault" with "the engine is finished" and told a host to tear a document
|
|
340
|
+
* down over a line it could simply have shown an error against. A site that
|
|
341
|
+
* has looked at its own case and concluded the instance really is gone can
|
|
342
|
+
* still pass `recoverable: false` explicitly. See {@link
|
|
343
|
+
* EngineErrorInit.recoverable}.
|
|
344
|
+
*/
|
|
285
345
|
static internal(code: string, message: string, context?: Record<string, unknown>): EngineError;
|
|
286
346
|
static internal(init: EngineErrorInit): EngineError;
|
|
287
|
-
/** Defaults `recoverable: false`, a
|
|
347
|
+
/** Defaults `recoverable: false`, and is now the only method that does: a configuration or package-registration failure is the one class that leaves no working engine to carry on with, rather than one bad line in a document. */
|
|
288
348
|
static config(code: string, message: string, context?: Record<string, unknown>): EngineError;
|
|
289
349
|
static config(init: EngineErrorInit): EngineError;
|
|
290
350
|
}
|
|
@@ -298,6 +358,26 @@ declare class ErrorFactory {
|
|
|
298
358
|
* per-line containment) so a raw `TypeError` from a genuine engine bug
|
|
299
359
|
* still surfaces as a structured, catalogued error instead of an opaque
|
|
300
360
|
* uncaught exception.
|
|
361
|
+
*
|
|
362
|
+
* Both branches state RECOVERABLE explicitly, while keeping the INTERNAL
|
|
363
|
+
* category. The two fields answer different questions and were previously
|
|
364
|
+
* answering the same one:
|
|
365
|
+
*
|
|
366
|
+
* category INTERNAL whose fault is this? The engine's. Worth reporting.
|
|
367
|
+
* recoverable is this engine instance still usable? Yes.
|
|
368
|
+
*
|
|
369
|
+
* Everything reaching here is by definition something the engine did not
|
|
370
|
+
* anticipate on ONE line, and per-line containment means the next line
|
|
371
|
+
* evaluates normally, which `__tests__/hardening/RobustnessEngineLifecycle`
|
|
372
|
+
* demonstrates over hundreds of alternating failure/success pairs. Reporting
|
|
373
|
+
* that as `isFatal()` told a host the opposite, and a host that honours the
|
|
374
|
+
* name would tear a document down over a single bad line. The "this is an
|
|
375
|
+
* engine bug rather than your syntax" signal a caller wants for telemetry is
|
|
376
|
+
* the CATEGORY, which is unchanged.
|
|
377
|
+
*
|
|
378
|
+
* The flag is written out here rather than inherited from
|
|
379
|
+
* `ErrorFactory.internal()`, which now defaults the same way, so that this
|
|
380
|
+
* function's contract survives a future change to that default.
|
|
301
381
|
*/
|
|
302
382
|
declare function normalizeUnknownError(error: unknown): EngineError;
|
|
303
383
|
|