footprintjs 9.32.0 → 9.34.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.
Files changed (124) hide show
  1. package/AGENTS.md +670 -194
  2. package/dist/esm/lib/capture/freeze.d.ts +47 -0
  3. package/dist/esm/lib/capture/freeze.js +60 -0
  4. package/dist/esm/lib/capture/index.d.ts +1 -0
  5. package/dist/esm/lib/capture/index.js +2 -1
  6. package/dist/esm/lib/detach/drivers/immediate.d.ts +25 -24
  7. package/dist/esm/lib/detach/drivers/immediate.js +26 -25
  8. package/dist/esm/lib/detach/drivers/microtaskBatch.d.ts +5 -4
  9. package/dist/esm/lib/detach/drivers/microtaskBatch.js +6 -5
  10. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +27 -7
  11. package/dist/esm/lib/engine/handlers/SubflowInputMapper.d.ts +4 -1
  12. package/dist/esm/lib/engine/handlers/SubflowInputMapper.js +15 -2
  13. package/dist/esm/lib/memory/EventLog.d.ts +28 -3
  14. package/dist/esm/lib/memory/EventLog.js +31 -4
  15. package/dist/esm/lib/memory/StageContext.d.ts +20 -0
  16. package/dist/esm/lib/memory/StageContext.js +36 -10
  17. package/dist/esm/lib/memory/TransactionBuffer.d.ts +2 -2
  18. package/dist/esm/lib/memory/TransactionBuffer.js +6 -4
  19. package/dist/esm/lib/memory/backtrack.d.ts +24 -0
  20. package/dist/esm/lib/memory/backtrack.js +52 -41
  21. package/dist/esm/lib/memory/commitLogUtils.d.ts +129 -38
  22. package/dist/esm/lib/memory/commitLogUtils.js +252 -59
  23. package/dist/esm/lib/memory/deltaEncoding.d.ts +1 -1
  24. package/dist/esm/lib/memory/deltaEncoding.js +2 -2
  25. package/dist/esm/lib/memory/honesty.d.ts +5 -1
  26. package/dist/esm/lib/memory/honesty.js +11 -6
  27. package/dist/esm/lib/memory/index.d.ts +2 -2
  28. package/dist/esm/lib/memory/index.js +3 -3
  29. package/dist/esm/lib/memory/keyPaths.d.ts +125 -0
  30. package/dist/esm/lib/memory/keyPaths.js +227 -0
  31. package/dist/esm/lib/memory/logModel.d.ts +76 -0
  32. package/dist/esm/lib/memory/logModel.js +357 -0
  33. package/dist/esm/lib/memory/placeholders.d.ts +3 -3
  34. package/dist/esm/lib/memory/placeholders.js +4 -4
  35. package/dist/esm/lib/memory/redaction.d.ts +34 -1
  36. package/dist/esm/lib/memory/redaction.js +55 -4
  37. package/dist/esm/lib/memory/utils.d.ts +2 -6
  38. package/dist/esm/lib/memory/utils.js +4 -21
  39. package/dist/esm/lib/memory/verbs.d.ts +65 -22
  40. package/dist/esm/lib/memory/verbs.js +64 -23
  41. package/dist/esm/lib/runner/ExecutionRuntime.js +5 -3
  42. package/dist/esm/lib/runner/FlowChartExecutor.js +5 -5
  43. package/dist/esm/lib/scope/protection/readonlyInput.d.ts +2 -6
  44. package/dist/esm/lib/scope/protection/readonlyInput.js +4 -18
  45. package/dist/esm/lib/slice/elementProvenance.d.ts +4 -1
  46. package/dist/esm/lib/slice/elementProvenance.js +33 -15
  47. package/dist/esm/lib/slice/forwardSliceForKey.d.ts +4 -0
  48. package/dist/esm/lib/slice/forwardSliceForKey.js +32 -9
  49. package/dist/esm/lib/slice/keyIndex.d.ts +50 -8
  50. package/dist/esm/lib/slice/keyIndex.js +97 -16
  51. package/dist/esm/lib/slice/keyTimeline.js +11 -6
  52. package/dist/esm/lib/slice/serialize.js +8 -2
  53. package/dist/esm/lib/slice/sliceForKey.js +22 -6
  54. package/dist/esm/lib/slice/types.d.ts +53 -7
  55. package/dist/esm/lib/slice/types.js +1 -1
  56. package/dist/esm/lib/time-travel/commitStops.js +46 -8
  57. package/dist/esm/lib/time-travel/stateAt.js +4 -14
  58. package/dist/esm/lib/time-travel/timeTravel.js +2 -2
  59. package/dist/esm/lib/time-travel/types.d.ts +4 -3
  60. package/dist/esm/lib/time-travel/types.js +1 -1
  61. package/dist/esm/trace.d.ts +2 -1
  62. package/dist/esm/trace.js +2 -6
  63. package/dist/lib/capture/freeze.js +64 -0
  64. package/dist/lib/capture/index.js +4 -2
  65. package/dist/lib/detach/drivers/immediate.js +26 -25
  66. package/dist/lib/detach/drivers/microtaskBatch.js +6 -5
  67. package/dist/lib/engine/handlers/SubflowExecutor.js +27 -7
  68. package/dist/lib/engine/handlers/SubflowInputMapper.js +15 -2
  69. package/dist/lib/memory/EventLog.js +31 -4
  70. package/dist/lib/memory/StageContext.js +35 -9
  71. package/dist/lib/memory/TransactionBuffer.js +6 -4
  72. package/dist/lib/memory/backtrack.js +52 -41
  73. package/dist/lib/memory/commitLogUtils.js +257 -61
  74. package/dist/lib/memory/deltaEncoding.js +2 -2
  75. package/dist/lib/memory/honesty.js +11 -6
  76. package/dist/lib/memory/index.js +3 -3
  77. package/dist/lib/memory/keyPaths.js +243 -0
  78. package/dist/lib/memory/logModel.js +365 -0
  79. package/dist/lib/memory/placeholders.js +4 -4
  80. package/dist/lib/memory/redaction.js +56 -3
  81. package/dist/lib/memory/utils.js +8 -26
  82. package/dist/lib/memory/verbs.js +64 -23
  83. package/dist/lib/runner/ExecutionRuntime.js +6 -4
  84. package/dist/lib/runner/FlowChartExecutor.js +6 -6
  85. package/dist/lib/scope/protection/readonlyInput.js +6 -21
  86. package/dist/lib/slice/elementProvenance.js +32 -14
  87. package/dist/lib/slice/forwardSliceForKey.js +31 -8
  88. package/dist/lib/slice/keyIndex.js +103 -17
  89. package/dist/lib/slice/keyTimeline.js +10 -5
  90. package/dist/lib/slice/serialize.js +8 -2
  91. package/dist/lib/slice/sliceForKey.js +21 -5
  92. package/dist/lib/slice/types.js +1 -1
  93. package/dist/lib/time-travel/commitStops.js +46 -8
  94. package/dist/lib/time-travel/stateAt.js +4 -14
  95. package/dist/lib/time-travel/timeTravel.js +2 -2
  96. package/dist/lib/time-travel/types.js +1 -1
  97. package/dist/trace.js +5 -7
  98. package/dist/types/lib/capture/freeze.d.ts +47 -0
  99. package/dist/types/lib/capture/index.d.ts +1 -0
  100. package/dist/types/lib/detach/drivers/immediate.d.ts +25 -24
  101. package/dist/types/lib/detach/drivers/microtaskBatch.d.ts +5 -4
  102. package/dist/types/lib/engine/handlers/SubflowInputMapper.d.ts +4 -1
  103. package/dist/types/lib/memory/EventLog.d.ts +28 -3
  104. package/dist/types/lib/memory/StageContext.d.ts +20 -0
  105. package/dist/types/lib/memory/TransactionBuffer.d.ts +2 -2
  106. package/dist/types/lib/memory/backtrack.d.ts +24 -0
  107. package/dist/types/lib/memory/commitLogUtils.d.ts +129 -38
  108. package/dist/types/lib/memory/deltaEncoding.d.ts +1 -1
  109. package/dist/types/lib/memory/honesty.d.ts +5 -1
  110. package/dist/types/lib/memory/index.d.ts +2 -2
  111. package/dist/types/lib/memory/keyPaths.d.ts +125 -0
  112. package/dist/types/lib/memory/logModel.d.ts +76 -0
  113. package/dist/types/lib/memory/placeholders.d.ts +3 -3
  114. package/dist/types/lib/memory/redaction.d.ts +34 -1
  115. package/dist/types/lib/memory/utils.d.ts +2 -6
  116. package/dist/types/lib/memory/verbs.d.ts +65 -22
  117. package/dist/types/lib/scope/protection/readonlyInput.d.ts +2 -6
  118. package/dist/types/lib/slice/elementProvenance.d.ts +4 -1
  119. package/dist/types/lib/slice/forwardSliceForKey.d.ts +4 -0
  120. package/dist/types/lib/slice/keyIndex.d.ts +50 -8
  121. package/dist/types/lib/slice/types.d.ts +53 -7
  122. package/dist/types/lib/time-travel/types.d.ts +4 -3
  123. package/dist/types/trace.d.ts +2 -1
  124. package/package.json +2 -1
