footprintjs 9.28.0 → 9.30.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/CLAUDE.md +8 -6
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +41 -2
- package/dist/esm/lib/memory/EventLog.d.ts +6 -0
- package/dist/esm/lib/memory/EventLog.js +10 -4
- package/dist/esm/lib/memory/SharedMemory.d.ts +18 -4
- package/dist/esm/lib/memory/SharedMemory.js +42 -11
- package/dist/esm/lib/memory/StageContext.d.ts +45 -18
- package/dist/esm/lib/memory/StageContext.js +82 -27
- package/dist/esm/lib/memory/TransactionBuffer.d.ts +256 -86
- package/dist/esm/lib/memory/TransactionBuffer.js +497 -273
- package/dist/esm/lib/memory/admission.d.ts +81 -0
- package/dist/esm/lib/memory/admission.js +152 -0
- package/dist/esm/lib/memory/borrowedMutation.d.ts +9 -0
- package/dist/esm/lib/memory/borrowedMutation.js +18 -1
- package/dist/esm/lib/memory/deltaEncoding.d.ts +66 -0
- package/dist/esm/lib/memory/deltaEncoding.js +122 -0
- package/dist/esm/lib/memory/pathOps.d.ts +63 -0
- package/dist/esm/lib/memory/pathOps.js +150 -1
- package/dist/esm/lib/memory/utils.d.ts +47 -16
- package/dist/esm/lib/memory/utils.js +119 -24
- package/dist/esm/lib/runner/FlowChartExecutor.d.ts +7 -5
- package/dist/esm/lib/runner/FlowChartExecutor.js +8 -6
- package/dist/lib/engine/handlers/SubflowExecutor.js +41 -2
- package/dist/lib/memory/EventLog.js +9 -3
- package/dist/lib/memory/SharedMemory.js +40 -9
- package/dist/lib/memory/StageContext.js +81 -26
- package/dist/lib/memory/TransactionBuffer.js +495 -271
- package/dist/lib/memory/admission.js +158 -0
- package/dist/lib/memory/borrowedMutation.js +20 -2
- package/dist/lib/memory/deltaEncoding.js +128 -0
- package/dist/lib/memory/pathOps.js +159 -2
- package/dist/lib/memory/utils.js +122 -25
- package/dist/lib/runner/FlowChartExecutor.js +8 -6
- package/dist/types/lib/memory/EventLog.d.ts +6 -0
- package/dist/types/lib/memory/SharedMemory.d.ts +18 -4
- package/dist/types/lib/memory/StageContext.d.ts +45 -18
- package/dist/types/lib/memory/TransactionBuffer.d.ts +256 -86
- package/dist/types/lib/memory/admission.d.ts +81 -0
- package/dist/types/lib/memory/borrowedMutation.d.ts +9 -0
- package/dist/types/lib/memory/deltaEncoding.d.ts +66 -0
- package/dist/types/lib/memory/pathOps.d.ts +63 -0
- package/dist/types/lib/memory/utils.d.ts +47 -16
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +7 -5
- package/package.json +2 -1
|
@@ -32,10 +32,45 @@
|
|
|
32
32
|
* `set` path — detaches that value first ({@link detachHeldAncestors}), so
|
|
33
33
|
* `workingCopy` and `overwritePatch` never share a container the engine
|
|
34
34
|
* mutates on one side only.
|
|
35
|
+
*
|
|
36
|
+
* COPY-ON-WRITE (9.29.0 — docs/design/2026-10-copy-on-write-commit.md). The
|
|
37
|
+
* buffer no longer clones the committed state it starts from. `baseSnapshot`
|
|
38
|
+
* IS the committed generation the stage first touched (held by reference —
|
|
39
|
+
* committed state is immutable-after-swap), and `workingCopy` starts as a
|
|
40
|
+
* copy of its ROOT only: each write copies the containers on its own path
|
|
41
|
+
* ({@link ownSpine}), and the first READ after the stage's first write of a
|
|
42
|
+
* container still shared with committed state takes a private deep copy of
|
|
43
|
+
* it ({@link privatise}) — what the whole-state clone used to give every
|
|
44
|
+
* read, so an in-place edit of a read value stays private exactly as it did.
|
|
45
|
+
* A read the working copy cannot answer is served from live state by the
|
|
46
|
+
* caller, as before; the buffer then replaces that path of its diff base
|
|
47
|
+
* with a private copy ({@link detachBase}), as the clone had it.
|
|
48
|
+
*
|
|
49
|
+
* THE ADMITTED RECORD (9.30.0 — docs/design/2026-10-admitted-record.md). A
|
|
50
|
+
* commit is admitted only if its bundle folds back to the stage's
|
|
51
|
+
* read-your-writes view at every path the stage touched (and at every
|
|
52
|
+
* container its writes created on the way there, below the stage's address).
|
|
53
|
+
* Both encoders commit the value the stage read: a compact row — a `merge`
|
|
54
|
+
* delta, an `append` tail — is kept where it provably folds back; a family of
|
|
55
|
+
* rows that does not is recorded as `set` rows of the values the stage read
|
|
56
|
+
* ({@link admit}). Before 9.30.0 the one accumulated merge delta per path was
|
|
57
|
+
* replayed at every `merge` row of that path, which lied across a hard write
|
|
58
|
+
* (`$update(k,{x}); k = {y}; $update(k,{z})` committed `{y, x, z}` for a
|
|
59
|
+
* read-back of `{y, z}`), an `[]` clear, a kind change or an array union
|
|
60
|
+
* deduplicated by reference. Verified when the stage staged a `merge` or a
|
|
61
|
+
* nested op; a stage that staged only `set` / `delete` of keys directly under
|
|
62
|
+
* its address is coherent by construction — both trees receive the same
|
|
63
|
+
* write — and is admitted without the fold.
|
|
35
64
|
*/
|
|
36
65
|
import type { CommitValuesMode, MemoryPatch, TraceEntry } from './types.js';
|
|
37
66
|
export declare class TransactionBuffer {
|
|
38
|
-
|
|
67
|
+
/**
|
|
68
|
+
* The net-change diff base: the committed generation the stage first
|
|
69
|
+
* touched, by reference — except where {@link detachBase} replaced a path
|
|
70
|
+
* of it with a private copy (its root and the containers on the way are
|
|
71
|
+
* then copies too, owned by {@link baseOwned}).
|
|
72
|
+
*/
|
|
73
|
+
private baseSnapshot;
|
|
39
74
|
private workingCopy;
|
|
40
75
|
private overwritePatch;
|
|
41
76
|
private updatePatch;
|
|
@@ -57,11 +92,59 @@ export declare class TransactionBuffer {
|
|
|
57
92
|
* keys tracked-read so far — the temporal-prefix attribution consumed by
|
|
58
93
|
* causal slicing. Undefined (default) = zero cost, byte-identical ops. */
|
|
59
94
|
private readonly readKeysProvider?;
|
|
60
|
-
|
|
95
|
+
/**
|
|
96
|
+
* The containers in `workingCopy` this buffer CREATED: its root copy, the
|
|
97
|
+
* path copies its writes made, the merge results it built, and every
|
|
98
|
+
* container of the private copies its reads took. A write edits these in
|
|
99
|
+
* place. Everything else in `workingCopy` is SHARED — with the committed
|
|
100
|
+
* generation the stage first touched, or with a value the stage staged —
|
|
101
|
+
* and is copied before a write passes through it ({@link ownSpine}).
|
|
102
|
+
*/
|
|
103
|
+
private owned;
|
|
104
|
+
/**
|
|
105
|
+
* Container values the stage staged with {@link set} — its own objects,
|
|
106
|
+
* held by reference (9.23.0). A read hands them back as they are, exactly
|
|
107
|
+
* as the private working copy always did, and never privatises them: a
|
|
108
|
+
* private copy of a staged value would leave `workingCopy` and
|
|
109
|
+
* `overwritePatch` disagreeing about a value the stage wrote.
|
|
110
|
+
*/
|
|
111
|
+
private staged;
|
|
112
|
+
/**
|
|
113
|
+
* Owned containers with no container still shared with committed state
|
|
114
|
+
* anywhere below them (a private deep copy, a merge result built on a
|
|
115
|
+
* private base, a path copy whose shared children were privatised). A read
|
|
116
|
+
* that lands on one needs no walk — see {@link privatiseBelow}.
|
|
117
|
+
*/
|
|
118
|
+
private privateTrees;
|
|
119
|
+
/**
|
|
120
|
+
* The containers of {@link baseSnapshot} that {@link detachBase} created:
|
|
121
|
+
* its root copy and the copies on the way to a detached path. Allocated
|
|
122
|
+
* only by the first detach — the in-contract stage never pays for it.
|
|
123
|
+
*/
|
|
124
|
+
private baseOwned?;
|
|
125
|
+
/**
|
|
126
|
+
* The private deep copies {@link detachBase} put into the diff base: nothing
|
|
127
|
+
* at or below one is committed state, so a later detach inside it is a no-op.
|
|
128
|
+
*/
|
|
129
|
+
private detachedBase?;
|
|
130
|
+
/**
|
|
131
|
+
* Where the stage writes (9.30.0): `['runs', runId]` for a run-namespaced
|
|
132
|
+
* stage, `[]` otherwise. Its containers are the stage's ADDRESS, not a value
|
|
133
|
+
* the stage reads (every read it makes is a key below it), so a shell a
|
|
134
|
+
* write leaves there is no part of the read-back the record must hold.
|
|
135
|
+
*/
|
|
136
|
+
private readonly address;
|
|
137
|
+
/** `merge` ops staged since the last commit — see {@link admit}. */
|
|
138
|
+
private stagedMerges;
|
|
139
|
+
/** Ops staged deeper than one key below the stage's address — see {@link admit}. */
|
|
140
|
+
private nestedOps;
|
|
141
|
+
constructor(base: any, commitValues?: CommitValuesMode, readKeysProvider?: () => string[], address?: readonly string[]);
|
|
61
142
|
/** Stamp the current read prefix onto a staged op — only when the
|
|
62
143
|
* provenance dial is on (provider present), so the default path allocates
|
|
63
144
|
* nothing and commit bundles stay byte-identical. */
|
|
64
145
|
private stampReadKeys;
|
|
146
|
+
/** Stage an op into `opTrace`, counting what decides whether {@link admit} must fold. */
|
|
147
|
+
private stage;
|
|
65
148
|
/**
|
|
66
149
|
* Hard overwrite at the specified path. Stores the REFERENCE in both trees
|
|
67
150
|
* (9.23.0) — the record's copy is taken once, at commit; see the header.
|
|
@@ -101,7 +184,15 @@ export declare class TransactionBuffer {
|
|
|
101
184
|
delete(path: (string | number)[], shouldRedact?: boolean): void;
|
|
102
185
|
/** Deep union merge at the specified path. `deepSmartMerge` builds fresh
|
|
103
186
|
* containers, so neither tree ever holds the merge INPUT by reference;
|
|
104
|
-
* the nested-op detach guards the ancestor it writes INTO.
|
|
187
|
+
* the nested-op detach guards the ancestor it writes INTO.
|
|
188
|
+
*
|
|
189
|
+
* The value merged INTO is made private first (`privatise`), as the
|
|
190
|
+
* whole-state clone made it before 9.29.0. Not only for the reads that
|
|
191
|
+
* follow: `deepSmartMerge` unions arrays BY REFERENCE, so whether an
|
|
192
|
+
* element the stage passes in is "already there" depends on which objects
|
|
193
|
+
* the base holds — a committed array would dedup an element the stage
|
|
194
|
+
* read from it before its first write, where the private copy (and
|
|
195
|
+
* 9.28.0) appends it. */
|
|
105
196
|
merge(path: (string | number)[], value: any, shouldRedact?: boolean): void;
|
|
106
197
|
/**
|
|
107
198
|
* Field-level redaction (9.19.0): mark dot-paths INSIDE the value staged at
|
|
@@ -113,14 +204,99 @@ export declare class TransactionBuffer {
|
|
|
113
204
|
* (see {@link survivingRedactedPaths}).
|
|
114
205
|
*/
|
|
115
206
|
markRedactedFields(path: (string | number)[], fields: readonly string[]): void;
|
|
207
|
+
/**
|
|
208
|
+
* Does a redaction mark address an array ELEMENT right below `path`
|
|
209
|
+
* (`list.1.token`)? An `append` row re-bases the indices onto the tail, so
|
|
210
|
+
* such a path takes the whole-value `set` (`deltaEncoding · pushValueRow`).
|
|
211
|
+
* A mark by NAME below an array (`obj.y`) means the same thing on the tail
|
|
212
|
+
* as on the whole value, so the compact row stays (byte-identical).
|
|
213
|
+
*/
|
|
214
|
+
private markedBelow;
|
|
116
215
|
/**
|
|
117
216
|
* The redacted paths that survive the net-change filter: a path survives
|
|
118
217
|
* when it IS a surviving op path (a whole-key redaction) or sits UNDER one
|
|
119
218
|
* (a field inside a surviving write). A dropped op takes its fields with it.
|
|
120
219
|
*/
|
|
121
220
|
private survivingRedactedPaths;
|
|
122
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* Read current value at path (includes uncommitted changes). The value is
|
|
223
|
+
* the stage's own — private to it, as the whole-state clone made every read
|
|
224
|
+
* after the first write before 9.29.0 (`privatise`).
|
|
225
|
+
*/
|
|
123
226
|
get(path: (string | number)[], defaultValue?: any): any;
|
|
227
|
+
/**
|
|
228
|
+
* Read the working copy at `path` WITHOUT taking a private copy — for a
|
|
229
|
+
* reader that only compares (the dev-mode borrowed-mutation report in
|
|
230
|
+
* `StageContext.commit`), never for a value handed to a stage.
|
|
231
|
+
*/
|
|
232
|
+
peek(path: (string | number)[]): unknown;
|
|
233
|
+
/**
|
|
234
|
+
* Keep the diff base exact under a read the working copy cannot answer.
|
|
235
|
+
*
|
|
236
|
+
* When `workingCopy` holds nothing at `path` — the stage deleted or unset
|
|
237
|
+
* it, or a write replaced a container above it — `StageContext ·
|
|
238
|
+
* readState` serves the read from LIVE committed state, exactly as 9.28.0
|
|
239
|
+
* did, so the value it hands out is committed state itself. On 9.28.0 the
|
|
240
|
+
* diff base was a whole-state clone taken at the stage's first write, and
|
|
241
|
+
* an in-place edit of that value (out of contract — reads are borrowed)
|
|
242
|
+
* could not move it: written back, the edit was recorded. Here the diff
|
|
243
|
+
* base IS a committed generation, and it very often holds that same
|
|
244
|
+
* container at `path` — the edit would move the base with it, and the
|
|
245
|
+
* write-back would commit no change. So before the value goes out, the
|
|
246
|
+
* base's value at `path` is replaced by a private deep copy (the base's
|
|
247
|
+
* root and the containers on the way copied shallowly) — what 9.28.0's
|
|
248
|
+
* base held there. The commit's net-change test compares against it and
|
|
249
|
+
* the delta encoder replays from it; the value handed out stays the live
|
|
250
|
+
* one, so whatever else an edit of it reaches (live state, the next
|
|
251
|
+
* generation) behaves as on 9.28.0 too.
|
|
252
|
+
*
|
|
253
|
+
* Not `privatise`: a private copy handed to the stage would also keep the
|
|
254
|
+
* edit out of live state, where 9.28.0 let it in — a run-namespaced read
|
|
255
|
+
* that falls back to a global key would then end differently (the
|
|
256
|
+
* differential's NESTED family fails within 100 programs that way).
|
|
257
|
+
*
|
|
258
|
+
* Copies nothing when the base holds no container at `path`, and nothing
|
|
259
|
+
* twice: a path at or below an earlier detach is already private. O(path)
|
|
260
|
+
* otherwise, plus O(value) once per detached path.
|
|
261
|
+
*/
|
|
262
|
+
detachBase(path: (string | number)[]): void;
|
|
263
|
+
/**
|
|
264
|
+
* Private reads (9.29.0): make the value at `path` the stage's own before
|
|
265
|
+
* a read hands it out — so a read after the stage's first write behaves
|
|
266
|
+
* exactly as it did when the buffer began with a deep clone of the whole
|
|
267
|
+
* state. An in-place edit of what the read returned (out of contract —
|
|
268
|
+
* reads are borrowed) stays inside the buffer, and committed state is never
|
|
269
|
+
* touched. Whether the RECORD keeps it is the admitted record's law
|
|
270
|
+
* (9.30.0, owner ruling R1 — the record keeps what the stage read back): it
|
|
271
|
+
* is kept when the edited value lies at or below a path the stage wrote in
|
|
272
|
+
* this stage — set, merged or written back alike — and lost otherwise.
|
|
273
|
+
*
|
|
274
|
+
* Walking the path from the (owned) root:
|
|
275
|
+
* - a container still SHARED with committed state at the same position is
|
|
276
|
+
* replaced by a private deep copy when it is the value read, or by a
|
|
277
|
+
* shallow copy when it is a container on the way to it (so a read of
|
|
278
|
+
* `runs/<id>/k` copies `k`, not every run's namespace);
|
|
279
|
+
* - a container the stage STAGED (or anything else the buffer did not
|
|
280
|
+
* take from committed state) is handed back by reference, as before;
|
|
281
|
+
* - a container the buffer owns is walked on — and when it is the value
|
|
282
|
+
* read, its still-shared descendants are privatised
|
|
283
|
+
* ({@link privatiseBelow}).
|
|
284
|
+
*
|
|
285
|
+
* Cost: O(path) per read, plus O(value) ONCE per shared container a stage
|
|
286
|
+
* reads after its first write. A stage that never reads after writing pays
|
|
287
|
+
* nothing; a read before the first write never reaches the buffer.
|
|
288
|
+
*/
|
|
289
|
+
private privatise;
|
|
290
|
+
/**
|
|
291
|
+
* An OWNED container is about to be read whole (a path copy a nested write
|
|
292
|
+
* made, say): its descendants still shared with committed state are
|
|
293
|
+
* replaced by private deep copies, so nothing the read hands out is
|
|
294
|
+
* committed state. Walks owned containers only; marks each one finished
|
|
295
|
+
* ({@link privateTrees}), so a container is scanned once per stage.
|
|
296
|
+
*/
|
|
297
|
+
private privatiseBelow;
|
|
298
|
+
/** A private deep copy of a committed container — owned through and through. */
|
|
299
|
+
private privateCopyOf;
|
|
124
300
|
/**
|
|
125
301
|
* Did any staged op touch `path`, or a path above or below it?
|
|
126
302
|
*
|
|
@@ -204,14 +380,14 @@ export declare class TransactionBuffer {
|
|
|
204
380
|
* Paths are compared at the exact granularity they were written (each trace
|
|
205
381
|
* entry's path), against `workingCopy` (final) vs `baseSnapshot` (start).
|
|
206
382
|
* Surviving `set` paths copy their final value from `overwritePatch`;
|
|
207
|
-
* surviving `merge` paths copy their accumulated delta from `updatePatch
|
|
208
|
-
*
|
|
209
|
-
*
|
|
383
|
+
* surviving `merge` paths copy their accumulated delta from `updatePatch`,
|
|
384
|
+
* keeping the set-vs-merge verb — and the result is ADMITTED ({@link admit}):
|
|
385
|
+
* a family of rows whose replay does not give back what the stage read is
|
|
386
|
+
* recorded as `set` rows of the read-back instead (9.30.0).
|
|
210
387
|
*
|
|
211
|
-
* This is the DEFAULT (`commitValues: 'full'`) payload
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* {@link toDeltaPayload}.
|
|
388
|
+
* This is the DEFAULT (`commitValues: 'full'`) payload, including the
|
|
389
|
+
* historical flattening of staged `delete` ops into `set`-of-`undefined`
|
|
390
|
+
* trace entries. The delta encoding lives in {@link toDeltaPayload}.
|
|
215
391
|
*
|
|
216
392
|
* Work, not bytes (9.22.1): the net-change verdict is paid once per PATH
|
|
217
393
|
* and the clone once per CONSECUTIVE run of ops on a path, not once per op
|
|
@@ -222,6 +398,8 @@ export declare class TransactionBuffer {
|
|
|
222
398
|
* place the record is detached from them.
|
|
223
399
|
*/
|
|
224
400
|
private toChangeOnlyPayload;
|
|
401
|
+
/** The 'full' rows; a family in `lossy` is written as its read-back, at its last touch ({@link emitReadBack}). */
|
|
402
|
+
private changeOnlyRows;
|
|
225
403
|
/**
|
|
226
404
|
* Delta-encoded payload (`commitValues: 'delta'`, #13c-B) — same net-change
|
|
227
405
|
* filter as {@link toChangeOnlyPayload}, three encoding differences:
|
|
@@ -240,40 +418,78 @@ export declare class TransactionBuffer {
|
|
|
240
418
|
* the `'full'` flattening (`_set` of `undefined`) COERCES that parent
|
|
241
419
|
* into an object; when they would disagree we keep the flattening;
|
|
242
420
|
* - ONLY `'merge'` ops → `merge` with the accumulated `updatePatch`
|
|
243
|
-
* delta
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* replays the full-mode bundle ({@link replayPathVerbs}) — for
|
|
249
|
-
* pure-set paths that is simply the last set value; for mixed
|
|
250
|
-
* set+merge interleavings it reproduces the full mode's quirk of
|
|
251
|
-
* applying the ACCUMULATED merge delta at every merge position
|
|
252
|
-
* (which can differ from the buffer's read-your-writes view; parity
|
|
253
|
-
* with the `'full'` mode's committed state is the contract). If base
|
|
254
|
-
* and that value are arrays and base is a STRICT PREFIX → `append`
|
|
255
|
-
* storing only the tail; else `set` storing the full value.
|
|
421
|
+
* delta;
|
|
422
|
+
* - otherwise (`set`/mixed): the value the stage's 'full' rows give the
|
|
423
|
+
* path ({@link fullRowsValue}). If base and that value are arrays and
|
|
424
|
+
* base is a STRICT PREFIX → `append` storing only the tail; else `set`
|
|
425
|
+
* storing the full value.
|
|
256
426
|
* 3. **OVERLAP FAMILIES take the full-value fallback.** `overwrite` /
|
|
257
427
|
* `updates` are nested path TREES, so two surviving paths where one is
|
|
258
|
-
* an ancestor of the other (`a` and `a.p`) share storage.
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* `
|
|
263
|
-
*
|
|
264
|
-
* `undefined`, so whichever entry is written second silently destroys or
|
|
265
|
-
* corrupts the other — the recorded write is LOST at replay. Therefore
|
|
266
|
-
* every path that has a surviving ancestor/descendant is committed as a
|
|
267
|
-
* plain `set` of the value it holds in the family's replayed value
|
|
268
|
-
* ({@link replayFamilyVerbs}), and the family's shallowest path (whose
|
|
269
|
-
* set covers the whole subtree) is emitted LAST. One coherent tree in,
|
|
270
|
-
* one coherent tree out: entries can no longer clobber each other and
|
|
271
|
-
* the replayed value is exact regardless of order.
|
|
428
|
+
* an ancestor of the other (`a` and `a.p`) share storage. Delta's
|
|
429
|
+
* per-path encodings would clobber each other there (an `append`
|
|
430
|
+
* ancestor stores only a TAIL, a `delete` ancestor `undefined`), so every
|
|
431
|
+
* path that has a surviving ancestor/descendant is committed as a plain
|
|
432
|
+
* `set` of the value the 'full' rows give it, and the family's
|
|
433
|
+
* shallowest path (whose set covers the whole subtree) is emitted LAST.
|
|
272
434
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
435
|
+
* The result is ADMITTED ({@link admit}) like the 'full' payload: a compact
|
|
436
|
+
* row is kept only where the bundle provably folds back, and a family that
|
|
437
|
+
* does not is recorded as what the stage read. So both encodings commit the
|
|
438
|
+
* value the stage read; before 9.30.0 they shared a replay that did not
|
|
439
|
+
* (`replayPathVerbs` / `replayFamilyVerbs`, deleted).
|
|
275
440
|
*/
|
|
276
441
|
private toDeltaPayload;
|
|
442
|
+
/** The delta rows; a family in `lossy` is written as its read-back, at its last touch ({@link emitReadBack}). */
|
|
443
|
+
private deltaRows;
|
|
444
|
+
/**
|
|
445
|
+
* THE ADMISSION — a commit is admitted only if its bundle folds back to the
|
|
446
|
+
* stage's read-your-writes view at every path the stage touched. `build`
|
|
447
|
+
* lays the rows out (one encoder: `'full'` or `'delta'`); the candidate is
|
|
448
|
+
* checked ({@link lossyFamilies}), and when some family's rows do not fold
|
|
449
|
+
* back the rows are laid out again with those families written as what the
|
|
450
|
+
* stage read ({@link emitReadBack}) — every other family's bytes exactly as
|
|
451
|
+
* the first layout made them.
|
|
452
|
+
*/
|
|
453
|
+
private admit;
|
|
454
|
+
/**
|
|
455
|
+
* The families whose rows do not fold back to what the stage read —
|
|
456
|
+
* `undefined` when there are none (`admission.ts · lossyFamilies`: the
|
|
457
|
+
* candidate replayed onto the diff base by `dryFold`, every family of
|
|
458
|
+
* touched paths compared with the working copy at its root and on the way
|
|
459
|
+
* down to it).
|
|
460
|
+
*
|
|
461
|
+
* Without a fold, when the stage staged no `merge` and no nested op: its
|
|
462
|
+
* rows are `set` / `delete` of keys directly under its address, each
|
|
463
|
+
* writing the SAME value into `workingCopy` and `overwritePatch` — the
|
|
464
|
+
* record is the read-back by construction (the property's ROOT-ONLY arm
|
|
465
|
+
* pins this). That keeps the commit of the typed scope's root-key writes
|
|
466
|
+
* exactly as cheap as it was.
|
|
467
|
+
*/
|
|
468
|
+
private lossyFamilies;
|
|
469
|
+
/**
|
|
470
|
+
* A family whose rows did not fold back, written as what the stage read:
|
|
471
|
+
* one `set` row per member that changed (descendants by last touch, each
|
|
472
|
+
* with its own read prefix), the ROOT last, and the root's read-back value
|
|
473
|
+
* in `overwrite` — one coherent tree, so no row can clobber another. The
|
|
474
|
+
* root's row is written even when the root itself did not change: it is the
|
|
475
|
+
* row that carries the family's read-back (and the containers L-1's dropped
|
|
476
|
+
* op left). A lone root under `'delta'` keeps the compact `append` when the
|
|
477
|
+
* value it read is its base plus a tail.
|
|
478
|
+
*/
|
|
479
|
+
private emitReadBack;
|
|
480
|
+
/**
|
|
481
|
+
* The value the stage's 'full' rows give a path — what the delta encoder
|
|
482
|
+
* commits for a family member or a path with a `set` — read from ONE
|
|
483
|
+
* {@link dryFold} of those rows over the patch trees themselves, taken the
|
|
484
|
+
* first time it is asked. By reference, as the stage held them: an element
|
|
485
|
+
* a `set` and a `merge` of one path share stays one element. A stage that
|
|
486
|
+
* staged no `merge` and no nested op needs no fold — a path's value is its
|
|
487
|
+
* last staged value. (Before 9.30.0: two replicas of the verb law,
|
|
488
|
+
* `replayPathVerbs` and `replayFamilyVerbs`.)
|
|
489
|
+
*/
|
|
490
|
+
private fullRowsValue;
|
|
491
|
+
/** The stage's 'full' rows: every op on a path the net-change filter keeps, a `delete` spelled `set`. */
|
|
492
|
+
private fullRows;
|
|
277
493
|
/**
|
|
278
494
|
* Did the stage change the value at `segments`? Base and final value are
|
|
279
495
|
* fixed at commit, so this is the ONE net-change verdict both payload
|
|
@@ -290,40 +506,6 @@ export declare class TransactionBuffer {
|
|
|
290
506
|
private opsByPath;
|
|
291
507
|
/** The net-change filter — identical to 'full' ({@link changedSinceBase}). Survivors keep last-touch order. */
|
|
292
508
|
private netChangeSurvivors;
|
|
293
|
-
/** Replay each overlapping family ONCE, lazily — most commits have none, and pay nothing. */
|
|
294
|
-
private memoisedFamilyValue;
|
|
295
|
-
/**
|
|
296
|
-
* Bucket the staged ops of every OVERLAPPING family by family root, in op
|
|
297
|
-
* order — the input {@link replayFamilyVerbs} folds. Ops on paths the
|
|
298
|
-
* net-change filter dropped are skipped (they are absent from `rootOf`),
|
|
299
|
-
* exactly as the `'full'` payload drops them from its trace. One pass over
|
|
300
|
-
* `opTrace`, and only when at least one family actually overlaps — commits
|
|
301
|
-
* that write no nested path pay nothing.
|
|
302
|
-
*/
|
|
303
|
-
private opsByFamily;
|
|
304
|
-
/**
|
|
305
|
-
* Replay ONE overlapping family's staged ops onto the family root's base
|
|
306
|
-
* value — the multi-path generalisation of {@link replayPathVerbs}, and
|
|
307
|
-
* byte-for-byte what `applySmartMerge` produces for the corresponding
|
|
308
|
-
* `'full'`-mode entries: each `set`/`delete` position writes that path's
|
|
309
|
-
* `overwritePatch` value, each `merge` position deep-merges that path's
|
|
310
|
-
* accumulated `updatePatch` delta into the value replayed so far.
|
|
311
|
-
*
|
|
312
|
-
* (Both patches are single coherent trees, so a full-mode bundle's stored
|
|
313
|
-
* value at any recorded path always reads back as its `overwritePatch` /
|
|
314
|
-
* `updatePatch` value — which is why sourcing from them here reproduces the
|
|
315
|
-
* full-mode replay exactly, including its intermediate-coercion quirks.)
|
|
316
|
-
*
|
|
317
|
-
* The value is held in a `{ v }` box so the root itself (relative path `[]`)
|
|
318
|
-
* can be REPLACED by `_set` the same way `applySmartMerge` replaces it
|
|
319
|
-
* inside the state tree — including `nativeSet`'s coercion of primitive
|
|
320
|
-
* intermediates.
|
|
321
|
-
*
|
|
322
|
-
* A `set` op the next op sets again is skipped on the same law as
|
|
323
|
-
* `applySmartMerge` ({@link supersededByNextSet}) — this is the delta
|
|
324
|
-
* encoder's own replay loop, and it clones per op just as the fold does.
|
|
325
|
-
*/
|
|
326
|
-
private replayFamilyVerbs;
|
|
327
509
|
/**
|
|
328
510
|
* May a staged `delete` at this path commit as the `'delete'` verb?
|
|
329
511
|
*
|
|
@@ -335,16 +517,4 @@ export declare class TransactionBuffer {
|
|
|
335
517
|
* so those (pathological) deletes keep the historical flattening.
|
|
336
518
|
*/
|
|
337
519
|
private deleteReplaysAsFlattening;
|
|
338
|
-
/**
|
|
339
|
-
* Replay ONE path's op-verb sequence against its base value, exactly the
|
|
340
|
-
* way `applySmartMerge` replays the corresponding full-mode bundle: every
|
|
341
|
-
* `set`/`delete` position applies the LAST staged overwrite value (the
|
|
342
|
-
* bag holds one value per path — last writer wins), every `merge`
|
|
343
|
-
* position applies the ACCUMULATED `updatePatch` delta. This reproduces
|
|
344
|
-
* the full mode's committed value for any interleaving — including the
|
|
345
|
-
* mixed set+merge quirk where the accumulated delta re-applies pre-set
|
|
346
|
-
* merge keys (full-mode replay semantics, kept for byte-parity across
|
|
347
|
-
* modes; property-tested in delta-replay-equivalence).
|
|
348
|
-
*/
|
|
349
|
-
private replayPathVerbs;
|
|
350
520
|
}
|