solve-engine 2.38.13 → 2.38.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/{PackageCompatibility-Cu_bHpwY.d.cts → PackageCompatibility-ByCnkoxf.d.cts} +1 -1
  2. package/dist/{PackageCompatibility-BILTZSHP.d.ts → PackageCompatibility-WzScL1Av.d.ts} +1 -1
  3. package/dist/{PackageRegistry-DMKDsYiA.d.ts → PackageRegistry-11ThI3Qg.d.ts} +36 -1
  4. package/dist/{PackageRegistry-wZgm2zzk.d.cts → PackageRegistry-BN4zOado.d.cts} +36 -1
  5. package/dist/{VMBuiltins-D3rXBm2k.d.cts → VMBuiltins-5ATHQ045.d.cts} +203 -1
  6. package/dist/{VMBuiltins-Dd0KZR4G.d.ts → VMBuiltins-BbNZtVJc.d.ts} +203 -1
  7. package/dist/{chunk-CW623SJ7.cjs → chunk-54MEZ574.cjs} +2 -2
  8. package/dist/{chunk-CW623SJ7.cjs.map → chunk-54MEZ574.cjs.map} +1 -1
  9. package/dist/{chunk-PN6S4SW3.js → chunk-5M4F7PCU.js} +2 -2
  10. package/dist/{chunk-PN6S4SW3.js.map → chunk-5M4F7PCU.js.map} +1 -1
  11. package/dist/chunk-CH6NLOVG.js +2 -0
  12. package/dist/chunk-CH6NLOVG.js.map +1 -0
  13. package/dist/{chunk-3X2DR2QH.js → chunk-NGHH4QKV.js} +4 -4
  14. package/dist/chunk-NGHH4QKV.js.map +1 -0
  15. package/dist/chunk-OXZWX5WY.cjs +2 -0
  16. package/dist/chunk-OXZWX5WY.cjs.map +1 -0
  17. package/dist/chunk-UM5A6VXI.js +3 -0
  18. package/dist/chunk-UM5A6VXI.js.map +1 -0
  19. package/dist/{chunk-QCH4JXSZ.cjs → chunk-V52TAWYV.cjs} +4 -4
  20. package/dist/chunk-V52TAWYV.cjs.map +1 -0
  21. package/dist/chunk-VR35R5MV.cjs +3 -0
  22. package/dist/chunk-VR35R5MV.cjs.map +1 -0
  23. package/dist/constants.cjs +1 -1
  24. package/dist/constants.js +1 -1
  25. package/dist/engine.cjs +1 -1
  26. package/dist/engine.d.cts +4 -4
  27. package/dist/engine.d.ts +4 -4
  28. package/dist/engine.js +1 -1
  29. package/dist/engine.worker.cjs +1 -1
  30. package/dist/engine.worker.js +1 -1
  31. package/dist/index.cjs +1 -1
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +4 -4
  34. package/dist/index.d.ts +4 -4
  35. package/dist/index.js +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/language.d.cts +3 -3
  38. package/dist/language.d.ts +3 -3
  39. package/dist/packages.d.cts +2 -2
  40. package/dist/packages.d.ts +2 -2
  41. package/dist/testing.cjs +2 -2
  42. package/dist/testing.d.cts +3 -3
  43. package/dist/testing.d.ts +3 -3
  44. package/dist/testing.js +1 -1
  45. package/dist/vm.cjs +1 -1
  46. package/dist/vm.cjs.map +1 -1
  47. package/dist/vm.d.cts +1 -2
  48. package/dist/vm.d.ts +1 -2
  49. package/dist/vm.js +1 -1
  50. package/dist/vm.js.map +1 -1
  51. package/dist/worker.cjs +2 -2
  52. package/dist/worker.d.cts +2 -2
  53. package/dist/worker.d.ts +2 -2
  54. package/dist/worker.js +1 -1
  55. package/package.json +1 -1
  56. package/dist/VMCheckpoints-BXavssWa.d.ts +0 -176
  57. package/dist/VMCheckpoints-BwLCnV8C.d.cts +0 -176
  58. package/dist/chunk-3X2DR2QH.js.map +0 -1
  59. package/dist/chunk-QCH4JXSZ.cjs.map +0 -1
  60. package/dist/chunk-SWONJJK3.js +0 -3
  61. package/dist/chunk-SWONJJK3.js.map +0 -1
  62. package/dist/chunk-WLY2OSWU.cjs +0 -3
  63. package/dist/chunk-WLY2OSWU.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { I as IEnginePackage } from './PackageRegistry-wZgm2zzk.cjs';
1
+ import { I as IEnginePackage } from './PackageRegistry-BN4zOado.cjs';
2
2
 