@@ -4,62 +4,153 @@
4
4
  * The commitLog is an ordered array of CommitBundle — one per stage commit.
5
5
  * These helpers provide type-safe queries without (b: any) casts.
6
6
  */
7
+ import type { RegisteredCode } from './honesty.js';
7
8
  import type { CommitBundle } from './types.js';
8
9
  import { type Touch } from './verbs.js';
9
- /** Find the first commit by stageId, optionally filtering by a written key. */
10
+ /**
11
+ * Find the first commit by `stageId`, optionally the first one that WROTE `key` — under the writer
12
+ * rule: a row on the key, inside it, or around it that changed it (see {@link writersOf}).
13
+ */
10
14
  export declare function findCommit(commitLog: CommitBundle[], stageId: string, key?: string): CommitBundle | undefined;
11
15
  /** Find all commits by stageId. */
12
16
  export declare function findCommits(commitLog: CommitBundle[], stageId: string): CommitBundle[];
13
- /** Find the last commit that wrote a specific key (for backtracking). */
14
- export declare function findLastWriter(commitLog: CommitBundle[], key: string, beforeIdx?: number): CommitBundle | undefined;
15
17
  /**
16
- * Every row on `key` in `commitLog[0..end]`, in commit order — the touches a
17
- * per-path fold ({@link foldKey}) runs over. `key` is matched against
18
- * `TraceEntry.path` exactly (DELIM-joined for nested paths). A row whose verb
19
- * is not one of the four is refused with {@link UnknownVerbError} naming the
20
- * row — whether or not the fold would have reached it: the answer must not
21
- * depend on where an optimisation starts.
18
+ * Find the last commit that WROTE `key` before `beforeIdx` (exclusive; default: the whole log) —
19
+ * the anchor of every backward question ("who made it this?").
22
20
  *
23
- * Internal: `commitValueAt` and `arrayProvenance` are its two readers.
21
+ * The writer rule (`keyPaths.ts`): a commit writes `key` when one of its rows is ON the key,
22
+ * INSIDE it (a subflow's input seed writes `cfg␟a`; an outputMapper merge-back writes `cfg␟b`;
23
+ * either is a write of `cfg`), or AROUND it with the value at the key different across the commit
24
+ * (a `set` of `cfg` writes `cfg␟a`; a `merge` of `cfg` that never reaches `a` does not). A write
25
+ * found through a row INSIDE the key wrote only PART of its value — the slice readers say so with
26
+ * the `'nested-rows'` note.
27
+ *
28
+ * @param key A DELIM-joined path (`normalisePath`); a top-level key is a one-segment path.
29
+ */
30
+ export declare function findLastWriter(commitLog: CommitBundle[], key: string, beforeIdx?: number): CommitBundle | undefined;
31
+ /** What {@link writersOf} is asked. */
32
+ export interface WritersOptions {
33
+ /** The last commit ARRAY index considered, inclusive. Default: the whole log. */
34
+ readonly end?: number;
35
+ }
36
+ /**
37
+ * The ARRAY positions (ascending) of every commit in `commitLog[0..end]` that WROTE `key` — the
38
+ * writer rule of `keyPaths.ts`, the one definition every key query shares (`logModel.ts` applies it).
39
+ */
40
+ export declare function writersOf(commitLog: readonly CommitBundle[], key: string, options?: WritersOptions): number[];
41
+ /**
42
+ * Every row under `key`'s TOP-LEVEL key in `commitLog[0..end]`, in commit order — the rows the value
43
+ * rule folds ({@link foldKey}), each with its {@link Touch.relation} to `key` (absent for a sibling
44
+ * under the same top-level key: applied, not observed) — found through the log's index. A row whose
45
+ * verb is not one of the four is refused with {@link UnknownVerbError} naming the row — whether or
46
+ * not the fold would have reached it: the answer must not depend on where an optimisation starts.
24
47
  */
