footprintjs 9.44.0 → 9.44.2
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 +6 -5
- package/dist/esm/lib/capture/freeze.d.ts +51 -7
- package/dist/esm/lib/capture/freeze.js +289 -24
- package/dist/esm/lib/capture/valueKinds.d.ts +57 -0
- package/dist/esm/lib/capture/valueKinds.js +142 -0
- package/dist/esm/lib/memory/EventLog.d.ts +24 -6
- package/dist/esm/lib/memory/EventLog.js +30 -10
- package/dist/esm/lib/memory/admission.js +4 -3
- package/dist/esm/lib/memory/borrowedMutation.js +2 -2
- package/dist/esm/lib/memory/equality.d.ts +55 -19
- package/dist/esm/lib/memory/equality.js +154 -51
- package/dist/esm/lib/memory/logModel.js +11 -4
- package/dist/esm/lib/memory/redaction.d.ts +21 -0
- package/dist/esm/lib/memory/redaction.js +64 -7
- package/dist/esm/lib/runner/ExecutionRuntime.d.ts +4 -1
- package/dist/esm/lib/runner/ExecutionRuntime.js +15 -10
- package/dist/esm/lib/runner/snapshot.js +17 -3
- package/dist/esm/lib/slice/elementProvenance.js +2 -2
- package/dist/esm/lib/time-travel/chain.js +28 -26
- package/dist/lib/capture/freeze.js +292 -25
- package/dist/lib/capture/valueKinds.js +146 -0
- package/dist/lib/memory/EventLog.js +29 -9
- package/dist/lib/memory/admission.js +4 -3
- package/dist/lib/memory/borrowedMutation.js +2 -2
- package/dist/lib/memory/equality.js +154 -51
- package/dist/lib/memory/logModel.js +11 -4
- package/dist/lib/memory/redaction.js +66 -8
- package/dist/lib/runner/ExecutionRuntime.js +14 -9
- package/dist/lib/runner/snapshot.js +16 -2
- package/dist/lib/slice/elementProvenance.js +2 -2
- package/dist/lib/time-travel/chain.js +27 -25
- package/dist/types/lib/capture/freeze.d.ts +51 -7
- package/dist/types/lib/capture/valueKinds.d.ts +57 -0
- package/dist/types/lib/memory/EventLog.d.ts +24 -6
- package/dist/types/lib/memory/equality.d.ts +55 -19
- package/dist/types/lib/memory/redaction.d.ts +21 -0
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +4 -1
- package/package.json +2 -1
|
@@ -1,21 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* freeze.ts — the ONE deep-freeze walk (moved here from `scope/protection/readonlyInput.ts` in F3)
|
|
2
|
+
* freeze.ts — the ONE deep-freeze walk (moved here from `scope/protection/readonlyInput.ts` in F3),
|
|
3
|
+
* and the serve-time law for the commit log (9.44.2).
|
|
3
4
|
*
|
|
4
5
|
* WHY HERE. Saved trees freeze through this walk: the fold base served as
|
|
5
6
|
* `initialState` (`ExecutionRuntime · getFoldBase`), the
|
|
6
7
|
* dev-mode snapshot (`runner/snapshot.ts · servedSnapshot`), every state `stateAt` hands out
|
|
7
8
|
* (`time-travel/stateAt.ts`) and, since F3, every commit bundle (`EventLog · record`). `memory/` must
|
|
8
9
|
* not import `scope/` (that edge closed the memory ⇄ scope ⇄ recorder module cycle F0 removed), so the
|
|
9
|
-
* walk lives in this leaf, which imports nothing. Args use an ownership snapshot
|
|
10
|
-
* (`scope/protection/readonlyInput.ts · createFrozenArgs`): never freeze borrowed caller values.
|
|
10
|
+
* walk lives in this leaf folder, which imports nothing outside itself. Args use an ownership snapshot
|
|
11
|
+
* instead (`scope/protection/readonlyInput.ts · createFrozenArgs`): never freeze borrowed caller values.
|
|
12
|
+
*
|
|
13
|
+
* FREEZE WHAT CAN BE FROZEN, COPY WHAT CAN'T (9.44.2). `Object.freeze` cannot seal a Date's time, a
|
|
14
|
+
* Map's or Set's entries, a buffer's bytes, a RegExp (`compile()` rewrites a frozen one) or an Error
|
|
15
|
+
* (V8's own `stack` accessor writes through a frozen one). A fresh tree handed to one caller (a `stateAt`
|
|
16
|
+
* state, the dev-mode snapshot) is that caller's own, so freezing it is a guard and nothing more. The
|
|
17
|
+
* COMMIT LOG — each bundle, and the fold base — is served to every reader, so {@link freezeRecord}
|
|
18
|
+
* remembers that a record holds such a value, its first serve maps the containers on the way to each
|
|
19
|
+
* one (its open PATHS), and {@link serveRecord} hands a reader a copy of those paths only: a fresh copy
|
|
20
|
+
* of each such value, fresh frozen containers on the way to it, and every other part shared, frozen, as
|
|
21
|
+
* it is. Until 9.44.2 `snapshot.commitLog[i].overwrite.when.setTime(0)` rewrote every later snapshot,
|
|
22
|
+
* `stateAt`, `commitValueAt`, slice and cursor answer.
|
|
23
|
+
*
|
|
24
|
+
* Every walk here is ITERATIVE (an explicit stack): a record's depth is not bounded by the call stack.
|
|
11
25
|
*
|
|
12
26
|
* @example
|
|
13
27
|
* ```typescript
|
|
14
|
-
* import { deepFreeze } from './freeze.js';
|
|
28
|
+
* import { deepFreeze, freezeRecord, serveRecord } from './freeze.js';
|
|
15
29
|
*
|
|
16
30
|
* const args = deepFreeze({ order: { lines: [{ sku: 'A' }] }, bytes: new Uint8Array([1]) });
|
|
17
31
|
* Object.isFrozen(args.order.lines[0]); // true
|
|
18
32
|
* Object.isFrozen(args.bytes); // false — a typed array cannot be frozen; it is skipped
|
|
33
|
+
*
|
|
34
|
+
* const record = freezeRecord({ when: new Date(0), tags: ['a'] });
|
|
35
|
+
* const served = serveRecord(record);
|
|
36
|
+
* served.when.setTime(5); // changes the reader's copy
|
|
37
|
+
* serveRecord(record).when.getTime(); // 0 — every serve is the record as recorded
|
|
38
|
+
* served.tags === record.tags; // true — the sealed part is shared, not copied
|
|
39
|
+
* serveRecord(freezeRecord({ n: 1 })); // a record freezing sealed whole: served as itself
|
|
19
40
|
* ```
|
|
20
41
|
*/
|
|
21
42
|
/**
|
|
@@ -25,8 +46,8 @@
|
|
|
25
46
|
* AND any expando (`arr.note = { … }`). The dev-mode snapshot relies on this contract.
|
|
26
47
|
* - `'indices'` — the index elements only, so no key string is allocated per element; this is what
|
|
27
48
|
* keeps freezing a 10,000-element commit inside its budget (`bench/element-writes.ts`). An object
|
|
28
|
-
* hung on an array EXPANDO is then left unfrozen — the commit
|
|
29
|
-
* (`EventLog · record
|
|
49
|
+
* hung on an array EXPANDO is then left unfrozen, and unseen by {@link serveRecord} — the commit
|
|
50
|
+
* log's named hole (`EventLog · record`; an array expando is out of contract for state).
|
|
30
51
|
*/
|
|
31
52
|
export type ArrayWalk = 'every-key' | 'indices';
|
|
32
53
|
/**
|
|
@@ -41,7 +62,30 @@ export type ArrayWalk = 'every-key' | 'indices';
|
|
|
41
62
|
* snapshot here.
|
|
42
63
|
* - What `Object.freeze` cannot reach stays mutable — Map and Set contents, a Date's time, the
|
|
43
64
|
* bytes behind a buffer. A frozen RegExp's `lastIndex` is read-only, so a `/g` or `/y` regex
|
|
44
|
-
* taken from a frozen tree throws when `exec` or `replace` advance it.
|
|
65
|
+
* taken from a frozen tree throws when `exec` or `replace` advance it. The commit log goes through
|
|
66
|
+
* {@link freezeRecord} and {@link serveRecord} instead.
|
|
45
67
|
* - Functions are not descended (they are not values the engine stores).
|
|
46
68
|
*/
|
|
47
69
|
export declare function deepFreeze<T>(obj: T, arrays?: ArrayWalk): T;
|
|
70
|
+
/**
|
|
71
|
+
* {@link deepFreeze} for a record many readers are SERVED (a commit bundle, the fold base), and two more
|
|
72
|
+
* things on the same walk:
|
|
73
|
+
*
|
|
74
|
+
* - it remembers THAT the record holds a value freezing cannot seal — one flag; the record's first
|
|
75
|
+
* serve maps WHERE (the containers on the way to each such value, its open paths), so the run pays
|
|
76
|
+
* nothing more and {@link serveRecord} copies those paths and nothing else;
|
|
77
|
+
* - a typed array or `DataView` that views a SLICE of a bigger buffer (a Node `Buffer` views its
|
|
78
|
+
* shared pool) is replaced, in the record, by a copy of the bytes it views — the record keeps the
|
|
79
|
+
* view's bytes, never the rest of the buffer (a resizable or shared buffer is kept as it is).
|
|
80
|
+
*
|
|
81
|
+
* A part frozen before is not frozen past (deepFreeze's contract) but is looked through, so a value
|
|
82
|
+
* freezing cannot seal inside it still opens the record. Returns `record`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function freezeRecord<T extends object>(record: T, arrays?: ArrayWalk): T;
|
|
85
|
+
/**
|
|
86
|
+
* The record as a reader may hold it — THE serve-time law: a record freezing sealed whole is served as
|
|
87
|
+
* itself (shared, no cost); any other as a copy of its open paths ({@link freezeRecord}): fresh frozen
|
|
88
|
+
* containers down to each value freezing cannot seal, a fresh copy of that value, and every other part
|
|
89
|
+
* shared, frozen, as it is. A copy is served the same way again.
|
|
90
|
+
*/
|
|
91
|
+
export declare function serveRecord<T>(record: T): T;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* valueKinds.ts — what KIND of value a record holds: the one classifier.
|
|
3
|
+
*
|
|
4
|
+
* WHY. Committed state is what survives `structuredClone` (an engine invariant), so the values a
|
|
5
|
+
* record holds are the kinds the clone keeps. Two laws need the kind, and both ask here:
|
|
6
|
+
*
|
|
7
|
+
* - what counts as a CHANGE (`memory/equality.ts · deepEqual`): two values are equal when a record
|
|
8
|
+
* holds the same thing for both, compared kind by kind. Until 9.44.2 only Date, Map and Set had a
|
|
9
|
+
* kind; a RegExp, an Error, a boxed number, a buffer, a DataView or a Blob was compared as an
|
|
10
|
+
* object with no own keys — equal to any other of its kind — so replacing one committed no row
|
|
11
|
+
* and live state kept the old value;
|
|
12
|
+
* - what a served record must COPY (`capture/freeze.ts · freezeRecord` / `serveRecord`):
|
|
13
|
+
* `Object.freeze` seals only the kinds whose content is their own properties ({@link SEALABLE}).
|
|
14
|
+
*
|
|
15
|
+
* A leaf: imports nothing (`capture/` is L0, below `memory/`).
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* kindOf(new Date(0)); // 'date'
|
|
20
|
+
* kindOf(new (class Point { x = 1 })()); // 'plain' — its clone is a plain object
|
|
21
|
+
* kindOf(new Blob(['a'])); // 'opaque' — its clone keeps content this library cannot read
|
|
22
|
+
* SEALABLE.date; // false — setTime writes a slot Object.freeze cannot reach
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/** Every kind a record can hold. */
|
|
26
|
+
export type ValueKind =
|
|
27
|
+
/** An object the clone keeps as its own enumerable keys — a class instance included. */
|
|
28
|
+
'plain' | 'array' | 'date' | 'regexp' | 'map' | 'set' | 'error'
|
|
29
|
+
/** A boxed primitive: `new Number(1)`, `new String('a')`, `new Boolean(true)`, `Object(1n)`. */
|
|
30
|
+
| 'boxed'
|
|
31
|
+
/** An `ArrayBuffer` (a `SharedArrayBuffer` too: shared memory is out of contract for state). */
|
|
32
|
+
| 'buffer'
|
|
33
|
+
/** A typed array or a `DataView`. */
|
|
34
|
+
| 'view'
|
|
35
|
+
/** Any other object the clone keeps as itself (a Blob, a File, a `DOMException`): content unread here. */
|
|
36
|
+
| 'opaque';
|
|
37
|
+
/**
|
|
38
|
+
* Does `Object.freeze` seal every part of a value of this kind? True only where the content is the
|
|
39
|
+
* value's own properties (or a slot nothing can write). A `Record`, so a new kind does not compile
|
|
40
|
+
* until it is decided here.
|
|
41
|
+
*/
|
|
42
|
+
export declare const SEALABLE: {
|
|
43
|
+
readonly [K in ValueKind]: boolean;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The kind of an object, as a record holds it. A plain object or an array — the bulk of state —
|
|
47
|
+
* costs one `Array.isArray` and one `getPrototypeOf`; a built-in a tag and one brand-checked read.
|
|
48
|
+
* The brand is the value's own slot, so a subclass (a `Map` subclass is a `'map'`, Node's `Buffer` a
|
|
49
|
+
* `'view'`) and a value from another realm land on their kind.
|
|
50
|
+
*
|
|
51
|
+
* Any other object is classified by WHAT ITS CLONE IS, once per prototype: a class instance comes
|
|
52
|
+
* back a plain object (`'plain'`), a fake built-in (`Object.create(Date.prototype)`, or a class whose
|
|
53
|
+
* `Symbol.toStringTag` says `'Date'`) comes back `{}` (`'plain'`), and a host object comes back as
|
|
54
|
+
* itself (`'opaque'`). A prototype whose instance the clone refuses is `'opaque'` — remembered too:
|
|
55
|
+
* it cannot be held, and its commit will refuse it.
|
|
56
|
+
*/
|
|
57
|
+
export declare function kindOf(value: object): ValueKind;
|
|
@@ -13,11 +13,17 @@
|
|
|
13
13
|
* (`verbs.ts · foldRows` detaches, `StageContext · retainedForm` clones) — pinned by
|
|
14
14
|
* test/lib/memory/property/record-reachability.property.test.ts.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* What `Object.freeze` cannot seal — a Date's time, a Map's or Set's entries, a buffer's bytes, a
|
|
17
|
+
* RegExp, an Error's stack — is never handed out to a reader (9.44.2): `record` remembers that a
|
|
18
|
+
* bundle holds such a value (`capture/freeze.ts · freezeRecord`), and `list()` — like the public
|
|
19
|
+
* snapshot, `runner/snapshot.ts · servedSnapshot` — serves that bundle as a copy of its open paths
|
|
20
|
+
* (`serveRecord`, mapped at its first serve): fresh frozen containers down to each such value, a
|
|
21
|
+
* fresh copy of it, every other part the log's own. A reader edits only its own copy; every later
|
|
22
|
+
* serve reads the record as recorded. `recorded()` is the engine's own, unserved read. Named holes: an object hung on an array EXPANDO (arrays are walked
|
|
23
|
+
* by index — the `'indices'` walk, which keeps the freeze inside its budget; an expando is out of
|
|
24
|
+
* contract for state) and a `SharedArrayBuffer` (every copy shares its memory). A frozen RegExp's
|
|
25
|
+
* `lastIndex` is read-only, so a `/g` or `/y` regex read from a bundle throws when `exec` or
|
|
26
|
+
* `replace` advance it.
|
|
21
27
|
*/
|
|
22
28
|
import type { EmitSourcePosition, LogAddress } from './eventPosition.js';
|
|
23
29
|
import type { CommitBundle } from './types.js';
|
|
@@ -72,8 +78,20 @@ export declare class EventLog {
|
|
|
72
78
|
* will no longer edit — its own object is the one frozen.
|
|
73
79
|
*/
|
|
74
80
|
record(bundle: CommitBundle): void;
|
|
75
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Every recorded commit bundle, AS SERVED (9.44.2): a new array each call; a bundle freezing sealed
|
|
83
|
+
* whole is the log's own (frozen) object, one holding a Date, Map, buffer … is a copy of its open
|
|
84
|
+
* paths (`capture/freeze.ts · serveRecord`) — the reader's own, so no edit reaches the log.
|
|
85
|
+
*/
|
|
76
86
|
list(): CommitBundle[];
|
|
87
|
+
/**
|
|
88
|
+
* @internal The bundles as the log holds them — each frozen, NOT served — in a new array, so a holder's
|
|
89
|
+
* `splice` reaches only its own. For the engine's own snapshot (`ExecutionRuntime · getSnapshot`, which
|
|
90
|
+
* a subflow mount takes once per mount), so the run serves nothing; a reader is served by `list()` and
|
|
91
|
+
* `getSnapshot().commitLog`. A known open door (CHANGELOG 9.44.2): a holder of these bundles can still
|
|
92
|
+
* edit a Date, Map or buffer inside them.
|
|
93
|
+
*/
|
|
94
|
+
recorded(): CommitBundle[];
|
|
77
95
|
/** Number of recorded commits. */
|
|
78
96
|
get length(): number;
|
|
79
97
|
/** Immutable identity of this log, absent until the engine binds it. */
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* equality.ts — structural equality of committed-state values
|
|
3
|
-
*
|
|
4
|
-
* record's compare
|
|
5
|
-
*
|
|
2
|
+
* equality.ts — structural equality of committed-state values: THE owner of
|
|
3
|
+
* "what counts as a change". What `TransactionBuffer`'s net-change filter, the
|
|
4
|
+
* admitted record's compare, the read model's writer rule and the
|
|
5
|
+
* borrowed-mutation guard all ask. Its one import is the classifier of value
|
|
6
|
+
* kinds (`capture/valueKinds.ts`, a leaf); re-exported from `utils.ts`, so no
|
|
7
|
+
* importer moved.
|
|
6
8
|
*/
|
|
7
9
|
/**
|
|
8
10
|
* Structural deep equality for committed-state values.
|
|
@@ -10,26 +12,42 @@
|
|
|
10
12
|
* Used by {@link TransactionBuffer} to decide whether a stage actually CHANGED
|
|
11
13
|
* a path or merely re-wrote / reverted it to the value it already held (a
|
|
12
14
|
* "no-op write"), and by `borrowedMutation.firstDifferingPath` to find where a
|
|
13
|
-
* borrowed read moved. Committed state must survive `structuredClone`,
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
15
|
+
* borrowed read moved. Committed state must survive `structuredClone`, so the
|
|
16
|
+
* values it can hold are the kinds the clone keeps, and two values are equal
|
|
17
|
+
* when a record holds the same thing for both. Every kind has ONE arm below
|
|
18
|
+
* (`equalPairs`' switch over `ValueKind` — a kind added to the union does not
|
|
19
|
+
* compile until it is compared). Before 9.22.0 a Date, Map or Set was compared
|
|
20
|
+
* as an object with no own keys, and until 9.44.2 so were a RegExp, an Error,
|
|
21
|
+
* a boxed primitive, an ArrayBuffer, a DataView and a Blob: any two of a kind
|
|
22
|
+
* were "equal", so `$setValue('re', /y/g)` over `/x/g` committed no row and
|
|
23
|
+
* live state kept the OLD value.
|
|
21
24
|
*
|
|
22
25
|
* Semantics:
|
|
23
26
|
* - reference / identical-primitive short-circuits first (cheap fast path)
|
|
24
27
|
* - `NaN` equals `NaN` (primitive compare falls back to `Object.is`)
|
|
28
|
+
* - two values of different KINDS are never equal (a `Date` is never `{}`,
|
|
29
|
+
* an array never `{ 0: … }`)
|
|
25
30
|
* - `Date`: same `getTime()` (two invalid dates are equal)
|
|
31
|
+
* - `RegExp`: same `source` and `flags` (the clone resets `lastIndex`)
|
|
32
|
+
* - `Error`: the same restored kind (one of the six built-in error names,
|
|
33
|
+
* else `Error`), own `message`, `stack` and own `cause` (deep) — what the
|
|
34
|
+
* clone keeps; any other own field is dropped by it, so it never counts
|
|
35
|
+
* (the admitted record's law)
|
|
36
|
+
* - a boxed primitive: the same wrapper type and the same primitive inside
|
|
37
|
+
* - an `ArrayBuffer`: length, resizability and bytes; a typed array or a
|
|
38
|
+
* `DataView`: its type and the bytes IT views — never the rest of its
|
|
39
|
+
* buffer (a Node `Buffer` views a slice of a shared pool, and the record
|
|
40
|
+
* keeps the view's bytes only: `capture/freeze.ts · freezeRecord`)
|
|
26
41
|
* - `Map`: same size AND, per key of `a`, `b` holds a deep-equal value —
|
|
27
42
|
* keys by `Map` identity (`SameValueZero`), values deep
|
|
28
43
|
* - `Set`: same size AND every member of `a` is deep-equal to SOME member
|
|
29
44
|
* of `b` (order-insensitive — a Set has no order to compare by)
|
|
30
|
-
* -
|
|
31
|
-
*
|
|
32
|
-
*
|
|
45
|
+
* - an OPAQUE value (a Blob, a `DOMException` — content this library cannot
|
|
46
|
+
* read): by the caller's {@link OpaqueRule} — equal only to itself by
|
|
47
|
+
* default (replacing one is always a change), or, when both sides are
|
|
48
|
+
* copies of one record, equal to any other of its kind
|
|
49
|
+
* - arrays: equal length AND deep-equal element-wise (order-sensitive; an
|
|
50
|
+
* array's expando keys are out of contract and not compared)
|
|
33
51
|
* - objects: identical set of keys that HOLD a value AND deep-equal per key.
|
|
34
52
|
* An own key whose value is `undefined` counts as ABSENT: it is this
|
|
35
53
|
* library's other spelling of a deleted key (`'full'` mode flattens
|
|
@@ -38,11 +56,13 @@
|
|
|
38
56
|
* mirror — reads the two spellings as one state. So must the net-change
|
|
39
57
|
* filter (9.19.1): `{}` and `{ k: undefined }` are not a change. Arrays
|
|
40
58
|
* are untouched — a slot holding `undefined` is still a slot.
|
|
41
|
-
* - mismatched kinds (array vs object, object vs null) → not equal
|
|
42
59
|
*
|
|
43
60
|
* Cost & safety:
|
|
44
|
-
* - Allocates
|
|
45
|
-
*
|
|
61
|
+
* - Allocates only transient `Object.keys` arrays, an error's property
|
|
62
|
+
* descriptors and a byte view per compared buffer — no clones (but one
|
|
63
|
+
* probe clone per class prototype, remembered: `capture/valueKinds.ts ·
|
|
64
|
+
* kindOf`). It is strictly cheaper than the `structuredClone` the commit
|
|
65
|
+
* already performs.
|
|
46
66
|
* - Primitive comparisons (the bulk of state) are O(1) via the `===` /
|
|
47
67
|
* `Object.is` fast paths; only nested objects/arrays incur a walk, bounded
|
|
48
68
|
* by the value's own size.
|
|
@@ -54,4 +74,20 @@
|
|
|
54
74
|
* change. Dev mode still warns about cycles at write time
|
|
55
75
|
* (`ScopeFacade.setValue`) because they surprise narrative and JSON.
|
|
56
76
|
*/
|
|
57
|
-
export declare function deepEqual(a: any, b: any): boolean;
|
|
77
|
+
export declare function deepEqual(a: any, b: any, opaque?: OpaqueRule): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* How {@link deepEqual} compares two OPAQUE values (a Blob, a `DOMException` —
|
|
80
|
+
* content this library cannot read):
|
|
81
|
+
*
|
|
82
|
+
* - `'identity'` (the default) — equal only to itself. The answer when one
|
|
83
|
+
* side is a value a stage handed in: the net-change filter
|
|
84
|
+
* (`TransactionBuffer`), the append check (`deltaEncoding`). A new object
|
|
85
|
+
* is a change, because nothing can say it is not.
|
|
86
|
+
* - `'copies'` — equal to any other of its kind. The answer when BOTH sides
|
|
87
|
+
* are copies of one recorded value — a fold's generations (`logModel`,
|
|
88
|
+
* `elementProvenance`), a retained read against live state
|
|
89
|
+
* (`borrowedMutation`), the record replayed against the read-back
|
|
90
|
+
* (`admission`): copies never share identity, so identity would call every
|
|
91
|
+
* opaque value changed.
|
|
92
|
+
*/
|
|
93
|
+
export type OpaqueRule = 'identity' | 'copies';
|
|
@@ -402,8 +402,29 @@ export declare function useWalkLimit(limit: number): () => void;
|
|
|
402
402
|
* inside one segment — and an ancestor whose NAME matches is masked whole
|
|
403
403
|
* before the walk goes below it, so the name decides. Conservative: `true`
|
|
404
404
|
* whenever in doubt.
|
|
405
|
+
*
|
|
406
|
+
* Linear in the length of `pattern.source`: every scan here is one forward
|
|
407
|
+
* pass ({@link classBodies}) or a regex that does constant work per position.
|
|
405
408
|
*/
|
|
406
409
|
export declare function needsPath(pattern: RegExp): boolean;
|
|
410
|
+
/**
|
|
411
|
+
* The body of each character class in `source`, left to right: the text
|
|
412
|
+
* between a `[` and the first `]` after it that no `\` escapes (a `\` takes
|
|
413
|
+
* the character after it along). ONE forward pass.
|
|
414
|
+
*
|
|
415
|
+
* It replaces `source.matchAll(/\[((?:\\[\s\S]|[^\]\\])*)\]/g)` and gives the
|
|
416
|
+
* same bodies for every string (pinned against that regex as the control,
|
|
417
|
+
* test/lib/memory/security/needs-path-linear.security.test.ts). The regex was
|
|
418
|
+
* quadratic: after a `[` that never closed it retried at every later `[`,
|
|
419
|
+
* each retry rescanning to the end — `\[` repeated 32,000 times took 0.7 s,
|
|
420
|
+
* and `setPolicy` runs once per stage's scope (CodeQL js/polynomial-redos).
|
|
421
|
+
* The pass stops at the first `[` that never closes instead: a later `[`
|
|
422
|
+
* starts its body where the failed scan was also between two tokens, so it
|
|
423
|
+
* reads the same tail the same way and closes nowhere either.
|
|
424
|
+
*
|
|
425
|
+
* @internal Exported for that test only — not on any public door.
|
|
426
|
+
*/
|
|
427
|
+
export declare function classBodies(source: string): string[];
|
|
407
428
|
/**
|
|
408
429
|
* THE TAINT RULE at a subflow mapper boundary (an `inputMapper` seeding a
|
|
409
430
|
* subflow, an `outputMapper` merging it back): a key the mapper writes
|
|
@@ -187,7 +187,10 @@ export declare class ExecutionRuntime {
|
|
|
187
187
|
* The frozen, detached fold base served as {@link RuntimeSnapshot.initialState}.
|
|
188
188
|
* Computed once (the base never changes after construction) and shared —
|
|
189
189
|
* safe because it is deeply frozen, so no holder can mutate it or reach the
|
|
190
|
-
* engine through it
|
|
190
|
+
* engine through it; a base holding a Date, Map, buffer … (what freezing
|
|
191
|
+
* cannot seal) is served by the public snapshot as a copy of its open paths
|
|
192
|
+
* (`runner/snapshot.ts · servedSnapshot`, `capture/freeze.ts · serveRecord`,
|
|
193
|
+
* 9.44.2).
|
|
191
194
|
*/
|
|
192
195
|
private getFoldBase;
|
|
193
196
|
getPipelines(): string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "footprintjs",
|
|
3
|
-
"version": "9.44.
|
|
3
|
+
"version": "9.44.2",
|
|
4
4
|
"description": "Explainable backend flows — automatic causal traces, decision evidence, and MCP tool generation for AI agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sanjay Krishna Anbalagan",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"check:layering": "node scripts/check-layering.mjs",
|
|
54
54
|
"check:doc-snippets": "node scripts/check-doc-snippets.mjs",
|
|
55
55
|
"audit:family": "node scripts/audit-family-versions.mjs",
|
|
56
|
+
"audit:consumers": "node scripts/audit-consumers.mjs",
|
|
56
57
|
"lint": "eslint '{src,test}/**/*.ts' --ext .ts",
|
|
57
58
|
"lint:fix": "eslint '{src,test}/**/*.ts' --ext .ts --fix",
|
|
58
59
|
"format:fix": "prettier --config .prettierrc.js --write '{src,test}/**/*.ts'",
|