3
3
  /**
4
4
  * Package load-time compatibility checking, the "detect overlapping
@@ -1,4 +1,4 @@
1
- import { I as IEnginePackage } from './PackageRegistry-DMKDsYiA.js';
1
+ import { I as IEnginePackage } from './PackageRegistry-11ThI3Qg.js';
2
2
 
3
3
  /**
4
4
  * Package load-time compatibility checking, the "detect overlapping
@@ -2,7 +2,7 @@ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from '
2
2
  import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-DPn5dfCH.js';
3
3
  import { IAsyncResolver } from './resolvers.js';
4
4
  import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-DAzPp-j1.js';
5
- import { D as DependencyGraph, V as VM, f as DagSnapshot, g as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-Dd0KZR4G.js';
5
+ import { b as VMCheckpointer, D as DependencyGraph, V as VM, h as DagSnapshot, i as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-BbNZtVJc.js';
6
6
  import { V as Value, b as ValueType, D as DatetimeGrain } from './Value-B55Hvn3e.js';
7
7
  import { a as BytecodeProgram } from './BytecodeBuilder-DSWKZi4f.js';
8
8
  import { C as CalendarBackend } from './CalendarBackend-MdS3Bb4P.js';
@@ -842,6 +842,23 @@ declare class AsyncResolutionBatcher {
842
842
  * not register async resolvers at all.
843
843
  */
844
844
  onLineResult: ((lineNumber: number, value: Value) => void) | null;
845
+ /**
846
+ * The checkpoint chain to rebuild variable state from, when there is one.
847
+ *
848
+ * A re-run reads whatever the VM currently holds, which after a full pass is
849
+ * the state at the END of the document. That is the right answer only for a
850
+ * name written once. `:x = <fetched>` above `:x = 99` left the fetched value
851
+ * standing at every line below the redefinition, because the line that
852
+ * redefines it was not itself affected and so was not re-run: a line reading
853
+ * `x` below it answered with the value from the top of the document.
854
+ *
855
+ * With a chain, the batch is run as a sweep through the document instead:
856
+ * restore to the line before the first affected one, then walk forward,
857
+ * executing the affected lines and applying the recorded bindings of every
858
+ * writing line passed on the way. Null leaves the previous behaviour exactly
859
+ * as it was, which is what a host driving the batcher on its own gets.
860
+ */
861
+ checkpointer: VMCheckpointer | null;
845
862
  /**
846
863
  * Whether {@link warnIfUnwired} has already fired.
847
864
  *
@@ -990,6 +1007,24 @@ declare class AsyncResolutionBatcher {
990
1007
  * learns about it and stops showing a stale Pending state) and the loop
991
1008
  * continues, one line's failure can no longer take out its neighbors.
992
1009
  */
1010
+ /**
1011
+ * Begin a sweep: put the VM into the state the document has just before the
1012
+ * earliest line about to be re-run.
1013
+ *
1014
+ * @param ordered - The lines about to run, in the order they will run.
1015
+ * @returns The sweep's position cursor, or null when there is nothing to do.
1016
+ */
1017
+ private prepareSweep;
1018
+ /**
1019
+ * Advance the sweep to `lineNumber`, applying every checkpoint passed.
1020
+ *
1021
+ * A line between two affected ones is not re-run, but if it defines
1022
+ * something it still governs what the lines below it read, so its recorded
1023
+ * bindings are put back as the sweep goes by. A line the sweep has already
1024
+ * passed is left alone: the batch runs producers before consumers, and going
1025
+ * backwards would undo a value one of them has just written.
1026
+ */
1027
+ private sweepTo;
993
1028
  private reExecuteMainThread;
994
1029
  /**
995
1030
  * Notify all consumers of an async resolution event.
@@ -2,7 +2,7 @@ import { a as PrecedenceParser, b as PrefixParselet, I as InfixParselet } from '
2
2
  import { M as MarkdownLineType, L as Lexer, e as TokenCategory, c as LexerVocabulary } from './Lexer-DaWkCu84.cjs';
3
3
  import { IAsyncResolver } from './resolvers.cjs';
4
4
  import { T as TokenFusion, c as TokenNormalizer, a as NormalizerRule } from './TokenNormalizer-D382VSYh.cjs';
5
- import { D as DependencyGraph, V as VM, f as DagSnapshot, g as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-D3rXBm2k.cjs';
5
+ import { b as VMCheckpointer, D as DependencyGraph, V as VM, h as DagSnapshot, i as EngineContext, S as ScopeManager, P as PluginFunctionHandler, A as AsConverter } from './VMBuiltins-5ATHQ045.cjs';
6
6
  import { V as Value, b as ValueType, D as DatetimeGrain } from './Value-B55Hvn3e.cjs';
7
7
  import { a as BytecodeProgram } from './BytecodeBuilder-DSWKZi4f.cjs';
8
8
  import { C as CalendarBackend } from './CalendarBackend-MdS3Bb4P.cjs';
@@ -842,6 +842,23 @@ declare class AsyncResolutionBatcher {
842
842
  * not register async resolvers at all.
843
843
  */
844
844
  onLineResult: ((lineNumber: number, value: Value) => void) | null;
845
+ /**
846
+ * The checkpoint chain to rebuild variable state from, when there is one.
847
+ *
848
+ * A re-run reads whatever the VM currently holds, which after a full pass is
849
+ * the state at the END of the document. That is the right answer only for a
850
+ * name written once. `:x = <fetched>` above `:x = 99` left the fetched value
851
+ * standing at every line below the redefinition, because the line that
852
+ * redefines it was not itself affected and so was not re-run: a line reading
853
+ * `x` below it answered with the value from the top of the document.
854
+ *
855
+ * With a chain, the batch is run as a sweep through the document instead:
856
+ * restore to the line before the first affected one, then walk forward,
857
+ * executing the affected lines and applying the recorded bindings of every
858
+ * writing line passed on the way. Null leaves the previous behaviour exactly
859
+ * as it was, which is what a host driving the batcher on its own gets.
860
+ */
861
+ checkpointer: VMCheckpointer | null;
845
862
  /**
846
863
  * Whether {@link warnIfUnwired} has already fired.
847
864
  *
@@ -990,6 +1007,24 @@ declare class AsyncResolutionBatcher {
990
1007
  * learns about it and stops showing a stale Pending state) and the loop
991
1008
  * continues, one line's failure can no longer take out its neighbors.
992
1009
  */
