solve-engine 2.24.0 → 2.26.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/dist/{BytecodeBuilder-aqVa7Plx.d.cts → BytecodeBuilder-DSWKZi4f.d.cts} +11 -9
- package/dist/{BytecodeBuilder-aqVa7Plx.d.ts → BytecodeBuilder-DSWKZi4f.d.ts} +11 -9
- package/dist/CalendarBackend-MdS3Bb4P.d.cts +181 -0
- package/dist/CalendarBackend-MdS3Bb4P.d.ts +181 -0
- package/dist/{Configuration-DnzYPmoK.d.cts → Configuration-BxriK9Kw.d.cts} +63 -4
- package/dist/{Configuration-DnzYPmoK.d.ts → Configuration-BxriK9Kw.d.ts} +63 -4
- package/dist/DateCalendar-B85t6Vk6.d.ts +90 -0
- package/dist/DateCalendar-CUGI1fT4.d.cts +90 -0
- package/dist/{EngineError-D1kXsjIj.d.cts → EngineError-C-kBwYlN.d.cts} +33 -4
- package/dist/{EngineError-D1kXsjIj.d.ts → EngineError-C-kBwYlN.d.ts} +33 -4
- package/dist/{FormattingSettings-bRKjU7wI.d.ts → FormattingSettings-ByO-4wiM.d.cts} +14 -0
- package/dist/{FormattingSettings-bRKjU7wI.d.cts → FormattingSettings-CvCzAfsz.d.ts} +14 -0
- package/dist/{Lexer-CqagTewQ.d.ts → Lexer-DhGhoAa2.d.cts} +96 -37
- package/dist/{Lexer-CCHFDcgP.d.cts → Lexer-DqtnZMC5.d.ts} +96 -37
- package/dist/{PackageCompatibility-CeAPVHZr.d.cts → PackageCompatibility-CNiTtC6S.d.cts} +1 -1
- package/dist/{PackageCompatibility-Bq82y0ZN.d.ts → PackageCompatibility-fj_f7uqC.d.ts} +1 -1
- package/dist/{PackageRegistry-B9-7fJGH.d.cts → PackageRegistry-BHBCFswo.d.cts} +377 -15
- package/dist/{PackageRegistry-CDEgfhT_.d.ts → PackageRegistry-CNZUwWHJ.d.ts} +377 -15
- package/dist/{Parselet-4oH5OuRt.d.cts → Parselet-DF3864la.d.cts} +26 -4
- package/dist/{Parselet-BwDqKFfK.d.ts → Parselet-Dgymjxzd.d.ts} +26 -4
- package/dist/{Token-CbP_OutD.d.cts → Token-D8f7yaz1.d.cts} +17 -0
- package/dist/{Token-CbP_OutD.d.ts → Token-D8f7yaz1.d.ts} +17 -0
- package/dist/{TokenNormalizer-OTPS0Otq.d.ts → TokenNormalizer-IF7nPRMp.d.ts} +46 -27
- package/dist/{TokenNormalizer-MXaKLJ_m.d.cts → TokenNormalizer-Pe3_390t.d.cts} +46 -27
- package/dist/{ScopeManager-BQBhlDAu.d.ts → VMBuiltins-CZiZRKn-.d.ts} +92 -10
- package/dist/{ScopeManager-DrXB3Nvm.d.cts → VMBuiltins-kv4rr0y5.d.cts} +92 -10
- package/dist/{VMCheckpoints-C5jC92o3.d.ts → VMCheckpoints-DXze6ypg.d.ts} +3 -3
- package/dist/{VMCheckpoints-BK32PTl2.d.cts → VMCheckpoints-yun4ocSv.d.cts} +3 -3
- package/dist/{Value-Ds3Gy07C.d.cts → Value-BYHw-x7q.d.cts} +57 -1
- package/dist/{Value-Ds3Gy07C.d.ts → Value-BYHw-x7q.d.ts} +57 -1
- package/dist/{WorkerError-DaNWQFp1.d.cts → WorkerError-BLZyNq6M.d.cts} +1 -1
- package/dist/{WorkerError-CVQbs6_D.d.ts → WorkerError-CfBJYueR.d.ts} +1 -1
- package/dist/chunk-2SYYKQ4Q.cjs +2 -0
- package/dist/chunk-2SYYKQ4Q.cjs.map +1 -0
- package/dist/chunk-6EYKVQT3.cjs +2 -0
- package/dist/chunk-6EYKVQT3.cjs.map +1 -0
- package/dist/chunk-7RC6RBS6.cjs +2 -0
- package/dist/chunk-7RC6RBS6.cjs.map +1 -0
- package/dist/chunk-AZX4NTYN.cjs +3 -0
- package/dist/chunk-AZX4NTYN.cjs.map +1 -0
- package/dist/{chunk-PTQCHMYA.js → chunk-BCR53EIL.js} +2 -2
- package/dist/{chunk-PTQCHMYA.js.map → chunk-BCR53EIL.js.map} +1 -1
- package/dist/chunk-BLJ6E577.js +2 -0
- package/dist/chunk-BLJ6E577.js.map +1 -0
- package/dist/{chunk-CMZSK6FY.cjs → chunk-C5GEMKUY.cjs} +3 -3
- package/dist/{chunk-CMZSK6FY.cjs.map → chunk-C5GEMKUY.cjs.map} +1 -1
- package/dist/chunk-CL7DM2FX.js +2 -0
- package/dist/chunk-CL7DM2FX.js.map +1 -0
- package/dist/chunk-CQLCTKMX.cjs +2 -0
- package/dist/chunk-CQLCTKMX.cjs.map +1 -0
- package/dist/chunk-CQY23OF5.cjs +5 -0
- package/dist/chunk-CQY23OF5.cjs.map +1 -0
- package/dist/chunk-D4VBWPWN.js +2 -0
- package/dist/chunk-D4VBWPWN.js.map +1 -0
- package/dist/chunk-E4HAKNBQ.cjs +2 -0
- package/dist/chunk-E4HAKNBQ.cjs.map +1 -0
- package/dist/{chunk-GP4H5OIT.js → chunk-ESZNJQ2C.js} +3 -3
- package/dist/{chunk-GP4H5OIT.js.map → chunk-ESZNJQ2C.js.map} +1 -1
- package/dist/{chunk-IJMNVBIS.js → chunk-FAO6DQ74.js} +2 -2
- package/dist/chunk-FAO6DQ74.js.map +1 -0
- package/dist/{chunk-2VE4OW4A.cjs → chunk-FDKTESBC.cjs} +2 -2
- package/dist/{chunk-2VE4OW4A.cjs.map → chunk-FDKTESBC.cjs.map} +1 -1
- package/dist/{chunk-RN3ISTM3.cjs → chunk-G2V33LFM.cjs} +2 -2
- package/dist/{chunk-RN3ISTM3.cjs.map → chunk-G2V33LFM.cjs.map} +1 -1
- package/dist/chunk-GJZIJK2Q.cjs +2 -0
- package/dist/chunk-GJZIJK2Q.cjs.map +1 -0
- package/dist/chunk-GQM6ICDM.js +2 -0
- package/dist/chunk-GQM6ICDM.js.map +1 -0
- package/dist/chunk-GVL3ZMS7.cjs +2 -0
- package/dist/chunk-GVL3ZMS7.cjs.map +1 -0
- package/dist/chunk-GXO7TSXQ.cjs +3 -0
- package/dist/chunk-GXO7TSXQ.cjs.map +1 -0
- package/dist/{chunk-OMHRBKAT.js → chunk-HANGBVEE.js} +2 -2
- package/dist/{chunk-OMHRBKAT.js.map → chunk-HANGBVEE.js.map} +1 -1
- package/dist/chunk-J4K72CQN.js +2 -0
- package/dist/chunk-J4K72CQN.js.map +1 -0
- package/dist/chunk-LE6WZLJ4.js +2 -0
- package/dist/chunk-LE6WZLJ4.js.map +1 -0
- package/dist/chunk-LMZDTQS5.js +3 -0
- package/dist/chunk-LMZDTQS5.js.map +1 -0
- package/dist/chunk-LQIRBP4Q.js +2 -0
- package/dist/chunk-LQIRBP4Q.js.map +1 -0
- package/dist/{chunk-UXR7JIPX.js → chunk-LTUYWJGO.js} +2 -2
- package/dist/{chunk-UXR7JIPX.js.map → chunk-LTUYWJGO.js.map} +1 -1
- package/dist/chunk-MCU7UYKG.js +3 -0
- package/dist/{chunk-7EP36NXE.js.map → chunk-MCU7UYKG.js.map} +1 -1
- package/dist/chunk-MTN53APQ.js +5 -0
- package/dist/chunk-MTN53APQ.js.map +1 -0
- package/dist/chunk-OPKJ2WWB.cjs +3 -0
- package/dist/chunk-OPKJ2WWB.cjs.map +1 -0
- package/dist/chunk-QQHHZPEW.cjs +2 -0
- package/dist/chunk-QQHHZPEW.cjs.map +1 -0
- package/dist/chunk-RRUGQ6DM.js +2 -0
- package/dist/chunk-RRUGQ6DM.js.map +1 -0
- package/dist/chunk-S3ODNMJS.js +2 -0
- package/dist/chunk-S3ODNMJS.js.map +1 -0
- package/dist/{chunk-CDNJZBM3.cjs → chunk-SE6ZCGZ5.cjs} +2 -2
- package/dist/{chunk-CDNJZBM3.cjs.map → chunk-SE6ZCGZ5.cjs.map} +1 -1
- package/dist/chunk-TC23XEAV.js +2 -0
- package/dist/chunk-TC23XEAV.js.map +1 -0
- package/dist/{chunk-MWWAKZOD.cjs → chunk-TMQ37JQY.cjs} +3 -3
- package/dist/{chunk-MWWAKZOD.cjs.map → chunk-TMQ37JQY.cjs.map} +1 -1
- package/dist/chunk-TPPM4QSS.cjs +2 -0
- package/dist/chunk-TPPM4QSS.cjs.map +1 -0
- package/dist/chunk-VB6QMU2W.js +3 -0
- package/dist/chunk-VB6QMU2W.js.map +1 -0
- package/dist/chunk-VQO54ZMA.js +2 -0
- package/dist/chunk-VQO54ZMA.js.map +1 -0
- package/dist/chunk-Y7XRA2EV.js +2 -0
- package/dist/chunk-Y7XRA2EV.js.map +1 -0
- package/dist/{chunk-5IJD5L3O.cjs → chunk-YBQOQTVL.cjs} +2 -2
- package/dist/chunk-YBQOQTVL.cjs.map +1 -0
- package/dist/chunk-YG7UWQFV.js +2 -0
- package/dist/chunk-YG7UWQFV.js.map +1 -0
- package/dist/chunk-YS2OW75C.cjs +2 -0
- package/dist/chunk-YS2OW75C.cjs.map +1 -0
- package/dist/chunk-ZC2NPRLO.js +3 -0
- package/dist/chunk-ZC2NPRLO.js.map +1 -0
- package/dist/chunk-ZDJTDFTR.cjs +2 -0
- package/dist/chunk-ZDJTDFTR.cjs.map +1 -0
- package/dist/chunk-ZE5KKMBV.cjs +2 -0
- package/dist/chunk-ZE5KKMBV.cjs.map +1 -0
- package/dist/chunk-ZSLVLMO5.cjs +2 -0
- package/dist/chunk-ZSLVLMO5.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 +15 -13
- package/dist/engine.d.ts +15 -13
- 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 +4 -3
- package/dist/format.d.ts +4 -3
- package/dist/format.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -14
- package/dist/index.d.ts +16 -14
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/language.d.cts +13 -12
- package/dist/language.d.ts +13 -12
- package/dist/lexer.cjs +1 -1
- package/dist/lexer.cjs.map +1 -1
- package/dist/lexer.d.cts +9 -8
- package/dist/lexer.d.ts +9 -8
- package/dist/lexer.js +1 -1
- package/dist/lexer.js.map +1 -1
- package/dist/normalizer.cjs +1 -1
- package/dist/normalizer.d.cts +3 -3
- package/dist/normalizer.d.ts +3 -3
- package/dist/normalizer.js +1 -1
- package/dist/packages.cjs +1 -1
- package/dist/packages.d.cts +12 -11
- package/dist/packages.d.ts +12 -11
- package/dist/packages.js +1 -1
- package/dist/parser.cjs +1 -1
- package/dist/parser.d.cts +6 -5
- package/dist/parser.d.ts +6 -5
- package/dist/parser.js +1 -1
- package/dist/{pipeline-69q2tsLE.d.cts → pipeline-BYkKNpql.d.cts} +1 -1
- package/dist/{pipeline-DTqGLPsV.d.ts → pipeline-Cl7KW8CB.d.ts} +1 -1
- package/dist/resolvers.d.cts +3 -3
- package/dist/resolvers.d.ts +3 -3
- package/dist/temporal.cjs +2 -0
- package/dist/temporal.cjs.map +1 -0
- package/dist/temporal.d.cts +201 -0
- package/dist/temporal.d.ts +201 -0
- package/dist/temporal.js +2 -0
- package/dist/temporal.js.map +1 -0
- package/dist/testing.cjs +2 -2
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +13 -12
- package/dist/testing.d.ts +13 -12
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/uom.cjs +1 -1
- package/dist/uom.d.cts +3 -3
- package/dist/uom.d.ts +3 -3
- package/dist/uom.js +1 -1
- package/dist/vm.cjs +1 -1
- package/dist/vm.cjs.map +1 -1
- package/dist/vm.d.cts +9 -57
- package/dist/vm.d.ts +9 -57
- package/dist/vm.js +1 -1
- package/dist/vm.js.map +1 -1
- package/dist/worker.cjs +2 -2
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +36 -14
- package/dist/worker.d.ts +36 -14
- package/dist/worker.js +2 -2
- package/dist/worker.js.map +1 -1
- package/package.json +16 -5
- package/dist/chunk-2BXNZM3G.js +0 -2
- package/dist/chunk-2BXNZM3G.js.map +0 -1
- package/dist/chunk-2JSNWYYA.cjs +0 -3
- package/dist/chunk-2JSNWYYA.cjs.map +0 -1
- package/dist/chunk-3SHWTGTP.js +0 -2
- package/dist/chunk-3SHWTGTP.js.map +0 -1
- package/dist/chunk-5GL4SAVH.cjs +0 -2
- package/dist/chunk-5GL4SAVH.cjs.map +0 -1
- package/dist/chunk-5IJD5L3O.cjs.map +0 -1
- package/dist/chunk-72N3ZRVE.cjs +0 -2
- package/dist/chunk-72N3ZRVE.cjs.map +0 -1
- package/dist/chunk-7EP36NXE.js +0 -3
- package/dist/chunk-A4JV7HRB.js +0 -3
- package/dist/chunk-A4JV7HRB.js.map +0 -1
- package/dist/chunk-AUOE7MFI.cjs +0 -2
- package/dist/chunk-AUOE7MFI.cjs.map +0 -1
- package/dist/chunk-BDF4VCQX.js +0 -3
- package/dist/chunk-BDF4VCQX.js.map +0 -1
- package/dist/chunk-D2Q2VFJ6.js +0 -2
- package/dist/chunk-D2Q2VFJ6.js.map +0 -1
- package/dist/chunk-DA7M6H63.cjs +0 -2
- package/dist/chunk-DA7M6H63.cjs.map +0 -1
- package/dist/chunk-ELQQKZN3.cjs +0 -3
- package/dist/chunk-ELQQKZN3.cjs.map +0 -1
- package/dist/chunk-F7QIBC4B.cjs +0 -3
- package/dist/chunk-F7QIBC4B.cjs.map +0 -1
- package/dist/chunk-HQ7BKXG7.js +0 -2
- package/dist/chunk-HQ7BKXG7.js.map +0 -1
- package/dist/chunk-IHAZL4EF.cjs +0 -2
- package/dist/chunk-IHAZL4EF.cjs.map +0 -1
- package/dist/chunk-IJMNVBIS.js.map +0 -1
- package/dist/chunk-IPSOWFCA.js +0 -2
- package/dist/chunk-IPSOWFCA.js.map +0 -1
- package/dist/chunk-LM6ZQUEU.js +0 -2
- package/dist/chunk-LM6ZQUEU.js.map +0 -1
- package/dist/chunk-LTJV3VJE.cjs +0 -2
- package/dist/chunk-LTJV3VJE.cjs.map +0 -1
- package/dist/chunk-NUF66H57.js +0 -5
- package/dist/chunk-NUF66H57.js.map +0 -1
- package/dist/chunk-PWWQZFAE.js +0 -2
- package/dist/chunk-PWWQZFAE.js.map +0 -1
- package/dist/chunk-RE6AIU6Y.js +0 -3
- package/dist/chunk-RE6AIU6Y.js.map +0 -1
- package/dist/chunk-RECRD45C.cjs +0 -5
- package/dist/chunk-RECRD45C.cjs.map +0 -1
- package/dist/chunk-RENO2AWO.js +0 -2
- package/dist/chunk-RENO2AWO.js.map +0 -1
- package/dist/chunk-RMCN5WFO.cjs +0 -2
- package/dist/chunk-RMCN5WFO.cjs.map +0 -1
- package/dist/chunk-S46R5QZP.cjs +0 -2
- package/dist/chunk-S46R5QZP.cjs.map +0 -1
- package/dist/chunk-UPH22K2T.cjs +0 -2
- package/dist/chunk-UPH22K2T.cjs.map +0 -1
- package/dist/chunk-UY3ID6JF.cjs +0 -2
- package/dist/chunk-UY3ID6JF.cjs.map +0 -1
- package/dist/chunk-WFOQRPA6.js +0 -2
- package/dist/chunk-WFOQRPA6.js.map +0 -1
- package/dist/chunk-XFJABR3B.js +0 -2
- package/dist/chunk-XFJABR3B.js.map +0 -1
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing/index.ts"],"names":["ExpectationError","_ExpectationError","init","createTestEngine","packages","options","locale","includeBuiltins","engine","ExpressionEngine","BUILTIN_PACKAGES","pkg","evaluate","expression","value","error","EngineError","message","Value","numbersEqual","a","b","epsilon","describeValue","describeOutcome","outcome","ExpressionAssertion","expected","unit","describeExpected","matches","valueToComparableString","code","base","expectExpression","COMMON_PROSE_WORDS","SEVERITY_ORDER","claimedWords","claimed","vocab","word","tokenType","phrase","PackageAssertion","words","prose","w","shadowed","c","list","s","others","strictness","report","checkPackageCompatibility","threshold","failing","engineVersion","ENGINE_VERSION","result","checkEngineVersionCompatibility","detail","expectPackage"],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * A test kit for package authors, the supported way to test a package by the\n * expressions it enables rather than by the opcodes it emits.\n *\n * Before this entry point a package author had two options, both bad: reach\n * into engine internals, or assert on whatever bytecode a parselet happened to\n * emit. The first is unstable across engine versions, the second pins the\n * implementation instead of the behaviour, so a refactor that keeps every\n * answer correct still breaks the tests. This module is a thin, dependency-free\n * layer over the same public surface a host uses: it constructs an\n * {@link ExpressionEngine} with the given packages and evaluates strings.\n *\n * It speaks in expressions. {@link expectExpression} evaluates a string and\n * asserts on the result or the failure code; {@link expectPackage} asserts on\n * the three mistakes a package actually makes (shadowing prose, colliding with\n * another package's vocabulary, and declaring an `engineVersion` range that the\n * running engine does not satisfy).\n *\n * Framework-agnostic on purpose. Nothing here imports jest, vitest or any\n * runner: an assertion that fails throws an {@link ExpectationError}, and one\n * that passes returns, so the kit drops into whatever runner the author already\n * has (or into a plain script, or `node:assert`). Runtime dependency-free and\n * side-effect free, so `solve-engine/testing` stays honest under the package's\n * `\"sideEffects\": false` contract.\n *\n * @example\n * ```ts\n * import { createTestEngine, expectExpression } from \"solve-engine/testing\";\n *\n * const engine = createTestEngine([myPackage]);\n * expectExpression(engine, \"2 gp + 3 gp\").toEqual(5, \"gp\");\n * expectExpression(engine, \"gp\").toFailWith(\"UNDEFINED_VARIABLE\");\n * ```\n */\n\nimport { ExpressionEngine } from \"@solve-js/engine/ExpressionEngine\";\nimport { BUILTIN_PACKAGES } from \"@solve-js/packages/builtins\";\nimport type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { Value, ValueType } from \"@solve-js/vm/Value\";\nimport { EngineError } from \"@solve-js/errors/EngineError\";\nimport {\n\tcheckPackageCompatibility,\n\ttype CompatibilityReport,\n\ttype CompatibilityConflict,\n\ttype CompatibilitySeverity,\n} from \"@solve-js/api/PackageCompatibility\";\nimport { checkEngineVersionCompatibility } from \"@solve-js/api/EngineVersionCompatibility\";\nimport { ENGINE_VERSION } from \"@solve-js/constants/version\";\n\n//#region Errors\n\n/**\n * The failure a matcher throws when an expectation is not met.\n *\n * A dedicated `Error` subclass rather than a bare `throw new Error`, so a\n * caller (or a reporter) can tell a kit assertion failure apart from an\n * unrelated exception with `instanceof`, and so the structured `expected` and\n * `actual` fields survive alongside the human-readable message. This is NOT an\n * {@link EngineError}: that type classifies pipeline-stage failures inside the\n * engine, whereas this classifies a test-assertion mismatch, a different\n * concern that would be miscategorised under any of `EngineError`'s categories.\n */\nexport class ExpectationError extends Error {\n\t/** A short, machine-readable label for which expectation failed. */\n\treadonly code: string;\n\t/** What the matcher was told to expect, in plain words. */\n\treadonly expected?: string;\n\t/** What it found instead. */\n\treadonly actual?: string;\n\n\tconstructor(init: { code: string; message: string; expected?: string; actual?: string }) {\n\t\tsuper(init.message);\n\t\tthis.name = \"ExpectationError\";\n\t\tthis.code = init.code;\n\t\tthis.expected = init.expected;\n\t\tthis.actual = init.actual;\n\t\t// Restore the prototype chain across the ES5 `Error` transpile target,\n\t\t// so `instanceof ExpectationError` holds for callers.\n\t\tObject.setPrototypeOf(this, ExpectationError.prototype);\n\t}\n}\n\n//#endregion\n\n//#region createTestEngine\n\n/** Options for {@link createTestEngine}. */\nexport interface TestEngineOptions {\n\t/** Locale passed to the engine. Defaults to `\"en\"`. */\n\tlocale?: string;\n\t/**\n\t * Whether to load the engine's built-in packages (arithmetic, units, dates,\n\t * and the rest) before the packages under test. Defaults to `true`, because\n\t * almost every package builds on arithmetic and most authors want a\n\t * realistic engine. Set `false` to test a package in isolation.\n\t */\n\tincludeBuiltins?: boolean;\n}\n\n/**\n * Build an {@link ExpressionEngine} loaded with the packages under test.\n *\n * The built-in packages load first (unless {@link TestEngineOptions.includeBuiltins}\n * is `false`), then each package in `packages` is registered in order, exactly\n * as a host would. Registration is honest: a package whose declared\n * `engineVersion` the running engine does not satisfy, or whose lexer keyword\n * collides with a built-in, throws here rather than being swallowed. That is\n * deliberately different from passing packages straight to the\n * `ExpressionEngine` constructor, which contains a bad package by logging and\n * continuing, useful in production, wrong for a test that needs to know the\n * package it is testing actually loaded.\n *\n * Call {@link ExpressionEngine.clear} when a test is finished with the engine\n * if the test creates many, see the engine's own lifecycle note.\n *\n * @example\n * ```ts\n * const engine = createTestEngine([myPackage]);\n * const engineOnly = createTestEngine([myPackage], { includeBuiltins: false });\n * ```\n */\nexport function createTestEngine(\n\tpackages: IEnginePackage[] = [],\n\toptions: TestEngineOptions = {},\n): ExpressionEngine {\n\tconst { locale = \"en\", includeBuiltins = true } = options;\n\n\t// Built-ins go through the constructor (their own containment applies);\n\t// the packages under test go through registerPackage() afterwards so their\n\t// failures surface as thrown errors instead of being logged and skipped.\n\tconst engine = new ExpressionEngine({ locale, packages: includeBuiltins ? BUILTIN_PACKAGES : [] });\n\tfor (const pkg of packages) {\n\t\tengine.registerPackage(pkg);\n\t}\n\treturn engine;\n}\n\n//#endregion\n\n//#region expectExpression\n\n/**\n * The normalised outcome of evaluating one expression, computed once when\n * {@link expectExpression} runs and read by every matcher on the result.\n *\n * The engine surfaces a failure two different ways, and a package author should\n * not have to know which: a parse/eval error is THROWN as an\n * {@link EngineError} (with a `.code`), while a plugin-raised error is RETURNED\n * as a {@link Value} of {@link ValueType.Error} (its `value` is the code, its\n * `unit` is the message). Both collapse to the `\"error\"` status here. A value\n * still resolving asynchronously is its own `\"pending\"` status, since the kit\n * evaluates synchronously and cannot report a final number for it.\n */\ntype Outcome =\n\t| { status: \"value\"; value: Value }\n\t| { status: \"pending\"; value: Value }\n\t| { status: \"error\"; code: string; message: string; source: EngineError | Value };\n\n/** Evaluate `expression` on `engine` and collapse both failure shapes to one outcome. */\nfunction evaluate(engine: ExpressionEngine, expression: string): Outcome {\n\ttry {\n\t\tconst value = engine.evaluateExpression(expression);\n\t\tif (value.isError()) {\n\t\t\treturn {\n\t\t\t\tstatus: \"error\",\n\t\t\t\tcode: value.errorCode ?? \"\",\n\t\t\t\tmessage: value.errorMessage ?? \"\",\n\t\t\t\tsource: value,\n\t\t\t};\n\t\t}\n\t\tif (value.isPending()) {\n\t\t\treturn { status: \"pending\", value };\n\t\t}\n\t\treturn { status: \"value\", value };\n\t} catch (error) {\n\t\tif (error instanceof EngineError) {\n\t\t\treturn { status: \"error\", code: error.code, message: error.message, source: error };\n\t\t}\n\t\t// Anything else thrown (a bug in a plugin handler, say) is still a\n\t\t// failure, wrapped so the matcher has a code and message to report.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn {\n\t\t\tstatus: \"error\",\n\t\t\tcode: \"UNKNOWN_ERROR\",\n\t\t\tmessage,\n\t\t\tsource: new Value(ValueType.Error, \"UNKNOWN_ERROR\", message),\n\t\t};\n\t}\n}\n\n/** Whether two numbers are equal within a small tolerance scaled to their magnitude. */\nfunction numbersEqual(a: number, b: number): boolean {\n\tif (Object.is(a, b)) return true;\n\tif (!Number.isFinite(a) || !Number.isFinite(b)) return false;\n\tconst epsilon = 1e-9 * Math.max(1, Math.abs(a), Math.abs(b));\n\treturn Math.abs(a - b) <= epsilon;\n}\n\n/** A short, readable rendering of a value for an assertion message, e.g. `5 gp` or `\"abc\"`. */\nfunction describeValue(value: Value): string {\n\tif (value.type === ValueType.String) return JSON.stringify(value.value);\n\tif (value.type === ValueType.Uom && typeof value.unit === \"string\") {\n\t\treturn `${String(value.value)} ${value.unit}`;\n\t}\n\treturn String(value.value);\n}\n\n/** A short, readable rendering of an outcome for an assertion message. */\nfunction describeOutcome(outcome: Outcome): string {\n\tswitch (outcome.status) {\n\t\tcase \"value\":\n\t\t\treturn describeValue(outcome.value);\n\t\tcase \"pending\":\n\t\t\treturn \"a pending async value\";\n\t\tcase \"error\":\n\t\t\treturn `an error (${outcome.code}: ${outcome.message})`;\n\t}\n}\n\n/**\n * Assertions about the result of one evaluated expression, returned by\n * {@link expectExpression}. Every matcher returns `this`, so assertions chain,\n * and throws an {@link ExpectationError} when it fails.\n */\nexport class ExpressionAssertion {\n\tconstructor(\n\t\tprivate readonly expression: string,\n\t\tprivate readonly outcome: Outcome,\n\t) {}\n\n\t/** The raw resolved {@link Value}, for an assertion the matchers do not cover. Throws if the expression failed. */\n\tget value(): Value {\n\t\tif (this.outcome.status === \"error\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_VALUE\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to evaluate, but it failed with ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"a resolved value\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this.outcome.value;\n\t}\n\n\t/**\n\t * Assert the expression evaluated to a value (not an error, not a pending\n\t * async result). Says nothing about what the value is.\n\t */\n\ttoEvaluate(): this {\n\t\tif (this.outcome.status !== \"value\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_EVALUATE\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to evaluate to a value, but got ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"a resolved value\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert the expression evaluated to `expected`, and, when `unit` is given,\n\t * that the result carries exactly that unit. A number compares against the\n\t * value's numeric magnitude within a small floating-point tolerance; a\n\t * string compares against the value's own string; a boolean against its\n\t * boolean reading. Omitting `unit` leaves the unit unchecked, so\n\t * `toEqual(5)` passes for both `5` and `5 gp`.\n\t */\n\ttoEqual(expected: number | string | boolean, unit?: string): this {\n\t\tif (this.outcome.status !== \"value\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_EQUAL\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to equal ${describeExpected(expected, unit)}, but got ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: describeExpected(expected, unit),\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\n\t\tconst value = this.outcome.value;\n\t\tlet matches: boolean;\n\t\tif (typeof expected === \"number\") {\n\t\t\tmatches = numbersEqual(value.toNumber(), expected);\n\t\t} else if (typeof expected === \"boolean\") {\n\t\t\t// A Boolean value stores the primitive directly; anything else can\n\t\t\t// never equal a boolean, so a mismatch reports the real value.\n\t\t\tmatches = value.type === ValueType.Boolean && value.value === expected;\n\t\t} else {\n\t\t\tmatches = valueToComparableString(value) === expected;\n\t\t}\n\n\t\tif (!matches) {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_EQUAL\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to equal ${describeExpected(expected, unit)}, but it was ${describeValue(value)}.`,\n\t\t\t\texpected: describeExpected(expected, unit),\n\t\t\t\tactual: describeValue(value),\n\t\t\t});\n\t\t}\n\n\t\tif (unit !== undefined && value.unit !== unit) {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_UNIT\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to carry the unit \"${unit}\", but it was ${value.unit === undefined ? \"unitless\" : `\"${value.unit}\"`}.`,\n\t\t\t\texpected: `unit \"${unit}\"`,\n\t\t\t\tactual: value.unit === undefined ? \"no unit\" : `unit \"${value.unit}\"`,\n\t\t\t});\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/** Assert the expression failed (a thrown engine error or a plugin-raised error value). Says nothing about the code. */\n\ttoBeError(): this {\n\t\tif (this.outcome.status !== \"error\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_ERROR\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to fail, but it produced ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"an error\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert the expression failed with exactly `code`, the error catalog code\n\t * (e.g. `\"UNDEFINED_VARIABLE\"`, or a package's own code). Reports the code\n\t * it actually got when they differ, so a near-miss is obvious.\n\t */\n\ttoFailWith(code: string): this {\n\t\tif (this.outcome.status !== \"error\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_FAIL_WITH\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to fail with \"${code}\", but it produced ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: `error code \"${code}\"`,\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\tif (this.outcome.code !== code) {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_FAIL_WITH\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to fail with \"${code}\", but it failed with \"${this.outcome.code}\" (${this.outcome.message}).`,\n\t\t\t\texpected: `error code \"${code}\"`,\n\t\t\t\tactual: `error code \"${this.outcome.code}\"`,\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert the expression returned a value still resolving asynchronously.\n\t * The kit evaluates synchronously, so a package whose result comes from an\n\t * async resolver reports pending on first evaluation, this is how a test\n\t * confirms the async path was taken without resolving it.\n\t */\n\ttoBePending(): this {\n\t\tif (this.outcome.status !== \"pending\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_PENDING\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to be pending, but got ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"a pending async value\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n}\n\n/** Render an expected value (with optional unit) for an assertion message. */\nfunction describeExpected(expected: number | string | boolean, unit?: string): string {\n\tconst base = typeof expected === \"string\" ? JSON.stringify(expected) : String(expected);\n\treturn unit === undefined ? base : `${base} ${unit}`;\n}\n\n/** The string a non-numeric value compares against in {@link ExpressionAssertion.toEqual}. */\nfunction valueToComparableString(value: Value): string {\n\t// The stored primitive stringified. For a String value this is the string\n\t// itself; for a Hex value, its `0x...` text, which is what an author writes\n\t// in a `toEqual(\"0xff\")` assertion.\n\treturn String(value.value);\n}\n\n/**\n * Evaluate `expression` on `engine` and return an assertion object.\n *\n * The expression is evaluated once, immediately, and every matcher reads that\n * one outcome, so calling several matchers on the same result does not\n * re-evaluate.\n *\n * @example\n * ```ts\n * expectExpression(engine, \"2 gp + 3 gp\").toEqual(5, \"gp\");\n * expectExpression(engine, \"gp\").toFailWith(\"UNDEFINED_VARIABLE\");\n * ```\n */\nexport function expectExpression(engine: ExpressionEngine, expression: string): ExpressionAssertion {\n\treturn new ExpressionAssertion(expression, evaluate(engine, expression));\n}\n\n//#endregion\n\n//#region expectPackage\n\n/**\n * A modest set of everyday English words a package's keywords should not claim.\n *\n * A starting point for {@link PackageAssertion.notToShadow}, not an exhaustive\n * dictionary: it leans on the function words and common nouns/verbs a package\n * is most tempted to grab as a trigger (`price`, `of`, `per`, `sum`, `total`).\n * Pass your own list to check against the specific prose your package sits in.\n */\nexport const COMMON_PROSE_WORDS: readonly string[] = [\n\t\"a\", \"an\", \"and\", \"or\", \"but\", \"if\", \"then\", \"the\", \"this\", \"that\", \"these\", \"those\",\n\t\"in\", \"on\", \"at\", \"to\", \"of\", \"for\", \"from\", \"with\", \"by\", \"as\", \"per\", \"into\", \"over\",\n\t\"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\", \"do\", \"does\", \"did\", \"have\", \"has\", \"had\",\n\t\"will\", \"would\", \"can\", \"could\", \"should\", \"may\", \"might\", \"must\",\n\t\"price\", \"cost\", \"value\", \"total\", \"sum\", \"count\", \"rate\", \"amount\", \"number\",\n\t\"time\", \"date\", \"day\", \"week\", \"month\", \"year\", \"hour\", \"minute\", \"second\",\n\t\"add\", \"buy\", \"sell\", \"hold\", \"make\", \"take\", \"give\", \"get\", \"set\", \"run\",\n\t\"up\", \"down\", \"out\", \"off\", \"all\", \"some\", \"any\", \"no\", \"not\", \"more\", \"less\",\n\t\"one\", \"two\", \"three\", \"first\", \"last\", \"next\", \"each\", \"every\", \"here\", \"there\",\n];\n\n/** How strict {@link PackageAssertion.notToCollideWith} is about the conflicts it will accept. */\nexport type CollisionStrictness = \"error\" | \"warning\" | \"info\";\n\n/** One shadowed word found by {@link PackageAssertion.notToShadow}: the prose word and how the package claims it. */\nexport interface ShadowedWord {\n\t/** The prose word the package claims. */\n\tword: string;\n\t/** Which descriptor field claims it. */\n\tvia: \"keyword\" | \"unit\" | \"operator\" | \"phrase\";\n\t/** The token type the word becomes, when the field maps to one. */\n\ttokenType?: string;\n}\n\n/** Severities at or above the strictness threshold, ordered most to least severe. */\nconst SEVERITY_ORDER: CompatibilitySeverity[] = [\"error\", \"warning\", \"info\"];\n\n/** Gather the single words a package claims through its declared vocabulary, which is where prose shadowing comes from. */\nfunction claimedWords(pkg: IEnginePackage): ShadowedWord[] {\n\tconst claimed: ShadowedWord[] = [];\n\tconst vocab = pkg.lexerVocabulary;\n\tif (vocab?.keywords) {\n\t\tfor (const [word, tokenType] of Object.entries(vocab.keywords)) {\n\t\t\tclaimed.push({ word, via: \"keyword\", tokenType });\n\t\t}\n\t}\n\tif (vocab?.units) {\n\t\tfor (const word of vocab.units) {\n\t\t\tclaimed.push({ word, via: \"unit\" });\n\t\t}\n\t}\n\tif (vocab?.operators) {\n\t\tfor (const [word, tokenType] of Object.entries(vocab.operators)) {\n\t\t\tclaimed.push({ word, via: \"operator\", tokenType });\n\t\t}\n\t}\n\tif (pkg.phrases) {\n\t\tfor (const [phrase, tokenType] of Object.entries(pkg.phrases)) {\n\t\t\t// A multi-word phrase is the SAFE pattern the trigger-words guide\n\t\t\t// recommends (`next friday` reserves neither word), so only a\n\t\t\t// single-word phrase can shadow a prose word.\n\t\t\tif (!phrase.includes(\" \")) {\n\t\t\t\tclaimed.push({ word: phrase, via: \"phrase\", tokenType });\n\t\t\t}\n\t\t}\n\t}\n\treturn claimed;\n}\n\n/**\n * Assertions about a package's declared descriptor, returned by\n * {@link expectPackage}. These catch the three mistakes the issue calls out,\n * before the package is ever registered. Every matcher returns `this` and\n * throws an {@link ExpectationError} when it fails.\n */\nexport class PackageAssertion {\n\tconstructor(private readonly pkg: IEnginePackage) {}\n\n\t/**\n\t * Assert none of the package's claimed words shadow a prose word.\n\t *\n\t * A trigger word that is also an ordinary word turns a line of prose into\n\t * arithmetic, the single most common package mistake and the subject of the\n\t * trigger-words guide. This checks the words the package declares\n\t * (lexer keywords, units, operators, and single-word phrases) against\n\t * `words` (defaulting to {@link COMMON_PROSE_WORDS}), case-insensitively.\n\t *\n\t * @returns the shadowed words, empty when the package is clean, for a test\n\t * that wants to inspect rather than assert.\n\t */\n\tnotToShadow(words: readonly string[] = COMMON_PROSE_WORDS): ShadowedWord[] {\n\t\tconst prose = new Set(words.map((w) => w.toLowerCase()));\n\t\tconst shadowed = claimedWords(this.pkg).filter((c) => prose.has(c.word.toLowerCase()));\n\t\tif (shadowed.length > 0) {\n\t\t\tconst list = shadowed.map((s) => `\"${s.word}\" (${s.via})`).join(\", \");\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"PACKAGE_SHADOWS_PROSE\",\n\t\t\t\tmessage: `Package \"${this.pkg.name}\" claims prose word(s) as syntax: ${list}. A word that is also ordinary English turns prose into arithmetic; prefer a multi-word phrase or a form that requires a parenthesis.`,\n\t\t\t\texpected: \"no prose words claimed as syntax\",\n\t\t\t\tactual: list,\n\t\t\t});\n\t\t}\n\t\treturn shadowed;\n\t}\n\n\t/**\n\t * Assert the package does not collide with any of `others` in a way that\n\t * would silently break one of them (two packages claiming the same lexer\n\t * keyword, plugin-function index, async-resolver namespace, and so on).\n\t *\n\t * `strictness` sets which severities fail the assertion: `\"error\"` (the\n\t * default) fails only on the collisions that always break something,\n\t * `\"warning\"` also fails on the ones that silently pick a winner, `\"info\"`\n\t * fails on cosmetic overlaps too.\n\t *\n\t * @returns the full compatibility report, so a test can inspect every\n\t * conflict, including ones below the strictness threshold.\n\t */\n\tnotToCollideWith(\n\t\tothers: IEnginePackage[],\n\t\tstrictness: CollisionStrictness = \"error\",\n\t): CompatibilityReport {\n\t\tconst report = checkPackageCompatibility(this.pkg, others);\n\t\tconst threshold = SEVERITY_ORDER.indexOf(strictness);\n\t\tconst failing = report.conflicts.filter(\n\t\t\t(c: CompatibilityConflict) => SEVERITY_ORDER.indexOf(c.severity) <= threshold,\n\t\t);\n\t\tif (failing.length > 0) {\n\t\t\tconst list = failing.map((c) => `[${c.severity}] ${c.detail}`).join(\"\\n\");\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"PACKAGE_VOCABULARY_COLLISION\",\n\t\t\t\tmessage: `Package \"${this.pkg.name}\" collides with another package:\\n${list}`,\n\t\t\t\texpected: `no conflicts at or above \"${strictness}\" severity`,\n\t\t\t\tactual: `${failing.length} conflict(s)`,\n\t\t\t});\n\t\t}\n\t\treturn report;\n\t}\n\n\t/**\n\t * Assert the package's declared `engineVersion` range is satisfied by the\n\t * running engine (or by `engineVersion` when given).\n\t *\n\t * A package that declares a range and never checks it resolves is the third\n\t * mistake the issue names: it looks fine until the one engine version it\n\t * cannot run against, where the range either fails to match or turns out to\n\t * be a malformed string. A package with no declared range passes (no\n\t * constraint is always compatible).\n\t */\n\ttoDeclareCompatibleEngineVersion(engineVersion: string = ENGINE_VERSION): this {\n\t\tconst result = checkEngineVersionCompatibility(this.pkg, engineVersion);\n\t\tif (!result.compatible) {\n\t\t\tconst detail =\n\t\t\t\tresult.reason === \"invalid-range\"\n\t\t\t\t\t? `its range \"${result.declaredRange}\" is not valid semver`\n\t\t\t\t\t: `its range \"${result.declaredRange}\" is not satisfied by engine version \"${result.engineVersion}\"`;\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"PACKAGE_ENGINE_VERSION_INCOMPATIBLE\",\n\t\t\t\tmessage: `Package \"${this.pkg.name}\" declares an engineVersion that will be rejected at registration: ${detail}.`,\n\t\t\t\texpected: `an engineVersion satisfied by \"${engineVersion}\"`,\n\t\t\t\tactual: `\"${result.declaredRange}\"`,\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n}\n\n/**\n * Begin an assertion about a package's declared descriptor.\n *\n * @example\n * ```ts\n * expectPackage(myPackage).notToShadow([\"price\", \"in\", \"of\"]);\n * expectPackage(myPackage).notToCollideWith(BUILTIN_PACKAGES);\n * expectPackage(myPackage).toDeclareCompatibleEngineVersion();\n * ```\n */\nexport function expectPackage(pkg: IEnginePackage): PackageAssertion {\n\treturn new PackageAssertion(pkg);\n}\n\n//#endregion\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing/index.ts"],"names":["ExpectationError","_ExpectationError","init","createTestEngine","packages","options","locale","includeBuiltins","engine","ExpressionEngine","BUILTIN_PACKAGES","pkg","evaluate","expression","value","error","EngineError","message","Value","numbersEqual","a","b","epsilon","describeValue","describeOutcome","outcome","ExpressionAssertion","expected","unit","describeExpected","matches","valueToComparableString","code","base","expectExpression","COMMON_PROSE_WORDS","SEVERITY_ORDER","claimedWords","claimed","vocab","word","tokenType","phrase","PackageAssertion","words","prose","w","shadowed","c","list","s","others","strictness","report","checkPackageCompatibility","threshold","failing","engineVersion","ENGINE_VERSION","result","checkEngineVersionCompatibility","detail","expectPackage"],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * A test kit for package authors, the supported way to test a package by the\n * expressions it enables rather than by the opcodes it emits.\n *\n * Before this entry point a package author had two options, both bad: reach\n * into engine internals, or assert on whatever bytecode a parselet happened to\n * emit. The first is unstable across engine versions, the second pins the\n * implementation instead of the behaviour, so a refactor that keeps every\n * answer correct still breaks the tests. This module is a thin, dependency-free\n * layer over the same public surface a host uses: it constructs an\n * {@link ExpressionEngine} with the given packages and evaluates strings.\n *\n * It speaks in expressions. {@link expectExpression} evaluates a string and\n * asserts on the result or the failure code; {@link expectPackage} asserts on\n * the three mistakes a package actually makes (shadowing prose, colliding with\n * another package's vocabulary, and declaring an `engineVersion` range that the\n * running engine does not satisfy).\n *\n * Framework-agnostic on purpose. Nothing here imports jest, vitest or any\n * runner: an assertion that fails throws an {@link ExpectationError}, and one\n * that passes returns, so the kit drops into whatever runner the author already\n * has (or into a plain script, or `node:assert`). Runtime dependency-free and\n * side-effect free, so `solve-engine/testing` stays honest under the package's\n * `\"sideEffects\": false` contract.\n *\n * @example\n * ```ts\n * import { createTestEngine, expectExpression } from \"solve-engine/testing\";\n *\n * const engine = createTestEngine([myPackage]);\n * expectExpression(engine, \"2 gp + 3 gp\").toEqual(5, \"gp\");\n * expectExpression(engine, \"gp\").toFailWith(\"UNDEFINED_VARIABLE\");\n * ```\n */\n\nimport { ExpressionEngine } from \"@solve-js/engine/ExpressionEngine\";\nimport { BUILTIN_PACKAGES } from \"@solve-js/packages/builtins\";\nimport type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { Value, ValueType } from \"@solve-js/vm/Value\";\nimport { EngineError } from \"@solve-js/errors/EngineError\";\nimport {\n\tcheckPackageCompatibility,\n\ttype CompatibilityReport,\n\ttype CompatibilityConflict,\n\ttype CompatibilitySeverity,\n} from \"@solve-js/api/PackageCompatibility\";\nimport { checkEngineVersionCompatibility } from \"@solve-js/api/EngineVersionCompatibility\";\nimport { ENGINE_VERSION } from \"@solve-js/constants/version\";\n\n//#region Errors\n\n/**\n * The failure a matcher throws when an expectation is not met.\n *\n * A dedicated `Error` subclass rather than a bare `throw new Error`, so a\n * caller (or a reporter) can tell a kit assertion failure apart from an\n * unrelated exception with `instanceof`, and so the structured `expected` and\n * `actual` fields survive alongside the human-readable message. This is NOT an\n * {@link EngineError}: that type classifies pipeline-stage failures inside the\n * engine, whereas this classifies a test-assertion mismatch, a different\n * concern that would be miscategorised under any of `EngineError`'s categories.\n */\nexport class ExpectationError extends Error {\n\t/** A short, machine-readable label for which expectation failed. */\n\treadonly code: string;\n\t/** What the matcher was told to expect, in plain words. */\n\treadonly expected?: string;\n\t/** What it found instead. */\n\treadonly actual?: string;\n\n\tconstructor(init: { code: string; message: string; expected?: string; actual?: string }) {\n\t\tsuper(init.message);\n\t\tthis.name = \"ExpectationError\";\n\t\tthis.code = init.code;\n\t\tthis.expected = init.expected;\n\t\tthis.actual = init.actual;\n\t\t// Restore the prototype chain across the ES5 `Error` transpile target,\n\t\t// so `instanceof ExpectationError` holds for callers.\n\t\tObject.setPrototypeOf(this, ExpectationError.prototype);\n\t}\n}\n\n//#endregion\n\n//#region createTestEngine\n\n/** Options for {@link createTestEngine}. */\nexport interface TestEngineOptions {\n\t/** Locale passed to the engine. Defaults to `\"en\"`. */\n\tlocale?: string;\n\t/**\n\t * Whether to load the engine's built-in packages (arithmetic, units, dates,\n\t * and the rest) before the packages under test. Defaults to `true`, because\n\t * almost every package builds on arithmetic and most authors want a\n\t * realistic engine. Set `false` to test a package in isolation.\n\t */\n\tincludeBuiltins?: boolean;\n}\n\n/**\n * Build an {@link ExpressionEngine} loaded with the packages under test.\n *\n * The built-in packages load first (unless {@link TestEngineOptions.includeBuiltins}\n * is `false`), then each package in `packages` is registered in order, exactly\n * as a host would. Registration is honest: a package whose declared\n * `engineVersion` the running engine does not satisfy, or whose lexer keyword\n * collides with a built-in, throws here rather than being swallowed. That is\n * deliberately different from passing packages straight to the\n * `ExpressionEngine` constructor, which contains a bad package by logging and\n * continuing, useful in production, wrong for a test that needs to know the\n * package it is testing actually loaded.\n *\n * Call {@link ExpressionEngine.clear} when a test is finished with the engine\n * if the test creates many, see the engine's own lifecycle note.\n *\n * @example\n * ```ts\n * const engine = createTestEngine([myPackage]);\n * const engineOnly = createTestEngine([myPackage], { includeBuiltins: false });\n * ```\n */\nexport function createTestEngine(\n\tpackages: IEnginePackage[] = [],\n\toptions: TestEngineOptions = {},\n): ExpressionEngine {\n\tconst { locale = \"en\", includeBuiltins = true } = options;\n\n\t// Built-ins go through the constructor (their own containment applies);\n\t// the packages under test go through registerPackage() afterwards so their\n\t// failures surface as thrown errors instead of being logged and skipped.\n\tconst engine = new ExpressionEngine({ locale, packages: includeBuiltins ? BUILTIN_PACKAGES : [] });\n\tfor (const pkg of packages) {\n\t\tengine.registerPackage(pkg);\n\t}\n\treturn engine;\n}\n\n//#endregion\n\n//#region expectExpression\n\n/**\n * The normalised outcome of evaluating one expression, computed once when\n * {@link expectExpression} runs and read by every matcher on the result.\n *\n * The engine surfaces a failure two different ways, and a package author should\n * not have to know which: a parse/eval error is THROWN as an\n * {@link EngineError} (with a `.code`), while a plugin-raised error is RETURNED\n * as a {@link Value} of {@link ValueType.Error} (its `value` is the code, its\n * `unit` is the message). Both collapse to the `\"error\"` status here. A value\n * still resolving asynchronously is its own `\"pending\"` status, since the kit\n * evaluates synchronously and cannot report a final number for it.\n */\ntype Outcome =\n\t| { status: \"value\"; value: Value }\n\t| { status: \"pending\"; value: Value }\n\t| { status: \"error\"; code: string; message: string; source: EngineError | Value };\n\n/** Evaluate `expression` on `engine` and collapse both failure shapes to one outcome. */\nfunction evaluate(engine: ExpressionEngine, expression: string): Outcome {\n\ttry {\n\t\tconst value = engine.evaluateExpression(expression);\n\t\tif (value.isError()) {\n\t\t\treturn {\n\t\t\t\tstatus: \"error\",\n\t\t\t\tcode: value.errorCode ?? \"\",\n\t\t\t\tmessage: value.errorMessage ?? \"\",\n\t\t\t\tsource: value,\n\t\t\t};\n\t\t}\n\t\tif (value.isPending()) {\n\t\t\treturn { status: \"pending\", value };\n\t\t}\n\t\treturn { status: \"value\", value };\n\t} catch (error) {\n\t\tif (error instanceof EngineError) {\n\t\t\treturn { status: \"error\", code: error.code, message: error.message, source: error };\n\t\t}\n\t\t// Anything else thrown (a bug in a plugin handler, say) is still a\n\t\t// failure, wrapped so the matcher has a code and message to report.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn {\n\t\t\tstatus: \"error\",\n\t\t\tcode: \"UNKNOWN_ERROR\",\n\t\t\tmessage,\n\t\t\tsource: new Value(ValueType.Error, \"UNKNOWN_ERROR\", message),\n\t\t};\n\t}\n}\n\n/** Whether two numbers are equal within a small tolerance scaled to their magnitude. */\nfunction numbersEqual(a: number, b: number): boolean {\n\tif (Object.is(a, b)) return true;\n\tif (!Number.isFinite(a) || !Number.isFinite(b)) return false;\n\tconst epsilon = 1e-9 * Math.max(1, Math.abs(a), Math.abs(b));\n\treturn Math.abs(a - b) <= epsilon;\n}\n\n/** A short, readable rendering of a value for an assertion message, e.g. `5 gp` or `\"abc\"`. */\nfunction describeValue(value: Value): string {\n\tif (value.type === ValueType.String) return JSON.stringify(value.value);\n\tif (value.type === ValueType.Uom && typeof value.unit === \"string\") {\n\t\treturn `${String(value.value)} ${value.unit}`;\n\t}\n\treturn String(value.value);\n}\n\n/** A short, readable rendering of an outcome for an assertion message. */\nfunction describeOutcome(outcome: Outcome): string {\n\tswitch (outcome.status) {\n\t\tcase \"value\":\n\t\t\treturn describeValue(outcome.value);\n\t\tcase \"pending\":\n\t\t\treturn \"a pending async value\";\n\t\tcase \"error\":\n\t\t\treturn `an error (${outcome.code}: ${outcome.message})`;\n\t}\n}\n\n/**\n * Assertions about the result of one evaluated expression, returned by\n * {@link expectExpression}. Every matcher returns `this`, so assertions chain,\n * and throws an {@link ExpectationError} when it fails.\n */\nexport class ExpressionAssertion {\n\tconstructor(\n\t\tprivate readonly expression: string,\n\t\tprivate readonly outcome: Outcome,\n\t) {}\n\n\t/** The raw resolved {@link Value}, for an assertion the matchers do not cover. Throws if the expression failed. */\n\tget value(): Value {\n\t\tif (this.outcome.status === \"error\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_VALUE\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to evaluate, but it failed with ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"a resolved value\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this.outcome.value;\n\t}\n\n\t/**\n\t * Assert the expression evaluated to a value (not an error, not a pending\n\t * async result). Says nothing about what the value is.\n\t */\n\ttoEvaluate(): this {\n\t\tif (this.outcome.status !== \"value\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_EVALUATE\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to evaluate to a value, but got ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"a resolved value\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert the expression evaluated to `expected`, and, when `unit` is given,\n\t * that the result carries exactly that unit. A number compares against the\n\t * value's numeric magnitude within a small floating-point tolerance; a\n\t * string compares against the value's own string; a boolean against its\n\t * boolean reading. Omitting `unit` leaves the unit unchecked, so\n\t * `toEqual(5)` passes for both `5` and `5 gp`.\n\t */\n\ttoEqual(expected: number | string | boolean, unit?: string): this {\n\t\tif (this.outcome.status !== \"value\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_EQUAL\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to equal ${describeExpected(expected, unit)}, but got ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: describeExpected(expected, unit),\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\n\t\tconst value = this.outcome.value;\n\t\tlet matches: boolean;\n\t\tif (typeof expected === \"number\") {\n\t\t\tmatches = numbersEqual(value.toNumber(), expected);\n\t\t} else if (typeof expected === \"boolean\") {\n\t\t\t// A Boolean value stores the primitive directly; anything else can\n\t\t\t// never equal a boolean, so a mismatch reports the real value.\n\t\t\tmatches = value.type === ValueType.Boolean && value.value === expected;\n\t\t} else {\n\t\t\tmatches = valueToComparableString(value) === expected;\n\t\t}\n\n\t\tif (!matches) {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_EQUAL\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to equal ${describeExpected(expected, unit)}, but it was ${describeValue(value)}.`,\n\t\t\t\texpected: describeExpected(expected, unit),\n\t\t\t\tactual: describeValue(value),\n\t\t\t});\n\t\t}\n\n\t\tif (unit !== undefined && value.unit !== unit) {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_UNIT\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to carry the unit \"${unit}\", but it was ${value.unit === undefined ? \"unitless\" : `\"${value.unit}\"`}.`,\n\t\t\t\texpected: `unit \"${unit}\"`,\n\t\t\t\tactual: value.unit === undefined ? \"no unit\" : `unit \"${value.unit}\"`,\n\t\t\t});\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/** Assert the expression failed (a thrown engine error or a plugin-raised error value). Says nothing about the code. */\n\ttoBeError(): this {\n\t\tif (this.outcome.status !== \"error\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_ERROR\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to fail, but it produced ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"an error\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert the expression failed with exactly `code`, the error catalog code\n\t * (e.g. `\"UNDEFINED_VARIABLE\"`, or a package's own code). Reports the code\n\t * it actually got when they differ, so a near-miss is obvious.\n\t */\n\ttoFailWith(code: string): this {\n\t\tif (this.outcome.status !== \"error\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_FAIL_WITH\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to fail with \"${code}\", but it produced ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: `error code \"${code}\"`,\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\tif (this.outcome.code !== code) {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_FAIL_WITH\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to fail with \"${code}\", but it failed with \"${this.outcome.code}\" (${this.outcome.message}).`,\n\t\t\t\texpected: `error code \"${code}\"`,\n\t\t\t\tactual: `error code \"${this.outcome.code}\"`,\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert the expression returned a value still resolving asynchronously.\n\t * The kit evaluates synchronously, so a package whose result comes from an\n\t * async resolver reports pending on first evaluation, this is how a test\n\t * confirms the async path was taken without resolving it.\n\t */\n\ttoBePending(): this {\n\t\tif (this.outcome.status !== \"pending\") {\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"EXPECTED_PENDING\",\n\t\t\t\tmessage: `Expected \"${this.expression}\" to be pending, but got ${describeOutcome(this.outcome)}.`,\n\t\t\t\texpected: \"a pending async value\",\n\t\t\t\tactual: describeOutcome(this.outcome),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n}\n\n/** Render an expected value (with optional unit) for an assertion message. */\nfunction describeExpected(expected: number | string | boolean, unit?: string): string {\n\tconst base = typeof expected === \"string\" ? JSON.stringify(expected) : String(expected);\n\treturn unit === undefined ? base : `${base} ${unit}`;\n}\n\n/** The string a non-numeric value compares against in {@link ExpressionAssertion.toEqual}. */\nfunction valueToComparableString(value: Value): string {\n\t// The stored primitive stringified. For a String value this is the string\n\t// itself; for a Hex value, its `0x...` text, which is what an author writes\n\t// in a `toEqual(\"0xff\")` assertion.\n\treturn String(value.value);\n}\n\n/**\n * Evaluate `expression` on `engine` and return an assertion object.\n *\n * The expression is evaluated once, immediately, and every matcher reads that\n * one outcome, so calling several matchers on the same result does not\n * re-evaluate.\n *\n * @example\n * ```ts\n * expectExpression(engine, \"2 gp + 3 gp\").toEqual(5, \"gp\");\n * expectExpression(engine, \"gp\").toFailWith(\"UNDEFINED_VARIABLE\");\n * ```\n */\nexport function expectExpression(engine: ExpressionEngine, expression: string): ExpressionAssertion {\n\treturn new ExpressionAssertion(expression, evaluate(engine, expression));\n}\n\n//#endregion\n\n//#region expectPackage\n\n/**\n * A modest set of everyday English words a package's keywords should not claim.\n *\n * A starting point for {@link PackageAssertion.notToShadow}, not an exhaustive\n * dictionary: it leans on the function words and common nouns/verbs a package\n * is most tempted to grab as a trigger (`price`, `of`, `per`, `sum`, `total`).\n * Pass your own list to check against the specific prose your package sits in.\n */\nexport const COMMON_PROSE_WORDS: readonly string[] = [\n\t\"a\", \"an\", \"and\", \"or\", \"but\", \"if\", \"then\", \"the\", \"this\", \"that\", \"these\", \"those\",\n\t\"in\", \"on\", \"at\", \"to\", \"of\", \"for\", \"from\", \"with\", \"by\", \"as\", \"per\", \"into\", \"over\",\n\t\"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\", \"do\", \"does\", \"did\", \"have\", \"has\", \"had\",\n\t\"will\", \"would\", \"can\", \"could\", \"should\", \"may\", \"might\", \"must\",\n\t\"price\", \"cost\", \"value\", \"total\", \"sum\", \"count\", \"rate\", \"amount\", \"number\",\n\t\"time\", \"date\", \"day\", \"week\", \"month\", \"year\", \"hour\", \"minute\", \"second\",\n\t\"add\", \"buy\", \"sell\", \"hold\", \"make\", \"take\", \"give\", \"get\", \"set\", \"run\",\n\t\"up\", \"down\", \"out\", \"off\", \"all\", \"some\", \"any\", \"no\", \"not\", \"more\", \"less\",\n\t\"one\", \"two\", \"three\", \"first\", \"last\", \"next\", \"each\", \"every\", \"here\", \"there\",\n];\n\n/** How strict {@link PackageAssertion.notToCollideWith} is about the conflicts it will accept. */\nexport type CollisionStrictness = \"error\" | \"warning\" | \"info\";\n\n/** One shadowed word found by {@link PackageAssertion.notToShadow}: the prose word and how the package claims it. */\nexport interface ShadowedWord {\n\t/** The prose word the package claims. */\n\tword: string;\n\t/** Which descriptor field claims it. */\n\tvia: \"keyword\" | \"unit\" | \"operator\" | \"phrase\";\n\t/** The token type the word becomes, when the field maps to one. */\n\ttokenType?: string;\n}\n\n/** Severities at or above the strictness threshold, ordered most to least severe. */\nconst SEVERITY_ORDER: CompatibilitySeverity[] = [\"error\", \"warning\", \"info\"];\n\n/** Gather the single words a package claims through its declared vocabulary, which is where prose shadowing comes from. */\nfunction claimedWords(pkg: IEnginePackage): ShadowedWord[] {\n\tconst claimed: ShadowedWord[] = [];\n\tconst vocab = pkg.lexerVocabulary;\n\tif (vocab?.keywords) {\n\t\tfor (const [word, tokenType] of Object.entries(vocab.keywords)) {\n\t\t\tclaimed.push({ word, via: \"keyword\", tokenType });\n\t\t}\n\t}\n\tif (vocab?.units) {\n\t\tfor (const word of vocab.units) {\n\t\t\tclaimed.push({ word, via: \"unit\" });\n\t\t}\n\t}\n\tif (vocab?.operators) {\n\t\tfor (const [word, tokenType] of Object.entries(vocab.operators)) {\n\t\t\tclaimed.push({ word, via: \"operator\", tokenType });\n\t\t}\n\t}\n\tif (pkg.phrases) {\n\t\tfor (const [phrase, tokenType] of Object.entries(pkg.phrases)) {\n\t\t\t// A multi-word phrase is the SAFE pattern the trigger-words guide\n\t\t\t// recommends (`next friday` reserves neither word), so only a\n\t\t\t// single-word phrase can shadow a prose word.\n\t\t\tif (!phrase.includes(\" \")) {\n\t\t\t\tclaimed.push({ word: phrase, via: \"phrase\", tokenType });\n\t\t\t}\n\t\t}\n\t}\n\treturn claimed;\n}\n\n/**\n * Assertions about a package's declared descriptor, returned by\n * {@link expectPackage}. These catch the three mistakes the issue calls out,\n * before the package is ever registered. Every matcher returns `this` and\n * throws an {@link ExpectationError} when it fails.\n */\nexport class PackageAssertion {\n\tconstructor(private readonly pkg: IEnginePackage) {}\n\n\t/**\n\t * Assert none of the package's claimed words shadow a prose word.\n\t *\n\t * A trigger word that is also an ordinary word turns a line of prose into\n\t * arithmetic, the single most common package mistake and the subject of the\n\t * trigger-words guide. This checks the words the package declares\n\t * (lexer keywords, units, operators, and single-word phrases) against\n\t * `words` (defaulting to {@link COMMON_PROSE_WORDS}), case-insensitively.\n\t *\n\t * @returns the shadowed words, empty when the package is clean, for a test\n\t * that wants to inspect rather than assert.\n\t */\n\tnotToShadow(words: readonly string[] = COMMON_PROSE_WORDS): ShadowedWord[] {\n\t\tconst prose = new Set(words.map((w) => w.toLowerCase()));\n\t\tconst shadowed = claimedWords(this.pkg).filter((c) => prose.has(c.word.toLowerCase()));\n\t\tif (shadowed.length > 0) {\n\t\t\tconst list = shadowed.map((s) => `\"${s.word}\" (${s.via})`).join(\", \");\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"PACKAGE_SHADOWS_PROSE\",\n\t\t\t\tmessage: `Package \"${this.pkg.name}\" claims prose word(s) as syntax: ${list}. A word that is also ordinary English turns prose into arithmetic; prefer a multi-word phrase or a form that requires a parenthesis.`,\n\t\t\t\texpected: \"no prose words claimed as syntax\",\n\t\t\t\tactual: list,\n\t\t\t});\n\t\t}\n\t\treturn shadowed;\n\t}\n\n\t/**\n\t * Assert the package does not collide with any of `others` in a way that\n\t * would silently break one of them (two packages claiming the same lexer\n\t * keyword, plugin-function index, async-resolver namespace, and so on).\n\t *\n\t * `strictness` sets which severities fail the assertion: `\"error\"` (the\n\t * default) fails only on the collisions that always break something,\n\t * `\"warning\"` also fails on the ones that silently pick a winner, `\"info\"`\n\t * fails on cosmetic overlaps too.\n\t *\n\t * @returns the full compatibility report, so a test can inspect every\n\t * conflict, including ones below the strictness threshold.\n\t */\n\tnotToCollideWith(\n\t\tothers: IEnginePackage[],\n\t\tstrictness: CollisionStrictness = \"error\",\n\t): CompatibilityReport {\n\t\tconst report = checkPackageCompatibility(this.pkg, others);\n\t\tconst threshold = SEVERITY_ORDER.indexOf(strictness);\n\t\tconst failing = report.conflicts.filter(\n\t\t\t(c: CompatibilityConflict) => SEVERITY_ORDER.indexOf(c.severity) <= threshold,\n\t\t);\n\t\tif (failing.length > 0) {\n\t\t\tconst list = failing.map((c) => `[${c.severity}] ${c.detail}`).join(\"\\n\");\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"PACKAGE_VOCABULARY_COLLISION\",\n\t\t\t\tmessage: `Package \"${this.pkg.name}\" collides with another package:\\n${list}`,\n\t\t\t\texpected: `no conflicts at or above \"${strictness}\" severity`,\n\t\t\t\tactual: `${failing.length} conflict(s)`,\n\t\t\t});\n\t\t}\n\t\treturn report;\n\t}\n\n\t/**\n\t * Assert the package's declared `engineVersion` range is satisfied by the\n\t * running engine (or by `engineVersion` when given).\n\t *\n\t * A package that declares a range and never checks it resolves is the third\n\t * mistake the issue names: it looks fine until the one engine version it\n\t * cannot run against, where the range either fails to match or turns out to\n\t * be a malformed string. A package with no declared range passes (no\n\t * constraint is always compatible).\n\t */\n\ttoDeclareCompatibleEngineVersion(engineVersion: string = ENGINE_VERSION): this {\n\t\tconst result = checkEngineVersionCompatibility(this.pkg, engineVersion);\n\t\tif (!result.compatible) {\n\t\t\tconst detail =\n\t\t\t\tresult.reason === \"invalid-range\"\n\t\t\t\t\t? `its range \"${result.declaredRange}\" is not valid semver`\n\t\t\t\t\t: `its range \"${result.declaredRange}\" is not satisfied by engine version \"${result.engineVersion}\"`;\n\t\t\tthrow new ExpectationError({\n\t\t\t\tcode: \"PACKAGE_ENGINE_VERSION_INCOMPATIBLE\",\n\t\t\t\tmessage: `Package \"${this.pkg.name}\" declares an engineVersion that will be rejected at registration: ${detail}.`,\n\t\t\t\texpected: `an engineVersion satisfied by \"${engineVersion}\"`,\n\t\t\t\tactual: `\"${result.declaredRange}\"`,\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n}\n\n/**\n * Begin an assertion about a package's declared descriptor.\n *\n * @example\n * ```ts\n * expectPackage(myPackage).notToShadow([\"price\", \"in\", \"of\"]);\n * expectPackage(myPackage).notToCollideWith(BUILTIN_PACKAGES);\n * expectPackage(myPackage).toDeclareCompatibleEngineVersion();\n * ```\n */\nexport function expectPackage(pkg: IEnginePackage): PackageAssertion {\n\treturn new PackageAssertion(pkg);\n}\n\n//#endregion\n"]}
|
package/dist/uom.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var chunkG2V33LFM_cjs=require('./chunk-G2V33LFM.cjs'),chunkTMQ37JQY_cjs=require('./chunk-TMQ37JQY.cjs');require('./chunk-ABVDKMNF.cjs'),require('./chunk-TLG7VZRX.cjs'),require('./chunk-GJZIJK2Q.cjs');var chunkZKFCKAIX_cjs=require('./chunk-ZKFCKAIX.cjs');require('./chunk-EZKR44MA.cjs'),require('./chunk-GXO7TSXQ.cjs');Object.defineProperty(exports,"CurrencyAsyncResolver",{enumerable:true,get:function(){return chunkG2V33LFM_cjs.b}});Object.defineProperty(exports,"CurrencyExchangeService",{enumerable:true,get:function(){return chunkTMQ37JQY_cjs.a}});Object.defineProperty(exports,"currencyExchangeService",{enumerable:true,get:function(){return chunkTMQ37JQY_cjs.b}});Object.defineProperty(exports,"sharedCurrencyExchange",{enumerable:true,get:function(){return chunkTMQ37JQY_cjs.c}});Object.defineProperty(exports,"canConvert",{enumerable:true,get:function(){return chunkZKFCKAIX_cjs.d}});Object.defineProperty(exports,"convertUnit",{enumerable:true,get:function(){return chunkZKFCKAIX_cjs.e}});Object.defineProperty(exports,"getBestUnit",{enumerable:true,get:function(){return chunkZKFCKAIX_cjs.h}});Object.defineProperty(exports,"getMeasure",{enumerable:true,get:function(){return chunkZKFCKAIX_cjs.c}});Object.defineProperty(exports,"isConvertibleUnit",{enumerable:true,get:function(){return chunkZKFCKAIX_cjs.g}});Object.defineProperty(exports,"resolveUnit",{enumerable:true,get:function(){return chunkZKFCKAIX_cjs.b}});//# sourceMappingURL=uom.cjs.map
|
|
2
2
|
//# sourceMappingURL=uom.cjs.map
|
package/dist/uom.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { QueryClient } from '@tanstack/query-core';
|
|
2
|
-
import { T as Token } from './Token-
|
|
3
|
-
import { a as BytecodeProgram } from './BytecodeBuilder-
|
|
2
|
+
import { T as Token } from './Token-D8f7yaz1.cjs';
|
|
3
|
+
import { a as BytecodeProgram } from './BytecodeBuilder-DSWKZi4f.cjs';
|
|
4
4
|
import { IAsyncResolver, AsyncCheckResult } from './resolvers.cjs';
|
|
5
|
-
import './Value-
|
|
5
|
+
import './Value-BYHw-x7q.cjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Caches exchange rates fetched from an external source.
|
package/dist/uom.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { QueryClient } from '@tanstack/query-core';
|
|
2
|
-
import { T as Token } from './Token-
|
|
3
|
-
import { a as BytecodeProgram } from './BytecodeBuilder-
|
|
2
|
+
import { T as Token } from './Token-D8f7yaz1.js';
|
|
3
|
+
import { a as BytecodeProgram } from './BytecodeBuilder-DSWKZi4f.js';
|
|
4
4
|
import { IAsyncResolver, AsyncCheckResult } from './resolvers.js';
|
|
5
|
-
import './Value-
|
|
5
|
+
import './Value-BYHw-x7q.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Caches exchange rates fetched from an external source.
|
package/dist/uom.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{b as CurrencyAsyncResolver}from'./chunk-
|
|
1
|
+
export{b as CurrencyAsyncResolver}from'./chunk-HANGBVEE.js';export{a as CurrencyExchangeService,b as currencyExchangeService,c as sharedCurrencyExchange}from'./chunk-ESZNJQ2C.js';import'./chunk-TZYMXNIB.js';import'./chunk-HFKDWQUL.js';import'./chunk-LQIRBP4Q.js';export{d as canConvert,e as convertUnit,h as getBestUnit,c as getMeasure,g as isConvertibleUnit,b as resolveUnit}from'./chunk-4CNYGIND.js';import'./chunk-PRSXXDTA.js';import'./chunk-VB6QMU2W.js';//# sourceMappingURL=uom.js.map
|
|
2
2
|
//# sourceMappingURL=uom.js.map
|
package/dist/vm.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var chunkGVL3ZMS7_cjs=require('./chunk-GVL3ZMS7.cjs');require('./chunk-HG4NPDN5.cjs');var chunkQQHHZPEW_cjs=require('./chunk-QQHHZPEW.cjs');require('./chunk-ZE5KKMBV.cjs'),require('./chunk-TMQ37JQY.cjs'),require('./chunk-ABVDKMNF.cjs'),require('./chunk-TLG7VZRX.cjs'),require('./chunk-FDKTESBC.cjs'),require('./chunk-TPPM4QSS.cjs'),require('./chunk-E4HAKNBQ.cjs');var chunkGJZIJK2Q_cjs=require('./chunk-GJZIJK2Q.cjs');require('./chunk-ZKFCKAIX.cjs'),require('./chunk-EZKR44MA.cjs'),require('./chunk-GXO7TSXQ.cjs');var l=class{constructor(t){this.checkpoints=[];this.vm=t;}snapshot(t,n,e){if(e.length===0)return null;let r=this.checkpoints.length>0?this.checkpoints[this.checkpoints.length-1]:null,o=Object.create(r?.variables??null),i=Object.create(r?.functions??null);for(let s of e){if(this.vm.hasUserFunction(s)){let p=this.vm.getUserFunction(s);p&&(i[s]=p);continue}let u=this.vm.getVar(s);u!==void 0&&(o[s]=u);}let c={lineNumber:t,lineId:n,variables:o,functions:i,parent:r};return this.checkpoints.push(c),c}restoreTo(t){let n=this.getNearestCheckpoint(t);if(!n){this.vm.reset();return}let e=[],r=n;for(;r;)e.unshift(r),r=r.parent;this.vm.reset();for(let o of e){for(let i of Object.keys(o.variables))this.vm.setVar(i,o.variables[i]);for(let i of Object.keys(o.functions)){let c=o.functions[i];this.vm.defineUserFunction(c.name,c.params,c.program);}}}getNearestCheckpoint(t){let n=null;for(let e of this.checkpoints)if(e.lineNumber<=t)n=e;else break;return n}getCheckpointAt(t){return this.checkpoints.find(n=>n.lineNumber===t)}getAllCheckpoints(){return this.checkpoints}lookupVariable(t){if(this.checkpoints.length===0)return;let e=this.checkpoints[this.checkpoints.length-1].variables;for(;e;){if(Object.prototype.hasOwnProperty.call(e,t))return e[t];e=Object.getPrototypeOf(e);}}clear(){this.checkpoints=[];}get count(){return this.checkpoints.length}get isEmpty(){return this.checkpoints.length===0}get vmInstance(){return this.vm}};Object.defineProperty(exports,"DependencyGraph",{enumerable:true,get:function(){return chunkGVL3ZMS7_cjs.a}});Object.defineProperty(exports,"ScopeManager",{enumerable:true,get:function(){return chunkGVL3ZMS7_cjs.b}});Object.defineProperty(exports,"createVM",{enumerable:true,get:function(){return chunkGVL3ZMS7_cjs.c}});Object.defineProperty(exports,"executeBytecode",{enumerable:true,get:function(){return chunkGVL3ZMS7_cjs.d}});Object.defineProperty(exports,"OpRegistry",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.v}});Object.defineProperty(exports,"allocatePluginFunctionIndex",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.E}});Object.defineProperty(exports,"binaryOp",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.u}});Object.defineProperty(exports,"builtinFunctions",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.C}});Object.defineProperty(exports,"pluginFunctionIndexFor",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.F}});Object.defineProperty(exports,"pluginFunctionRegistry",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.D}});Object.defineProperty(exports,"sharedOpRegistry",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.w}});Object.defineProperty(exports,"unifyUom",{enumerable:true,get:function(){return chunkQQHHZPEW_cjs.j}});Object.defineProperty(exports,"Value",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.m}});Object.defineProperty(exports,"ValueType",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.g}});Object.defineProperty(exports,"bigIntValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.s}});Object.defineProperty(exports,"chargeAllocation",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.c}});Object.defineProperty(exports,"checkAllocation",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.d}});Object.defineProperty(exports,"checkedArray",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.e}});Object.defineProperty(exports,"colVectorValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.E}});Object.defineProperty(exports,"hexValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.r}});Object.defineProperty(exports,"matrixValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.C}});Object.defineProperty(exports,"numberValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.n}});Object.defineProperty(exports,"rangeValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.F}});Object.defineProperty(exports,"rowVectorValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.D}});Object.defineProperty(exports,"stringValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.t}});Object.defineProperty(exports,"uomValue",{enumerable:true,get:function(){return chunkGJZIJK2Q_cjs.u}});exports.VMCheckpointer=l;//# sourceMappingURL=vm.cjs.map
|
|
2
2
|
//# sourceMappingURL=vm.cjs.map
|
package/dist/vm.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vm/VMCheckpoints.ts"],"names":["VMCheckpointer","vm","lineNumber","lineId","variableNames","parent","variables","functions","name","fn","val","checkpoint","target","chain","current","cp","key","result","scope"],"mappings":"+aAuFO,IAAMA,CAAAA,CAAN,KAAqB,CAM3B,WAAA,CAAYC,EAAQ,CAJpB,IAAA,CAAQ,WAAA,CAA8B,GAKrC,IAAA,CAAK,EAAA,CAAKA,EACX,CAiBA,SACCC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACsB,CACtB,GAAIA,CAAAA,CAAc,MAAA,GAAW,CAAA,CAAG,OAAO,KAEvC,IAAMC,CAAAA,CACL,IAAA,CAAK,WAAA,CAAY,OAAS,CAAA,CACvB,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,YAAY,MAAA,CAAS,CAAC,CAAA,CAC5C,IAAA,CAGEC,CAAAA,CAAmC,MAAA,CAAO,MAAA,CAC/CD,CAAAA,EAAQ,WAAa,IACtB,CAAA,CACME,CAAAA,CAA6C,MAAA,CAAO,OACzDF,CAAAA,EAAQ,SAAA,EAAa,IACtB,CAAA,CAMA,QAAWG,CAAAA,IAAQJ,CAAAA,CAAe,CACjC,GAAI,KAAK,EAAA,CAAG,eAAA,CAAgBI,CAAI,CAAA,CAAG,CAClC,IAAMC,CAAAA,CAAK,IAAA,CAAK,EAAA,CAAG,gBAAgBD,CAAI,CAAA,CACnCC,CAAAA,GAAIF,CAAAA,CAAUC,CAAI,CAAA,CAAIC,CAAAA,CAAAA,CAC1B,QACD,CACA,IAAMC,CAAAA,CAAM,IAAA,CAAK,EAAA,CAAG,OAAOF,CAAI,CAAA,CAC3BE,CAAAA,GAAQ,MAAA,GACXJ,EAAUE,CAAI,CAAA,CAAIE,CAAAA,EAEpB,CAEA,IAAMC,CAAAA,CAA2B,CAChC,UAAA,CAAAT,CAAAA,CACA,OAAAC,CAAAA,CACA,SAAA,CAAAG,CAAAA,CACA,SAAA,CAAAC,EACA,MAAA,CAAAF,CACD,CAAA,CACA,OAAA,IAAA,CAAK,YAAY,IAAA,CAAKM,CAAU,CAAA,CACzBA,CACR,CAuBA,SAAA,CAAUT,CAAAA,CAA0B,CACnC,IAAMU,CAAAA,CAAS,IAAA,CAAK,oBAAA,CAAqBV,CAAU,EACnD,GAAI,CAACU,CAAAA,CAAQ,CACZ,KAAK,EAAA,CAAG,KAAA,EAAM,CACd,MACD,CAIA,IAAMC,CAAAA,CAAwB,EAAC,CAC3BC,EAA+BF,CAAAA,CACnC,KAAOE,CAAAA,EACND,CAAAA,CAAM,QAAQC,CAAO,CAAA,CACrBA,CAAAA,CAAUA,CAAAA,CAAQ,OAGnB,IAAA,CAAK,EAAA,CAAG,KAAA,EAAM,CACd,QAAWC,CAAAA,IAAMF,CAAAA,CAAO,CAKvB,IAAA,IAAWG,CAAAA,IAAO,MAAA,CAAO,IAAA,CAAKD,CAAAA,CAAG,SAAS,CAAA,CACzC,IAAA,CAAK,EAAA,CAAG,MAAA,CAAOC,EAAKD,CAAAA,CAAG,SAAA,CAAUC,CAAG,CAAC,EAKtC,IAAA,IAAWA,CAAAA,IAAO,MAAA,CAAO,IAAA,CAAKD,EAAG,SAAS,CAAA,CAAG,CAC5C,IAAMN,EAAKM,CAAAA,CAAG,SAAA,CAAUC,CAAG,CAAA,CAC3B,KAAK,EAAA,CAAG,kBAAA,CAAmBP,CAAAA,CAAG,IAAA,CAAMA,EAAG,MAAA,CAAQA,CAAAA,CAAG,OAAO,EAC1D,CACD,CACD,CAaA,oBAAA,CAAqBP,EAAyC,CAC7D,IAAIe,CAAAA,CAA8B,IAAA,CAClC,QAAWF,CAAAA,IAAM,IAAA,CAAK,WAAA,CACrB,GAAIA,EAAG,UAAA,EAAcb,CAAAA,CACpBe,CAAAA,CAASF,CAAAA,CAAAA,WAKX,OAAOE,CACR,CAMA,eAAA,CAAgBf,EAA8C,CAC7D,OAAO,IAAA,CAAK,WAAA,CAAY,KAAMa,CAAAA,EAAOA,CAAAA,CAAG,UAAA,GAAeb,CAAU,CAClE,CAMA,iBAAA,EAA6C,CAC5C,OAAO,IAAA,CAAK,WACb,CAgBA,cAAA,CAAeM,EAAiC,CAC/C,GAAI,IAAA,CAAK,WAAA,CAAY,SAAW,CAAA,CAAG,OAGnC,IAAIU,CAAAA,CADW,KAAK,WAAA,CAAY,IAAA,CAAK,WAAA,CAAY,MAAA,CAAS,CAAC,CAAA,CACV,SAAA,CACjD,KAAOA,CAAAA,EAAO,CACb,GAAI,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,KAAKA,CAAAA,CAAOV,CAAI,CAAA,CACnD,OAAOU,EAAMV,CAAI,CAAA,CAElBU,CAAAA,CAAQ,MAAA,CAAO,cAAA,CAAeA,CAAK,EACpC,CAED,CAQA,KAAA,EAAc,CACb,IAAA,CAAK,WAAA,CAAc,GACpB,CAGA,IAAI,KAAA,EAAgB,CACnB,OAAO,IAAA,CAAK,WAAA,CAAY,MACzB,CAGA,IAAI,OAAA,EAAmB,CACtB,OAAO,KAAK,WAAA,CAAY,MAAA,GAAW,CACpC,CAGA,IAAI,UAAA,EAAiB,CACpB,OAAO,IAAA,CAAK,EACb,CACD","file":"vm.cjs","sourcesContent":["import { Value } from \"@solve-js/vm/Value\";\nimport type { VM } from \"@solve-js/vm/OpRegistry\";\nimport type { UserFunctionDef } from \"@solve-js/parser/BytecodeBuilder\";\n\n// ── VMCheckpoint ────────────────────────────────────────────────────────\n\n/**\n * A point-in-time snapshot of VM variable state.\n *\n * Uses **prototypal inheritance** for memory efficiency: each checkpoint's\n * `variables` object has its parent checkpoint's `variables` as its\n * `__proto__`. This means a `getVar(\"x\")` lookup walks the prototype chain\n * until it finds `x`, and only variables that CHANGED at this checkpoint\n * consume heap space. Unchanged variables are inherited from the parent.\n *\n * ```text\n * Checkpoint 0 (root): {} // empty scope\n * Checkpoint 1 (:x=5): { x: 5 } __proto__ → 0\n * Checkpoint 2 (:y=8): { y: 8 } __proto__ → 1\n * Checkpoint 3 (:x=3): { x: 3 } __proto__ → 2 // shadows x=5\n * ```\n *\n * To look up `x` at checkpoint 3: find own `x=3` → done.\n * To look up `y` at checkpoint 3: not own → walk proto to checkpoint 2 → `y=8`.\n * To look up `z` at checkpoint 3: not found anywhere → undefined.\n *\n * **Memory:** O(number of variable definitions) heap, independent of\n * document length. Typical Obsidian documents have < 100 variable defs,\n * so total checkpoint heap is < 10 KB.\n */\nexport interface VMCheckpoint {\n\t/** 1-based line number where this checkpoint was created. */\n\tlineNumber: number;\n\t/** Persistent line ID from DocumentModel. */\n\tlineId: number;\n\t/**\n\t * Variable name → Value at this checkpoint.\n\t * Own properties are variables set/updated at this line.\n\t * The prototype chain provides inherited variables from parent checkpoints.\n\t */\n\tvariables: Record<string, Value>;\n\t/**\n\t * User-defined-function name → definition at this checkpoint. SEPARATE\n\t * from `variables` above (not prototypally chained the same way\n\t * `restoreTo()` replays every checkpoint in the chain in order, so a\n\t * later redefinition of the same function name naturally overwrites an\n\t * earlier one during replay, without needing its own prototype walk).\n\t *\n\t * Without this field, a function definition's checkpoint entry would be\n\t * SILENTLY LOST: `snapshot()` used to call `vm.getVar(name)` for every\n\t * written name, which returns `undefined` for a function name (function\n\t * defs live in `vm.userFunctions`, not the flat variable store), and a\n\t * `val !== undefined` guard silently skipped it. A scroll-triggered\n\t * `restoreTo()` would then reset the VM and replay only `variables`,\n\t * making a function defined above the new viewport vanish (calling it\n\t * would throw `UNDEFINED_FUNCTION`) even though the document still\n\t * shows its definition line as clean/cached.\n\t */\n\tfunctions: Record<string, UserFunctionDef>;\n\t/** Parent checkpoint (closer to document start), or null for root. */\n\tparent: VMCheckpoint | null;\n}\n\n// ── VMCheckpointer ──────────────────────────────────────────────────────\n\n/**\n * Manages VM state checkpoints for the three-tier evaluation strategy.\n *\n * **Checkpoint creation:** After a variable-definition line executes\n * (Tier 1 or Tier 3), `snapshot()` records the current values of the\n * written variables. The checkpoint is linked via prototypal inheritance\n * to the previous checkpoint, so only changed variables consume memory.\n *\n * **Checkpoint restoration:** Before evaluating a viewport whose start line\n * is not line 1, `restoreTo(lineNumber)` resets the VM and replays all\n * variable definitions up to and including that line. This avoids\n * re-evaluating the entire document from line 1 on every scroll.\n *\n * **Thread safety:** Checkpoints are created synchronously on the main\n * thread during evaluation. They are immutable after creation (Value is\n * an immutable type), so no synchronization is needed.\n *\n * **Integration with Phase 5.2e:** `setViewport()` will use `getNearestCheckpoint()`\n * to find the checkpoint just before the new viewport start, then call\n * `restoreTo()` to set up the VM before evaluating only the visible lines.\n * This is the key to O(visible lines) scrolling instead of O(document).\n */\nexport class VMCheckpointer {\n\t/** Ordered array of checkpoints (ascending lineNumber). */\n\tprivate checkpoints: VMCheckpoint[] = [];\n\t/** The VM instance whose variables are snapshotted/restored. */\n\tprivate vm: VM;\n\n\tconstructor(vm: VM) {\n\t\tthis.vm = vm;\n\t}\n\n\t// ── Snapshot ─────────────────────────────────────────────────────\n\n\t/**\n\t * Create a checkpoint at the current line, recording the VM values of\n\t * the specified variables.\n\t *\n\t * Uses prototypal inheritance: `Object.create(parent.variables)` so\n\t * that inherited variable lookups fall through to previous checkpoints\n\t * without copying all variables into each checkpoint.\n\t *\n\t * @param lineNumber 1-based line position.\n\t * @param lineId Persistent line ID from DocumentModel.\n\t * @param variableNames Names of variables that were written at this line.\n\t * @returns The new checkpoint, or null if no variable names provided.\n\t */\n\tsnapshot(\n\t\tlineNumber: number,\n\t\tlineId: number,\n\t\tvariableNames: string[]\n\t): VMCheckpoint | null {\n\t\tif (variableNames.length === 0) return null;\n\n\t\tconst parent =\n\t\t\tthis.checkpoints.length > 0\n\t\t\t\t? this.checkpoints[this.checkpoints.length - 1]\n\t\t\t\t: null;\n\n\t\t// Create prototypal chain: new checkpoint inherits from parent\n\t\tconst variables: Record<string, Value> = Object.create(\n\t\t\tparent?.variables ?? null\n\t\t) as Record<string, Value>;\n\t\tconst functions: Record<string, UserFunctionDef> = Object.create(\n\t\t\tparent?.functions ?? null\n\t\t) as Record<string, UserFunctionDef>;\n\n\t\t// Record current VM values for the written names, routing each into\n\t\t// the right bag (a name is either a variable or a user-defined\n\t\t// function, never both; see VMCheckpoint.functions's doc comment for\n\t\t// why this dispatch is required, not optional).\n\t\tfor (const name of variableNames) {\n\t\t\tif (this.vm.hasUserFunction(name)) {\n\t\t\t\tconst fn = this.vm.getUserFunction(name);\n\t\t\t\tif (fn) functions[name] = fn;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst val = this.vm.getVar(name);\n\t\t\tif (val !== undefined) {\n\t\t\t\tvariables[name] = val;\n\t\t\t}\n\t\t}\n\n\t\tconst checkpoint: VMCheckpoint = {\n\t\t\tlineNumber,\n\t\t\tlineId,\n\t\t\tvariables,\n\t\t\tfunctions,\n\t\t\tparent,\n\t\t};\n\t\tthis.checkpoints.push(checkpoint);\n\t\treturn checkpoint;\n\t}\n\n\t// ── Restore ──────────────────────────────────────────────────────\n\n\t/**\n\t * Restore the VM to the state at or just after the given line number.\n\t *\n\t * Finds the nearest checkpoint whose `lineNumber <= targetLineNumber`,\n\t * then replays all variable definitions from root → that checkpoint\n\t * into the VM via `setVar()`. The VM's stack is also reset.\n\t *\n\t * If no checkpoint exists at or before the target line, the VM is\n\t * fully reset (empty scope, empty stack).\n\t *\n\t * **Performance:** O(number of checkpoints × variables per checkpoint).\n\t * With prototypal inheritance, `Object.keys()` on each checkpoint\n\t * returns only the variables that were set at that checkpoint (not\n\t * inherited ones), so the total work is O(total variable definitions\n\t * in the document), which is < 100 for typical Obsidian documents.\n\t *\n\t * @param lineNumber Target 1-based line number. The VM will have the\n\t * state that existed AFTER evaluating lines up to `lineNumber`.\n\t */\n\trestoreTo(lineNumber: number): void {\n\t\tconst target = this.getNearestCheckpoint(lineNumber);\n\t\tif (!target) {\n\t\t\tthis.vm.reset();\n\t\t\treturn;\n\t\t}\n\n\t\t// Collect the checkpoint chain from root to target.\n\t\t// Walk parent links and reverse so root is first.\n\t\tconst chain: VMCheckpoint[] = [];\n\t\tlet current: VMCheckpoint | null = target;\n\t\twhile (current) {\n\t\t\tchain.unshift(current);\n\t\t\tcurrent = current.parent;\n\t\t}\n\n\t\tthis.vm.reset();\n\t\tfor (const cp of chain) {\n\t\t\t// Object.keys() returns only OWN enumerable properties\n\t\t\t// it does NOT include inherited properties from the prototype chain.\n\t\t\t// This means we only set variables that were defined/updated at this\n\t\t\t// specific checkpoint, not all variables from parent checkpoints.\n\t\t\tfor (const key of Object.keys(cp.variables)) {\n\t\t\t\tthis.vm.setVar(key, cp.variables[key]);\n\t\t\t}\n\t\t\t// Replay function definitions the same way, a later checkpoint's\n\t\t\t// redefinition of the same name naturally overwrites an earlier\n\t\t\t// one since the chain replays in root-to-target order.\n\t\t\tfor (const key of Object.keys(cp.functions)) {\n\t\t\t\tconst fn = cp.functions[key];\n\t\t\t\tthis.vm.defineUserFunction(fn.name, fn.params, fn.program);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Queries ──────────────────────────────────────────────────────\n\n\t/**\n\t * Find the nearest checkpoint at or before the given line number.\n\t *\n\t * Uses linear scan (checkpoints are sorted by lineNumber and the list\n\t * is short, typically < 20 for Obsidian documents). Can be upgraded\n\t * to binary search if needed for documents with 1000+ variable defs.\n\t *\n\t * @returns The nearest checkpoint, or null if none exists before the line.\n\t */\n\tgetNearestCheckpoint(lineNumber: number): VMCheckpoint | null {\n\t\tlet result: VMCheckpoint | null = null;\n\t\tfor (const cp of this.checkpoints) {\n\t\t\tif (cp.lineNumber <= lineNumber) {\n\t\t\t\tresult = cp;\n\t\t\t} else {\n\t\t\t\tbreak; // checkpoints are sorted ascending\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get a specific checkpoint by its line number.\n\t * @returns The checkpoint, or undefined if not found.\n\t */\n\tgetCheckpointAt(lineNumber: number): VMCheckpoint | undefined {\n\t\treturn this.checkpoints.find((cp) => cp.lineNumber === lineNumber);\n\t}\n\n\t/**\n\t * Get the entire checkpoint chain from root to the last checkpoint.\n\t * Useful for debugging and serialization.\n\t */\n\tgetAllCheckpoints(): readonly VMCheckpoint[] {\n\t\treturn this.checkpoints;\n\t}\n\n\t/**\n\t * Look up a variable's value through the checkpoint chain.\n\t *\n\t * Walks the prototype chain starting from the most recent checkpoint,\n\t * looking for the variable name as an own property. This is O(depth)\n\t * where depth is the number of checkpoints since the variable was\n\t * last set.\n\t *\n\t * **Note:** This queries the checkpointer's snapshot, not the VM.\n\t * The VM may have been modified since the last snapshot (e.g., by\n\t * Tier 2 execution of non-variable-def lines that don't create checkpoints).\n\t *\n\t * @returns The Value, or undefined if the variable was never set.\n\t */\n\tlookupVariable(name: string): Value | undefined {\n\t\tif (this.checkpoints.length === 0) return undefined;\n\n\t\tconst latest = this.checkpoints[this.checkpoints.length - 1];\n\t\tlet scope: Record<string, Value> | null = latest.variables;\n\t\twhile (scope) {\n\t\t\tif (Object.prototype.hasOwnProperty.call(scope, name)) {\n\t\t\t\treturn scope[name];\n\t\t\t}\n\t\t\tscope = Object.getPrototypeOf(scope) as Record<string, Value> | null;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────\n\n\t/**\n\t * Clear all checkpoints. The underlying VM is NOT reset, call\n\t * `vm.reset()` separately if needed.\n\t */\n\tclear(): void {\n\t\tthis.checkpoints = [];\n\t}\n\n\t/** Number of checkpoints stored. */\n\tget count(): number {\n\t\treturn this.checkpoints.length;\n\t}\n\n\t/** Returns true if no checkpoints have been created. */\n\tget isEmpty(): boolean {\n\t\treturn this.checkpoints.length === 0;\n\t}\n\n\t/** The associated VM instance. */\n\tget vmInstance(): VM {\n\t\treturn this.vm;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/vm/VMCheckpoints.ts"],"names":["VMCheckpointer","vm","lineNumber","lineId","variableNames","parent","variables","functions","name","fn","val","checkpoint","target","chain","current","cp","key","result","scope"],"mappings":"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","file":"vm.cjs","sourcesContent":["import { Value } from \"@solve-js/vm/Value\";\nimport type { VM } from \"@solve-js/vm/OpRegistry\";\nimport type { UserFunctionDef } from \"@solve-js/parser/BytecodeBuilder\";\n\n// ── VMCheckpoint ────────────────────────────────────────────────────────\n\n/**\n * A point-in-time snapshot of VM variable state.\n *\n * Uses **prototypal inheritance** for memory efficiency: each checkpoint's\n * `variables` object has its parent checkpoint's `variables` as its\n * `__proto__`. This means a `getVar(\"x\")` lookup walks the prototype chain\n * until it finds `x`, and only variables that CHANGED at this checkpoint\n * consume heap space. Unchanged variables are inherited from the parent.\n *\n * ```text\n * Checkpoint 0 (root): {} // empty scope\n * Checkpoint 1 (:x=5): { x: 5 } __proto__ → 0\n * Checkpoint 2 (:y=8): { y: 8 } __proto__ → 1\n * Checkpoint 3 (:x=3): { x: 3 } __proto__ → 2 // shadows x=5\n * ```\n *\n * To look up `x` at checkpoint 3: find own `x=3` → done.\n * To look up `y` at checkpoint 3: not own → walk proto to checkpoint 2 → `y=8`.\n * To look up `z` at checkpoint 3: not found anywhere → undefined.\n *\n * **Memory:** O(number of variable definitions) heap, independent of\n * document length. Typical Obsidian documents have < 100 variable defs,\n * so total checkpoint heap is < 10 KB.\n */\nexport interface VMCheckpoint {\n\t/** 1-based line number where this checkpoint was created. */\n\tlineNumber: number;\n\t/** Persistent line ID from DocumentModel. */\n\tlineId: number;\n\t/**\n\t * Variable name → Value at this checkpoint.\n\t * Own properties are variables set/updated at this line.\n\t * The prototype chain provides inherited variables from parent checkpoints.\n\t */\n\tvariables: Record<string, Value>;\n\t/**\n\t * User-defined-function name → definition at this checkpoint. SEPARATE\n\t * from `variables` above (not prototypally chained the same way\n\t * `restoreTo()` replays every checkpoint in the chain in order, so a\n\t * later redefinition of the same function name naturally overwrites an\n\t * earlier one during replay, without needing its own prototype walk).\n\t *\n\t * Without this field, a function definition's checkpoint entry would be\n\t * SILENTLY LOST: `snapshot()` used to call `vm.getVar(name)` for every\n\t * written name, which returns `undefined` for a function name (function\n\t * defs live in `vm.userFunctions`, not the flat variable store), and a\n\t * `val !== undefined` guard silently skipped it. A scroll-triggered\n\t * `restoreTo()` would then reset the VM and replay only `variables`,\n\t * making a function defined above the new viewport vanish (calling it\n\t * would throw `UNDEFINED_FUNCTION`) even though the document still\n\t * shows its definition line as clean/cached.\n\t */\n\tfunctions: Record<string, UserFunctionDef>;\n\t/** Parent checkpoint (closer to document start), or null for root. */\n\tparent: VMCheckpoint | null;\n}\n\n// ── VMCheckpointer ──────────────────────────────────────────────────────\n\n/**\n * Manages VM state checkpoints for the three-tier evaluation strategy.\n *\n * **Checkpoint creation:** After a variable-definition line executes\n * (Tier 1 or Tier 3), `snapshot()` records the current values of the\n * written variables. The checkpoint is linked via prototypal inheritance\n * to the previous checkpoint, so only changed variables consume memory.\n *\n * **Checkpoint restoration:** Before evaluating a viewport whose start line\n * is not line 1, `restoreTo(lineNumber)` resets the VM and replays all\n * variable definitions up to and including that line. This avoids\n * re-evaluating the entire document from line 1 on every scroll.\n *\n * **Thread safety:** Checkpoints are created synchronously on the main\n * thread during evaluation. They are immutable after creation (Value is\n * an immutable type), so no synchronization is needed.\n *\n * **Integration with Phase 5.2e:** `setViewport()` will use `getNearestCheckpoint()`\n * to find the checkpoint just before the new viewport start, then call\n * `restoreTo()` to set up the VM before evaluating only the visible lines.\n * This is the key to O(visible lines) scrolling instead of O(document).\n */\nexport class VMCheckpointer {\n\t/** Ordered array of checkpoints (ascending lineNumber). */\n\tprivate checkpoints: VMCheckpoint[] = [];\n\t/** The VM instance whose variables are snapshotted/restored. */\n\tprivate vm: VM;\n\n\tconstructor(vm: VM) {\n\t\tthis.vm = vm;\n\t}\n\n\t// ── Snapshot ─────────────────────────────────────────────────────\n\n\t/**\n\t * Create a checkpoint at the current line, recording the VM values of\n\t * the specified variables.\n\t *\n\t * Uses prototypal inheritance: `Object.create(parent.variables)` so\n\t * that inherited variable lookups fall through to previous checkpoints\n\t * without copying all variables into each checkpoint.\n\t *\n\t * @param lineNumber 1-based line position.\n\t * @param lineId Persistent line ID from DocumentModel.\n\t * @param variableNames Names of variables that were written at this line.\n\t * @returns The new checkpoint, or null if no variable names provided.\n\t */\n\tsnapshot(\n\t\tlineNumber: number,\n\t\tlineId: number,\n\t\tvariableNames: string[]\n\t): VMCheckpoint | null {\n\t\tif (variableNames.length === 0) return null;\n\n\t\tconst parent =\n\t\t\tthis.checkpoints.length > 0\n\t\t\t\t? this.checkpoints[this.checkpoints.length - 1]\n\t\t\t\t: null;\n\n\t\t// Create prototypal chain: new checkpoint inherits from parent\n\t\tconst variables: Record<string, Value> = Object.create(\n\t\t\tparent?.variables ?? null\n\t\t) as Record<string, Value>;\n\t\tconst functions: Record<string, UserFunctionDef> = Object.create(\n\t\t\tparent?.functions ?? null\n\t\t) as Record<string, UserFunctionDef>;\n\n\t\t// Record current VM values for the written names, routing each into\n\t\t// the right bag (a name is either a variable or a user-defined\n\t\t// function, never both; see VMCheckpoint.functions's doc comment for\n\t\t// why this dispatch is required, not optional).\n\t\tfor (const name of variableNames) {\n\t\t\tif (this.vm.hasUserFunction(name)) {\n\t\t\t\tconst fn = this.vm.getUserFunction(name);\n\t\t\t\tif (fn) functions[name] = fn;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst val = this.vm.getVar(name);\n\t\t\tif (val !== undefined) {\n\t\t\t\tvariables[name] = val;\n\t\t\t}\n\t\t}\n\n\t\tconst checkpoint: VMCheckpoint = {\n\t\t\tlineNumber,\n\t\t\tlineId,\n\t\t\tvariables,\n\t\t\tfunctions,\n\t\t\tparent,\n\t\t};\n\t\tthis.checkpoints.push(checkpoint);\n\t\treturn checkpoint;\n\t}\n\n\t// ── Restore ──────────────────────────────────────────────────────\n\n\t/**\n\t * Restore the VM to the state at or just after the given line number.\n\t *\n\t * Finds the nearest checkpoint whose `lineNumber <= targetLineNumber`,\n\t * then replays all variable definitions from root → that checkpoint\n\t * into the VM via `setVar()`. The VM's stack is also reset.\n\t *\n\t * If no checkpoint exists at or before the target line, the VM is\n\t * fully reset (empty scope, empty stack).\n\t *\n\t * **Performance:** O(number of checkpoints × variables per checkpoint).\n\t * With prototypal inheritance, `Object.keys()` on each checkpoint\n\t * returns only the variables that were set at that checkpoint (not\n\t * inherited ones), so the total work is O(total variable definitions\n\t * in the document), which is < 100 for typical Obsidian documents.\n\t *\n\t * @param lineNumber Target 1-based line number. The VM will have the\n\t * state that existed AFTER evaluating lines up to `lineNumber`.\n\t */\n\trestoreTo(lineNumber: number): void {\n\t\tconst target = this.getNearestCheckpoint(lineNumber);\n\t\tif (!target) {\n\t\t\tthis.vm.reset();\n\t\t\treturn;\n\t\t}\n\n\t\t// Collect the checkpoint chain from root to target.\n\t\t// Walk parent links and reverse so root is first.\n\t\tconst chain: VMCheckpoint[] = [];\n\t\tlet current: VMCheckpoint | null = target;\n\t\twhile (current) {\n\t\t\tchain.unshift(current);\n\t\t\tcurrent = current.parent;\n\t\t}\n\n\t\tthis.vm.reset();\n\t\tfor (const cp of chain) {\n\t\t\t// Object.keys() returns only OWN enumerable properties\n\t\t\t// it does NOT include inherited properties from the prototype chain.\n\t\t\t// This means we only set variables that were defined/updated at this\n\t\t\t// specific checkpoint, not all variables from parent checkpoints.\n\t\t\tfor (const key of Object.keys(cp.variables)) {\n\t\t\t\tthis.vm.setVar(key, cp.variables[key]);\n\t\t\t}\n\t\t\t// Replay function definitions the same way, a later checkpoint's\n\t\t\t// redefinition of the same name naturally overwrites an earlier\n\t\t\t// one since the chain replays in root-to-target order.\n\t\t\tfor (const key of Object.keys(cp.functions)) {\n\t\t\t\tconst fn = cp.functions[key];\n\t\t\t\tthis.vm.defineUserFunction(fn.name, fn.params, fn.program);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Queries ──────────────────────────────────────────────────────\n\n\t/**\n\t * Find the nearest checkpoint at or before the given line number.\n\t *\n\t * Uses linear scan (checkpoints are sorted by lineNumber and the list\n\t * is short, typically < 20 for Obsidian documents). Can be upgraded\n\t * to binary search if needed for documents with 1000+ variable defs.\n\t *\n\t * @returns The nearest checkpoint, or null if none exists before the line.\n\t */\n\tgetNearestCheckpoint(lineNumber: number): VMCheckpoint | null {\n\t\tlet result: VMCheckpoint | null = null;\n\t\tfor (const cp of this.checkpoints) {\n\t\t\tif (cp.lineNumber <= lineNumber) {\n\t\t\t\tresult = cp;\n\t\t\t} else {\n\t\t\t\tbreak; // checkpoints are sorted ascending\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get a specific checkpoint by its line number.\n\t * @returns The checkpoint, or undefined if not found.\n\t */\n\tgetCheckpointAt(lineNumber: number): VMCheckpoint | undefined {\n\t\treturn this.checkpoints.find((cp) => cp.lineNumber === lineNumber);\n\t}\n\n\t/**\n\t * Get the entire checkpoint chain from root to the last checkpoint.\n\t * Useful for debugging and serialization.\n\t */\n\tgetAllCheckpoints(): readonly VMCheckpoint[] {\n\t\treturn this.checkpoints;\n\t}\n\n\t/**\n\t * Look up a variable's value through the checkpoint chain.\n\t *\n\t * Walks the prototype chain starting from the most recent checkpoint,\n\t * looking for the variable name as an own property. This is O(depth)\n\t * where depth is the number of checkpoints since the variable was\n\t * last set.\n\t *\n\t * **Note:** This queries the checkpointer's snapshot, not the VM.\n\t * The VM may have been modified since the last snapshot (e.g., by\n\t * Tier 2 execution of non-variable-def lines that don't create checkpoints).\n\t *\n\t * @returns The Value, or undefined if the variable was never set.\n\t */\n\tlookupVariable(name: string): Value | undefined {\n\t\tif (this.checkpoints.length === 0) return undefined;\n\n\t\tconst latest = this.checkpoints[this.checkpoints.length - 1];\n\t\tlet scope: Record<string, Value> | null = latest.variables;\n\t\twhile (scope) {\n\t\t\tif (Object.prototype.hasOwnProperty.call(scope, name)) {\n\t\t\t\treturn scope[name];\n\t\t\t}\n\t\t\tscope = Object.getPrototypeOf(scope) as Record<string, Value> | null;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────\n\n\t/**\n\t * Clear all checkpoints. The underlying VM is NOT reset, call\n\t * `vm.reset()` separately if needed.\n\t */\n\tclear(): void {\n\t\tthis.checkpoints = [];\n\t}\n\n\t/** Number of checkpoints stored. */\n\tget count(): number {\n\t\treturn this.checkpoints.length;\n\t}\n\n\t/** Returns true if no checkpoints have been created. */\n\tget isEmpty(): boolean {\n\t\treturn this.checkpoints.length === 0;\n\t}\n\n\t/** The associated VM instance. */\n\tget vmInstance(): VM {\n\t\treturn this.vm;\n\t}\n}\n"]}
|
package/dist/vm.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
import './
|
|
7
|
-
import './EngineError-
|
|
8
|
-
import './pipeline-
|
|
1
|
+
export { B as Bytecode, D as DependencyGraph, E as ExpressionRecord, L as LineExecutionContext, O as OpRegistry, S as ScopeManager, V as VM, a as allocatePluginFunctionIndex, b as builtinFunctions, c as createVM, e as executeBytecode, p as pluginFunctionIndexFor, d as pluginFunctionRegistry, s as sharedOpRegistry } from './VMBuiltins-kv4rr0y5.cjs';
|
|
2
|
+
import { V as Value } from './Value-BYHw-x7q.cjs';
|
|
3
|
+
export { M as MatrixData, a as MatrixEntry, R as RangeData, b as ValueType, c as bigIntValue, d as colVectorValue, h as hexValue, m as matrixValue, n as numberValue, r as rangeValue, e as rowVectorValue, s as stringValue, u as uomValue } from './Value-BYHw-x7q.cjs';
|
|
4
|
+
export { V as VMCheckpoint, a as VMCheckpointer } from './VMCheckpoints-yun4ocSv.cjs';
|
|
5
|
+
import './BytecodeBuilder-DSWKZi4f.cjs';
|
|
6
|
+
import './CalendarBackend-MdS3Bb4P.cjs';
|
|
7
|
+
import './EngineError-C-kBwYlN.cjs';
|
|
8
|
+
import './pipeline-BYkKNpql.cjs';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The ceiling on how much memory one evaluation is allowed to ask for.
|
|
@@ -103,54 +103,6 @@ declare function checkAllocation(count: number, what: string): void;
|
|
|
103
103
|
*/
|
|
104
104
|
declare function checkedArray<T>(count: number, what: string): T[];
|
|
105
105
|
|
|
106
|
-
/**
|
|
107
|
-
* Registry of built-in mathematical functions.
|
|
108
|
-
* Indexed by the number pushed as an operand of OpCode.CALL_BUILTIN.
|
|
109
|
-
*/
|
|
110
|
-
declare const builtinFunctions: Record<number, (args: Value[]) => Value>;
|
|
111
|
-
/**
|
|
112
|
-
* Registry of package-registered functions, indexed by the number pushed as an
|
|
113
|
-
* operand of `OpCode.CALL_PLUGIN`.
|
|
114
|
-
*
|
|
115
|
-
* Functions may return a promise. The orchestrator resolves it and re-executes
|
|
116
|
-
* rather than blocking the VM.
|
|
117
|
-
*
|
|
118
|
-
* Populated declaratively via {@link IEnginePackage.pluginFunctions} at
|
|
119
|
-
* package-registration time. Entries are cleared on unregister.
|
|
120
|
-
*
|
|
121
|
-
* @deprecated This is the {@link defaultEngineContext}'s map, kept as a
|
|
122
|
-
* module-level alias so existing callers keep working during the context
|
|
123
|
-
* migration. An engine registers into its own context, so writing here affects
|
|
124
|
-
* only code that has not been migrated. Take an {@link EngineContext} instead.
|
|
125
|
-
*/
|
|
126
|
-
declare const pluginFunctionRegistry: Record<number, PluginFunctionHandler>;
|
|
127
|
-
/**
|
|
128
|
-
* Allocate a unique index into {@link pluginFunctionRegistry} for a package's
|
|
129
|
-
* plugin function.
|
|
130
|
-
*
|
|
131
|
-
* Call once per function, typically at module scope, right where the
|
|
132
|
-
* function is defined, and store the result in a constant. Don't hardcode
|
|
133
|
-
* an index: two packages independently picking the same arbitrary number
|
|
134
|
-
* would silently overwrite each other's handler in the shared registry.
|
|
135
|
-
*
|
|
136
|
-
* @throws If the index pool is exhausted (up to 65536: two opcode-stream bytes
|
|
137
|
-
* via CALL_PLUGIN_WIDE, the first 256 via the one-byte CALL_PLUGIN).
|
|
138
|
-
*/
|
|
139
|
-
declare function allocatePluginFunctionIndex(): number;
|
|
140
|
-
/**
|
|
141
|
-
* The stable index for a package's named plugin function, allocated once per
|
|
142
|
-
* `${packageName}:${functionName}` and cached process-wide, so the same function
|
|
143
|
-
* always maps to the same {@link pluginFunctionRegistry} slot no matter which
|
|
144
|
-
* engine registers it or in what order: the property compiled bytecode, and so
|
|
145
|
-
* a snapshot, relies on.
|
|
146
|
-
*
|
|
147
|
-
* The engine calls this at {@link ExpressionEngine.registerPackage} time for each
|
|
148
|
-
* entry of a package's `pluginFunctions` record, so a package author names the
|
|
149
|
-
* function and never sees an index. {@link allocatePluginFunctionIndex} remains
|
|
150
|
-
* for a host that needs a raw index directly.
|
|
151
|
-
*/
|
|
152
|
-
declare function pluginFunctionIndexFor(qualifiedName: string): number;
|
|
153
|
-
|
|
154
106
|
/**
|
|
155
107
|
* Unify two Value operands that may carry units of measurement.
|
|
156
108
|
* Returns numeric values in a common unit (or undefined unit if incompatible).
|
|
@@ -174,4 +126,4 @@ declare function unifyUom(l: Value, r: Value): {
|
|
|
174
126
|
*/
|
|
175
127
|
declare function binaryOp(l: Value, r: Value, op: (a: number, b: number) => number, bigOp?: (a: bigint, b: bigint) => bigint, symbolicOp?: "add" | "sub" | "mul" | "div"): Value;
|
|
176
128
|
|
|
177
|
-
export { Value,
|
|
129
|
+
export { Value, binaryOp, chargeAllocation, checkAllocation, checkedArray, unifyUom };
|
package/dist/vm.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
import './
|
|
7
|
-
import './EngineError-
|
|
8
|
-
import './pipeline-
|
|
1
|
+
export { B as Bytecode, D as DependencyGraph, E as ExpressionRecord, L as LineExecutionContext, O as OpRegistry, S as ScopeManager, V as VM, a as allocatePluginFunctionIndex, b as builtinFunctions, c as createVM, e as executeBytecode, p as pluginFunctionIndexFor, d as pluginFunctionRegistry, s as sharedOpRegistry } from './VMBuiltins-CZiZRKn-.js';
|
|
2
|
+
import { V as Value } from './Value-BYHw-x7q.js';
|
|
3
|
+
export { M as MatrixData, a as MatrixEntry, R as RangeData, b as ValueType, c as bigIntValue, d as colVectorValue, h as hexValue, m as matrixValue, n as numberValue, r as rangeValue, e as rowVectorValue, s as stringValue, u as uomValue } from './Value-BYHw-x7q.js';
|
|
4
|
+
export { V as VMCheckpoint, a as VMCheckpointer } from './VMCheckpoints-DXze6ypg.js';
|
|
5
|
+
import './BytecodeBuilder-DSWKZi4f.js';
|
|
6
|
+
import './CalendarBackend-MdS3Bb4P.js';
|
|
7
|
+
import './EngineError-C-kBwYlN.js';
|
|
8
|
+
import './pipeline-Cl7KW8CB.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The ceiling on how much memory one evaluation is allowed to ask for.
|
|
@@ -103,54 +103,6 @@ declare function checkAllocation(count: number, what: string): void;
|
|
|
103
103
|
*/
|
|
104
104
|
declare function checkedArray<T>(count: number, what: string): T[];
|
|
105
105
|
|
|
106
|
-
/**
|
|
107
|
-
* Registry of built-in mathematical functions.
|
|
108
|
-
* Indexed by the number pushed as an operand of OpCode.CALL_BUILTIN.
|
|
109
|
-
*/
|
|
110
|
-
declare const builtinFunctions: Record<number, (args: Value[]) => Value>;
|
|
111
|
-
/**
|
|
112
|
-
* Registry of package-registered functions, indexed by the number pushed as an
|
|
113
|
-
* operand of `OpCode.CALL_PLUGIN`.
|
|
114
|
-
*
|
|
115
|
-
* Functions may return a promise. The orchestrator resolves it and re-executes
|
|
116
|
-
* rather than blocking the VM.
|
|
117
|
-
*
|
|
118
|
-
* Populated declaratively via {@link IEnginePackage.pluginFunctions} at
|
|
119
|
-
* package-registration time. Entries are cleared on unregister.
|
|
120
|
-
*
|
|
121
|
-
* @deprecated This is the {@link defaultEngineContext}'s map, kept as a
|
|
122
|
-
* module-level alias so existing callers keep working during the context
|
|
123
|
-
* migration. An engine registers into its own context, so writing here affects
|
|
124
|
-
* only code that has not been migrated. Take an {@link EngineContext} instead.
|
|
125
|
-
*/
|
|
126
|
-
declare const pluginFunctionRegistry: Record<number, PluginFunctionHandler>;
|
|
127
|
-
/**
|
|
128
|
-
* Allocate a unique index into {@link pluginFunctionRegistry} for a package's
|
|
129
|
-
* plugin function.
|
|
130
|
-
*
|
|
131
|
-
* Call once per function, typically at module scope, right where the
|
|
132
|
-
* function is defined, and store the result in a constant. Don't hardcode
|
|
133
|
-
* an index: two packages independently picking the same arbitrary number
|
|
134
|
-
* would silently overwrite each other's handler in the shared registry.
|
|
135
|
-
*
|
|
136
|
-
* @throws If the index pool is exhausted (up to 65536: two opcode-stream bytes
|
|
137
|
-
* via CALL_PLUGIN_WIDE, the first 256 via the one-byte CALL_PLUGIN).
|
|
138
|
-
*/
|
|
139
|
-
declare function allocatePluginFunctionIndex(): number;
|
|
140
|
-
/**
|
|
141
|
-
* The stable index for a package's named plugin function, allocated once per
|
|
142
|
-
* `${packageName}:${functionName}` and cached process-wide, so the same function
|
|
143
|
-
* always maps to the same {@link pluginFunctionRegistry} slot no matter which
|
|
144
|
-
* engine registers it or in what order: the property compiled bytecode, and so
|
|
145
|
-
* a snapshot, relies on.
|
|
146
|
-
*
|
|
147
|
-
* The engine calls this at {@link ExpressionEngine.registerPackage} time for each
|
|
148
|
-
* entry of a package's `pluginFunctions` record, so a package author names the
|
|
149
|
-
* function and never sees an index. {@link allocatePluginFunctionIndex} remains
|
|
150
|
-
* for a host that needs a raw index directly.
|
|
151
|
-
*/
|
|
152
|
-
declare function pluginFunctionIndexFor(qualifiedName: string): number;
|
|
153
|
-
|
|
154
106
|
/**
|
|
155
107
|
* Unify two Value operands that may carry units of measurement.
|
|
156
108
|
* Returns numeric values in a common unit (or undefined unit if incompatible).
|
|
@@ -174,4 +126,4 @@ declare function unifyUom(l: Value, r: Value): {
|
|
|
174
126
|
*/
|
|
175
127
|
declare function binaryOp(l: Value, r: Value, op: (a: number, b: number) => number, bigOp?: (a: bigint, b: bigint) => bigint, symbolicOp?: "add" | "sub" | "mul" | "div"): Value;
|
|
176
128
|
|
|
177
|
-
export { Value,
|
|
129
|
+
export { Value, binaryOp, chargeAllocation, checkAllocation, checkedArray, unifyUom };
|
package/dist/vm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{a as DependencyGraph,b as ScopeManager,c as createVM,d as executeBytecode}from'./chunk-
|
|
1
|
+
export{a as DependencyGraph,b as ScopeManager,c as createVM,d as executeBytecode}from'./chunk-TC23XEAV.js';import'./chunk-JP4JMQOG.js';export{v as OpRegistry,E as allocatePluginFunctionIndex,u as binaryOp,C as builtinFunctions,F as pluginFunctionIndexFor,D as pluginFunctionRegistry,w as sharedOpRegistry,j as unifyUom}from'./chunk-CL7DM2FX.js';import'./chunk-D4VBWPWN.js';import'./chunk-ESZNJQ2C.js';import'./chunk-TZYMXNIB.js';import'./chunk-HFKDWQUL.js';import'./chunk-LTUYWJGO.js';import'./chunk-BLJ6E577.js';import'./chunk-J4K72CQN.js';export{m as Value,g as ValueType,s as bigIntValue,c as chargeAllocation,d as checkAllocation,e as checkedArray,E as colVectorValue,r as hexValue,C as matrixValue,n as numberValue,F as rangeValue,D as rowVectorValue,t as stringValue,u as uomValue}from'./chunk-LQIRBP4Q.js';import'./chunk-4CNYGIND.js';import'./chunk-PRSXXDTA.js';import'./chunk-VB6QMU2W.js';var l=class{constructor(t){this.checkpoints=[];this.vm=t;}snapshot(t,n,e){if(e.length===0)return null;let r=this.checkpoints.length>0?this.checkpoints[this.checkpoints.length-1]:null,o=Object.create(r?.variables??null),i=Object.create(r?.functions??null);for(let s of e){if(this.vm.hasUserFunction(s)){let p=this.vm.getUserFunction(s);p&&(i[s]=p);continue}let u=this.vm.getVar(s);u!==void 0&&(o[s]=u);}let c={lineNumber:t,lineId:n,variables:o,functions:i,parent:r};return this.checkpoints.push(c),c}restoreTo(t){let n=this.getNearestCheckpoint(t);if(!n){this.vm.reset();return}let e=[],r=n;for(;r;)e.unshift(r),r=r.parent;this.vm.reset();for(let o of e){for(let i of Object.keys(o.variables))this.vm.setVar(i,o.variables[i]);for(let i of Object.keys(o.functions)){let c=o.functions[i];this.vm.defineUserFunction(c.name,c.params,c.program);}}}getNearestCheckpoint(t){let n=null;for(let e of this.checkpoints)if(e.lineNumber<=t)n=e;else break;return n}getCheckpointAt(t){return this.checkpoints.find(n=>n.lineNumber===t)}getAllCheckpoints(){return this.checkpoints}lookupVariable(t){if(this.checkpoints.length===0)return;let e=this.checkpoints[this.checkpoints.length-1].variables;for(;e;){if(Object.prototype.hasOwnProperty.call(e,t))return e[t];e=Object.getPrototypeOf(e);}}clear(){this.checkpoints=[];}get count(){return this.checkpoints.length}get isEmpty(){return this.checkpoints.length===0}get vmInstance(){return this.vm}};export{l as VMCheckpointer};//# sourceMappingURL=vm.js.map
|
|
2
2
|
//# sourceMappingURL=vm.js.map
|
package/dist/vm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vm/VMCheckpoints.ts"],"names":["VMCheckpointer","vm","lineNumber","lineId","variableNames","parent","variables","functions","name","fn","val","checkpoint","target","chain","current","cp","key","result","scope"],"mappings":"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","file":"vm.js","sourcesContent":["import { Value } from \"@solve-js/vm/Value\";\nimport type { VM } from \"@solve-js/vm/OpRegistry\";\nimport type { UserFunctionDef } from \"@solve-js/parser/BytecodeBuilder\";\n\n// ── VMCheckpoint ────────────────────────────────────────────────────────\n\n/**\n * A point-in-time snapshot of VM variable state.\n *\n * Uses **prototypal inheritance** for memory efficiency: each checkpoint's\n * `variables` object has its parent checkpoint's `variables` as its\n * `__proto__`. This means a `getVar(\"x\")` lookup walks the prototype chain\n * until it finds `x`, and only variables that CHANGED at this checkpoint\n * consume heap space. Unchanged variables are inherited from the parent.\n *\n * ```text\n * Checkpoint 0 (root): {} // empty scope\n * Checkpoint 1 (:x=5): { x: 5 } __proto__ → 0\n * Checkpoint 2 (:y=8): { y: 8 } __proto__ → 1\n * Checkpoint 3 (:x=3): { x: 3 } __proto__ → 2 // shadows x=5\n * ```\n *\n * To look up `x` at checkpoint 3: find own `x=3` → done.\n * To look up `y` at checkpoint 3: not own → walk proto to checkpoint 2 → `y=8`.\n * To look up `z` at checkpoint 3: not found anywhere → undefined.\n *\n * **Memory:** O(number of variable definitions) heap, independent of\n * document length. Typical Obsidian documents have < 100 variable defs,\n * so total checkpoint heap is < 10 KB.\n */\nexport interface VMCheckpoint {\n\t/** 1-based line number where this checkpoint was created. */\n\tlineNumber: number;\n\t/** Persistent line ID from DocumentModel. */\n\tlineId: number;\n\t/**\n\t * Variable name → Value at this checkpoint.\n\t * Own properties are variables set/updated at this line.\n\t * The prototype chain provides inherited variables from parent checkpoints.\n\t */\n\tvariables: Record<string, Value>;\n\t/**\n\t * User-defined-function name → definition at this checkpoint. SEPARATE\n\t * from `variables` above (not prototypally chained the same way\n\t * `restoreTo()` replays every checkpoint in the chain in order, so a\n\t * later redefinition of the same function name naturally overwrites an\n\t * earlier one during replay, without needing its own prototype walk).\n\t *\n\t * Without this field, a function definition's checkpoint entry would be\n\t * SILENTLY LOST: `snapshot()` used to call `vm.getVar(name)` for every\n\t * written name, which returns `undefined` for a function name (function\n\t * defs live in `vm.userFunctions`, not the flat variable store), and a\n\t * `val !== undefined` guard silently skipped it. A scroll-triggered\n\t * `restoreTo()` would then reset the VM and replay only `variables`,\n\t * making a function defined above the new viewport vanish (calling it\n\t * would throw `UNDEFINED_FUNCTION`) even though the document still\n\t * shows its definition line as clean/cached.\n\t */\n\tfunctions: Record<string, UserFunctionDef>;\n\t/** Parent checkpoint (closer to document start), or null for root. */\n\tparent: VMCheckpoint | null;\n}\n\n// ── VMCheckpointer ──────────────────────────────────────────────────────\n\n/**\n * Manages VM state checkpoints for the three-tier evaluation strategy.\n *\n * **Checkpoint creation:** After a variable-definition line executes\n * (Tier 1 or Tier 3), `snapshot()` records the current values of the\n * written variables. The checkpoint is linked via prototypal inheritance\n * to the previous checkpoint, so only changed variables consume memory.\n *\n * **Checkpoint restoration:** Before evaluating a viewport whose start line\n * is not line 1, `restoreTo(lineNumber)` resets the VM and replays all\n * variable definitions up to and including that line. This avoids\n * re-evaluating the entire document from line 1 on every scroll.\n *\n * **Thread safety:** Checkpoints are created synchronously on the main\n * thread during evaluation. They are immutable after creation (Value is\n * an immutable type), so no synchronization is needed.\n *\n * **Integration with Phase 5.2e:** `setViewport()` will use `getNearestCheckpoint()`\n * to find the checkpoint just before the new viewport start, then call\n * `restoreTo()` to set up the VM before evaluating only the visible lines.\n * This is the key to O(visible lines) scrolling instead of O(document).\n */\nexport class VMCheckpointer {\n\t/** Ordered array of checkpoints (ascending lineNumber). */\n\tprivate checkpoints: VMCheckpoint[] = [];\n\t/** The VM instance whose variables are snapshotted/restored. */\n\tprivate vm: VM;\n\n\tconstructor(vm: VM) {\n\t\tthis.vm = vm;\n\t}\n\n\t// ── Snapshot ─────────────────────────────────────────────────────\n\n\t/**\n\t * Create a checkpoint at the current line, recording the VM values of\n\t * the specified variables.\n\t *\n\t * Uses prototypal inheritance: `Object.create(parent.variables)` so\n\t * that inherited variable lookups fall through to previous checkpoints\n\t * without copying all variables into each checkpoint.\n\t *\n\t * @param lineNumber 1-based line position.\n\t * @param lineId Persistent line ID from DocumentModel.\n\t * @param variableNames Names of variables that were written at this line.\n\t * @returns The new checkpoint, or null if no variable names provided.\n\t */\n\tsnapshot(\n\t\tlineNumber: number,\n\t\tlineId: number,\n\t\tvariableNames: string[]\n\t): VMCheckpoint | null {\n\t\tif (variableNames.length === 0) return null;\n\n\t\tconst parent =\n\t\t\tthis.checkpoints.length > 0\n\t\t\t\t? this.checkpoints[this.checkpoints.length - 1]\n\t\t\t\t: null;\n\n\t\t// Create prototypal chain: new checkpoint inherits from parent\n\t\tconst variables: Record<string, Value> = Object.create(\n\t\t\tparent?.variables ?? null\n\t\t) as Record<string, Value>;\n\t\tconst functions: Record<string, UserFunctionDef> = Object.create(\n\t\t\tparent?.functions ?? null\n\t\t) as Record<string, UserFunctionDef>;\n\n\t\t// Record current VM values for the written names, routing each into\n\t\t// the right bag (a name is either a variable or a user-defined\n\t\t// function, never both; see VMCheckpoint.functions's doc comment for\n\t\t// why this dispatch is required, not optional).\n\t\tfor (const name of variableNames) {\n\t\t\tif (this.vm.hasUserFunction(name)) {\n\t\t\t\tconst fn = this.vm.getUserFunction(name);\n\t\t\t\tif (fn) functions[name] = fn;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst val = this.vm.getVar(name);\n\t\t\tif (val !== undefined) {\n\t\t\t\tvariables[name] = val;\n\t\t\t}\n\t\t}\n\n\t\tconst checkpoint: VMCheckpoint = {\n\t\t\tlineNumber,\n\t\t\tlineId,\n\t\t\tvariables,\n\t\t\tfunctions,\n\t\t\tparent,\n\t\t};\n\t\tthis.checkpoints.push(checkpoint);\n\t\treturn checkpoint;\n\t}\n\n\t// ── Restore ──────────────────────────────────────────────────────\n\n\t/**\n\t * Restore the VM to the state at or just after the given line number.\n\t *\n\t * Finds the nearest checkpoint whose `lineNumber <= targetLineNumber`,\n\t * then replays all variable definitions from root → that checkpoint\n\t * into the VM via `setVar()`. The VM's stack is also reset.\n\t *\n\t * If no checkpoint exists at or before the target line, the VM is\n\t * fully reset (empty scope, empty stack).\n\t *\n\t * **Performance:** O(number of checkpoints × variables per checkpoint).\n\t * With prototypal inheritance, `Object.keys()` on each checkpoint\n\t * returns only the variables that were set at that checkpoint (not\n\t * inherited ones), so the total work is O(total variable definitions\n\t * in the document), which is < 100 for typical Obsidian documents.\n\t *\n\t * @param lineNumber Target 1-based line number. The VM will have the\n\t * state that existed AFTER evaluating lines up to `lineNumber`.\n\t */\n\trestoreTo(lineNumber: number): void {\n\t\tconst target = this.getNearestCheckpoint(lineNumber);\n\t\tif (!target) {\n\t\t\tthis.vm.reset();\n\t\t\treturn;\n\t\t}\n\n\t\t// Collect the checkpoint chain from root to target.\n\t\t// Walk parent links and reverse so root is first.\n\t\tconst chain: VMCheckpoint[] = [];\n\t\tlet current: VMCheckpoint | null = target;\n\t\twhile (current) {\n\t\t\tchain.unshift(current);\n\t\t\tcurrent = current.parent;\n\t\t}\n\n\t\tthis.vm.reset();\n\t\tfor (const cp of chain) {\n\t\t\t// Object.keys() returns only OWN enumerable properties\n\t\t\t// it does NOT include inherited properties from the prototype chain.\n\t\t\t// This means we only set variables that were defined/updated at this\n\t\t\t// specific checkpoint, not all variables from parent checkpoints.\n\t\t\tfor (const key of Object.keys(cp.variables)) {\n\t\t\t\tthis.vm.setVar(key, cp.variables[key]);\n\t\t\t}\n\t\t\t// Replay function definitions the same way, a later checkpoint's\n\t\t\t// redefinition of the same name naturally overwrites an earlier\n\t\t\t// one since the chain replays in root-to-target order.\n\t\t\tfor (const key of Object.keys(cp.functions)) {\n\t\t\t\tconst fn = cp.functions[key];\n\t\t\t\tthis.vm.defineUserFunction(fn.name, fn.params, fn.program);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Queries ──────────────────────────────────────────────────────\n\n\t/**\n\t * Find the nearest checkpoint at or before the given line number.\n\t *\n\t * Uses linear scan (checkpoints are sorted by lineNumber and the list\n\t * is short, typically < 20 for Obsidian documents). Can be upgraded\n\t * to binary search if needed for documents with 1000+ variable defs.\n\t *\n\t * @returns The nearest checkpoint, or null if none exists before the line.\n\t */\n\tgetNearestCheckpoint(lineNumber: number): VMCheckpoint | null {\n\t\tlet result: VMCheckpoint | null = null;\n\t\tfor (const cp of this.checkpoints) {\n\t\t\tif (cp.lineNumber <= lineNumber) {\n\t\t\t\tresult = cp;\n\t\t\t} else {\n\t\t\t\tbreak; // checkpoints are sorted ascending\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get a specific checkpoint by its line number.\n\t * @returns The checkpoint, or undefined if not found.\n\t */\n\tgetCheckpointAt(lineNumber: number): VMCheckpoint | undefined {\n\t\treturn this.checkpoints.find((cp) => cp.lineNumber === lineNumber);\n\t}\n\n\t/**\n\t * Get the entire checkpoint chain from root to the last checkpoint.\n\t * Useful for debugging and serialization.\n\t */\n\tgetAllCheckpoints(): readonly VMCheckpoint[] {\n\t\treturn this.checkpoints;\n\t}\n\n\t/**\n\t * Look up a variable's value through the checkpoint chain.\n\t *\n\t * Walks the prototype chain starting from the most recent checkpoint,\n\t * looking for the variable name as an own property. This is O(depth)\n\t * where depth is the number of checkpoints since the variable was\n\t * last set.\n\t *\n\t * **Note:** This queries the checkpointer's snapshot, not the VM.\n\t * The VM may have been modified since the last snapshot (e.g., by\n\t * Tier 2 execution of non-variable-def lines that don't create checkpoints).\n\t *\n\t * @returns The Value, or undefined if the variable was never set.\n\t */\n\tlookupVariable(name: string): Value | undefined {\n\t\tif (this.checkpoints.length === 0) return undefined;\n\n\t\tconst latest = this.checkpoints[this.checkpoints.length - 1];\n\t\tlet scope: Record<string, Value> | null = latest.variables;\n\t\twhile (scope) {\n\t\t\tif (Object.prototype.hasOwnProperty.call(scope, name)) {\n\t\t\t\treturn scope[name];\n\t\t\t}\n\t\t\tscope = Object.getPrototypeOf(scope) as Record<string, Value> | null;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────\n\n\t/**\n\t * Clear all checkpoints. The underlying VM is NOT reset, call\n\t * `vm.reset()` separately if needed.\n\t */\n\tclear(): void {\n\t\tthis.checkpoints = [];\n\t}\n\n\t/** Number of checkpoints stored. */\n\tget count(): number {\n\t\treturn this.checkpoints.length;\n\t}\n\n\t/** Returns true if no checkpoints have been created. */\n\tget isEmpty(): boolean {\n\t\treturn this.checkpoints.length === 0;\n\t}\n\n\t/** The associated VM instance. */\n\tget vmInstance(): VM {\n\t\treturn this.vm;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/vm/VMCheckpoints.ts"],"names":["VMCheckpointer","vm","lineNumber","lineId","variableNames","parent","variables","functions","name","fn","val","checkpoint","target","chain","current","cp","key","result","scope"],"mappings":"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","file":"vm.js","sourcesContent":["import { Value } from \"@solve-js/vm/Value\";\nimport type { VM } from \"@solve-js/vm/OpRegistry\";\nimport type { UserFunctionDef } from \"@solve-js/parser/BytecodeBuilder\";\n\n// ── VMCheckpoint ────────────────────────────────────────────────────────\n\n/**\n * A point-in-time snapshot of VM variable state.\n *\n * Uses **prototypal inheritance** for memory efficiency: each checkpoint's\n * `variables` object has its parent checkpoint's `variables` as its\n * `__proto__`. This means a `getVar(\"x\")` lookup walks the prototype chain\n * until it finds `x`, and only variables that CHANGED at this checkpoint\n * consume heap space. Unchanged variables are inherited from the parent.\n *\n * ```text\n * Checkpoint 0 (root): {} // empty scope\n * Checkpoint 1 (:x=5): { x: 5 } __proto__ → 0\n * Checkpoint 2 (:y=8): { y: 8 } __proto__ → 1\n * Checkpoint 3 (:x=3): { x: 3 } __proto__ → 2 // shadows x=5\n * ```\n *\n * To look up `x` at checkpoint 3: find own `x=3` → done.\n * To look up `y` at checkpoint 3: not own → walk proto to checkpoint 2 → `y=8`.\n * To look up `z` at checkpoint 3: not found anywhere → undefined.\n *\n * **Memory:** O(number of variable definitions) heap, independent of\n * document length. Typical Obsidian documents have < 100 variable defs,\n * so total checkpoint heap is < 10 KB.\n */\nexport interface VMCheckpoint {\n\t/** 1-based line number where this checkpoint was created. */\n\tlineNumber: number;\n\t/** Persistent line ID from DocumentModel. */\n\tlineId: number;\n\t/**\n\t * Variable name → Value at this checkpoint.\n\t * Own properties are variables set/updated at this line.\n\t * The prototype chain provides inherited variables from parent checkpoints.\n\t */\n\tvariables: Record<string, Value>;\n\t/**\n\t * User-defined-function name → definition at this checkpoint. SEPARATE\n\t * from `variables` above (not prototypally chained the same way\n\t * `restoreTo()` replays every checkpoint in the chain in order, so a\n\t * later redefinition of the same function name naturally overwrites an\n\t * earlier one during replay, without needing its own prototype walk).\n\t *\n\t * Without this field, a function definition's checkpoint entry would be\n\t * SILENTLY LOST: `snapshot()` used to call `vm.getVar(name)` for every\n\t * written name, which returns `undefined` for a function name (function\n\t * defs live in `vm.userFunctions`, not the flat variable store), and a\n\t * `val !== undefined` guard silently skipped it. A scroll-triggered\n\t * `restoreTo()` would then reset the VM and replay only `variables`,\n\t * making a function defined above the new viewport vanish (calling it\n\t * would throw `UNDEFINED_FUNCTION`) even though the document still\n\t * shows its definition line as clean/cached.\n\t */\n\tfunctions: Record<string, UserFunctionDef>;\n\t/** Parent checkpoint (closer to document start), or null for root. */\n\tparent: VMCheckpoint | null;\n}\n\n// ── VMCheckpointer ──────────────────────────────────────────────────────\n\n/**\n * Manages VM state checkpoints for the three-tier evaluation strategy.\n *\n * **Checkpoint creation:** After a variable-definition line executes\n * (Tier 1 or Tier 3), `snapshot()` records the current values of the\n * written variables. The checkpoint is linked via prototypal inheritance\n * to the previous checkpoint, so only changed variables consume memory.\n *\n * **Checkpoint restoration:** Before evaluating a viewport whose start line\n * is not line 1, `restoreTo(lineNumber)` resets the VM and replays all\n * variable definitions up to and including that line. This avoids\n * re-evaluating the entire document from line 1 on every scroll.\n *\n * **Thread safety:** Checkpoints are created synchronously on the main\n * thread during evaluation. They are immutable after creation (Value is\n * an immutable type), so no synchronization is needed.\n *\n * **Integration with Phase 5.2e:** `setViewport()` will use `getNearestCheckpoint()`\n * to find the checkpoint just before the new viewport start, then call\n * `restoreTo()` to set up the VM before evaluating only the visible lines.\n * This is the key to O(visible lines) scrolling instead of O(document).\n */\nexport class VMCheckpointer {\n\t/** Ordered array of checkpoints (ascending lineNumber). */\n\tprivate checkpoints: VMCheckpoint[] = [];\n\t/** The VM instance whose variables are snapshotted/restored. */\n\tprivate vm: VM;\n\n\tconstructor(vm: VM) {\n\t\tthis.vm = vm;\n\t}\n\n\t// ── Snapshot ─────────────────────────────────────────────────────\n\n\t/**\n\t * Create a checkpoint at the current line, recording the VM values of\n\t * the specified variables.\n\t *\n\t * Uses prototypal inheritance: `Object.create(parent.variables)` so\n\t * that inherited variable lookups fall through to previous checkpoints\n\t * without copying all variables into each checkpoint.\n\t *\n\t * @param lineNumber 1-based line position.\n\t * @param lineId Persistent line ID from DocumentModel.\n\t * @param variableNames Names of variables that were written at this line.\n\t * @returns The new checkpoint, or null if no variable names provided.\n\t */\n\tsnapshot(\n\t\tlineNumber: number,\n\t\tlineId: number,\n\t\tvariableNames: string[]\n\t): VMCheckpoint | null {\n\t\tif (variableNames.length === 0) return null;\n\n\t\tconst parent =\n\t\t\tthis.checkpoints.length > 0\n\t\t\t\t? this.checkpoints[this.checkpoints.length - 1]\n\t\t\t\t: null;\n\n\t\t// Create prototypal chain: new checkpoint inherits from parent\n\t\tconst variables: Record<string, Value> = Object.create(\n\t\t\tparent?.variables ?? null\n\t\t) as Record<string, Value>;\n\t\tconst functions: Record<string, UserFunctionDef> = Object.create(\n\t\t\tparent?.functions ?? null\n\t\t) as Record<string, UserFunctionDef>;\n\n\t\t// Record current VM values for the written names, routing each into\n\t\t// the right bag (a name is either a variable or a user-defined\n\t\t// function, never both; see VMCheckpoint.functions's doc comment for\n\t\t// why this dispatch is required, not optional).\n\t\tfor (const name of variableNames) {\n\t\t\tif (this.vm.hasUserFunction(name)) {\n\t\t\t\tconst fn = this.vm.getUserFunction(name);\n\t\t\t\tif (fn) functions[name] = fn;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst val = this.vm.getVar(name);\n\t\t\tif (val !== undefined) {\n\t\t\t\tvariables[name] = val;\n\t\t\t}\n\t\t}\n\n\t\tconst checkpoint: VMCheckpoint = {\n\t\t\tlineNumber,\n\t\t\tlineId,\n\t\t\tvariables,\n\t\t\tfunctions,\n\t\t\tparent,\n\t\t};\n\t\tthis.checkpoints.push(checkpoint);\n\t\treturn checkpoint;\n\t}\n\n\t// ── Restore ──────────────────────────────────────────────────────\n\n\t/**\n\t * Restore the VM to the state at or just after the given line number.\n\t *\n\t * Finds the nearest checkpoint whose `lineNumber <= targetLineNumber`,\n\t * then replays all variable definitions from root → that checkpoint\n\t * into the VM via `setVar()`. The VM's stack is also reset.\n\t *\n\t * If no checkpoint exists at or before the target line, the VM is\n\t * fully reset (empty scope, empty stack).\n\t *\n\t * **Performance:** O(number of checkpoints × variables per checkpoint).\n\t * With prototypal inheritance, `Object.keys()` on each checkpoint\n\t * returns only the variables that were set at that checkpoint (not\n\t * inherited ones), so the total work is O(total variable definitions\n\t * in the document), which is < 100 for typical Obsidian documents.\n\t *\n\t * @param lineNumber Target 1-based line number. The VM will have the\n\t * state that existed AFTER evaluating lines up to `lineNumber`.\n\t */\n\trestoreTo(lineNumber: number): void {\n\t\tconst target = this.getNearestCheckpoint(lineNumber);\n\t\tif (!target) {\n\t\t\tthis.vm.reset();\n\t\t\treturn;\n\t\t}\n\n\t\t// Collect the checkpoint chain from root to target.\n\t\t// Walk parent links and reverse so root is first.\n\t\tconst chain: VMCheckpoint[] = [];\n\t\tlet current: VMCheckpoint | null = target;\n\t\twhile (current) {\n\t\t\tchain.unshift(current);\n\t\t\tcurrent = current.parent;\n\t\t}\n\n\t\tthis.vm.reset();\n\t\tfor (const cp of chain) {\n\t\t\t// Object.keys() returns only OWN enumerable properties\n\t\t\t// it does NOT include inherited properties from the prototype chain.\n\t\t\t// This means we only set variables that were defined/updated at this\n\t\t\t// specific checkpoint, not all variables from parent checkpoints.\n\t\t\tfor (const key of Object.keys(cp.variables)) {\n\t\t\t\tthis.vm.setVar(key, cp.variables[key]);\n\t\t\t}\n\t\t\t// Replay function definitions the same way, a later checkpoint's\n\t\t\t// redefinition of the same name naturally overwrites an earlier\n\t\t\t// one since the chain replays in root-to-target order.\n\t\t\tfor (const key of Object.keys(cp.functions)) {\n\t\t\t\tconst fn = cp.functions[key];\n\t\t\t\tthis.vm.defineUserFunction(fn.name, fn.params, fn.program);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Queries ──────────────────────────────────────────────────────\n\n\t/**\n\t * Find the nearest checkpoint at or before the given line number.\n\t *\n\t * Uses linear scan (checkpoints are sorted by lineNumber and the list\n\t * is short, typically < 20 for Obsidian documents). Can be upgraded\n\t * to binary search if needed for documents with 1000+ variable defs.\n\t *\n\t * @returns The nearest checkpoint, or null if none exists before the line.\n\t */\n\tgetNearestCheckpoint(lineNumber: number): VMCheckpoint | null {\n\t\tlet result: VMCheckpoint | null = null;\n\t\tfor (const cp of this.checkpoints) {\n\t\t\tif (cp.lineNumber <= lineNumber) {\n\t\t\t\tresult = cp;\n\t\t\t} else {\n\t\t\t\tbreak; // checkpoints are sorted ascending\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get a specific checkpoint by its line number.\n\t * @returns The checkpoint, or undefined if not found.\n\t */\n\tgetCheckpointAt(lineNumber: number): VMCheckpoint | undefined {\n\t\treturn this.checkpoints.find((cp) => cp.lineNumber === lineNumber);\n\t}\n\n\t/**\n\t * Get the entire checkpoint chain from root to the last checkpoint.\n\t * Useful for debugging and serialization.\n\t */\n\tgetAllCheckpoints(): readonly VMCheckpoint[] {\n\t\treturn this.checkpoints;\n\t}\n\n\t/**\n\t * Look up a variable's value through the checkpoint chain.\n\t *\n\t * Walks the prototype chain starting from the most recent checkpoint,\n\t * looking for the variable name as an own property. This is O(depth)\n\t * where depth is the number of checkpoints since the variable was\n\t * last set.\n\t *\n\t * **Note:** This queries the checkpointer's snapshot, not the VM.\n\t * The VM may have been modified since the last snapshot (e.g., by\n\t * Tier 2 execution of non-variable-def lines that don't create checkpoints).\n\t *\n\t * @returns The Value, or undefined if the variable was never set.\n\t */\n\tlookupVariable(name: string): Value | undefined {\n\t\tif (this.checkpoints.length === 0) return undefined;\n\n\t\tconst latest = this.checkpoints[this.checkpoints.length - 1];\n\t\tlet scope: Record<string, Value> | null = latest.variables;\n\t\twhile (scope) {\n\t\t\tif (Object.prototype.hasOwnProperty.call(scope, name)) {\n\t\t\t\treturn scope[name];\n\t\t\t}\n\t\t\tscope = Object.getPrototypeOf(scope) as Record<string, Value> | null;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// ── Lifecycle ────────────────────────────────────────────────────\n\n\t/**\n\t * Clear all checkpoints. The underlying VM is NOT reset, call\n\t * `vm.reset()` separately if needed.\n\t */\n\tclear(): void {\n\t\tthis.checkpoints = [];\n\t}\n\n\t/** Number of checkpoints stored. */\n\tget count(): number {\n\t\treturn this.checkpoints.length;\n\t}\n\n\t/** Returns true if no checkpoints have been created. */\n\tget isEmpty(): boolean {\n\t\treturn this.checkpoints.length === 0;\n\t}\n\n\t/** The associated VM instance. */\n\tget vmInstance(): VM {\n\t\treturn this.vm;\n\t}\n}\n"]}
|