25
- export declare function keyTouches(commitLog: readonly CommitBundle[], key: string, end: number): Touch[];
48
+ export declare function rowsUnderRoot(commitLog: readonly CommitBundle[], key: string, end: number): Touch[];
26
49
  /**
27
50
  * Reconstruct the FULL value of `key` as of commit array index `idx`
28
51
  * (inclusive) — the migration helper for the "read `bundle.overwrite[key]`
29
52
  * as the full value written" pattern (#13c-B).
30
53
  *
31
- * Under `commitValues: 'delta'`, an `append` bundle's `overwrite[key]` holds
32
- * only the TAIL of the array; this helper folds the verbs back together:
33
- * it scans `commitLog[0..idx]` for trace entries on `key`, anchors at the
34
- * latest full-value write (`set` — or `delete`, which resets to absent), and
35
- * folds forward with the SAME step and clone discipline the replay uses
36
- * (`verbs.ts` · `foldKey`) — the per-key slice of `applySmartMerge`'s replay,
37
- * O(key's commit span) instead of a full `materialise()`. Two `merge` rows of
38
- * one bundle see one copy of the bundle's delta, as the replay's do (a clone per
39
- * row, as this helper once took, made an array union — which deduplicates by
40
- * reference — duplicate the elements the first row had placed).
54
+ * The value rule (`keyPaths.ts`, 9.33.0): every row under the key's TOP-LEVEL
55
+ * key in `commitLog[0..idx]` is folded, anchored at that key's last `set` /
56
+ * `delete`, with the SAME step and clone discipline the replay uses
57
+ * (`verbs.ts` · `foldKey`), and the result is read at `key`. So the answer is
58
+ * the value `stateAt` gives at that path when it folds the log alone — rows
59
+ * INSIDE the key (a subflow seed, an outputMapper merge-back) and AROUND it (a
60
+ * `set` of the container) count; before 9.33.0 only rows on the exact path did,
61
+ * and a seeded `cfg` read as `undefined`. Under `commitValues: 'delta'`, an
62
+ * `append` row's `overwrite[key]` holds only the TAIL; the fold puts the array
63
+ * back together. Two `merge` rows of one bundle see one copy of the bundle's
64
+ * delta, as the replay's do.
41
65
  *
42
- * Works on full-mode logs too (every `set` is its own anchor — equivalent to
43
- * `findLastWriter(...).overwrite[key]`).
44
- *
45
- * @param key Matched against `TraceEntry.path` exactly (same contract as
46
- * `findLastWriter`) — DELIM-joined for nested paths.
66
+ * @param key A DELIM-joined path (`normalisePath`); a top-level key is a
67
+ * one-segment path.
47
68
  * @param idx CommitBundle ARRAY index (the `bundle.idx` position),
48
69
  * inclusive. NOT the executionIndex from a runtimeStageId.
49
- * @returns The reconstructed value (a detached clone), or `undefined` when
50
- * the key was never written in `commitLog[0..idx]` or its last write was a
51
- * delete. Caveat: values derived purely from the run's INITIAL state (no
52
- * `set` anchor in the log — e.g. merges onto a seeded key) fold from
53
- * absent; the commit log alone cannot see the pre-run base (the same blind
54
- * spot `findLastWriter` has). Since 9.17.0 the base TRAVELS with the log, so
55
- * `stateAt(snapshot, idx).state[key]` (footprintjs/trace) answers this case
56
- * correctly — it folds from `RuntimeSnapshot.initialState`, which this
57
- * function never receives.
58
- * @throws {@link UnknownVerbError} when a row on `key` carries a verb other
59
- * than `set | merge | append | delete` — a foreign or corrupted log is
60
- * refused, not folded as a `merge`. Engine-written logs never carry one.
70
+ * @returns The reconstructed value (a detached clone), or `undefined` when no
71
+ * row in `commitLog[0..idx]` is on, inside or around the key, or the fold
72
+ * leaves it absent (its last write was a delete). Caveat: a key that held a
73
+ * value in the run's INITIAL state folds from absent here — the log alone
74
+ * cannot see the pre-run base, so merges and nested writes onto a seeded key
75
+ * give only what they added. Since 9.17.0 the base TRAVELS with the log:
76
+ * `stateAt(snapshot, idx)` (footprintjs/trace) folds from
77
+ * `RuntimeSnapshot.initialState`, which this function never receives.
78
+ * @throws {@link UnknownVerbError} when a row under the key's top-level key
79
+ * carries a verb other than `set | merge | append | delete` — a foreign or
80
+ * corrupted log is refused, not folded as a `merge`. Engine-written logs
81
+ * never carry one.
82
+ * @see commitValueAtWithBasis — the same answer, with the codes that say WHY it is
83
+ * `undefined` or partial (and the `initialState` base this function never receives).
61
84
  */