1010
+ /**
1011
+ * Begin a sweep: put the VM into the state the document has just before the
1012
+ * earliest line about to be re-run.
1013
+ *
1014
+ * @param ordered - The lines about to run, in the order they will run.
1015
+ * @returns The sweep's position cursor, or null when there is nothing to do.
1016
+ */
1017
+ private prepareSweep;
1018
+ /**
1019
+ * Advance the sweep to `lineNumber`, applying every checkpoint passed.
1020
+ *
1021
+ * A line between two affected ones is not re-run, but if it defines
1022
+ * something it still governs what the lines below it read, so its recorded
1023
+ * bindings are put back as the sweep goes by. A line the sweep has already
1024
+ * passed is left alone: the batch runs producers before consumers, and going
1025
+ * backwards would undo a value one of them has just written.
1026
+ */
1027
+ private sweepTo;
993
1028
  private reExecuteMainThread;
994
1029
  /**
995
1030
  * Notify all consumers of an async resolution event.
@@ -892,6 +892,208 @@ declare class ScopeManager {
892
892
  clear(): void;
893
893
  }
894
894
 
895
+ /**
896
+ * A point-in-time snapshot of VM variable state.
897
+ *
898
+ * Each checkpoint holds only the variables that CHANGED at its line, and
899
+ * reaches the rest through its `parent` link. A lookup walks that chain until
900
+ * it finds the name; a restore walks it from the root applying each link's own
901
+ * bindings in turn, so a later line's value naturally overwrites an earlier
902
+ * one's.
903
+ *
904
+ * ```text
905
+ * Checkpoint 0 (root): {} // empty scope
906
+ * Checkpoint 1 (:x=5): { x: 5 } parent → 0
907
+ * Checkpoint 2 (:y=8): { y: 8 } parent → 1
908
+ * Checkpoint 3 (:x=3): { x: 3 } parent → 2 // shadows x=5
909
+ * ```
910
+ *
911
+ * To look up `x` at checkpoint 3: find own `x=3` → done.
912
+ * To look up `y` at checkpoint 3: not own → walk parent to checkpoint 2 → `y=8`.
913
+ * To look up `z` at checkpoint 3: not found anywhere → undefined.
914
+ *
915
+ * **Memory:** O(number of variable definitions) heap, independent of
916
+ * document length. Typical Obsidian documents have < 100 variable defs,
917
+ * so total checkpoint heap is < 10 KB.
918
+ */
919
+ interface VMCheckpoint {
920
+ /** 1-based line number where this checkpoint was created. */
921
+ lineNumber: number;
922
+ /** Persistent line ID from DocumentModel. */
923
+ lineId: number;
924
+ /**
925
+ * Variable name → Value at this checkpoint.
926
+ * Only the variables set or updated at this line; the rest are reached
927
+ * through {@link VMCheckpoint.parent}.
928
+ */
929
+ variables: Record<string, Value>;
930
+ /**
931
+ * User-defined-function name → definition at this checkpoint. SEPARATE
932
+ * from `variables` above (not prototypally chained the same way
933
+ * `restoreTo()` replays every checkpoint in the chain in order, so a
934
+ * later redefinition of the same function name naturally overwrites an
935
+ * earlier one during replay, without needing its own prototype walk).
936
+ *
937
+ * Without this field, a function definition's checkpoint entry would be
938
+ * SILENTLY LOST: `snapshot()` used to call `vm.getVar(name)` for every
939
+ * written name, which returns `undefined` for a function name (function
940
+ * defs live in `vm.userFunctions`, not the flat variable store), and a
941
+ * `val !== undefined` guard silently skipped it. A scroll-triggered
942
+ * `restoreTo()` would then reset the VM and replay only `variables`,
943
+ * making a function defined above the new viewport vanish (calling it
944
+ * would throw `UNDEFINED_FUNCTION`) even though the document still
945
+ * shows its definition line as clean/cached.
946
+ */
947
+ functions: Record<string, UserFunctionDef>;
948
+ /** Parent checkpoint (closer to document start), or null for root. */
949
+ parent: VMCheckpoint | null;
950
+ }
951
+ /**
952
+ * Manages VM state checkpoints for the three-tier evaluation strategy.
953
+ *
954
+ * **Checkpoint creation:** After a variable-definition line executes
955
+ * (Tier 1 or Tier 3), `snapshot()` records the current values of the
956
+ * written variables. The checkpoint is linked via prototypal inheritance
957
+ * to the previous checkpoint, so only changed variables consume memory.
958
+ *
959
+ * **Checkpoint restoration:** Before evaluating a viewport whose start line
960
+ * is not line 1, `restoreTo(lineNumber)` resets the VM and replays all
961
+ * variable definitions up to and including that line. This avoids
962
+ * re-evaluating the entire document from line 1 on every scroll.
963
+ *
964
+ * **Thread safety:** Checkpoints are created synchronously on the main
965
+ * thread during evaluation. They are immutable after creation (Value is
966
+ * an immutable type), so no synchronization is needed.
967
+ *
968
+ * **Integration with Phase 5.2e:** `setViewport()` will use `getNearestCheckpoint()`
969
+ * to find the checkpoint just before the new viewport start, then call
970
+ * `restoreTo()` to set up the VM before evaluating only the visible lines.
971
+ * This is the key to O(visible lines) scrolling instead of O(document).
972
+ */
973
+ declare class VMCheckpointer {
974
+ /** Ordered array of checkpoints (ascending lineNumber). */
975
+ private checkpoints;
976
+ /** The VM instance whose variables are snapshotted/restored. */
977
+ private vm;
978
+ constructor(vm: VM);
979
+ /**
980
+ * Create a checkpoint at the current line, recording the VM values of
981
+ * the specified variables.
982
+ *
983
+ * Records only what this line wrote; the rest is reached through the
984
+ * parent link rather than copied.
985
+ *
986
+ * A line that is snapshotted again drops every checkpoint at or after it
987
+ * first, so the list stays in document order and the new checkpoint
988
+ * inherits from the line before it rather than from one after it. See the
989
+ * body for what went wrong without that.
990
+ *
991
+ * @param lineNumber 1-based line position.
992
+ * @param lineId Persistent line ID from DocumentModel.
993
+ * @param variableNames Names of variables that were written at this line.
994
+ * @returns The new checkpoint, or null if no variable names provided.
995
+ */
996
+ snapshot(lineNumber: number, lineId: number, variableNames: string[]): VMCheckpoint | null;
997
+ /**
998
+ * Restore the VM to the state at or just after the given line number.
999
+ *
1000
+ * Finds the nearest checkpoint whose `lineNumber <= targetLineNumber`,
1001
+ * then replays all variable definitions from root → that checkpoint
1002
+ * into the VM via `setVar()`. The VM's stack is also reset.
1003
+ *
1004
+ * If no checkpoint exists at or before the target line, the VM is
1005
+ * fully reset (empty scope, empty stack).
1006
+ *
1007
+ * **Performance:** O(total variable definitions before the line), since
1008
+ * each checkpoint in the chain contributes only the names its own line
1009
+ * wrote.
1010
+ *
1011
+ * @param lineNumber Target 1-based line number. The VM will have the
1012
+ * state that existed AFTER evaluating lines up to `lineNumber`.
1013
+ */
1014
+ restoreTo(lineNumber: number): void;
1015
+ /**
1016
+ * Record what `lineNumber` has just written, without disturbing the chain
1017
+ * after it.
1018
+ *
1019
+ * {@link snapshot} drops every checkpoint at or after the line, which is
1020
+ * right for a pass running forward in document order: it re-takes them as it
1021
+ * goes. A caller re-running a few lines out of a document does not, and
1022
+ * dropping the entries for lines it will never visit would lose the very
1023
+ * bindings it is sweeping through them to collect. So this overwrites in
1024
+ * place instead.
1025
+ *
1026
+ * Safe against the prototype chain, because it replaces only the
1027
+ * checkpoint's OWN bindings: a later checkpoint that also writes the name
1028
+ * holds its own copy and goes on shadowing this one.
1029
+ *
1030
+ * @param lineNumber 1-based line whose recorded bindings are refreshed.
1031
+ * @param variableNames The names it wrote.
1032
+ * @returns Whether a checkpoint existed at that line to update.
1033
+ */
1034
+ updateCheckpointAt(lineNumber: number, variableNames: string[]): boolean;
1035
+ /**
1036
+ * Apply the bindings recorded AT `lineNumber`, leaving the rest of the VM
1037
+ * alone.
1038
+ *
1039
+ * {@link restoreTo} rebuilds the whole prefix, which costs the chain every
1040
+ * time it is called. A caller moving forward through the document already
1041
+ * holds the prefix up to the line before, and needs only what this line
1042
+ * added: restoring once and then applying each line in turn as it is passed
1043
+ * costs the chain once rather than once per line.
1044
+ *
1045
+ * @param lineNumber 1-based line whose own bindings are applied.
1046
+ * @returns Whether a checkpoint existed at that line.
1047
+ */
1048
+ applyCheckpointAt(lineNumber: number): boolean;
1049
+ /**
1050
+ * Find the nearest checkpoint at or before the given line number.
1051
+ *
1052
+ * Uses linear scan (checkpoints are sorted by lineNumber and the list
1053
+ * is short, typically < 20 for Obsidian documents). Can be upgraded
1054
+ * to binary search if needed for documents with 1000+ variable defs.
1055
+ *
1056
+ * @returns The nearest checkpoint, or null if none exists before the line.
1057
+ */
1058
+ getNearestCheckpoint(lineNumber: number): VMCheckpoint | null;
1059
+ /**
1060
+ * Get a specific checkpoint by its line number.
1061
+ * @returns The checkpoint, or undefined if not found.
1062
+ */
1063
+ getCheckpointAt(lineNumber: number): VMCheckpoint | undefined;
1064
+ /**
1065
+ * Get the entire checkpoint chain from root to the last checkpoint.
1066
+ * Useful for debugging and serialization.
1067
+ */
1068
+ getAllCheckpoints(): readonly VMCheckpoint[];
1069
+ /**
1070
+ * Look up a variable's value through the checkpoint chain.
1071
+ *
1072
+ * Walks the prototype chain starting from the most recent checkpoint,
1073
+ * looking for the variable name as an own property. This is O(depth)
1074
+ * where depth is the number of checkpoints since the variable was
1075
+ * last set.
1076
+ *
1077
+ * **Note:** This queries the checkpointer's snapshot, not the VM.
1078
+ * The VM may have been modified since the last snapshot (e.g., by
1079
+ * Tier 2 execution of non-variable-def lines that don't create checkpoints).
1080
+ *
1081
+ * @returns The Value, or undefined if the variable was never set.
1082
+ */
1083
+ lookupVariable(name: string): Value | undefined;
1084
+ /**
1085
+ * Clear all checkpoints. The underlying VM is NOT reset, call
1086
+ * `vm.reset()` separately if needed.
1087
+ */
1088
+ clear(): void;
1089
+ /** Number of checkpoints stored. */
1090
+ get count(): number;
1091
+ /** Returns true if no checkpoints have been created. */
1092
+ get isEmpty(): boolean;
1093
+ /** The associated VM instance. */
1094
+ get vmInstance(): VM;
1095
+ }
1096
+
895
1097
  /**
896
1098
  * Registry of built-in mathematical functions.
897
1099
  * Indexed by the number pushed as an operand of OpCode.CALL_BUILTIN.
@@ -951,4 +1153,4 @@ declare function pluginFunctionIndexFor(qualifiedName: string): number;
951
1153
  */
