footprintjs 9.23.0 → 9.23.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/dist/esm/lib/memory/TransactionBuffer.d.ts +18 -0
- package/dist/esm/lib/memory/TransactionBuffer.js +125 -86
- package/dist/esm/lib/reactive/arrayTraps.d.ts +23 -3
- package/dist/esm/lib/reactive/arrayTraps.js +149 -188
- package/dist/esm/lib/reactive/createTypedScope.js +150 -283
- package/dist/esm/lib/reactive/liveView.d.ts +51 -0
- package/dist/esm/lib/reactive/liveView.js +83 -1
- package/dist/esm/lib/reactive/writeTraps.d.ts +59 -0
- package/dist/esm/lib/reactive/writeTraps.js +132 -0
- package/dist/lib/memory/TransactionBuffer.js +125 -86
- package/dist/lib/reactive/arrayTraps.js +149 -187
- package/dist/lib/reactive/createTypedScope.js +147 -280
- package/dist/lib/reactive/liveView.js +87 -2
- package/dist/lib/reactive/writeTraps.js +139 -0
- package/dist/types/lib/memory/TransactionBuffer.d.ts +18 -0
- package/dist/types/lib/reactive/arrayTraps.d.ts +23 -3
- package/dist/types/lib/reactive/liveView.d.ts +51 -0
- package/dist/types/lib/reactive/writeTraps.d.ts +59 -0
- package/package.json +1 -1
|
@@ -10,9 +10,71 @@
|
|
|
10
10
|
* found on element proxies, then the same root on nested and terminal ones).
|
|
11
11
|
* So every read trap resolves through the CURRENT value and falls back to the
|
|
12
12
|
* captured object only when the path is gone or no longer holds an object.
|
|
13
|
+
*
|
|
14
|
+
* The READ side of every object proxy lives here — {@link liveObject},
|
|
15
|
+
* {@link liveGetTrap}, {@link liveInspectionTraps}, and the ONE per-member
|
|
16
|
+
* proxy cache {@link cachedMember} (9.23.2); the WRITE side is
|
|
17
|
+
* `writeTraps.ts`. A factory only wires them (9.23.1).
|
|
13
18
|
*/
|
|
14
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.liveInspectionTraps = exports.liveObject = void 0;
|
|
20
|
+
exports.liveInspectionTraps = exports.liveGetTrap = exports.liveObject = exports.cachedMember = exports.MemberCache = void 0;
|
|
21
|
+
const jsonProjection_js_1 = require("./jsonProjection.js");
|
|
22
|
+
/**
|
|
23
|
+
* A proxy per member NAME under one parent proxy, validated by the raw
|
|
24
|
+
* member's identity. The map is allocated on the FIRST insert: an element
|
|
25
|
+
* proxy over `{ id, n }` reads only primitives and must not pay for one
|
|
26
|
+
* (10,000 of them in the naive re-read loop — measured, `bench/nested-reads.ts`).
|
|
27
|
+
*/
|
|
28
|
+
class MemberCache {
|
|
29
|
+
entries;
|
|
30
|
+
/** The proxy cached under `prop`, if it was built over exactly this raw `value`. */
|
|
31
|
+
hit(prop, value) {
|
|
32
|
+
const cached = this.entries?.get(prop);
|
|
33
|
+
return cached !== undefined && cached.ref === value ? cached.proxy : undefined;
|
|
34
|
+
}
|
|
35
|
+
remember(prop, value, proxy) {
|
|
36
|
+
(this.entries ??= new Map()).set(prop, { ref: value, proxy });
|
|
37
|
+
}
|
|
38
|
+
/** Forget `prop` — a write replaced its value, so the next read must build over the new one. */
|
|
39
|
+
delete(prop) {
|
|
40
|
+
this.entries?.delete(prop);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.MemberCache = MemberCache;
|
|
44
|
+
/**
|
|
45
|
+
* WHY the cache: `o.arr === o.arr` must hold within a stage, and
|
|
46
|
+
* `for (i < N) s.k.arr[i]` must build ONE array proxy — whose element cache
|
|
47
|
+
* then serves every index — not N (the 9.22.0 perf review, finding 4: the
|
|
48
|
+
* nested get trap built a fresh array proxy, with a fresh empty element
|
|
49
|
+
* cache, on every `.arr`). The top-level scope has cached its child proxies
|
|
50
|
+
* this way since before 9.22.0; this is that leaf, shared by all four.
|
|
51
|
+
*
|
|
52
|
+
* Validated by IDENTITY, invalidated by replacement: every write through a
|
|
53
|
+
* proxy rebuilds the containers on its path (`structuralWrite.setInPath`),
|
|
54
|
+
* so after `s.k.arr[0].n = 1` the raw `arr` is a new array, the ref check
|
|
55
|
+
* misses, and the next read builds a proxy over the NEW value. A hit can
|
|
56
|
+
* never be stale — every proxy reads live (`liveGetTrap`), so the cached one
|
|
57
|
+
* and a fresh one answer the same.
|
|
58
|
+
*
|
|
59
|
+
* Keyed by NAME under THIS parent, never by the raw value alone: a diamond
|
|
60
|
+
* (one array under `k.a` and `k.b`) needs two proxies, each bound to its own
|
|
61
|
+
* path and sink. Only what `build` actually WRAPPED is kept — a member handed
|
|
62
|
+
* back raw (a `Date`, a frozen object, a cycle edge) is not worth an entry.
|
|
63
|
+
* `build` is the caller's stable child step, not a closure made per read: a
|
|
64
|
+
* hit allocates nothing, and a primitive member skips the cache entirely.
|
|
65
|
+
*/
|
|
66
|
+
function cachedMember(cache, prop, value, build, segments) {
|
|
67
|
+
if (value === null || typeof value !== 'object')
|
|
68
|
+
return build(value, [...segments, prop]);
|
|
69
|
+
const hit = cache.hit(prop, value);
|
|
70
|
+
if (hit !== undefined)
|
|
71
|
+
return hit;
|
|
72
|
+
const proxy = build(value, [...segments, prop]);
|
|
73
|
+
if (proxy !== value)
|
|
74
|
+
cache.remember(prop, value, proxy);
|
|
75
|
+
return proxy;
|
|
76
|
+
}
|
|
77
|
+
exports.cachedMember = cachedMember;
|
|
16
78
|
/** The object at `segments` under the current root, or `raw` when it is gone. */
|
|
17
79
|
function liveObject(raw, current) {
|
|
18
80
|
return current !== null && typeof current === 'object' && !Array.isArray(current)
|
|
@@ -20,6 +82,29 @@ function liveObject(raw, current) {
|
|
|
20
82
|
: raw;
|
|
21
83
|
}
|
|
22
84
|
exports.liveObject = liveObject;
|
|
85
|
+
/**
|
|
86
|
+
* WHY one get trap: the names a proxy answers ITSELF (promise/matcher guards,
|
|
87
|
+
* `constructor`, and the JSON law — serializing a proxied value equals
|
|
88
|
+
* serializing the raw one, `jsonProjection.ts`) must be the same three lines
|
|
89
|
+
* on every object proxy; any other member is handed to `child` with its full
|
|
90
|
+
* path — through the parent's {@link cachedMember} — and only THAT step
|
|
91
|
+
* differs per factory (the cycle policy).
|
|
92
|
+
*/
|
|
93
|
+
function liveGetTrap(live, segments, child, members) {
|
|
94
|
+
return (_target, prop) => {
|
|
95
|
+
const raw = live();
|
|
96
|
+
if (typeof prop === 'symbol')
|
|
97
|
+
return raw[prop];
|
|
98
|
+
if (prop === 'then' || prop === 'asymmetricMatch')
|
|
99
|
+
return undefined;
|
|
100
|
+
if (prop === 'constructor')
|
|
101
|
+
return Object;
|
|
102
|
+
if (prop === 'toJSON')
|
|
103
|
+
return () => (0, jsonProjection_js_1.toJSONView)(raw);
|
|
104
|
+
return cachedMember(members, prop, raw[prop], child, segments);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
exports.liveGetTrap = liveGetTrap;
|
|
23
108
|
/**
|
|
24
109
|
* The three inspection traps (`in`, `Object.keys`, descriptors) answered from
|
|
25
110
|
* the live object. The one Proxy invariant that could bite — a non-extensible
|
|
@@ -45,4 +130,4 @@ function liveInspectionTraps(live) {
|
|
|
45
130
|
};
|
|
46
131
|
}
|
|
47
132
|
exports.liveInspectionTraps = liveInspectionTraps;
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGl2ZVZpZXcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3JlYWN0aXZlL2xpdmVWaWV3LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7R0FXRzs7O0FBRUgsaUZBQWlGO0FBQ2pGLFNBQWdCLFVBQVUsQ0FBQyxHQUE0QixFQUFFLE9BQWdCO0lBQ3ZFLE9BQU8sT0FBTyxLQUFLLElBQUksSUFBSSxPQUFPLE9BQU8sS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztRQUMvRSxDQUFDLENBQUUsT0FBbUM7UUFDdEMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztBQUNWLENBQUM7QUFKRCxnQ0FJQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxTQUFnQixtQkFBbUIsQ0FDakMsSUFBa0I7SUFFbEIsT0FBTztRQUNMLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSTtZQUNkLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzFFLENBQUM7UUFDRCxPQUFPLENBQUMsTUFBTTtZQUNaLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDeEUsQ0FBQztRQUNELHdCQUF3QixDQUFDLE1BQU0sRUFBRSxJQUFJO1lBQ25DLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQztnQkFBRSxPQUFPLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDdkYsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLElBQUksRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ2pFLE9BQU8sVUFBVSxJQUFJLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLFVBQVUsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQztRQUNyRyxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFoQkQsa0RBZ0JDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiByZWFjdGl2ZS9saXZlVmlldyDigJQgd2hhdCBhIHByb3h5IHN0YW5kcyBmb3IsIEFTIElUIFNUQU5EUyBOT1cuXG4gKlxuICogRXZlcnkgd3JpdGUgdGhyb3VnaCB0aGUgdHlwZWQgc2NvcGUgcmVidWlsZHMgdGhlIHJvb3QgaW1tdXRhYmx5XG4gKiAoYHN0cnVjdHVyYWxXcml0ZS5zZXRJblBhdGhgLCBhIGZyZXNoIG1lcmdlKSwgc28gdGhlIG9iamVjdCBhIHByb3h5IHdhc1xuICogY3JlYXRlZCBvdmVyIGlzIHRoZSBQUkUtd3JpdGUgdmFsdWUgZm9yIGV2ZXIgYWZ0ZXIuIEEgaGVsZCBwcm94eSB0aGF0IHJlYWRcbiAqIGZyb20gaXQgc2F3IGl0cyBvd24gd3JpdGVzIHZhbmlzaDogYG8ueCArPSAxYCB0d2ljZSBnYXZlIDIsIGBvLmIgPSBvLmEgKiAxMGBcbiAqIHVzZWQgdGhlIG9sZCBgYWAsIGAneCcgaW4gb2Agd2FzIGZhbHNlIHJpZ2h0IGFmdGVyIGBvLnggPSAxYCAoOS4yMi4wIOKAlCBmaXJzdFxuICogZm91bmQgb24gZWxlbWVudCBwcm94aWVzLCB0aGVuIHRoZSBzYW1lIHJvb3Qgb24gbmVzdGVkIGFuZCB0ZXJtaW5hbCBvbmVzKS5cbiAqIFNvIGV2ZXJ5IHJlYWQgdHJhcCByZXNvbHZlcyB0aHJvdWdoIHRoZSBDVVJSRU5UIHZhbHVlIGFuZCBmYWxscyBiYWNrIHRvIHRoZVxuICogY2FwdHVyZWQgb2JqZWN0IG9ubHkgd2hlbiB0aGUgcGF0aCBpcyBnb25lIG9yIG5vIGxvbmdlciBob2xkcyBhbiBvYmplY3QuXG4gKi9cblxuLyoqIFRoZSBvYmplY3QgYXQgYHNlZ21lbnRzYCB1bmRlciB0aGUgY3VycmVudCByb290LCBvciBgcmF3YCB3aGVuIGl0IGlzIGdvbmUuICovXG5leHBvcnQgZnVuY3Rpb24gbGl2ZU9iamVjdChyYXc6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBjdXJyZW50OiB1bmtub3duKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4gY3VycmVudCAhPT0gbnVsbCAmJiB0eXBlb2YgY3VycmVudCA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoY3VycmVudClcbiAgICA/IChjdXJyZW50IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KVxuICAgIDogcmF3O1xufVxuXG4vKipcbiAqIFRoZSB0aHJlZSBpbnNwZWN0aW9uIHRyYXBzIChgaW5gLCBgT2JqZWN0LmtleXNgLCBkZXNjcmlwdG9ycykgYW5zd2VyZWQgZnJvbVxuICogdGhlIGxpdmUgb2JqZWN0LiBUaGUgb25lIFByb3h5IGludmFyaWFudCB0aGF0IGNvdWxkIGJpdGUg4oCUIGEgbm9uLWV4dGVuc2libGVcbiAqIHRhcmdldCBtdXN0IHJlcG9ydCBleGFjdGx5IGl0cyBvd24ga2V5cywgYW5kIGEgcHJvcGVydHkgdGhlIHRhcmdldCBsYWNrc1xuICogbXVzdCBiZSByZXBvcnRlZCBjb25maWd1cmFibGUg4oCUIGlzIGhvbm91cmVkOiB0aGUgYWxsb3dsaXN0IGFscmVhZHkgcmVmdXNlc1xuICogZnJvemVuIGFuZCBzZWFsZWQgdmFsdWVzLCBzbyB0aGUgYHByZXZlbnRFeHRlbnNpb25zYC1vbmx5IGNvcm5lciBhbnN3ZXJzXG4gKiBmcm9tIHRoZSB0YXJnZXQsIGFuZCBhIGxpdmUgZGVzY3JpcHRvciBpcyBhbHdheXMgcmVwb3J0ZWQgY29uZmlndXJhYmxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbGl2ZUluc3BlY3Rpb25UcmFwcyhcbiAgbGl2ZTogKCkgPT4gb2JqZWN0LFxuKTogUGljazxQcm94eUhhbmRsZXI8b2JqZWN0PiwgJ2hhcycgfCAnb3duS2V5cycgfCAnZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yJz4ge1xuICByZXR1cm4ge1xuICAgIGhhcyh0YXJnZXQsIHByb3ApIHtcbiAgICAgIHJldHVybiBSZWZsZWN0LmhhcyhPYmplY3QuaXNFeHRlbnNpYmxlKHRhcmdldCkgPyBsaXZlKCkgOiB0YXJnZXQsIHByb3ApO1xuICAgIH0sXG4gICAgb3duS2V5cyh0YXJnZXQpIHtcbiAgICAgIHJldHVybiBSZWZsZWN0Lm93bktleXMoT2JqZWN0LmlzRXh0ZW5zaWJsZSh0YXJnZXQpID8gbGl2ZSgpIDogdGFyZ2V0KTtcbiAgICB9LFxuICAgIGdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIHByb3ApIHtcbiAgICAgIGlmICghT2JqZWN0LmlzRXh0ZW5zaWJsZSh0YXJnZXQpKSByZXR1cm4gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIHByb3ApO1xuICAgICAgY29uc3QgZGVzY3JpcHRvciA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IobGl2ZSgpLCBwcm9wKTtcbiAgICAgIHJldHVybiBkZXNjcmlwdG9yICYmICFkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA/IHsgLi4uZGVzY3JpcHRvciwgY29uZmlndXJhYmxlOiB0cnVlIH0gOiBkZXNjcmlwdG9yO1xuICAgIH0sXG4gIH07XG59XG4iXX0=
|
|
133
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGl2ZVZpZXcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3JlYWN0aXZlL2xpdmVWaWV3LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRzs7O0FBRUgsMkRBQWlEO0FBRWpEOzs7OztHQUtHO0FBQ0gsTUFBYSxXQUFXO0lBQ2QsT0FBTyxDQUFnRDtJQUUvRCxvRkFBb0Y7SUFDcEYsR0FBRyxDQUFDLElBQVksRUFBRSxLQUFhO1FBQzdCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZDLE9BQU8sTUFBTSxLQUFLLFNBQVMsSUFBSSxNQUFNLENBQUMsR0FBRyxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQ2pGLENBQUM7SUFFRCxRQUFRLENBQUMsSUFBWSxFQUFFLEtBQWEsRUFBRSxLQUFjO1FBQ2xELENBQUMsSUFBSSxDQUFDLE9BQU8sS0FBSyxJQUFJLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUNoRSxDQUFDO0lBRUQsZ0dBQWdHO0lBQ2hHLE1BQU0sQ0FBQyxJQUFZO1FBQ2pCLElBQUksQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzdCLENBQUM7Q0FDRjtBQWpCRCxrQ0FpQkM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUJHO0FBQ0gsU0FBZ0IsWUFBWSxDQUMxQixLQUFrQixFQUNsQixJQUFZLEVBQ1osS0FBYyxFQUNkLEtBQWtELEVBQ2xELFFBQTJCO0lBRTNCLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRO1FBQUUsT0FBTyxLQUFLLENB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LEVBQUUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDO1FBQ3JHLENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQztBQWhCRCxrREFnQkMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIHJlYWN0aXZlL2xpdmVWaWV3IOKAlCB3aGF0IGEgcHJveHkgc3RhbmRzIGZvciwgQVMgSVQgU1RBTkRTIE5PVy5cbiAqXG4gKiBFdmVyeSB3cml0ZSB0aHJvdWdoIHRoZSB0eXBlZCBzY29wZSByZWJ1aWxkcyB0aGUgcm9vdCBpbW11dGFibHlcbiAqIChgc3RydWN0dXJhbFdyaXRlLnNldEluUGF0aGAsIGEgZnJlc2ggbWVyZ2UpLCBzbyB0aGUgb2JqZWN0IGEgcHJveHkgd2FzXG4gKiBjcmVhdGVkIG92ZXIgaXMgdGhlIFBSRS13cml0ZSB2YWx1ZSBmb3IgZXZlciBhZnRlci4gQSBoZWxkIHByb3h5IHRoYXQgcmVhZFxuICogZnJvbSBpdCBzYXcgaXRzIG93biB3cml0ZXMgdmFuaXNoOiBgby54ICs9IDFgIHR3aWNlIGdhdmUgMiwgYG8uYiA9IG8uYSAqIDEwYFxuICogdXNlZCB0aGUgb2xkIGBhYCwgYCd4JyBpbiBvYCB3YXMgZmFsc2UgcmlnaHQgYWZ0ZXIgYG8ueCA9IDFgICg5LjIyLjAg4oCUIGZpcnN0XG4gKiBmb3VuZCBvbiBlbGVtZW50IHByb3hpZXMsIHRoZW4gdGhlIHNhbWUgcm9vdCBvbiBuZXN0ZWQgYW5kIHRlcm1pbmFsIG9uZXMpLlxuICogU28gZXZlcnkgcmVhZCB0cmFwIHJlc29sdmVzIHRocm91Z2ggdGhlIENVUlJFTlQgdmFsdWUgYW5kIGZhbGxzIGJhY2sgdG8gdGhlXG4gKiBjYXB0dXJlZCBvYmplY3Qgb25seSB3aGVuIHRoZSBwYXRoIGlzIGdvbmUgb3Igbm8gbG9uZ2VyIGhvbGRzIGFuIG9iamVjdC5cbiAqXG4gKiBUaGUgUkVBRCBzaWRlIG9mIGV2ZXJ5IG9iamVjdCBwcm94eSBsaXZlcyBoZXJlIOKAlCB7QGxpbmsgbGl2ZU9iamVjdH0sXG4gKiB7QGxpbmsgbGl2ZUdldFRyYXB9LCB7QGxpbmsgbGl2ZUluc3BlY3Rpb25UcmFwc30sIGFuZCB0aGUgT05FIHBlci1tZW1iZXJcbiAqIHByb3h5IGNhY2hlIHtAbGluayBjYWNoZWRNZW1iZXJ9ICg5LjIzLjIpOyB0aGUgV1JJVEUgc2lkZSBpc1xuICogYHdyaXRlVHJhcHMudHNgLiBBIGZhY3Rvcnkgb25seSB3aXJlcyB0aGVtICg5LjIzLjEpLlxuICovXG5cbmltcG9ydCB7IHRvSlNPTlZpZXcgfSBmcm9tICcuL2pzb25Qcm9qZWN0aW9uLmpzJztcblxuLyoqXG4gKiBBIHByb3h5IHBlciBtZW1iZXIgTkFNRSB1bmRlciBvbmUgcGFyZW50IHByb3h5LCB2YWxpZGF0ZWQgYnkgdGhlIHJhd1xuICogbWVtYmVyJ3MgaWRlbnRpdHkuIFRoZSBtYXAgaXMgYWxsb2NhdGVkIG9uIHRoZSBGSVJTVCBpbnNlcnQ6IGFuIGVsZW1lbnRcbiAqIHByb3h5IG92ZXIgYHsgaWQsIG4gfWAgcmVhZHMgb25seSBwcmltaXRpdmVzIGFuZCBtdXN0IG5vdCBwYXkgZm9yIG9uZVxuICogKDEwLDAwMCBvZiB0aGVtIGluIHRoZSBuYWl2ZSByZS1yZWFkIGxvb3Ag4oCUIG1lYXN1cmVkLCBgYmVuY2gvbmVzdGVkLXJlYWRzLnRzYCkuXG4gKi9cbmV4cG9ydCBjbGFzcyBNZW1iZXJDYWNoZSB7XG4gIHByaXZhdGUgZW50cmllcz86IE1hcDxzdHJpbmcsIHsgcmVmOiBvYmplY3Q7IHByb3h5OiB1bmtub3duIH0+O1xuXG4gIC8qKiBUaGUgcHJveHkgY2FjaGVkIHVuZGVyIGBwcm9wYCwgaWYgaXQgd2FzIGJ1aWx0IG92ZXIgZXhhY3RseSB0aGlzIHJhdyBgdmFsdWVgLiAqL1xuICBoaXQocHJvcDogc3RyaW5nLCB2YWx1ZTogb2JqZWN0KTogdW5rbm93biB7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5lbnRyaWVzPy5nZXQocHJvcCk7XG4gICAgcmV0dXJuIGNhY2hlZCAhPT0gdW5kZWZpbmVkICYmIGNhY2hlZC5yZWYgPT09IHZhbHVlID8gY2FjaGVkLnByb3h5IDogdW5kZWZpbmVkO1xuICB9XG5cbiAgcmVtZW1iZXIocHJvcDogc3RyaW5nLCB2YWx1ZTogb2JqZWN0LCBwcm94eTogdW5rbm93bik6IHZvaWQge1xuICAgICh0aGlzLmVudHJpZXMgPz89IG5ldyBNYXAoKSkuc2V0KHByb3AsIHsgcmVmOiB2YWx1ZSwgcHJveHkgfSk7XG4gIH1cblxuICAvKiogRm9yZ2V0IGBwcm9wYCDigJQgYSB3cml0ZSByZXBsYWNlZCBpdHMgdmFsdWUsIHNvIHRoZSBuZXh0IHJlYWQgbXVzdCBidWlsZCBvdmVyIHRoZSBuZXcgb25lLiAqL1xuICBkZWxldGUocHJvcDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5lbnRyaWVzPy5kZWxldGUocHJvcCk7XG4gIH1cbn1cblxuLyoqXG4gKiBXSFkgdGhlIGNhY2hlOiBgby5hcnIgPT09IG8uYXJyYCBtdXN0IGhvbGQgd2l0aGluIGEgc3RhZ2UsIGFuZFxuICogYGZvciAoaSA8IE4pIHMuay5hcnJbaV1gIG11c3QgYnVpbGQgT05FIGFycmF5IHByb3h5IOKAlCB3aG9zZSBlbGVtZW50IGNhY2hlXG4gKiB0aGVuIHNlcnZlcyBldmVyeSBpbmRleCDigJQgbm90IE4gKHRoZSA5LjIyLjAgcGVyZiByZXZpZXcsIGZpbmRpbmcgNDogdGhlXG4gKiBuZXN0ZWQgZ2V0IHRyYXAgYnVpbHQgYSBmcmVzaCBhcnJheSBwcm94eSwgd2l0aCBhIGZyZXNoIGVtcHR5IGVsZW1lbnRcbiAqIGNhY2hlLCBvbiBldmVyeSBgLmFycmApLiBUaGUgdG9wLWxldmVsIHNjb3BlIGhhcyBjYWNoZWQgaXRzIGNoaWxkIHByb3hpZXNcbiAqIHRoaXMgd2F5IHNpbmNlIGJlZm9yZSA5LjIyLjA7IHRoaXMgaXMgdGhhdCBsZWFmLCBzaGFyZWQgYnkgYWxsIGZvdXIuXG4gKlxuICogVmFsaWRhdGVkIGJ5IElERU5USVRZLCBpbnZhbGlkYXRlZCBieSByZXBsYWNlbWVudDogZXZlcnkgd3JpdGUgdGhyb3VnaCBhXG4gKiBwcm94eSByZWJ1aWxkcyB0aGUgY29udGFpbmVycyBvbiBpdHMgcGF0aCAoYHN0cnVjdHVyYWxXcml0ZS5zZXRJblBhdGhgKSxcbiAqIHNvIGFmdGVyIGBzLmsuYXJyWzBdLm4gPSAxYCB0aGUgcmF3IGBhcnJgIGlzIGEgbmV3IGFycmF5LCB0aGUgcmVmIGNoZWNrXG4gKiBtaXNzZXMsIGFuZCB0aGUgbmV4dCByZWFkIGJ1aWxkcyBhIHByb3h5IG92ZXIgdGhlIE5FVyB2YWx1ZS4gQSBoaXQgY2FuXG4gKiBuZXZlciBiZSBzdGFsZSDigJQgZXZlcnkgcHJveHkgcmVhZHMgbGl2ZSAoYGxpdmVHZXRUcmFwYCksIHNvIHRoZSBjYWNoZWQgb25lXG4gKiBhbmQgYSBmcmVzaCBvbmUgYW5zd2VyIHRoZSBzYW1lLlxuICpcbiAqIEtleWVkIGJ5IE5BTUUgdW5kZXIgVEhJUyBwYXJlbnQsIG5ldmVyIGJ5IHRoZSByYXcgdmFsdWUgYWxvbmU6IGEgZGlhbW9uZFxuICogKG9uZSBhcnJheSB1bmRlciBgay5hYCBhbmQgYGsuYmApIG5lZWRzIHR3byBwcm94aWVzLCBlYWNoIGJvdW5kIHRvIGl0cyBvd25cbiAqIHBhdGggYW5kIHNpbmsuIE9ubHkgd2hhdCBgYnVpbGRgIGFjdHVhbGx5IFdSQVBQRUQgaXMga2VwdCDigJQgYSBtZW1iZXIgaGFuZGVkXG4gKiBiYWNrIHJhdyAoYSBgRGF0ZWAsIGEgZnJvemVuIG9iamVjdCwgYSBjeWNsZSBlZGdlKSBpcyBub3Qgd29ydGggYW4gZW50cnkuXG4gKiBgYnVpbGRgIGlzIHRoZSBjYWxsZXIncyBzdGFibGUgY2hpbGQgc3RlcCwgbm90IGEgY2xvc3VyZSBtYWRlIHBlciByZWFkOiBhXG4gKiBoaXQgYWxsb2NhdGVzIG5vdGhpbmcsIGFuZCBhIHByaW1pdGl2ZSBtZW1iZXIgc2tpcHMgdGhlIGNhY2hlIGVudGlyZWx5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY2FjaGVkTWVtYmVyKFxuICBjYWNoZTogTWVtYmVyQ2FjaGUsXG4gIHByb3A6IHN0cmluZyxcbiAgdmFsdWU6IHVua25vd24sXG4gIGJ1aWxkOiAodmFsdWU6IHVua25vd24sIHBhdGg6IHN0cmluZ1tdKSA9PiB1bmtub3duLFxuICBzZWdtZW50czogcmVhZG9ubHkgc3RyaW5nW10sXG4pOiB1bmtub3duIHtcbiAgaWYgKHZhbHVlID09PSBudWxsIHx8IHR5cGVvZiB2YWx1ZSAhPT0gJ29iamVjdCcpIHJldHVybiBidWlsZCh2YWx1ZSwgWy4uLnNlZ21lbnRzLCBwcm9wXSk7XG4gIGNvbnN0IGhpdCA9IGNhY2hlLmhpdChwcm9wLCB2YWx1ZSk7XG4gIGlmIChoaXQgIT09IHVuZGVmaW5lZCkgcmV0dXJuIGhpdDtcbiAgY29uc3QgcHJveHkgPSBidWlsZCh2YWx1ZSwgWy4uLnNlZ21lbnRzLCBwcm9wXSk7XG4gIGlmIChwcm94eSAhPT0gdmFsdWUpIGNhY2hlLnJlbWVtYmVyKHByb3AsIHZhbHVlLCBwcm94eSk7XG4gIHJldHVybiBwcm94eTtcbn1cblxuLyoqIFRoZSBvYmplY3QgYXQgYHNlZ21lbnRzYCB1bmRlciB0aGUgY3VycmVudCByb290LCBvciBgcmF3YCB3aGVuIGl0IGlzIGdvbmUuICovXG5leHBvcnQgZnVuY3Rpb24gbGl2ZU9iamVjdChyYXc6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBjdXJyZW50OiB1bmtub3duKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4gY3VycmVudCAhPT0gbnVsbCAmJiB0eXBlb2YgY3VycmVudCA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoY3VycmVudClcbiAgICA/IChjdXJyZW50IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KVxuICAgIDogcmF3O1xufVxuXG4vKipcbiAqIFdIWSBvbmUgZ2V0IHRyYXA6IHRoZSBuYW1lcyBhIHByb3h5IGFuc3dlcnMgSVRTRUxGIChwcm9taXNlL21hdGNoZXIgZ3VhcmRzLFxuICogYGNvbnN0cnVjdG9yYCwgYW5kIHRoZSBKU09OIGxhdyDigJQgc2VyaWFsaXppbmcgYSBwcm94aWVkIHZhbHVlIGVxdWFsc1xuICogc2VyaWFsaXppbmcgdGhlIHJhdyBvbmUsIGBqc29uUHJvamVjdGlvbi50c2ApIG11c3QgYmUgdGhlIHNhbWUgdGhyZWUgbGluZXNcbiAqIG9uIGV2ZXJ5IG9iamVjdCBwcm94eTsgYW55IG90aGVyIG1lbWJlciBpcyBoYW5kZWQgdG8gYGNoaWxkYCB3aXRoIGl0cyBmdWxsXG4gKiBwYXRoIOKAlCB0aHJvdWdoIHRoZSBwYXJlbnQncyB7QGxpbmsgY2FjaGVkTWVtYmVyfSDigJQgYW5kIG9ubHkgVEhBVCBzdGVwXG4gKiBkaWZmZXJzIHBlciBmYWN0b3J5ICh0aGUgY3ljbGUgcG9saWN5KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxpdmVHZXRUcmFwKFxuICBsaXZlOiAoKSA9PiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgc2VnbWVudHM6IHJlYWRvbmx5IHN0cmluZ1tdLFxuICBjaGlsZDogKHZhbHVlOiB1bmtub3duLCBwYXRoOiBzdHJpbmdbXSkgPT4gdW5rbm93bixcbiAgbWVtYmVyczogTWVtYmVyQ2FjaGUsXG4pOiBQcm94eUhhbmRsZXI8b2JqZWN0PlsnZ2V0J10ge1xuICByZXR1cm4gKF90YXJnZXQsIHByb3ApID0+IHtcbiAgICBjb25zdCByYXcgPSBsaXZlKCk7XG4gICAgaWYgKHR5cGVvZiBwcm9wID09PSAnc3ltYm9sJykgcmV0dXJuIChyYXcgYXMgYW55KVtwcm9wXTtcbiAgICBpZiAocHJvcCA9PT0gJ3RoZW4nIHx8IHByb3AgPT09ICdhc3ltbWV0cmljTWF0Y2gnKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIGlmIChwcm9wID09PSAnY29uc3RydWN0b3InKSByZXR1cm4gT2JqZWN0O1xuICAgIGlmIChwcm9wID09PSAndG9KU09OJykgcmV0dXJuICgpID0+IHRvSlNPTlZpZXcocmF3KTtcbiAgICByZXR1cm4gY2FjaGVkTWVtYmVyKG1lbWJlcnMsIHByb3AsIHJhd1twcm9wXSwgY2hpbGQsIHNlZ21lbnRzKTtcbiAgfTtcbn1cblxuLyoqXG4gKiBUaGUgdGhyZWUgaW5zcGVjdGlvbiB0cmFwcyAoYGluYCwgYE9iamVjdC5rZXlzYCwgZGVzY3JpcHRvcnMpIGFuc3dlcmVkIGZyb21cbiAqIHRoZSBsaXZlIG9iamVjdC4gVGhlIG9uZSBQcm94eSBpbnZhcmlhbnQgdGhhdCBjb3VsZCBiaXRlIOKAlCBhIG5vbi1leHRlbnNpYmxlXG4gKiB0YXJnZXQgbXVzdCByZXBvcnQgZXhhY3RseSBpdHMgb3duIGtleXMsIGFuZCBhIHByb3BlcnR5IHRoZSB0YXJnZXQgbGFja3NcbiAqIG11c3QgYmUgcmVwb3J0ZWQgY29uZmlndXJhYmxlIOKAlCBpcyBob25vdXJlZDogdGhlIGFsbG93bGlzdCBhbHJlYWR5IHJlZnVzZXNcbiAqIGZyb3plbiBhbmQgc2VhbGVkIHZhbHVlcywgc28gdGhlIGBwcmV2ZW50RXh0ZW5zaW9uc2Atb25seSBjb3JuZXIgYW5zd2Vyc1xuICogZnJvbSB0aGUgdGFyZ2V0LCBhbmQgYSBsaXZlIGRlc2NyaXB0b3IgaXMgYWx3YXlzIHJlcG9ydGVkIGNvbmZpZ3VyYWJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxpdmVJbnNwZWN0aW9uVHJhcHMoXG4gIGxpdmU6ICgpID0+IG9iamVjdCxcbik6IFBpY2s8UHJveHlIYW5kbGVyPG9iamVjdD4sICdoYXMnIHwgJ293bktleXMnIHwgJ2dldE93blByb3BlcnR5RGVzY3JpcHRvcic+IHtcbiAgcmV0dXJuIHtcbiAgICBoYXModGFyZ2V0LCBwcm9wKSB7XG4gICAgICByZXR1cm4gUmVmbGVjdC5oYXMoT2JqZWN0LmlzRXh0ZW5zaWJsZSh0YXJnZXQpID8gbGl2ZSgpIDogdGFyZ2V0LCBwcm9wKTtcbiAgICB9LFxuICAgIG93bktleXModGFyZ2V0KSB7XG4gICAgICByZXR1cm4gUmVmbGVjdC5vd25LZXlzKE9iamVjdC5pc0V4dGVuc2libGUodGFyZ2V0KSA/IGxpdmUoKSA6IHRhcmdldCk7XG4gICAgfSxcbiAgICBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodGFyZ2V0LCBwcm9wKSB7XG4gICAgICBpZiAoIU9iamVjdC5pc0V4dGVuc2libGUodGFyZ2V0KSkgcmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodGFyZ2V0LCBwcm9wKTtcbiAgICAgIGNvbnN0IGRlc2NyaXB0b3IgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGxpdmUoKSwgcHJvcCk7XG4gICAgICByZXR1cm4gZGVzY3JpcHRvciAmJiAhZGVzY3JpcHRvci5jb25maWd1cmFibGUgPyB7IC4uLmRlc2NyaXB0b3IsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9IDogZGVzY3JpcHRvcjtcbiAgICB9LFxuICB9O1xufVxuIl19
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* reactive/writeTraps — where a proxy's writes LAND, and the two traps that
|
|
4
|
+
* send them there.
|
|
5
|
+
*
|
|
6
|
+
* Every object proxy in this folder (nested, terminal, element) intercepts
|
|
7
|
+
* the same three things — a property set, a property delete, and an array
|
|
8
|
+
* mutation somewhere below it — and differs only in where the write goes:
|
|
9
|
+
* a nested/terminal proxy commits under a TOP-LEVEL KEY (an object write as
|
|
10
|
+
* `merge` of a patch, an array write or a delete as `set` of the rebuilt
|
|
11
|
+
* root); an element proxy commits the WHOLE OWNING ARRAY back through the
|
|
12
|
+
* array proxy's own callback. That difference is a {@link WriteSink}; the
|
|
13
|
+
* traps are written once over it (9.23.1 — the 9.22.0 stale-read bug was
|
|
14
|
+
* fixed in one copy of these traps and then found again in the other two).
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.sinkDeleteTrap = exports.sinkSetTrap = exports.elementSink = exports.rootKeySink = void 0;
|
|
18
|
+
const pathOps_js_1 = require("../memory/pathOps.js");
|
|
19
|
+
const pathBuilder_js_1 = require("./pathBuilder.js");
|
|
20
|
+
const structuralWrite_js_1 = require("./structuralWrite.js");
|
|
21
|
+
/**
|
|
22
|
+
* WHY: a write below a top-level key has TWO verbs (reactive/README.md, law
|
|
23
|
+
* 3) — an object leaf is a `merge` of a nested patch, an array leaf or a
|
|
24
|
+
* delete rebuilds the root immutably and commits a `set` of the key. Only the
|
|
25
|
+
* sink knows that; the traps do not. Every write drops the key's cached child
|
|
26
|
+
* proxy (`cache`), so the next read is built over the new value.
|
|
27
|
+
*/
|
|
28
|
+
function rootKeySink(target, readSilent, rootKey, cache) {
|
|
29
|
+
return new RootKeySink(target, readSilent, rootKey, cache);
|
|
30
|
+
}
|
|
31
|
+
exports.rootKeySink = rootKeySink;
|
|
32
|
+
/** One allocation per top-level read miss (the methods live on the prototype). */
|
|
33
|
+
class RootKeySink {
|
|
34
|
+
target;
|
|
35
|
+
readSilent;
|
|
36
|
+
rootKey;
|
|
37
|
+
cache;
|
|
38
|
+
constructor(target, readSilent, rootKey, cache) {
|
|
39
|
+
this.target = target;
|
|
40
|
+
this.readSilent = readSilent;
|
|
41
|
+
this.rootKey = rootKey;
|
|
42
|
+
this.cache = cache;
|
|
43
|
+
}
|
|
44
|
+
/** An empty path is the key's own value — `nativeGet(v, [])` would return it unchanged, so the walk is skipped. */
|
|
45
|
+
readAt(path) {
|
|
46
|
+
const root = this.readSilent(this.rootKey);
|
|
47
|
+
return path.length === 0 ? root : (0, pathOps_js_1.nativeGet)(root, path);
|
|
48
|
+
}
|
|
49
|
+
put(path, value) {
|
|
50
|
+
if (!Array.isArray(value))
|
|
51
|
+
this.target.updateValue(this.rootKey, (0, pathBuilder_js_1.buildNestedPatch)(path, value));
|
|
52
|
+
else
|
|
53
|
+
this.target.setValue(this.rootKey, path.length === 0 ? value : (0, structuralWrite_js_1.setInPath)(this.readAt([]), path, value));
|
|
54
|
+
this.cache.delete(this.rootKey);
|
|
55
|
+
}
|
|
56
|
+
remove(path) {
|
|
57
|
+
this.target.setValue(this.rootKey, (0, structuralWrite_js_1.deleteInPath)(this.readAt([]), path));
|
|
58
|
+
this.cache.delete(this.rootKey);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* WHY: a write inside an array element is an array write — it rebuilds the
|
|
63
|
+
* owning array immutably and hands the WHOLE new array to the array proxy's
|
|
64
|
+
* commit, the funnel `push` already uses. `path` is measured from the
|
|
65
|
+
* element; `[]` is the element slot itself.
|
|
66
|
+
*/
|
|
67
|
+
function elementSink(getCurrent, commit, index) {
|
|
68
|
+
return new ElementSink(getCurrent, commit, index);
|
|
69
|
+
}
|
|
70
|
+
exports.elementSink = elementSink;
|
|
71
|
+
/** One allocation per element proxy (the methods live on the prototype) — a 10k-element loop builds 10k of these. */
|
|
72
|
+
class ElementSink {
|
|
73
|
+
getCurrent;
|
|
74
|
+
commit;
|
|
75
|
+
index;
|
|
76
|
+
constructor(getCurrent, commit, index) {
|
|
77
|
+
this.getCurrent = getCurrent;
|
|
78
|
+
this.commit = commit;
|
|
79
|
+
this.index = index;
|
|
80
|
+
}
|
|
81
|
+
readAt(path) {
|
|
82
|
+
return readInElement(this.getCurrent, this.index, path);
|
|
83
|
+
}
|
|
84
|
+
put(path, value) {
|
|
85
|
+
replaceInElement(this.getCurrent, this.commit, this.index, path, value);
|
|
86
|
+
}
|
|
87
|
+
remove(path) {
|
|
88
|
+
removeInElement(this.getCurrent, this.commit, this.index, path);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** The value at `path` inside element `index`, as it stands NOW (read-your-writes). */
|
|
92
|
+
function readInElement(getCurrent, index, path) {
|
|
93
|
+
const element = getCurrent()[index];
|
|
94
|
+
return path.length === 0 ? element : (0, pathOps_js_1.nativeGet)(element, path);
|
|
95
|
+
}
|
|
96
|
+
/** A copy of the array with `value` at `path` inside element `index`; a gone element has nothing to write into. */
|
|
97
|
+
function replaceInElement(getCurrent, commit, index, path, value) {
|
|
98
|
+
const next = [...getCurrent()];
|
|
99
|
+
if (index >= next.length)
|
|
100
|
+
return;
|
|
101
|
+
next[index] = path.length === 0 ? value : (0, structuralWrite_js_1.setInPath)(next[index], path, value);
|
|
102
|
+
commit(next);
|
|
103
|
+
}
|
|
104
|
+
/** A copy of the array with the key at `path` inside element `index` removed. */
|
|
105
|
+
function removeInElement(getCurrent, commit, index, path) {
|
|
106
|
+
const next = [...getCurrent()];
|
|
107
|
+
if (index >= next.length)
|
|
108
|
+
return;
|
|
109
|
+
next[index] = (0, structuralWrite_js_1.deleteInPath)(next[index], path);
|
|
110
|
+
commit(next);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* WHY one set trap: the assigned value is unwrapped HERE (landmine 1 — the
|
|
114
|
+
* JSON round-trip applies to what the caller handed in, never to the
|
|
115
|
+
* surrounding state) and handed to the sink at the proxy's path plus the key.
|
|
116
|
+
*/
|
|
117
|
+
function sinkSetTrap(sink, segments) {
|
|
118
|
+
return (_target, prop, value) => {
|
|
119
|
+
if (typeof prop !== 'string')
|
|
120
|
+
return true;
|
|
121
|
+
sink.put([...segments, prop], (0, structuralWrite_js_1.unwrapProxy)(value));
|
|
122
|
+
return true;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
exports.sinkSetTrap = sinkSetTrap;
|
|
126
|
+
/**
|
|
127
|
+
* WHY one delete trap: `merge` cannot express a removal (a patch of
|
|
128
|
+
* `undefined` reads as absent), so a delete always rebuilds through the sink.
|
|
129
|
+
*/
|
|
130
|
+
function sinkDeleteTrap(sink, segments) {
|
|
131
|
+
return (_target, prop) => {
|
|
132
|
+
if (typeof prop !== 'string')
|
|
133
|
+
return true;
|
|
134
|
+
sink.remove([...segments, prop]);
|
|
135
|
+
return true;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
exports.sinkDeleteTrap = sinkDeleteTrap;
|
|
139
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGVUcmFwcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvcmVhY3RpdmUvd3JpdGVUcmFwcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRzs7O0FBRUgscURBQWlEO0FBRWpELHFEQUFvRDtBQUNwRCw2REFBNEU7QUFzQjVFOzs7Ozs7R0FNRztBQUNILFNBQWdCLFdBQVcsQ0FDekIsTUFBcUIsRUFDckIsVUFBb0MsRUFDcEMsT0FBZSxFQUNmLEtBQWtDO0lBRWxDLE9BQU8sSUFBSSxXQUFXLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7QUFDN0QsQ0FBQztBQVBELGtDQU9DO0FBRUQsa0ZBQWtGO0FBQ2xGLE1BQU0sV0FBVztJQUVJO0lBQ0E7SUFDQTtJQUNBO0lBSm5CLFlBQ21CLE1BQXFCLEVBQ3JCLFVBQW9DLEVBQ3BDLE9BQWUsRUFDZixLQUFrQztRQUhsQyxXQUFNLEdBQU4sTUFBTSxDQUFlO1FBQ3JCLGVBQVUsR0FBVixVQUFVLENBQTBCO1FBQ3BDLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFDZixVQUFLLEdBQUwsS0FBSyxDQUE2QjtJQUNsRCxDQUFDO0lBRUosbUhBQW1IO0lBQ25ILE1BQU0sQ0FBQyxJQUF1QjtRQUM1QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMzQyxPQUFPLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUEsc0JBQVMsRUFBQyxJQUFJLEVBQUUsSUFBZ0IsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFFRCxHQUFHLENBQUMsSUFBdUIsRUFBRSxLQUFjO1FBQ3pDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUFFLElBQUksQ0FBQyxNQUFN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QyxDQUFDO1FBQ2pDLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQU5ELHdDQU1DIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiByZWFjdGl2ZS93cml0ZVRyYXBzIOKAlCB3aGVyZSBhIHByb3h5J3Mgd3JpdGVzIExBTkQsIGFuZCB0aGUgdHdvIHRyYXBzIHRoYXRcbiAqIHNlbmQgdGhlbSB0aGVyZS5cbiAqXG4gKiBFdmVyeSBvYmplY3QgcHJveHkgaW4gdGhpcyBmb2xkZXIgKG5lc3RlZCwgdGVybWluYWwsIGVsZW1lbnQpIGludGVyY2VwdHNcbiAqIHRoZSBzYW1lIHRocmVlIHRoaW5ncyDigJQgYSBwcm9wZXJ0eSBzZXQsIGEgcHJvcGVydHkgZGVsZXRlLCBhbmQgYW4gYXJyYXlcbiAqIG11dGF0aW9uIHNvbWV3aGVyZSBiZWxvdyBpdCDigJQgYW5kIGRpZmZlcnMgb25seSBpbiB3aGVyZSB0aGUgd3JpdGUgZ29lczpcbiAqIGEgbmVzdGVkL3Rlcm1pbmFsIHByb3h5IGNvbW1pdHMgdW5kZXIgYSBUT1AtTEVWRUwgS0VZIChhbiBvYmplY3Qgd3JpdGUgYXNcbiAqIGBtZXJnZWAgb2YgYSBwYXRjaCwgYW4gYXJyYXkgd3JpdGUgb3IgYSBkZWxldGUgYXMgYHNldGAgb2YgdGhlIHJlYnVpbHRcbiAqIHJvb3QpOyBhbiBlbGVtZW50IHByb3h5IGNvbW1pdHMgdGhlIFdIT0xFIE9XTklORyBBUlJBWSBiYWNrIHRocm91Z2ggdGhlXG4gKiBhcnJheSBwcm94eSdzIG93biBjYWxsYmFjay4gVGhhdCBkaWZmZXJlbmNlIGlzIGEge0BsaW5rIFdyaXRlU2lua307IHRoZVxuICogdHJhcHMgYXJlIHdyaXR0ZW4gb25jZSBvdmVyIGl0ICg5LjIzLjEg4oCUIHRoZSA5LjIyLjAgc3RhbGUtcmVhZCBidWcgd2FzXG4gKiBmaXhlZCBpbiBvbmUgY29weSBvZiB0aGVzZSB0cmFwcyBhbmQgdGhlbiBmb3VuZCBhZ2FpbiBpbiB0aGUgb3RoZXIgdHdvKS5cbiAqL1xuXG5pbXBvcnQgeyBuYXRpdmVHZXQgfSBmcm9tICcuLi9tZW1vcnkvcGF0aE9wcy5qcyc7XG5pbXBvcnQgdHlwZSB7IE1lbWJlckNhY2hlIH0gZnJvbSAnLi9saXZlVmlldy5qcyc7XG5pbXBvcnQgeyBidWlsZE5lc3RlZFBhdGNoIH0gZnJvbSAnLi9wYXRoQnVpbGRlci5qcyc7XG5pbXBvcnQgeyBkZWxldGVJblBhdGgsIHNldEluUGF0aCwgdW53cmFwUHJveHkgfSBmcm9tICcuL3N0cnVjdHVyYWxXcml0ZS5qcyc7XG5cbi8qKlxuICogVGhlIHNlYW0gYmV0d2VlbiBhIHByb3h5IGFuZCBzdGF0ZS4gYHBhdGhgIGlzIG1lYXN1cmVkIGZyb20gdGhlIHNpbmsnc1xuICogcm9vdCDigJQgdGhlIGtleSdzIHZhbHVlIGZvciB7QGxpbmsgcm9vdEtleVNpbmt9LCB0aGUgZWxlbWVudCBmb3JcbiAqIHtAbGluayBlbGVtZW50U2lua307IGBbXWAgaXMgdGhlIHJvb3QgaXRzZWxmLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFdyaXRlU2luayB7XG4gIC8qKiBUaGUgdmFsdWUgYXQgYHBhdGhgLCBhcyBpdCBzdGFuZHMgTk9XIChyZWFkLXlvdXItd3JpdGVzOyBuZXZlciBhIHRyYWNrZWQgcmVhZCkuICovXG4gIHJlYWRBdChwYXRoOiByZWFkb25seSBzdHJpbmdbXSk6IHVua25vd247XG4gIC8qKiBQdXQgYW4gYWxyZWFkeS11bndyYXBwZWQgYHZhbHVlYCBhdCBgcGF0aGAuIFRoZSBzaW5rIGNob29zZXMgdGhlIHZlcmIuICovXG4gIHB1dChwYXRoOiByZWFkb25seSBzdHJpbmdbXSwgdmFsdWU6IHVua25vd24pOiB2b2lkO1xuICAvKiogUmVtb3ZlIHRoZSBrZXkgYXQgYHBhdGhgLiAqL1xuICByZW1vdmUocGF0aDogcmVhZG9ubHkgc3RyaW5nW10pOiB2b2lkO1xufVxuXG4vKiogVGhlIHR3byB3YXlzIGEgdmFsdWUgcmVhY2hlcyBhIHN0YXRlIGtleTogd2hhdCB0aGUgc2luayB3cml0ZXMgdGhyb3VnaC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUm9vdEtleVRhcmdldCB7XG4gIHNldFZhbHVlKGtleTogc3RyaW5nLCB2YWx1ZTogdW5rbm93bik6IHZvaWQ7XG4gIHVwZGF0ZVZhbHVlKGtleTogc3RyaW5nLCB2YWx1ZTogdW5rbm93bik6IHZvaWQ7XG59XG5cbi8qKlxuICogV0hZOiBhIHdyaXRlIGJlbG93IGEgdG9wLWxldmVsIGtleSBoYXMgVFdPIHZlcmJzIChyZWFjdGl2ZS9SRUFETUUubWQsIGxhd1xuICogMykg4oCUIGFuIG9iamVjdCBsZWFmIGlzIGEgYG1lcmdlYCBvZiBhIG5lc3RlZCBwYXRjaCwgYW4gYXJyYXkgbGVhZiBvciBhXG4gKiBkZWxldGUgcmVidWlsZHMgdGhlIHJvb3QgaW1tdXRhYmx5IGFuZCBjb21taXRzIGEgYHNldGAgb2YgdGhlIGtleS4gT25seSB0aGVcbiAqIHNpbmsga25vd3MgdGhhdDsgdGhlIHRyYXBzIGRvIG5vdC4gRXZlcnkgd3JpdGUgZHJvcHMgdGhlIGtleSdzIGNhY2hlZCBjaGlsZFxuICogcHJveHkgKGBjYWNoZWApLCBzbyB0aGUgbmV4dCByZWFkIGlzIGJ1aWx0IG92ZXIgdGhlIG5ldyB2YWx1ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJvb3RLZXlTaW5rKFxuICB0YXJnZXQ6IFJvb3RLZXlUYXJnZXQsXG4gIHJlYWRTaWxlbnQ6IChrZXk6IHN0cmluZykgPT4gdW5rbm93bixcbiAgcm9vdEtleTogc3RyaW5nLFxuICBjYWNoZTogUGljazxNZW1iZXJDYWNoZSwgJ2RlbGV0ZSc+LFxuKTogV3JpdGVTaW5rIHtcbiAgcmV0dXJuIG5ldyBSb290S2V5U2luayh0YXJnZXQsIHJlYWRTaWxlbnQsIHJvb3RLZXksIGNhY2hlKTtcbn1cblxuLyoqIE9uZSBhbGxvY2F0aW9uIHBlciB0b3AtbGV2ZWwgcmVhZCBtaXNzICh0aGUgbWV0aG9kcyBsaXZlIG9uIHRoZSBwcm90b3R5cGUpLiAqL1xuY2xhc3MgUm9vdEtleVNpbmsgaW1wbGVtZW50cyBXcml0ZVNpbmsge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IHRhcmdldDogUm9vdEtleVRhcmdldCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHJlYWRTaWxlbnQ6IChrZXk6IHN0cmluZykgPT4gdW5rbm93bixcbiAgICBwcml2YXRlIHJlYWRvbmx5IHJvb3RLZXk6IHN0cmluZyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNhY2hlOiBQaWNrPE1lbWJlckNhY2hlLCAnZGVsZXRlJz4sXG4gICkge31cblxuICAvKiogQW4gZW1wdHkgcGF0aCBpcyB0aGUga2V5J3Mgb3duIHZhbHVlIOKAlCBgbmF0aXZlR2V0KHYsIFtdKWAgd291bGQgcmV0dXJuIGl0IHVuY2hhbmdlZCwgc28gdGhlIHdhbGsgaXMgc2tpcHBlZC4gKi9cbiAgcmVhZEF0KHBhdGg6IHJlYWRvbmx5IHN0cmluZ1tdKTogdW5rbm93biB7XG4gICAgY29uc3Qgcm9vdCA9IHRoaXMucmVhZFNpbGVudCh0aGlzLnJvb3RLZXkpO1xuICAgIHJldHVybiBwYXRoLmxlbmd0aCA9PT0gMCA/IHJvb3QgOiBuYXRpdmVHZXQocm9vdCwgcGF0aCBhcyBzdHJpbmdbXSk7XG4gIH1cblxuICBwdXQocGF0aDogcmVhZG9ubHkgc3RyaW5nW10sIHZhbHVlOiB1bmtub3duKTogdm9pZCB7XG4gICAgaWYgKCFBcnJheS5pc0FycmF5KHZhbHVlKSkgdGhpcy50YXJnZXQudXBkYXRlVmFsdWUodGhpcy5yb290S2V5LCBidWlsZE5lc3RlZFBhdGNoKHBhdGggYXMgc3RyaW5nW10sIHZhbHVlKSk7XG4gICAgZWxzZSB0aGlzLnRhcmdldC5zZXRWYWx1ZSh0aGlzLnJvb3RLZXksIHBhdGgubGVuZ3RoID09PSAwID8gdmFsdWUgOiBzZXRJblBhdGgodGhpcy5yZWFkQXQoW10pLCBwYXRoLCB2YWx1ZSkpO1xuICAgIHRoaXMuY2FjaGUuZGVsZXRlKHRoaXMucm9vdEtleSk7XG4gIH1cblxuICByZW1vdmUocGF0aDogcmVhZG9ubHkgc3RyaW5nW10pOiB2b2lkIHtcbiAgICB0aGlzLnRhcmdldC5zZXRWYWx1ZSh0aGlzLnJvb3RLZXksIGRlbGV0ZUluUGF0aCh0aGlzLnJlYWRBdChbXSksIHBhdGgpKTtcbiAgICB0aGlzLmNhY2hlLmRlbGV0ZSh0aGlzLnJvb3RLZXkpO1xuICB9XG59XG5cbi8qKlxuICogV0hZOiBhIHdyaXRlIGluc2lkZSBhbiBhcnJheSBlbGVtZW50IGlzIGFuIGFycmF5IHdyaXRlIOKAlCBpdCByZWJ1aWxkcyB0aGVcbiAqIG93bmluZyBhcnJheSBpbW11dGFibHkgYW5kIGhhbmRzIHRoZSBXSE9MRSBuZXcgYXJyYXkgdG8gdGhlIGFycmF5IHByb3h5J3NcbiAqIGNvbW1pdCwgdGhlIGZ1bm5lbCBgcHVzaGAgYWxyZWFkeSB1c2VzLiBgcGF0aGAgaXMgbWVhc3VyZWQgZnJvbSB0aGVcbiAqIGVsZW1lbnQ7IGBbXWAgaXMgdGhlIGVsZW1lbnQgc2xvdCBpdHNlbGYuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbGVtZW50U2luayhnZXRDdXJyZW50OiAoKSA9PiB1bmtub3duW10sIGNvbW1pdDogKG5leHQ6IHVua25vd25bXSkgPT4gdm9pZCwgaW5kZXg6IG51bWJlcik6IFdyaXRlU2luayB7XG4gIHJldHVybiBuZXcgRWxlbWVudFNpbmsoZ2V0Q3VycmVudCwgY29tbWl0LCBpbmRleCk7XG59XG5cbi8qKiBPbmUgYWxsb2NhdGlvbiBwZXIgZWxlbWVudCBwcm94eSAodGhlIG1ldGhvZHMgbGl2ZSBvbiB0aGUgcHJvdG90eXBlKSDigJQgYSAxMGstZWxlbWVudCBsb29wIGJ1aWxkcyAxMGsgb2YgdGhlc2UuICovXG5jbGFzcyBFbGVtZW50U2luayBpbXBsZW1lbnRzIFdyaXRlU2luayB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZ2V0Q3VycmVudDogKCkgPT4gdW5rbm93bltdLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY29tbWl0OiAobmV4dDogdW5rbm93bltdKSA9PiB2b2lkLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgaW5kZXg6IG51bWJlcixcbiAgKSB7fVxuXG4gIHJlYWRBdChwYXRoOiByZWFkb25seSBzdHJpbmdbXSk6IHVua25vd24ge1xuICAgIHJldHVybiByZWFkSW5FbGVtZW50KHRoaXMuZ2V0Q3VycmVudCwgdGhpcy5pbmRleCwgcGF0aCk7XG4gIH1cblxuICBwdXQocGF0aDogcmVhZG9ubHkgc3RyaW5nW10sIHZhbHVlOiB1bmtub3duKTogdm9pZCB7XG4gICAgcmVwbGFjZUluRWxlbWVudCh0aGlzLmdldEN1cnJlbnQsIHRoaXMuY29tbWl0LCB0aGlzLmluZGV4LCBwYXRoLCB2YWx1ZSk7XG4gIH1cblxuICByZW1vdmUocGF0aDogcmVhZG9ubHkgc3RyaW5nW10pOiB2b2lkIHtcbiAgICByZW1vdmVJbkVsZW1lbnQodGhpcy5nZXRDdXJyZW50LCB0aGlzLmNvbW1pdCwgdGhpcy5pbmRleCwgcGF0aCk7XG4gIH1cbn1cblxuLyoqIFRoZSB2YWx1ZSBhdCBgcGF0aGAgaW5zaWRlIGVsZW1lbnQgYGluZGV4YCwgYXMgaXQgc3RhbmRzIE5PVyAocmVhZC15b3VyLXdyaXRlcykuICovXG5mdW5jdGlvbiByZWFkSW5FbGVtZW50KGdldEN1cnJlbnQ6ICgpID0+IHVua25vd25bXSwgaW5kZXg6IG51bWJlciwgcGF0aDogcmVhZG9ubHkgc3RyaW5nW10pOiB1bmtub3duIHtcbiAgY29uc3QgZWxlbWVudCA9IGdldEN1cnJlbnQoKVtpbmRleF07XG4gIHJldHVybiBwYXRoLmxlbmd0aCA9PT0gMCA/IGVsZW1lbnQgOiBuYXRpdmVHZXQoZWxlbWVudCwgcGF0aCBhcyBzdHJpbmdbXSk7XG59XG5cbi8qKiBBIGNvcHkgb2YgdGhlIGFycmF5IHdpdGggYHZhbHVlYCBhdCBgcGF0aGAgaW5zaWRlIGVsZW1lbnQgYGluZGV4YDsgYSBnb25lIGVsZW1lbnQgaGFzIG5vdGhpbmcgdG8gd3JpdGUgaW50by4gKi9cbmZ1bmN0aW9uIHJlcGxhY2VJbkVsZW1lbnQoXG4gIGdldEN1cnJlbnQ6ICgpID0+IHVua25vd25bXSxcbiAgY29tbWl0OiAobmV4dDogdW5rbm93bltdKSA9PiB2b2lkLFxuICBpbmRleDogbnVtYmVyLFxuICBwYXRoOiByZWFkb25seSBzdHJpbmdbXSxcbiAgdmFsdWU6IHVua25vd24sXG4pOiB2b2lkIHtcbiAgY29uc3QgbmV4dCA9IFsuLi5nZXRDdXJyZW50KCldO1xuICBpZiAoaW5kZXggPj0gbmV4dC5sZW5ndGgpIHJldHVybjtcbiAgbmV4dFtpbmRleF0gPSBwYXRoLmxlbmd0aCA9PT0gMCA/IHZhbHVlIDogc2V0SW5QYXRoKG5leHRbaW5kZXhdLCBwYXRoLCB2YWx1ZSk7XG4gIGNvbW1pdChuZXh0KTtcbn1cblxuLyoqIEEgY29weSBvZiB0aGUgYXJyYXkgd2l0aCB0aGUga2V5IGF0IGBwYXRoYCBpbnNpZGUgZWxlbWVudCBgaW5kZXhgIHJlbW92ZWQuICovXG5mdW5jdGlvbiByZW1vdmVJbkVsZW1lbnQoXG4gIGdldEN1cnJlbnQ6ICgpID0+IHVua25vd25bXSxcbiAgY29tbWl0OiAobmV4dDogdW5rbm93bltdKSA9PiB2b2lkLFxuICBpbmRleDogbnVtYmVyLFxuICBwYXRoOiByZWFkb25seSBzdHJpbmdbXSxcbik6IHZvaWQge1xuICBjb25zdCBuZXh0ID0gWy4uLmdldEN1cnJlbnQoKV07XG4gIGlmIChpbmRleCA+PSBuZXh0Lmxlbmd0aCkgcmV0dXJuO1xuICBuZXh0W2luZGV4XSA9IGRlbGV0ZUluUGF0aChuZXh0W2luZGV4XSwgcGF0aCk7XG4gIGNvbW1pdChuZXh0KTtcbn1cblxuLyoqXG4gKiBXSFkgb25lIHNldCB0cmFwOiB0aGUgYXNzaWduZWQgdmFsdWUgaXMgdW53cmFwcGVkIEhFUkUgKGxhbmRtaW5lIDEg4oCUIHRoZVxuICogSlNPTiByb3VuZC10cmlwIGFwcGxpZXMgdG8gd2hhdCB0aGUgY2FsbGVyIGhhbmRlZCBpbiwgbmV2ZXIgdG8gdGhlXG4gKiBzdXJyb3VuZGluZyBzdGF0ZSkgYW5kIGhhbmRlZCB0byB0aGUgc2luayBhdCB0aGUgcHJveHkncyBwYXRoIHBsdXMgdGhlIGtleS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNpbmtTZXRUcmFwKHNpbms6IFdyaXRlU2luaywgc2VnbWVudHM6IHJlYWRvbmx5IHN0cmluZ1tdKTogUHJveHlIYW5kbGVyPG9iamVjdD5bJ3NldCddIHtcbiAgcmV0dXJuIChfdGFyZ2V0LCBwcm9wLCB2YWx1ZSkgPT4ge1xuICAgIGlmICh0eXBlb2YgcHJvcCAhPT0gJ3N0cmluZycpIHJldHVybiB0cnVlO1xuICAgIHNpbmsucHV0KFsuLi5zZWdtZW50cywgcHJvcF0sIHVud3JhcFByb3h5KHZhbHVlKSk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH07XG59XG5cbi8qKlxuICogV0hZIG9uZSBkZWxldGUgdHJhcDogYG1lcmdlYCBjYW5ub3QgZXhwcmVzcyBhIHJlbW92YWwgKGEgcGF0Y2ggb2ZcbiAqIGB1bmRlZmluZWRgIHJlYWRzIGFzIGFic2VudCksIHNvIGEgZGVsZXRlIGFsd2F5cyByZWJ1aWxkcyB0aHJvdWdoIHRoZSBzaW5rLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2lua0RlbGV0ZVRyYXAoc2luazogV3JpdGVTaW5rLCBzZWdtZW50czogcmVhZG9ubHkgc3RyaW5nW10pOiBQcm94eUhhbmRsZXI8b2JqZWN0PlsnZGVsZXRlUHJvcGVydHknXSB7XG4gIHJldHVybiAoX3RhcmdldCwgcHJvcCkgPT4ge1xuICAgIGlmICh0eXBlb2YgcHJvcCAhPT0gJ3N0cmluZycpIHJldHVybiB0cnVlO1xuICAgIHNpbmsucmVtb3ZlKFsuLi5zZWdtZW50cywgcHJvcF0pO1xuICAgIHJldHVybiB0cnVlO1xuICB9O1xufVxuIl19
|
|
@@ -274,6 +274,24 @@ export declare class TransactionBuffer {
|
|
|
274
274
|
* today's full-value `set`.
|
|
275
275
|
*/
|
|
276
276
|
private toDeltaPayload;
|
|
277
|
+
/**
|
|
278
|
+
* Did the stage change the value at `segments`? Base and final value are
|
|
279
|
+
* fixed at commit, so this is the ONE net-change verdict both payload
|
|
280
|
+
* encodings apply (a no-op write and a write-then-revert both fail it).
|
|
281
|
+
*/
|
|
282
|
+
private changedSinceBase;
|
|
283
|
+
/**
|
|
284
|
+
* Path → its op-verb sequence, ordered by LAST touch (delete + re-insert
|
|
285
|
+
* moves a re-touched path to the end of the Map's insertion order —
|
|
286
|
+
* preserving last-writer-wins for nested/overlapping paths). Per-write
|
|
287
|
+
* provenance (#P1): the LAST op's readKeys is kept — read prefixes only grow
|
|
288
|
+
* within a stage, so last == union across the path.
|
|
289
|
+
*/
|
|
290
|
+
private opsByPath;
|
|
291
|
+
/** The net-change filter — identical to 'full' ({@link changedSinceBase}). Survivors keep last-touch order. */
|
|
292
|
+
private netChangeSurvivors;
|
|
293
|
+
/** Replay each overlapping family ONCE, lazily — most commits have none, and pay nothing. */
|
|
294
|
+
private memoisedFamilyValue;
|
|
277
295
|
/**
|
|
278
296
|
* Bucket the staged ops of every OVERLAPPING family by family root, in op
|
|
279
297
|
* order — the input {@link replayFamilyVerbs} folds. Ops on paths the
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
*
|
|
39
39
|
* Now an indexed read of a wrappable element returns an ELEMENT PROXY that
|
|
40
40
|
* accumulates the path inside the element and, on write, rebuilds the array
|
|
41
|
-
* immutably (
|
|
42
|
-
* the
|
|
43
|
-
* whatever depth it is addressed at.
|
|
41
|
+
* immutably (`structuralWrite.setInPath`, behind `writeTraps.elementSink`)
|
|
42
|
+
* and hands the whole new array to `commit` — the same funnel `push` already
|
|
43
|
+
* used. One write path for the whole array, whatever depth it is addressed at.
|
|
44
44
|
*
|
|
45
45
|
* ── What is still NOT seen (by design) ───────────────────────────────────
|
|
46
46
|
* Elements reached WITHOUT an index — `find()`, `filter()`, `for…of`,
|
|
@@ -50,10 +50,30 @@
|
|
|
50
50
|
* not lost in silence either: `StageContext` compares what a stage READ with
|
|
51
51
|
* what the value holds at commit and, in dev mode, warns with the exact path.
|
|
52
52
|
* See src/lib/reactive/README.md.
|
|
53
|
+
*
|
|
54
|
+
* ── Shape (9.23.1) ───────────────────────────────────────────────────────
|
|
55
|
+
* `createArrayProxy` and `createElementProxy` are ORCHESTRATORS: they wire
|
|
56
|
+
* traps and hold the caches. Every trap body is a named leaf below — one
|
|
57
|
+
* concern each — and the element proxy's set/delete/get traps are the SAME
|
|
58
|
+
* leaves the nested and terminal object proxies use (`writeTraps.ts`,
|
|
59
|
+
* `liveView.ts`), over an {@link elementSink}. A fix lands in a leaf once.
|
|
60
|
+
*/
|
|
61
|
+
import { type WriteSink } from './writeTraps.js';
|
|
62
|
+
/**
|
|
63
|
+
* WHY: an array anywhere below a sink's root is ONE write path — it reads
|
|
64
|
+
* through the sink and commits the WHOLE new array back through it, so
|
|
65
|
+
* `push` three levels down and `arr[i].x = v` at the top share a funnel.
|
|
53
66
|
*/
|
|
67
|
+
export declare function arrayProxyAt(sink: WriteSink, path: readonly string[]): unknown[];
|
|
54
68
|
/**
|
|
55
69
|
* Creates a Proxy over an array that intercepts mutating operations.
|
|
56
70
|
*
|
|
71
|
+
* The actual current array is the Proxy target: Node.js console.log inspects
|
|
72
|
+
* the target directly (bypasses Proxy traps), so the real array shows correct
|
|
73
|
+
* values. The target reference is fixed at creation time — after mutations,
|
|
74
|
+
* the proxy returns a new proxy (via cache invalidation) with the fresh array
|
|
75
|
+
* as target.
|
|
76
|
+
*
|
|
57
77
|
* @param getCurrent - Returns the current array snapshot from state
|
|
58
78
|
* @param commit - Called with the new array after a mutation (triggers setValue)
|
|
59
79
|
* @returns Proxied array with copy-on-write semantics
|
|
@@ -9,9 +9,60 @@
|
|
|
9
9
|
* found on element proxies, then the same root on nested and terminal ones).
|
|
10
10
|
* So every read trap resolves through the CURRENT value and falls back to the
|
|
11
11
|
* captured object only when the path is gone or no longer holds an object.
|
|
12
|
+
*
|
|
13
|
+
* The READ side of every object proxy lives here — {@link liveObject},
|
|
14
|
+
* {@link liveGetTrap}, {@link liveInspectionTraps}, and the ONE per-member
|
|
15
|
+
* proxy cache {@link cachedMember} (9.23.2); the WRITE side is
|
|
16
|
+
* `writeTraps.ts`. A factory only wires them (9.23.1).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* A proxy per member NAME under one parent proxy, validated by the raw
|
|
20
|
+
* member's identity. The map is allocated on the FIRST insert: an element
|
|
21
|
+
* proxy over `{ id, n }` reads only primitives and must not pay for one
|
|
22
|
+
* (10,000 of them in the naive re-read loop — measured, `bench/nested-reads.ts`).
|
|
12
23
|
*/
|
|
24
|
+
export declare class MemberCache {
|
|
25
|
+
private entries?;
|
|
26
|
+
/** The proxy cached under `prop`, if it was built over exactly this raw `value`. */
|
|
27
|
+
hit(prop: string, value: object): unknown;
|
|
28
|
+
remember(prop: string, value: object, proxy: unknown): void;
|
|
29
|
+
/** Forget `prop` — a write replaced its value, so the next read must build over the new one. */
|
|
30
|
+
delete(prop: string): void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* WHY the cache: `o.arr === o.arr` must hold within a stage, and
|
|
34
|
+
* `for (i < N) s.k.arr[i]` must build ONE array proxy — whose element cache
|
|
35
|
+
* then serves every index — not N (the 9.22.0 perf review, finding 4: the
|
|
36
|
+
* nested get trap built a fresh array proxy, with a fresh empty element
|
|
37
|
+
* cache, on every `.arr`). The top-level scope has cached its child proxies
|
|
38
|
+
* this way since before 9.22.0; this is that leaf, shared by all four.
|
|
39
|
+
*
|
|
40
|
+
* Validated by IDENTITY, invalidated by replacement: every write through a
|
|
41
|
+
* proxy rebuilds the containers on its path (`structuralWrite.setInPath`),
|
|
42
|
+
* so after `s.k.arr[0].n = 1` the raw `arr` is a new array, the ref check
|
|
43
|
+
* misses, and the next read builds a proxy over the NEW value. A hit can
|
|
44
|
+
* never be stale — every proxy reads live (`liveGetTrap`), so the cached one
|
|
45
|
+
* and a fresh one answer the same.
|
|
46
|
+
*
|
|
47
|
+
* Keyed by NAME under THIS parent, never by the raw value alone: a diamond
|
|
48
|
+
* (one array under `k.a` and `k.b`) needs two proxies, each bound to its own
|
|
49
|
+
* path and sink. Only what `build` actually WRAPPED is kept — a member handed
|
|
50
|
+
* back raw (a `Date`, a frozen object, a cycle edge) is not worth an entry.
|
|
51
|
+
* `build` is the caller's stable child step, not a closure made per read: a
|
|
52
|
+
* hit allocates nothing, and a primitive member skips the cache entirely.
|
|
53
|
+
*/
|
|
54
|
+
export declare function cachedMember(cache: MemberCache, prop: string, value: unknown, build: (value: unknown, path: string[]) => unknown, segments: readonly string[]): unknown;
|
|
13
55
|
/** The object at `segments` under the current root, or `raw` when it is gone. */
|
|
14
56
|
export declare function liveObject(raw: Record<string, unknown>, current: unknown): Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* WHY one get trap: the names a proxy answers ITSELF (promise/matcher guards,
|
|
59
|
+
* `constructor`, and the JSON law — serializing a proxied value equals
|
|
60
|
+
* serializing the raw one, `jsonProjection.ts`) must be the same three lines
|
|
61
|
+
* on every object proxy; any other member is handed to `child` with its full
|
|
62
|
+
* path — through the parent's {@link cachedMember} — and only THAT step
|
|
63
|
+
* differs per factory (the cycle policy).
|
|
64
|
+
*/
|
|
65
|
+
export declare function liveGetTrap(live: () => Record<string, unknown>, segments: readonly string[], child: (value: unknown, path: string[]) => unknown, members: MemberCache): ProxyHandler<object>['get'];
|
|
15
66
|
/**
|
|
16
67
|
* The three inspection traps (`in`, `Object.keys`, descriptors) answered from
|
|
17
68
|
* the live object. The one Proxy invariant that could bite — a non-extensible
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reactive/writeTraps — where a proxy's writes LAND, and the two traps that
|
|
3
|
+
* send them there.
|
|
4
|
+
*
|
|
5
|
+
* Every object proxy in this folder (nested, terminal, element) intercepts
|
|
6
|
+
* the same three things — a property set, a property delete, and an array
|
|
7
|
+
* mutation somewhere below it — and differs only in where the write goes:
|
|
8
|
+
* a nested/terminal proxy commits under a TOP-LEVEL KEY (an object write as
|
|
9
|
+
* `merge` of a patch, an array write or a delete as `set` of the rebuilt
|
|
10
|
+
* root); an element proxy commits the WHOLE OWNING ARRAY back through the
|
|
11
|
+
* array proxy's own callback. That difference is a {@link WriteSink}; the
|
|
12
|
+
* traps are written once over it (9.23.1 — the 9.22.0 stale-read bug was
|
|
13
|
+
* fixed in one copy of these traps and then found again in the other two).
|
|
14
|
+
*/
|
|
15
|
+
import type { MemberCache } from './liveView.js';
|
|
16
|
+
/**
|
|
17
|
+
* The seam between a proxy and state. `path` is measured from the sink's
|
|
18
|
+
* root — the key's value for {@link rootKeySink}, the element for
|
|
19
|
+
* {@link elementSink}; `[]` is the root itself.
|
|
20
|
+
*/
|
|
21
|
+
export interface WriteSink {
|
|
22
|
+
/** The value at `path`, as it stands NOW (read-your-writes; never a tracked read). */
|
|
23
|
+
readAt(path: readonly string[]): unknown;
|
|
24
|
+
/** Put an already-unwrapped `value` at `path`. The sink chooses the verb. */
|
|
25
|
+
put(path: readonly string[], value: unknown): void;
|
|
26
|
+
/** Remove the key at `path`. */
|
|
27
|
+
remove(path: readonly string[]): void;
|
|
28
|
+
}
|
|
29
|
+
/** The two ways a value reaches a state key: what the sink writes through. */
|
|
30
|
+
export interface RootKeyTarget {
|
|
31
|
+
setValue(key: string, value: unknown): void;
|
|
32
|
+
updateValue(key: string, value: unknown): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* WHY: a write below a top-level key has TWO verbs (reactive/README.md, law
|
|
36
|
+
* 3) — an object leaf is a `merge` of a nested patch, an array leaf or a
|
|
37
|
+
* delete rebuilds the root immutably and commits a `set` of the key. Only the
|
|
38
|
+
* sink knows that; the traps do not. Every write drops the key's cached child
|
|
39
|
+
* proxy (`cache`), so the next read is built over the new value.
|
|
40
|
+
*/
|
|
41
|
+
export declare function rootKeySink(target: RootKeyTarget, readSilent: (key: string) => unknown, rootKey: string, cache: Pick<MemberCache, 'delete'>): WriteSink;
|
|
42
|
+
/**
|
|
43
|
+
* WHY: a write inside an array element is an array write — it rebuilds the
|
|
44
|
+
* owning array immutably and hands the WHOLE new array to the array proxy's
|
|
45
|
+
* commit, the funnel `push` already uses. `path` is measured from the
|
|
46
|
+
* element; `[]` is the element slot itself.
|
|
47
|
+
*/
|
|
48
|
+
export declare function elementSink(getCurrent: () => unknown[], commit: (next: unknown[]) => void, index: number): WriteSink;
|
|
49
|
+
/**
|
|
50
|
+
* WHY one set trap: the assigned value is unwrapped HERE (landmine 1 — the
|
|
51
|
+
* JSON round-trip applies to what the caller handed in, never to the
|
|
52
|
+
* surrounding state) and handed to the sink at the proxy's path plus the key.
|
|
53
|
+
*/
|
|
54
|
+
export declare function sinkSetTrap(sink: WriteSink, segments: readonly string[]): ProxyHandler<object>['set'];
|
|
55
|
+
/**
|
|
56
|
+
* WHY one delete trap: `merge` cannot express a removal (a patch of
|
|
57
|
+
* `undefined` reads as absent), so a delete always rebuilds through the sink.
|
|
58
|
+
*/
|
|
59
|
+
export declare function sinkDeleteTrap(sink: WriteSink, segments: readonly string[]): ProxyHandler<object>['deleteProperty'];
|
package/package.json
CHANGED