62
85
  export declare function commitValueAt(commitLog: CommitBundle[], idx: number, key: string): unknown;
86
+ /**
87
+ * Why {@link commitValueAtWithBasis} answered what it did. Each code is registered, with the one
88
+ * sentence that says what it means, in `memory/honesty.ts · HONESTY_CODES` (served on
89
+ * `footprintjs/trace`); the union declares its members through `RegisteredCode`.
90
+ *
91
+ * - `'never-written'` — no commit in range wrote the key (the writer rule — the answer
92
+ * `findLastWriter` and the slice layer give), unless the run removed a value
93
+ * the passed `initialState` held (then `'deleted'`), or a redaction at or
94
+ * around the key hid the write (then `'redacted'` alone).
95
+ * - `'deleted'` — the answer is `undefined` and the key WAS written: its last write left it
96
+ * absent.
97
+ * - `'nested-rows'` — the value rests on rows INSIDE the key (a subflow seed, an outputMapper
98
+ * merge-back, a fork child's namespace) with no `set`/`delete` of the key or
99
+ * of a container around it in range.
100
+ * - `'from-initial-state'` — no `set`/`delete` of the key or around it in range, so the value rests on
101
+ * the pre-run base: folded from `options.initialState` when it holds the
102
+ * key's top-level key, partial when no `initialState` was passed.
103
+ * - `'redacted'` — a path at, inside or around the key is in the `redactedPaths` of a commit
104
+ * the value rests on (from its last `set`/`delete` on, or the whole range).
105
+ */
106
+ export type ValueBasis = RegisteredCode<'never-written' | 'deleted' | 'nested-rows' | 'from-initial-state' | 'redacted'>;
107
+ /** What {@link commitValueAtWithBasis} is asked besides the log, the index and the key. */
108
+ export interface ValueBasisOptions {
109
+ /**
110
+ * The state before the run — `RuntimeSnapshot.initialState` (or a subflow's
111
+ * `treeContext.initialState`). When given, a key with no `set`/`delete` in range folds from it, as
112
+ * `stateAt` does; when absent, such an answer is the log-only fold and carries `'from-initial-state'`.
113
+ */
114
+ readonly initialState?: Readonly<Record<string, unknown>>;
115
+ }
116
+ /** A key's value read off the log, and the codes that say what it rests on — absent codes = an exact answer. */
117
+ export interface ValueWithBasis {
118
+ readonly value: unknown;
119
+ /** In the order of {@link ValueBasis}'s list; EMPTY when a `set`/`delete` of the key or around it anchors the value and no redaction touched it. */
120
+ readonly basis: ValueBasis[];
121
+ }
122
+ /**
123
+ * {@link commitValueAt}, with the codes that say what the answer rests on. Without `options.initialState`
124
+ * the value IS `commitValueAt(commitLog, idx, key)` (pinned by a property); with it, a key that has no
125
+ * `set`/`delete` of itself or of a container around it in range folds from that base — the value
126
+ * `stateAt(snapshot, idx)` gives at the key. Refuses an unknown verb exactly as `commitValueAt` does.
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const { value, basis } = commitValueAtWithBasis(snap.commitLog, i, 'cfg', { initialState: snap.initialState });
131
+ * for (const code of basis) console.log(code, HONESTY_CODES[code]);
132
+ * ```
133
+ */
134
+ export declare function commitValueAtWithBasis(commitLog: CommitBundle[], idx: number, key: string, options?: ValueBasisOptions): ValueWithBasis;
135
+ /**
136
+ * Why {@link findLastWriterWithBasis} answered what it did — registered in `HONESTY_CODES`:
137
+ * `'never-written'` (no writer before the bound), `'nested-rows'` (the writer reached the key only
138
+ * through rows inside it, so it wrote part of the value), `'redacted'` (no writer the log can see, because a
139
+ * redaction at or around the key hid the write — the rule `commitValueAtWithBasis` follows).
140
+ */
141
+ export type WriterBasis = RegisteredCode<'never-written' | 'nested-rows' | 'redacted'>;
142
+ /** The last writer of a key, and the codes that say what kind of write it was — absent codes = a write on or around the key. */
143
+ export interface WriterWithBasis {
144
+ /** Absent exactly when `basis` holds `'never-written'` or `'redacted'`. */
145
+ readonly writer?: CommitBundle;
146
+ readonly basis: WriterBasis[];
147
+ }
148
+ /**
149
+ * {@link findLastWriter}, with the code that says why: `writer` is `findLastWriter(commitLog, key, beforeIdx)`;
150
+ * `basis` is `['never-written']` when there is none and `['nested-rows']` when that commit wrote the key
151
+ * only through rows inside it.
152
+ */
153
+ export declare function findLastWriterWithBasis(commitLog: CommitBundle[], key: string, beforeIdx?: number): WriterWithBasis;
63
154
  /**
64
155
  * Position index over a commit log: `runtimeStageId` → the ARRAY INDEX of the
65
156
  * FIRST bundle that stage committed.