952
1154
  type AsConverter = (value: Value, context?: LineExecutionContext) => Value;
953
1155
 
954
- export { type AsConverter as A, type Bytecode as B, DependencyGraph as D, type ExpressionRecord as E, type LineExecutionContext as L, OpRegistry as O, type PluginFunctionHandler as P, ScopeManager as S, type VM as V, allocatePluginFunctionIndex as a, builtinFunctions as b, createVM as c, pluginFunctionRegistry as d, executeBytecode as e, type DagSnapshot as f, type EngineContext as g, pluginFunctionIndexFor as p, sharedOpRegistry as s };
1156
+ export { type AsConverter as A, type Bytecode as B, DependencyGraph as D, type ExpressionRecord as E, type LineExecutionContext as L, OpRegistry as O, type PluginFunctionHandler as P, ScopeManager as S, type VM as V, type VMCheckpoint as a, VMCheckpointer as b, allocatePluginFunctionIndex as c, builtinFunctions as d, createVM as e, executeBytecode as f, pluginFunctionRegistry as g, type DagSnapshot as h, type EngineContext as i, pluginFunctionIndexFor as p, sharedOpRegistry as s };
@@ -892,6 +892,208 @@ declare class ScopeManager {
892
892
  clear(): void;
893
893
  }
894
894
 
895
+ /**
896
+ * A point-in-time snapshot of VM variable state.
897
+ *
898
+ * Each checkpoint holds only the variables that CHANGED at its line, and
899
+ * reaches the rest through its `parent` link. A lookup walks that chain until
900
+ * it finds the name; a restore walks it from the root applying each link's own
901
+ * bindings in turn, so a later line's value naturally overwrites an earlier
902
+ * one's.
903
+ *
904
+ * ```text
905
+ * Checkpoint 0 (root): {} // empty scope
906
+ * Checkpoint 1 (:x=5): { x: 5 } parent → 0
907
+ * Checkpoint 2 (:y=8): { y: 8 } parent → 1
908
+ * Checkpoint 3 (:x=3): { x: 3 } parent → 2 // shadows x=5
909
+ * ```
910
+ *
911
+ * To look up `x` at checkpoint 3: find own `x=3` → done.
912
+ * To look up `y` at checkpoint 3: not own → walk parent to checkpoint 2 → `y=8`.
913
+ * To look up `z` at checkpoint 3: not found anywhere → undefined.
914
+ *
915
+ * **Memory:** O(number of variable definitions) heap, independent of
916
+ * document length. Typical Obsidian documents have < 100 variable defs,
917
+ * so total checkpoint heap is < 10 KB.
918
+ */
919
+ interface VMCheckpoint {
920
+ /** 1-based line number where this checkpoint was created. */
921
+ lineNumber: number;
922
+ /** Persistent line ID from DocumentModel. */
923
+ lineId: number;
924
+ /**
925
+ * Variable name → Value at this checkpoint.
926
+ * Only the variables set or updated at this line; the rest are reached
927
+ * through {@link VMCheckpoint.parent}.
928
+ */
929
+ variables: Record<string, Value>;
930
+ /**
931
+ * User-defined-function name → definition at this checkpoint. SEPARATE
932
+ * from `variables` above (not prototypally chained the same way
933
+ * `restoreTo()` replays every checkpoint in the chain in order, so a
934
+ * later redefinition of the same function name naturally overwrites an
935
+ * earlier one during replay, without needing its own prototype walk).
936
+ *
937
+ * Without this field, a function definition's checkpoint entry would be
938
+ * SILENTLY LOST: `snapshot()` used to call `vm.getVar(name)` for every
939
+ * written name, which returns `undefined` for a function name (function
940
+ * defs live in `vm.userFunctions`, not the flat variable store), and a
941
+ * `val !== undefined` guard silently skipped it. A scroll-triggered
942
+ * `restoreTo()` would then reset the VM and replay only `variables`,
943
+ * making a function defined above the new viewport vanish (calling it
944
+ * would throw `UNDEFINED_FUNCTION`) even though the document still
945
+ * shows its definition line as clean/cached.
946
+ */
947
+ functions: Record<string, UserFunctionDef>;
948
+ /** Parent checkpoint (closer to document start), or null for root. */
949
+ parent: VMCheckpoint | null;
950
+ }
951
+ /**
952
+ * Manages VM state checkpoints for the three-tier evaluation strategy.
953
+ *
954
+ * **Checkpoint creation:** After a variable-definition line executes
955
+ * (Tier 1 or Tier 3), `snapshot()` records the current values of the
956
+ * written variables. The checkpoint is linked via prototypal inheritance
957
+ * to the previous checkpoint, so only changed variables consume memory.
958
+ *
959
+ * **Checkpoint restoration:** Before evaluating a viewport whose start line
960
+ * is not line 1, `restoreTo(lineNumber)` resets the VM and replays all
961
+ * variable definitions up to and including that line. This avoids
962
+ * re-evaluating the entire document from line 1 on every scroll.
963
+ *
964
+ * **Thread safety:** Checkpoints are created synchronously on the main
965
+ * thread during evaluation. They are immutable after creation (Value is
966
+ * an immutable type), so no synchronization is needed.
967
+ *
968
+ * **Integration with Phase 5.2e:** `setViewport()` will use `getNearestCheckpoint()`
969
+ * to find the checkpoint just before the new viewport start, then call
970
+ * `restoreTo()` to set up the VM before evaluating only the visible lines.
971
+ * This is the key to O(visible lines) scrolling instead of O(document).
972
+ */
973
+ declare class VMCheckpointer {
974
+ /** Ordered array of checkpoints (ascending lineNumber). */
975
+ private checkpoints;
976
+ /** The VM instance whose variables are snapshotted/restored. */
977
+ private vm;
978
+ constructor(vm: VM);
979
+ /**
980
+ * Create a checkpoint at the current line, recording the VM values of
981
+ * the specified variables.
982
+ *
983
+ * Records only what this line wrote; the rest is reached through the
984
+ * parent link rather than copied.
985
+ *
986
+ * A line that is snapshotted again drops every checkpoint at or after it
987
+ * first, so the list stays in document order and the new checkpoint
988
+ * inherits from the line before it rather than from one after it. See the
989
+ * body for what went wrong without that.
990
+ *
991
+ * @param lineNumber 1-based line position.
992
+ * @param lineId Persistent line ID from DocumentModel.
993
+ * @param variableNames Names of variables that were written at this line.
994
+ * @returns The new checkpoint, or null if no variable names provided.
995
+ */
996
+ snapshot(lineNumber: number, lineId: number, variableNames: string[]): VMCheckpoint | null;
997
+ /**
998
+ * Restore the VM to the state at or just after the given line number.
999
+ *
1000
+ * Finds the nearest checkpoint whose `lineNumber <= targetLineNumber`,
1001
+ * then replays all variable definitions from root → that checkpoint
1002
+ * into the VM via `setVar()`. The VM's stack is also reset.
1003
+ *
1004
+ * If no checkpoint exists at or before the target line, the VM is
1005
+ * fully reset (empty scope, empty stack).
1006
+ *
1007
+ * **Performance:** O(total variable definitions before the line), since
1008
+ * each checkpoint in the chain contributes only the names its own line
1009
+ * wrote.
1010
+ *
1011
+ * @param lineNumber Target 1-based line number. The VM will have the
1012
+ * state that existed AFTER evaluating lines up to `lineNumber`.
1013
+ */
1014
+ restoreTo(lineNumber: number): void;
1015
+ /**
1016
+ * Record what `lineNumber` has just written, without disturbing the chain
1017
+ * after it.
1018
+ *
1019
+ * {@link snapshot} drops every checkpoint at or after the line, which is
1020
+ * right for a pass running forward in document order: it re-takes them as it
1021
+ * goes. A caller re-running a few lines out of a document does not, and
1022
+ * dropping the entries for lines it will never visit would lose the very
1023
+ * bindings it is sweeping through them to collect. So this overwrites in
1024
+ * place instead.
1025
+ *
1026
+ * Safe against the prototype chain, because it replaces only the
1027
+ * checkpoint's OWN bindings: a later checkpoint that also writes the name
1028
+ * holds its own copy and goes on shadowing this one.
1029
+ *
1030
+ * @param lineNumber 1-based line whose recorded bindings are refreshed.
1031
+ * @param variableNames The names it wrote.
1032
+ * @returns Whether a checkpoint existed at that line to update.
1033
+ */
1034
+ updateCheckpointAt(lineNumber: number, variableNames: string[]): boolean;
1035
+ /**
1036
+ * Apply the bindings recorded AT `lineNumber`, leaving the rest of the VM
1037
+ * alone.
1038
+ *
1039
+ * {@link restoreTo} rebuilds the whole prefix, which costs the chain every
1040
+ * time it is called. A caller moving forward through the document already
1041
+ * holds the prefix up to the line before, and needs only what this line
1042
+ * added: restoring once and then applying each line in turn as it is passed
1043
+ * costs the chain once rather than once per line.
1044
+ *
1045
+ * @param lineNumber 1-based line whose own bindings are applied.
1046
+ * @returns Whether a checkpoint existed at that line.
1047
+ */
1048
+ applyCheckpointAt(lineNumber: number): boolean;
1049
+ /**
1050
+ * Find the nearest checkpoint at or before the given line number.
1051
+ *
1052
+ * Uses linear scan (checkpoints are sorted by lineNumber and the list
1053
+ * is short, typically < 20 for Obsidian documents). Can be upgraded
1054
+ * to binary search if needed for documents with 1000+ variable defs.
1055
+ *
1056
+ * @returns The nearest checkpoint, or null if none exists before the line.
1057
+ */
1058
+ getNearestCheckpoint(lineNumber: number): VMCheckpoint | null;
1059
+ /**
1060
+ * Get a specific checkpoint by its line number.
1061
+ * @returns The checkpoint, or undefined if not found.
1062
+ */
1063
+ getCheckpointAt(lineNumber: number): VMCheckpoint | undefined;
1064
+ /**
1065
+ * Get the entire checkpoint chain from root to the last checkpoint.
1066
+ * Useful for debugging and serialization.
1067
+ */
1068
+ getAllCheckpoints(): readonly VMCheckpoint[];
1069
+ /**
1070
+ * Look up a variable's value through the checkpoint chain.
1071
+ *
1072
+ * Walks the prototype chain starting from the most recent checkpoint,
1073
+ * looking for the variable name as an own property. This is O(depth)
1074
+ * where depth is the number of checkpoints since the variable was
1075
+ * last set.
1076
+ *
1077
+ * **Note:** This queries the checkpointer's snapshot, not the VM.
1078
+ * The VM may have been modified since the last snapshot (e.g., by
1079
+ * Tier 2 execution of non-variable-def lines that don't create checkpoints).
1080
+ *
1081
+ * @returns The Value, or undefined if the variable was never set.
1082
+ */
1083
+ lookupVariable(name: string): Value | undefined;
1084
+ /**
1085
+ * Clear all checkpoints. The underlying VM is NOT reset, call
1086
+ * `vm.reset()` separately if needed.
1087
+ */
1088
+ clear(): void;
1089
+ /** Number of checkpoints stored. */
1090
+ get count(): number;
1091
+ /** Returns true if no checkpoints have been created. */
1092
+ get isEmpty(): boolean;
1093
+ /** The associated VM instance. */
1094
+ get vmInstance(): VM;
1095
+ }
1096
+
895
1097
  /**
896
1098
  * Registry of built-in mathematical functions.
897
1099
  * Indexed by the number pushed as an operand of OpCode.CALL_BUILTIN.
@@ -951,4 +1153,4 @@ declare function pluginFunctionIndexFor(qualifiedName: string): number;
951
1153
  */
952
1154
  type AsConverter = (value: Value, context?: LineExecutionContext) => Value;
953
1155
 
954
- export { type AsConverter as A, type Bytecode as B, DependencyGraph as D, type ExpressionRecord as E, type LineExecutionContext as L, OpRegistry as O, type PluginFunctionHandler as P, ScopeManager as S, type VM as V, allocatePluginFunctionIndex as a, builtinFunctions as b, createVM as c, pluginFunctionRegistry as d, executeBytecode as e, type DagSnapshot as f, type EngineContext as g, pluginFunctionIndexFor as p, sharedOpRegistry as s };
1156
+ export { type AsConverter as A, type Bytecode as B, DependencyGraph as D, type ExpressionRecord as E, type LineExecutionContext as L, OpRegistry as O, type PluginFunctionHandler as P, ScopeManager as S, type VM as V, type VMCheckpoint as a, VMCheckpointer as b, allocatePluginFunctionIndex as c, builtinFunctions as d, createVM as e, executeBytecode as f, pluginFunctionRegistry as g, type DagSnapshot as h, type EngineContext as i, pluginFunctionIndexFor as p, sharedOpRegistry as s };
@@ -1,2 +1,2 @@
1
- 'use strict';var chunk6SYU4MH7_cjs=require('./chunk-6SYU4MH7.cjs');var d="2.38.13";var m=d;var l={date:{maxOffsetYears:100,minOffsetYears:-100,defaultFormat:"YYYY-MM-DD",inputOrder:"auto",onAmbiguous:"refuse"},performance:{defaultCacheSize:2e3,maxDocumentLines:1e5},validation:{maxExpressionLength:2e3,maxComplexity:500,maxNestingDepth:50,autoBalanceParens:false},vm:{maxStackDepth:200,maxInstructions:5e4,maxCollectionSize:1e5,maxAllocatedElements:2e6,maxFunctionCalls:1e4,maxGoalSeekIterations:100},worker:{maxConcurrentWorkers:4,idleTimeoutMs:3e4,maxRetries:3,baseBackoffMs:1e3},diagnostic:{enabled:false,vmTraceEnabled:false},backgroundRefresh:{enabled:false},network:{enabled:true}};function n(s,e){return {date:{...s.date,...e.date},performance:{...s.performance,...e.performance},validation:{...s.validation,...e.validation},vm:{...s.vm,...e.vm},worker:{...s.worker,...e.worker},diagnostic:{...s.diagnostic,...e.diagnostic},backgroundRefresh:{...s.backgroundRefresh,...e.backgroundRefresh},network:{...s.network,...e.network}}}var c=class{constructor(e={}){this.config=n(l,e);}get(e){let o=e.split("."),t=this.config;for(let i of o)if(t&&typeof t=="object"&&i in t)t=t[i];else throw chunk6SYU4MH7_cjs.c.config("CONFIG_PATH_NOT_FOUND",`Configuration path not found: ${e}`,{path:e});return t}set(e,o){let t=e.split(".");if(t.length<2)throw chunk6SYU4MH7_cjs.c.config("INVALID_CONFIG_PATH",`Invalid path: ${e}. Must be in format 'section.property'`,{path:e});let i=t[0],u=t[1];if(!(i in this.config))throw chunk6SYU4MH7_cjs.c.config("CONFIG_SECTION_NOT_FOUND",`Configuration section not found: ${i}`,{section:i,path:e});let a=this.config[i];if(a&&typeof a=="object")a[u]=o;else throw chunk6SYU4MH7_cjs.c.config("CONFIG_PROPERTY_NOT_FOUND",`Configuration property not found: ${e}`,{path:e})}getConfig(){return n(this.config,{})}update(e){this.config=n(this.config,e);}reset(){this.config=n(l,{});}validate(){let e=[];return this.config.performance.maxDocumentLines>1e5&&e.push("maxDocumentLines cannot exceed 100,000"),this.config.date.maxOffsetYears>1e3&&e.push("maxOffsetYears cannot exceed 1000"),{valid:e.length===0,error:e.join("; "),warnings:[]}}};exports.a=m;exports.b=l;exports.c=n;exports.d=c;//# sourceMappingURL=chunk-CW623SJ7.cjs.map
2
- //# sourceMappingURL=chunk-CW623SJ7.cjs.map
1
+ 'use strict';var chunk6SYU4MH7_cjs=require('./chunk-6SYU4MH7.cjs');var d="2.38.14";var m=d;var l={date:{maxOffsetYears:100,minOffsetYears:-100,defaultFormat:"YYYY-MM-DD",inputOrder:"auto",onAmbiguous:"refuse"},performance:{defaultCacheSize:2e3,maxDocumentLines:1e5},validation:{maxExpressionLength:2e3,maxComplexity:500,maxNestingDepth:50,autoBalanceParens:false},vm:{maxStackDepth:200,maxInstructions:5e4,maxCollectionSize:1e5,maxAllocatedElements:2e6,maxFunctionCalls:1e4,maxGoalSeekIterations:100},worker:{maxConcurrentWorkers:4,idleTimeoutMs:3e4,maxRetries:3,baseBackoffMs:1e3},diagnostic:{enabled:false,vmTraceEnabled:false},backgroundRefresh:{enabled:false},network:{enabled:true}};function n(s,e){return {date:{...s.date,...e.date},performance:{...s.performance,...e.performance},validation:{...s.validation,...e.validation},vm:{...s.vm,...e.vm},worker:{...s.worker,...e.worker},diagnostic:{...s.diagnostic,...e.diagnostic},backgroundRefresh:{...s.backgroundRefresh,...e.backgroundRefresh},network:{...s.network,...e.network}}}var c=class{constructor(e={}){this.config=n(l,e);}get(e){let o=e.split("."),t=this.config;for(let i of o)if(t&&typeof t=="object"&&i in t)t=t[i];else throw chunk6SYU4MH7_cjs.c.config("CONFIG_PATH_NOT_FOUND",`Configuration path not found: ${e}`,{path:e});return t}set(e,o){let t=e.split(".");if(t.length<2)throw chunk6SYU4MH7_cjs.c.config("INVALID_CONFIG_PATH",`Invalid path: ${e}. Must be in format 'section.property'`,{path:e});let i=t[0],u=t[1];if(!(i in this.config))throw chunk6SYU4MH7_cjs.c.config("CONFIG_SECTION_NOT_FOUND",`Configuration section not found: ${i}`,{section:i,path:e});let a=this.config[i];if(a&&typeof a=="object")a[u]=o;else throw chunk6SYU4MH7_cjs.c.config("CONFIG_PROPERTY_NOT_FOUND",`Configuration property not found: ${e}`,{path:e})}getConfig(){return n(this.config,{})}update(e){this.config=n(this.config,e);}reset(){this.config=n(l,{});}validate(){let e=[];return this.config.performance.maxDocumentLines>1e5&&e.push("maxDocumentLines cannot exceed 100,000"),this.config.date.maxOffsetYears>1e3&&e.push("maxOffsetYears cannot exceed 1000"),{valid:e.length===0,error:e.join("; "),warnings:[]}}};exports.a=m;exports.b=l;exports.c=n;exports.d=c;//# sourceMappingURL=chunk-54MEZ574.cjs.map
2
+ //# sourceMappingURL=chunk-54MEZ574.cjs.map