@solidjs/signals 2.0.0-rc.17 → 2.0.0-rc.18
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/dev-shared.js +296 -191
- package/dist/dev.attribution.js +16 -16
- package/dist/dev.js +527 -311
- package/dist/observe/core/async.js +148 -115
- package/dist/observe/core/constants.js +3 -1
- package/dist/observe/core/core.js +27 -27
- package/dist/observe/core/effect.js +29 -32
- package/dist/observe/core/graph.js +5 -5
- package/dist/observe/core/owner.js +7 -7
- package/dist/observe/core/scheduler.js +23 -12
- package/dist/observe/core/verdict.js +2 -2
- package/dist/observe/deferred.js +16 -13
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +2 -0
- package/dist/observe/store/reconcile.js +137 -121
- package/dist/observe/store/store.js +595 -618
- package/dist/observe/store/target.js +1 -5
- package/dist/prod/core/async.js +209 -176
- package/dist/prod/core/constants.js +3 -1
- package/dist/prod/core/core.js +33 -33
- package/dist/prod/core/effect.js +24 -27
- package/dist/prod/core/graph.js +2 -2
- package/dist/prod/core/lanes.js +14 -14
- package/dist/prod/core/scheduler.js +35 -24
- package/dist/prod/core/truth.js +4 -4
- package/dist/prod/core/verdict.js +7 -7
- package/dist/prod/deferred.js +17 -14
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +2 -0
- package/dist/prod/store/optimistic.js +1 -1
- package/dist/prod/store/reconcile.js +137 -121
- package/dist/prod/store/store.js +94 -117
- package/dist/prod/store/target.js +13 -17
- package/dist/types/core/async.d.ts +12 -2
- package/dist/types/core/constants.d.ts +5 -0
- package/dist/types/core/core.d.ts +19 -3
- package/dist/types/core/scheduler.d.ts +15 -3
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -16,7 +16,7 @@ import { insertIntoHeap } from "../core/heap.js";
|
|
|
16
16
|
|
|
17
17
|
import { txOf, flushTransaction, holdNode, joinFuture, insertSubs, schedule, passLane, globalQueue, queuePendingNode, resolveTx, GlobalQueue, installStoreHooks, joinPassTx, dirtyQueue } from "../core/scheduler.js";
|
|
18
18
|
|
|
19
|
-
import { lookupTarget, storeOwners, setLookup, $OWNER, isOwned, markDescendants
|
|
19
|
+
import { lookupTarget, storeOwners, setLookup, $OWNER, isOwned, markDescendants } from "./target.js";
|
|
20
20
|
|
|
21
21
|
import { optHooks, affectsHooks } from "./hooks.js";
|
|
22
22
|
|
|
@@ -97,7 +97,7 @@ export { markRaw } from "./types.js";
|
|
|
97
97
|
|
|
98
98
|
TargetShape.prototype = Object.prototype;
|
|
99
99
|
|
|
100
|
-
function createTarget(e,
|
|
100
|
+
function createTarget(e, t, n, r = t?.fam ?? null) {
|
|
101
101
|
// The proxy target carries the array exotic class when the value is an
|
|
102
102
|
// array, so Array.isArray(proxy) is true; the fields live on it directly.
|
|
103
103
|
// Direct field assignment in one fixed order (no Object.assign literal
|
|
@@ -114,8 +114,8 @@ function createTarget(e, n, t, r = n?.fam ?? null) {
|
|
|
114
114
|
o.k = null;
|
|
115
115
|
o.dk = null;
|
|
116
116
|
o.wk = null;
|
|
117
|
-
o.u =
|
|
118
|
-
o.pk =
|
|
117
|
+
o.u = t;
|
|
118
|
+
o.pk = n;
|
|
119
119
|
o.px = null;
|
|
120
120
|
o.d = false;
|
|
121
121
|
o.a = false;
|
|
@@ -131,20 +131,20 @@ function createTarget(e, n, t, r = n?.fam ?? null) {
|
|
|
131
131
|
return o;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function wrap(e,
|
|
134
|
+
function wrap(e, t = null, n = null, r = t?.fam ?? null) {
|
|
135
135
|
// markRaw'd values never wrap through ANY store (R42; sticky raw-marking
|
|
136
136
|
// is one half of the never-both-wrapped-and-raw invariant, RUL-12).
|
|
137
137
|
if (rawValuesUsed && isRawValue(e)) return e;
|
|
138
138
|
const o = lookupTarget(e, r);
|
|
139
139
|
if (o !== undefined) return o.px;
|
|
140
140
|
const i = e[$TARGET];
|
|
141
|
-
if (i
|
|
141
|
+
if (i?.px === e) {
|
|
142
142
|
// Foreign-family proxies re-wrap into THIS family (writes stay isolated);
|
|
143
143
|
// same-family and plain-store proxies pass through.
|
|
144
144
|
if (r === null || i.fam === r) return e;
|
|
145
|
-
return createTarget(e,
|
|
145
|
+
return createTarget(e, t, n, r).px;
|
|
146
146
|
}
|
|
147
|
-
return createTarget(e,
|
|
147
|
+
return createTarget(e, t, n, r).px;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/** Unwrap our own proxies to their current backing; leave everything else.
|
|
@@ -152,13 +152,13 @@ function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
|
152
152
|
* (projection.ts `wrapDraft`) — adopted as a backing, it would forward to
|
|
153
153
|
* itself (#3767). */ function unwrapValue(e) {
|
|
154
154
|
if (e == null || typeof e !== "object") return e;
|
|
155
|
-
const
|
|
156
|
-
if (
|
|
155
|
+
const t = e[$TARGET];
|
|
156
|
+
if (t !== undefined && (t.px === e || lookupTarget(e, t.fam) === t) && t.v !== undefined) {
|
|
157
157
|
// A draft escaping into other storage must be a REAL container that
|
|
158
158
|
// becomes this target's committed backing at fold (the shared-raw
|
|
159
159
|
// contract) — a prototype overlay is neither.
|
|
160
|
-
if (
|
|
161
|
-
return
|
|
160
|
+
if (t.ovl) materializePB(t);
|
|
161
|
+
return t.pb ?? t.v;
|
|
162
162
|
}
|
|
163
163
|
return e;
|
|
164
164
|
}
|
|
@@ -171,14 +171,14 @@ function wrap(e, n = null, t = null, r = n?.fam ?? null) {
|
|
|
171
171
|
// resolving to the same child target are the same slot (privatization/
|
|
172
172
|
// adoption swap raw identity without changing the logical value — only
|
|
173
173
|
// changed leaves notify, R9).
|
|
174
|
-
const slotNodeEquals = function(e,
|
|
175
|
-
return isEqual(e,
|
|
174
|
+
const slotNodeEquals = function(e, t) {
|
|
175
|
+
return isEqual(e, t) || sameLogicalSlot(this.O, e, t);
|
|
176
176
|
};
|
|
177
177
|
|
|
178
178
|
/** The deep witness: the store decides when it notifies. */ const never = false;
|
|
179
179
|
|
|
180
|
-
function sameKey(e,
|
|
181
|
-
return e ===
|
|
180
|
+
function sameKey(e, t) {
|
|
181
|
+
return e === t || e !== e && t !== t;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/** The container node's arrangement guess: the rows (or keys) the setter
|
|
@@ -191,14 +191,14 @@ function sameKey(e, n) {
|
|
|
191
191
|
constructor(e,
|
|
192
192
|
/** The backing the guess is shown over: the committed one at the
|
|
193
193
|
* guess, the composed arrangement after a re-base (optimistic.ts). */
|
|
194
|
-
|
|
194
|
+
t,
|
|
195
195
|
/** Keys the setter REMOVED from the view it saw (a deliberate delete,
|
|
196
196
|
* not a row the truth has yet to show): a re-base does not restore
|
|
197
197
|
* them. `null` when none. */
|
|
198
|
-
|
|
198
|
+
n = null) {
|
|
199
199
|
this.rows = e;
|
|
200
|
-
this.base =
|
|
201
|
-
this.removed =
|
|
200
|
+
this.base = t;
|
|
201
|
+
this.removed = n;
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -209,38 +209,38 @@ function sameKey(e, n) {
|
|
|
209
209
|
|
|
210
210
|
/** The container node's comparator: a staging is a backing (identity — a
|
|
211
211
|
* pending backing is never "equal" to the committed one); a lane value is
|
|
212
|
-
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e,
|
|
213
|
-
return e ===
|
|
212
|
+
* an arrangement (`LaneView`), judged by the optimistic machinery. */ const containerEquals = function(e, t) {
|
|
213
|
+
return e === t || !!optHooks?.arrangement(this.O, e, t);
|
|
214
214
|
};
|
|
215
215
|
|
|
216
|
-
function sameLogicalSlot(e,
|
|
217
|
-
if (
|
|
218
|
-
const r = lookupTarget(
|
|
219
|
-
return r !== undefined && r === lookupTarget(
|
|
216
|
+
function sameLogicalSlot(e, t, n) {
|
|
217
|
+
if (t === null || typeof t !== "object" || n === null || typeof n !== "object") return false;
|
|
218
|
+
const r = lookupTarget(t, e.fam);
|
|
219
|
+
return r !== undefined && r === lookupTarget(n, e.fam);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/** Slot nodes whose last subscriber left while they still held a staging:
|
|
223
223
|
* released at the commit that resolves it (`drainFolds`). */ const deferredReleases = new Set;
|
|
224
224
|
|
|
225
225
|
function releaseSlot(e) {
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
if (
|
|
230
|
-
} else if (
|
|
231
|
-
if (
|
|
232
|
-
} else if (
|
|
233
|
-
delete
|
|
234
|
-
|
|
235
|
-
} else if (
|
|
226
|
+
const t = e.O;
|
|
227
|
+
const n = e.Vt;
|
|
228
|
+
if (n === $TRACK) {
|
|
229
|
+
if (t.k === e) t.k = null;
|
|
230
|
+
} else if (n === $DEEP) {
|
|
231
|
+
if (t.dk === e) t.dk = null;
|
|
232
|
+
} else if (t.n?.[n] === e) {
|
|
233
|
+
delete t.n[n];
|
|
234
|
+
t.nc--;
|
|
235
|
+
} else if (t.h?.[n] === e) delete t.h[n];
|
|
236
236
|
// The family's index keeps only targets that still carry a node.
|
|
237
|
-
if (
|
|
237
|
+
if (t.fam !== null && t.nc === 0 && t.k === null && t.dk === null && (t.h === null || Reflect.ownKeys(t.h).length === 0)) t.fam.live.delete(t);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e,
|
|
240
|
+
/** A node was born on `target`: a family target enters the live index. */ function noteNode(e, t, n) {
|
|
241
241
|
if (e.fam !== null) e.fam.live.add(e);
|
|
242
242
|
markDescendants(e);
|
|
243
|
-
if (affectsHooks !== null) affectsHooks.ro(e,
|
|
243
|
+
if (affectsHooks !== null) affectsHooks.ro(e, t, n);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
// Shared slot-node release handler: registered once; the core sweep
|
|
@@ -261,10 +261,10 @@ setSlotUnobserved(e => {
|
|
|
261
261
|
* frames (A29): committed from the committed backing, the staging from the
|
|
262
262
|
* pending one — and held by the container's transaction when the container
|
|
263
263
|
* is held (#3706: the unit of the hold is the key — a key the batch left
|
|
264
|
-
* unchanged stages nothing and holds no one). */ function bornStaged(e,
|
|
265
|
-
if (
|
|
266
|
-
queuePendingNode(
|
|
267
|
-
|
|
264
|
+
* unchanged stages nothing and holds no one). */ function bornStaged(e, t, n, r) {
|
|
265
|
+
if (t.Ye && t.Ye(t.Ie, n)) return;
|
|
266
|
+
queuePendingNode(t);
|
|
267
|
+
t.ue = n;
|
|
268
268
|
const o = e.k;
|
|
269
269
|
if (o === null) return;
|
|
270
270
|
// a node-less staging holds nothing (`stageOn`)
|
|
@@ -275,28 +275,28 @@ setSlotUnobserved(e => {
|
|
|
275
275
|
// correction's hold's.)
|
|
276
276
|
if (o.l & CONFIG_HELD && heldKeyChanged(e, r)) {
|
|
277
277
|
const e = txOf(o);
|
|
278
|
-
if (!(o.l & CONFIG_OVERRIDE) || e.folded) holdNode(
|
|
278
|
+
if (!(o.l & CONFIG_OVERRIDE) || e.folded) holdNode(t, resolveTx(e._parent ?? e));
|
|
279
279
|
}
|
|
280
280
|
// A28 (4): a batch written inside a creation-time pass is promoted — its
|
|
281
281
|
// late-materialized leaf with it.
|
|
282
|
-
if (o.l & CONFIG_PROMOTED) notePromotedWrite(
|
|
282
|
+
if (o.l & CONFIG_PROMOTED) notePromotedWrite(t);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
function getNode(e,
|
|
285
|
+
function getNode(e, t,
|
|
286
286
|
// First-read dedupe (create-floor slice 2): the get trap probes
|
|
287
287
|
// accessor-ness right before creating the node — pass the verdict through
|
|
288
288
|
// so creation skips the second descriptor scan. -1 = unknown (other
|
|
289
289
|
// callers), 0/1 = probed.
|
|
290
|
-
|
|
290
|
+
n = -1) {
|
|
291
291
|
const r = e.n ??= Object.create(null);
|
|
292
|
-
let o = r[
|
|
292
|
+
let o = r[t];
|
|
293
293
|
if (o === undefined) {
|
|
294
294
|
// Accessor-ness resolved ONCE per node (no per-object descriptor scan
|
|
295
295
|
// on reads): accessor keys serve through Reflect.get with the proxy
|
|
296
296
|
// receiver.
|
|
297
297
|
const i = e.pb;
|
|
298
|
-
const l = o = slotSignal(committed(e)[
|
|
299
|
-
if (i !== null && !l.acc) bornStaged(e, l, e.del
|
|
298
|
+
const l = o = slotSignal(committed(e)[t], slotNodeEquals, e, t, n === -1 ? isOwnAccessor(i ?? e.v, t) : n === 1);
|
|
299
|
+
if (i !== null && !l.acc) bornStaged(e, l, e.del?.has(t) ? undefined : i[t], t);
|
|
300
300
|
// Attribution-only: name store property nodes by path segment so
|
|
301
301
|
// attribution chains and wide-scope warnings read "store.todos" (or
|
|
302
302
|
// "todos.title" when the store was declared with a name), not
|
|
@@ -304,28 +304,28 @@ t = -1) {
|
|
|
304
304
|
// the hottest store path, and the disabled cost must stay one null
|
|
305
305
|
// check (nodes created before enable() stay generically named).
|
|
306
306
|
if (attrHooks !== null) {
|
|
307
|
-
const
|
|
308
|
-
l._name = (storeNames.get(
|
|
309
|
-
l._loc = storeLocs.get(
|
|
307
|
+
const n = storeRoot(e);
|
|
308
|
+
l._name = (storeNames.get(n) ?? "store") + "." + String(t);
|
|
309
|
+
l._loc = storeLocs.get(n);
|
|
310
310
|
stampNodeOwner(l, e);
|
|
311
311
|
}
|
|
312
|
-
r[
|
|
312
|
+
r[t] = o;
|
|
313
313
|
e.nc++;
|
|
314
|
-
noteNode(e, l,
|
|
314
|
+
noteNode(e, l, t);
|
|
315
315
|
}
|
|
316
316
|
return o;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
function getHasNode(e,
|
|
320
|
-
const
|
|
321
|
-
let r = t
|
|
319
|
+
function getHasNode(e, t) {
|
|
320
|
+
const n = e.h ??= Object.create(null);
|
|
321
|
+
let r = n[t];
|
|
322
322
|
if (r === undefined) {
|
|
323
|
-
const o = r = slotSignal(
|
|
323
|
+
const o = r = slotSignal(t in committed(e), isEqual, e, t, false);
|
|
324
324
|
const i = e.pb;
|
|
325
|
-
if (i !== null) bornStaged(e, o,
|
|
325
|
+
if (i !== null) bornStaged(e, o, t in i && !e.del?.has(t), t);
|
|
326
326
|
if (attrHooks !== null) stampNodeOwner(o, e);
|
|
327
|
-
t
|
|
328
|
-
noteNode(e, o,
|
|
327
|
+
n[t] = r;
|
|
328
|
+
noteNode(e, o, t);
|
|
329
329
|
}
|
|
330
330
|
return r;
|
|
331
331
|
}
|
|
@@ -333,22 +333,22 @@ function getHasNode(e, n) {
|
|
|
333
333
|
/** The container node (plan sec. 31.3): created by the first structural subscription
|
|
334
334
|
* or the first write that needs to stage. Its committed value is the
|
|
335
335
|
* committed backing. */ function getContainerNode(e) {
|
|
336
|
-
let
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
if (attrHooks !== null) stampNodeOwner(
|
|
340
|
-
noteNode(e,
|
|
336
|
+
let t = e.k;
|
|
337
|
+
if (t === null) {
|
|
338
|
+
t = e.k = slotSignal(e.v, containerEquals, e, $TRACK, false);
|
|
339
|
+
if (attrHooks !== null) stampNodeOwner(t, e);
|
|
340
|
+
noteNode(e, t, $TRACK);
|
|
341
341
|
// Born while a batch is staged without a node (`stageOn`): the node
|
|
342
342
|
// takes the staging as its own — the pre-batch backing committed, the
|
|
343
343
|
// pending backing staged — so whoever asked for it reads the frame it
|
|
344
344
|
// would have read had the node been there from the write.
|
|
345
345
|
if (e.pb !== null && !directCommit) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
queuePendingNode(
|
|
346
|
+
t.Ie = preBatch(e);
|
|
347
|
+
t.ue = e.pb;
|
|
348
|
+
queuePendingNode(t);
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
|
-
return
|
|
351
|
+
return t;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
/** Q-A's escape hatch (plan sec. 33; measured plan sec. 41.4): a staging needs the container
|
|
@@ -360,38 +360,38 @@ function getHasNode(e, n) {
|
|
|
360
360
|
* staging: nothing allocates, nothing is swept at the commit (dbmon's tick
|
|
361
361
|
* re-created, queued, swept and dropped ~7000 container nodes). If the
|
|
362
362
|
* flush parks, the seam materializes and holds the node then
|
|
363
|
-
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e,
|
|
363
|
+
* (`storeParkHook`) — the one case the staging home exists for. */ function stageOn(e, t) {
|
|
364
364
|
if (e.k === null && passLane === null && (context === null || globalQueue.q) && !deriveHolds(e)) return;
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
367
|
-
|
|
365
|
+
const n = getContainerNode(e);
|
|
366
|
+
if (n.ue === NOT_PENDING) queuePendingNode(n);
|
|
367
|
+
n.ue = t;
|
|
368
368
|
// A28 (4): a write issued inside a pass outside a flush (a creation-time
|
|
369
369
|
// derive) is promoted at the pass's end — the batch with it.
|
|
370
|
-
if (context !== null) notePromotedWrite(
|
|
371
|
-
if (e.fam !== null) holdWithDerive(e,
|
|
370
|
+
if (context !== null) notePromotedWrite(n);
|
|
371
|
+
if (e.fam !== null) holdWithDerive(e, n);
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
/** The projection's derive is held by a transaction and this is its own
|
|
375
375
|
* write (not a user setter's): the staging is the derive's (`holdWithDerive`). */ function deriveHolds(e) {
|
|
376
|
-
const
|
|
377
|
-
return
|
|
376
|
+
const t = e.fam?.node;
|
|
377
|
+
return t != null && !userWrite && (t.l & CONFIG_HELD) !== 0;
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
const storePark = e => {
|
|
381
|
-
for (let
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
381
|
+
for (let t = 0; t < foldList.length; t++) {
|
|
382
|
+
const n = foldList[t];
|
|
383
|
+
if (n.k === null && n.pb !== null) holdNode(getContainerNode(n), e);
|
|
384
384
|
}
|
|
385
385
|
};
|
|
386
386
|
|
|
387
387
|
function getDeepNode(e) {
|
|
388
|
-
let
|
|
389
|
-
if (
|
|
390
|
-
|
|
391
|
-
if (attrHooks !== null) stampNodeOwner(
|
|
392
|
-
noteNode(e,
|
|
388
|
+
let t = e.dk;
|
|
389
|
+
if (t === null) {
|
|
390
|
+
t = e.dk = slotSignal(0, never, e, $DEEP, false);
|
|
391
|
+
if (attrHooks !== null) stampNodeOwner(t, e);
|
|
392
|
+
noteNode(e, t, $DEEP);
|
|
393
393
|
}
|
|
394
|
-
return
|
|
394
|
+
return t;
|
|
395
395
|
}
|
|
396
396
|
|
|
397
397
|
function bumpDeep(e) {
|
|
@@ -401,12 +401,12 @@ function bumpDeep(e) {
|
|
|
401
401
|
/** The structural readers' one notification: a membership/arrangement
|
|
402
402
|
* change on the container (the staging is already on the node; `setSignal`
|
|
403
403
|
* re-stages the same backing and walks the subscribers — and, on later
|
|
404
|
-
* steps, routes a held or lane-owned container the way any node is). */ function notifyContainer(e,
|
|
404
|
+
* steps, routes a held or lane-owned container the way any node is). */ function notifyContainer(e, t) {
|
|
405
405
|
// A container carrying an arrangement guess takes the write as a landing
|
|
406
406
|
// on the guess (`laneWrite` → `supersede`, judged by `containerEquals`);
|
|
407
407
|
// otherwise the structural readers are walked (the staging is already on
|
|
408
408
|
// the node; the comparator is the store's, done).
|
|
409
|
-
if (e.l & CONFIG_OVERRIDE) setSignal(e,
|
|
409
|
+
if (e.l & CONFIG_OVERRIDE) setSignal(e, t); else {
|
|
410
410
|
if (e.l & CONFIG_HELD) joinFuture(txOf(e));
|
|
411
411
|
if (snapshotCaptureActive) captureWriteSnapshot(e, e.Ie);
|
|
412
412
|
insertSubs(e);
|
|
@@ -416,17 +416,17 @@ function bumpDeep(e) {
|
|
|
416
416
|
|
|
417
417
|
// Observe-tier labels and owners (attribution).
|
|
418
418
|
function storeRoot(e) {
|
|
419
|
-
let
|
|
420
|
-
while (
|
|
421
|
-
return
|
|
419
|
+
let t = e;
|
|
420
|
+
while (t.u !== null) t = t.u;
|
|
421
|
+
return t;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
function storeRootOwner(e) {
|
|
425
425
|
return storeOwners.get(storeRoot(e));
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
function stampNodeOwner(e,
|
|
429
|
-
e.H = storeRootOwner(
|
|
428
|
+
function stampNodeOwner(e, t) {
|
|
429
|
+
e.H = storeRootOwner(t) ?? null;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
const storeNames = new WeakMap;
|
|
@@ -436,11 +436,11 @@ const storeNames = new WeakMap;
|
|
|
436
436
|
// neither).
|
|
437
437
|
const storeLocs = new WeakMap;
|
|
438
438
|
|
|
439
|
-
function nameStore(e,
|
|
439
|
+
function nameStore(e, t, n) {
|
|
440
440
|
if (attrHooks !== null) {
|
|
441
441
|
const r = e[$TARGET];
|
|
442
|
-
if (
|
|
443
|
-
if (
|
|
442
|
+
if (t) storeNames.set(r, t);
|
|
443
|
+
if (n) storeLocs.set(r, n);
|
|
444
444
|
}
|
|
445
445
|
}
|
|
446
446
|
|
|
@@ -454,59 +454,59 @@ const lookupSetter = Object.prototype.__lookupSetter__;
|
|
|
454
454
|
|
|
455
455
|
const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
|
|
456
456
|
|
|
457
|
-
function isOwnAccessor(e,
|
|
458
|
-
return hasOwn.call(e,
|
|
457
|
+
function isOwnAccessor(e, t) {
|
|
458
|
+
return hasOwn.call(e, t) && (lookupGetter.call(e, t) !== undefined || lookupSetter.call(e, t) !== undefined);
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
/** Shallow clone into store ownership. Plain data (grade 2) spreads; anything
|
|
462
|
-
* else copies descriptors (accessors kept, everything made configurable). */ function cloneRaw(e,
|
|
463
|
-
|
|
464
|
-
if (
|
|
465
|
-
const
|
|
462
|
+
* else copies descriptors (accessors kept, everything made configurable). */ function cloneRaw(e, t) {
|
|
463
|
+
t.sc || scanAccessorsOnce(t);
|
|
464
|
+
if (t.sc === 2) {
|
|
465
|
+
const n = {
|
|
466
466
|
...e
|
|
467
467
|
};
|
|
468
|
-
|
|
469
|
-
return
|
|
468
|
+
n[$OWNER] = t;
|
|
469
|
+
return n;
|
|
470
470
|
}
|
|
471
|
-
const
|
|
472
|
-
for (const r of Reflect.ownKeys(
|
|
473
|
-
const o =
|
|
471
|
+
const n = Object.getOwnPropertyDescriptors(e);
|
|
472
|
+
for (const r of Reflect.ownKeys(n)) {
|
|
473
|
+
const o = n[r];
|
|
474
474
|
if (r === "length" && Array.isArray(e)) continue;
|
|
475
475
|
o.configurable = true;
|
|
476
|
-
if (!o.get && !o.set) o.writable = true; else
|
|
476
|
+
if (!o.get && !o.set) o.writable = true; else t.a = true;
|
|
477
477
|
}
|
|
478
|
-
const r = Array.isArray(e) ? Object.defineProperties([],
|
|
479
|
-
r[$OWNER] =
|
|
478
|
+
const r = Array.isArray(e) ? Object.defineProperties([], n) : Object.create(Object.getPrototypeOf(e), n);
|
|
479
|
+
r[$OWNER] = t;
|
|
480
480
|
return r;
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
-
/** Wide plain clone (many keys): null-prototype build, then reset. */ function wideClone(e,
|
|
484
|
-
const
|
|
485
|
-
for (const
|
|
486
|
-
Object.setPrototypeOf(
|
|
487
|
-
|
|
488
|
-
return
|
|
483
|
+
/** Wide plain clone (many keys): null-prototype build, then reset. */ function wideClone(e, t) {
|
|
484
|
+
const n = Object.create(null);
|
|
485
|
+
for (const t of Reflect.ownKeys(e)) n[t] = e[t];
|
|
486
|
+
Object.setPrototypeOf(n, Object.prototype);
|
|
487
|
+
n[$OWNER] = t;
|
|
488
|
+
return n;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
function copyOwn(e,
|
|
492
|
-
const r = Object.getOwnPropertyDescriptor(
|
|
493
|
-
if (r.get || r.set || !r.enumerable || !r.writable || !r.configurable) Object.defineProperty(e,
|
|
491
|
+
function copyOwn(e, t, n) {
|
|
492
|
+
const r = Object.getOwnPropertyDescriptor(t, n);
|
|
493
|
+
if (r.get || r.set || !r.enumerable || !r.writable || !r.configurable) Object.defineProperty(e, n, r); else e[n] = r.value;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
/** One descriptor scan per container (grade 1/2, `a`, `kc`). */ function scanAccessorsOnce(e) {
|
|
497
|
-
const
|
|
498
|
-
const
|
|
499
|
-
let r = Object.getPrototypeOf(
|
|
500
|
-
for (const o of
|
|
501
|
-
if (
|
|
497
|
+
const t = e.v;
|
|
498
|
+
const n = Reflect.ownKeys(t);
|
|
499
|
+
let r = Object.getPrototypeOf(t) === Object.prototype;
|
|
500
|
+
for (const o of n) {
|
|
501
|
+
if (isOwnAccessor(t, o)) {
|
|
502
502
|
e.a = true;
|
|
503
503
|
r = false;
|
|
504
504
|
break;
|
|
505
505
|
}
|
|
506
|
-
if (r && !propertyIsEnumerable.call(
|
|
506
|
+
if (r && !propertyIsEnumerable.call(t, o)) r = false;
|
|
507
507
|
}
|
|
508
508
|
e.sc = r ? 2 : 1;
|
|
509
|
-
e.kc =
|
|
509
|
+
e.kc = n.length;
|
|
510
510
|
return !e.a;
|
|
511
511
|
}
|
|
512
512
|
|
|
@@ -521,84 +521,76 @@ const OVERLAY_REBUILD_MIN_ADDS = 16;
|
|
|
521
521
|
const WK_ALL = new Set;
|
|
522
522
|
|
|
523
523
|
const plainProto = e => {
|
|
524
|
-
const
|
|
525
|
-
return
|
|
524
|
+
const t = Object.getPrototypeOf(e);
|
|
525
|
+
return t === Object.prototype || t === Array.prototype || t === null;
|
|
526
526
|
};
|
|
527
527
|
|
|
528
528
|
/** An overlay with many adds (or any delete) rebuilds the committed
|
|
529
|
-
* container at flatten so its shape stays fast (#3689). */ function overlayRebuilds(e,
|
|
529
|
+
* container at flatten so its shape stays fast (#3689). */ function overlayRebuilds(e, t) {
|
|
530
530
|
if (e.kc > OVERLAY_REBUILD_MAX_KEYS) return false;
|
|
531
|
-
if (e.del
|
|
532
|
-
const
|
|
533
|
-
if (
|
|
531
|
+
if (e.del?.size) return true;
|
|
532
|
+
const n = e.wk;
|
|
533
|
+
if (n !== null && n !== WK_ALL && n.u === undefined && n.size <= OVERLAY_REBUILD_MIN_ADDS) return false;
|
|
534
534
|
const r = e.v;
|
|
535
535
|
let o = 0;
|
|
536
|
-
for (const e of Reflect.ownKeys(
|
|
536
|
+
for (const e of Reflect.ownKeys(t)) if (!hasOwn.call(r, e) && ++o > OVERLAY_REBUILD_MIN_ADDS) return true;
|
|
537
537
|
return false;
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
/** Downgrade an overlay to a real clone (a consumer needs a container). */ function materializePB(e) {
|
|
541
|
-
const
|
|
542
|
-
if (!e.ovl) return
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
for (const n of e.del) delete t[n];
|
|
546
|
-
e.kc -= e.del.size;
|
|
547
|
-
e.del = null;
|
|
548
|
-
}
|
|
549
|
-
for (const r of Reflect.ownKeys(n)) e.sc === 2 ? t[r] = n[r] : copyOwn(t, n, r);
|
|
550
|
-
e.pb = t;
|
|
551
|
-
e.ovl = false;
|
|
541
|
+
const t = e.pb;
|
|
542
|
+
if (!e.ovl) return t;
|
|
543
|
+
const n = overlayOnto(e, e.sc === 2 ? wideClone(e.v, e) : cloneRaw(e.v, e), t);
|
|
544
|
+
e.pb = n;
|
|
552
545
|
// The staging IS the pending backing: re-point the container node.
|
|
553
|
-
if (e.k
|
|
554
|
-
return
|
|
546
|
+
if (e.k?.ue === t) e.k.ue = n;
|
|
547
|
+
return n;
|
|
555
548
|
}
|
|
556
549
|
|
|
557
550
|
/** The batch's pending backing, created on the first draft write: an overlay
|
|
558
551
|
* of an owned plain-data record above the key threshold, a clone otherwise.
|
|
559
552
|
* Staged on the container node (the scheduler owns its lifetime) and queued
|
|
560
553
|
* for the fold at the flush's commit. */ function ensurePB(e) {
|
|
561
|
-
let
|
|
554
|
+
let t = e.pb;
|
|
562
555
|
// An optimistic edit composes on the view the user saw and becomes
|
|
563
556
|
// guesses at the setter's exit: nothing is staged, nothing folds
|
|
564
557
|
// (optimistic.ts).
|
|
565
558
|
if (userWrite && e.fam?.opt === true && !pendingNotify.has(e)) return e.pb = optHooks.draft(e);
|
|
566
|
-
if (
|
|
559
|
+
if (t !== null && !isOwned(t)) {
|
|
567
560
|
// An adopted object awaiting its fold is the user's: the draft writes a
|
|
568
561
|
// clone of it. The clone is the batch's staging — unless the adoption is
|
|
569
562
|
// HELD (another transaction's): then the held staging stays the
|
|
570
563
|
// adoption and the draft is a mainline layer above it (#3612/#3688: a
|
|
571
564
|
// mainline setter mid-hold publishes mainline; the held keys stay held).
|
|
572
|
-
|
|
573
|
-
const
|
|
574
|
-
if (
|
|
575
|
-
} else if (
|
|
576
|
-
const
|
|
565
|
+
t = e.pb = cloneRaw(t, e);
|
|
566
|
+
const n = e.k;
|
|
567
|
+
if (n !== null && !(n.l & CONFIG_HELD)) n.ue = t;
|
|
568
|
+
} else if (t === null) {
|
|
569
|
+
const n = e.v;
|
|
577
570
|
if (!directCommit) queueFold(e);
|
|
578
|
-
if (!e.ch && !Array.isArray(
|
|
579
|
-
|
|
571
|
+
if (!e.ch && !Array.isArray(n) && (e.sc !== 0 ? !e.a : scanAccessorsOnce(e)) && e.kc > OVERLAY_MIN_KEYS && isOwned(n)) {
|
|
572
|
+
t = e.pb = Object.create(n);
|
|
580
573
|
e.ovl = true;
|
|
581
|
-
} else
|
|
582
|
-
if (!directCommit) stageOn(e,
|
|
574
|
+
} else t = e.pb = cloneRaw(n, e);
|
|
575
|
+
if (!directCommit) stageOn(e, t);
|
|
583
576
|
}
|
|
584
|
-
return
|
|
577
|
+
return t;
|
|
585
578
|
}
|
|
586
579
|
|
|
587
580
|
/** Privatize `target`'s committed backing (and its ancestor chain — a shared
|
|
588
581
|
* parent cannot point at an owned child). Not a reactive event. */ function privatizeCommitted(e) {
|
|
589
582
|
if (isOwned(e.v)) return;
|
|
590
|
-
const
|
|
591
|
-
const
|
|
583
|
+
const t = e.v;
|
|
584
|
+
const n = cloneRaw(t, e);
|
|
592
585
|
// Mid-batch (a staging is open on this target) the fold still sees the
|
|
593
586
|
// backing the batch started from: the queue recorded it (`foldOlds`).
|
|
594
|
-
e.v =
|
|
587
|
+
e.v = n;
|
|
595
588
|
e.ch = false;
|
|
596
589
|
if (e.u) {
|
|
597
590
|
privatizeCommitted(e.u);
|
|
598
|
-
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
if (t[r] === n) t[r] = e.v;
|
|
591
|
+
const n = e.u.v;
|
|
592
|
+
const r = parentSlotKey(e, t);
|
|
593
|
+
if (n[r] === t) n[r] = e.v;
|
|
602
594
|
}
|
|
603
595
|
}
|
|
604
596
|
|
|
@@ -606,30 +598,33 @@ const plainProto = e => {
|
|
|
606
598
|
* when an array parent moved it (#3282). Not searched when the slot already
|
|
607
599
|
* holds this target's backing (a parent adopted whole carries its children's
|
|
608
600
|
* new raws before the children fold — the miss was O(rows) per changed row
|
|
609
|
-
* of a keyed reconcile). */ function parentSlotKey(e,
|
|
610
|
-
const
|
|
601
|
+
* of a keyed reconcile). */ function parentSlotKey(e, t) {
|
|
602
|
+
const n = e.pk;
|
|
611
603
|
const r = e.u.v;
|
|
612
|
-
if (r[
|
|
613
|
-
const o = r.indexOf(
|
|
614
|
-
if (o === -1) return
|
|
604
|
+
if (r[n] === t || r[n] === e.v || !Array.isArray(r)) return n;
|
|
605
|
+
const o = r.indexOf(t);
|
|
606
|
+
if (o === -1) return n;
|
|
615
607
|
e.pk = o;
|
|
616
608
|
return o;
|
|
617
609
|
}
|
|
618
610
|
|
|
619
611
|
/** Overlay flatten (#3044): own keys onto the owned committed backing in
|
|
620
|
-
* place — the backing keeps its identity. */ function flattenOverlay(e,
|
|
612
|
+
* place — the backing keeps its identity. */ function flattenOverlay(e, t) {
|
|
621
613
|
privatizeCommitted(e);
|
|
622
|
-
|
|
614
|
+
overlayOnto(e, e.v, t);
|
|
615
|
+
// the caller (`foldTarget`) closes the staging
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/** An overlay's own keys and deletions onto `to` (the two are disjoint: a
|
|
619
|
+
* write un-deletes its key); the overlay is closed. */ function overlayOnto(e, t, n) {
|
|
623
620
|
for (const r of Reflect.ownKeys(n)) e.sc === 2 ? t[r] = n[r] : copyOwn(t, n, r);
|
|
624
621
|
if (e.del !== null) {
|
|
625
622
|
for (const n of e.del) delete t[n];
|
|
626
623
|
e.kc -= e.del.size;
|
|
627
624
|
e.del = null;
|
|
628
625
|
}
|
|
629
|
-
e.pb = null;
|
|
630
626
|
e.ovl = false;
|
|
631
|
-
|
|
632
|
-
// written-keys window closes with the commit
|
|
627
|
+
return t;
|
|
633
628
|
}
|
|
634
629
|
|
|
635
630
|
/** Adoption (a setter's returned replacement; reconcile and projection
|
|
@@ -641,7 +636,7 @@ const plainProto = e => {
|
|
|
641
636
|
* they were LAST told (#3296): the draft's pending backing when one
|
|
642
637
|
* preceded the adoption this batch — its node writes were real, and the
|
|
643
638
|
* adoption's re-stage cancels or confirms each — else the committed
|
|
644
|
-
* backing. */ function adoptPB(e,
|
|
639
|
+
* backing. */ function adoptPB(e, t, n = true) {
|
|
645
640
|
if (!directCommit) queueFold(e);
|
|
646
641
|
let r;
|
|
647
642
|
if (e.pb !== null) {
|
|
@@ -650,27 +645,27 @@ const plainProto = e => {
|
|
|
650
645
|
} else r = e.v;
|
|
651
646
|
if (directCommit) {
|
|
652
647
|
e.pb = null;
|
|
653
|
-
if (e.k !== null) e.k.Ie =
|
|
648
|
+
if (e.k !== null) e.k.Ie = t;
|
|
654
649
|
} else {
|
|
655
650
|
// Eager (INTERNALS §3): the backing swaps now — handlers read the
|
|
656
651
|
// adopted object before the flush — while the container node, when
|
|
657
652
|
// there is one (`stageOn`), keeps the committed frame (`_value`) for
|
|
658
653
|
// readers a hold keeps on it, and stages the adoption for the fold's
|
|
659
654
|
// path copy.
|
|
660
|
-
e.pb =
|
|
661
|
-
stageOn(e,
|
|
655
|
+
e.pb = t;
|
|
656
|
+
stageOn(e, t);
|
|
662
657
|
}
|
|
663
|
-
e.v =
|
|
664
|
-
e.ch =
|
|
658
|
+
e.v = t;
|
|
659
|
+
e.ch = t[$TARGET] !== undefined;
|
|
665
660
|
e.ovl = false;
|
|
666
661
|
e.del = null;
|
|
667
662
|
e.sc = 0;
|
|
668
663
|
e.a = false;
|
|
669
664
|
e.wk = null;
|
|
670
665
|
// adoption supersedes staged trap writes
|
|
671
|
-
const o =
|
|
672
|
-
if (o
|
|
673
|
-
if (
|
|
666
|
+
const o = t[$OWNER];
|
|
667
|
+
if (o?.fam === e.fam) t[$OWNER] = e; else setLookup(t, e);
|
|
668
|
+
if (n && r !== t) notifyFold(e, r, t);
|
|
674
669
|
}
|
|
675
670
|
|
|
676
671
|
// ---------------------------------------------------------------------------
|
|
@@ -724,8 +719,8 @@ let foldIndex = null;
|
|
|
724
719
|
foldIndex = new Map;
|
|
725
720
|
for (let e = 0; e < foldList.length; e++) foldIndex.set(foldList[e], e);
|
|
726
721
|
}
|
|
727
|
-
const
|
|
728
|
-
return
|
|
722
|
+
const t = foldIndex.get(e);
|
|
723
|
+
return t === undefined ? e.v : foldOlds[t];
|
|
729
724
|
}
|
|
730
725
|
|
|
731
726
|
/** The flush committed its pending nodes: fold every pending backing whose
|
|
@@ -735,25 +730,25 @@ let foldIndex = null;
|
|
|
735
730
|
* releases. */ function drainFolds() {
|
|
736
731
|
if (foldList.length !== 0) {
|
|
737
732
|
const e = foldList;
|
|
738
|
-
const
|
|
733
|
+
const t = foldOlds;
|
|
739
734
|
foldList = foldSpare;
|
|
740
735
|
foldOlds = foldOldsSpare;
|
|
741
736
|
foldSpare = e;
|
|
742
|
-
foldOldsSpare =
|
|
737
|
+
foldOldsSpare = t;
|
|
743
738
|
foldIndex = null;
|
|
744
|
-
for (let
|
|
745
|
-
const r = e[
|
|
739
|
+
for (let n = 0; n < e.length; n++) {
|
|
740
|
+
const r = e[n];
|
|
746
741
|
if (r.pb === null) continue;
|
|
747
742
|
// folded already (a direct commit)
|
|
748
743
|
const o = r.k;
|
|
749
744
|
if (o !== null && o.ue !== NOT_PENDING) {
|
|
750
745
|
// held: commits with its hold
|
|
751
746
|
foldList.push(r);
|
|
752
|
-
foldOlds.push(n
|
|
753
|
-
} else foldTarget(r, n
|
|
747
|
+
foldOlds.push(t[n]);
|
|
748
|
+
} else foldTarget(r, t[n]);
|
|
754
749
|
}
|
|
755
750
|
e.length = 0;
|
|
756
|
-
|
|
751
|
+
t.length = 0;
|
|
757
752
|
}
|
|
758
753
|
if (deferredReleases.size !== 0) {
|
|
759
754
|
for (const e of deferredReleases) {
|
|
@@ -769,19 +764,19 @@ let foldIndex = null;
|
|
|
769
764
|
* lanes (A17, #2864: a guess mid-refetch stands in for the flight) and
|
|
770
765
|
* marks (an ambient `affects()` over a declared reload lives to the
|
|
771
766
|
* landing) read it. */ GlobalQueue.k = e => {
|
|
772
|
-
const
|
|
773
|
-
return
|
|
767
|
+
const t = e.O?.fam;
|
|
768
|
+
return (t?.node?.m & STATUS_PENDING) !== 0;
|
|
774
769
|
};
|
|
775
770
|
|
|
776
771
|
/** Core asks (lanes.ts `covered`): the value a slot node's guess covered.
|
|
777
772
|
* A chained link's committed value is the inner store's at the last
|
|
778
773
|
* read-through — refreshed here, since a guess served the reads since
|
|
779
774
|
* (#3672: the revert compares against the base's live value). */ GlobalQueue.fn = e => {
|
|
780
|
-
const
|
|
781
|
-
if (
|
|
782
|
-
const
|
|
783
|
-
const r =
|
|
784
|
-
if (
|
|
775
|
+
const t = e.O;
|
|
776
|
+
if (t.ch) {
|
|
777
|
+
const n = e.Vt;
|
|
778
|
+
const r = t.v;
|
|
779
|
+
if (t.h?.[n] === e) e.Ie = untrack(() => n in r); else if (t.n?.[n] === e) e.Ie = untrack(() => unwrapValue(r[n]));
|
|
785
780
|
}
|
|
786
781
|
return e.Ie;
|
|
787
782
|
};
|
|
@@ -802,76 +797,66 @@ GlobalQueue.fo = isWrappable;
|
|
|
802
797
|
* containers at once. Only the synchronous first run. */ let directCommit = false;
|
|
803
798
|
|
|
804
799
|
function runDirect(e) {
|
|
805
|
-
const
|
|
800
|
+
const t = directCommit;
|
|
806
801
|
directCommit = true;
|
|
807
802
|
try {
|
|
808
803
|
e();
|
|
809
804
|
} finally {
|
|
810
|
-
directCommit =
|
|
805
|
+
directCommit = t;
|
|
811
806
|
}
|
|
812
807
|
}
|
|
813
808
|
|
|
814
|
-
/** Fold one target's pending backing into its committed home. */ function foldTarget(e,
|
|
809
|
+
/** Fold one target's pending backing into its committed home. */ function foldTarget(e, t) {
|
|
815
810
|
{
|
|
816
811
|
{
|
|
817
|
-
const
|
|
812
|
+
const n = e.k;
|
|
818
813
|
const r = e.pb;
|
|
819
814
|
if (r !== null) {
|
|
820
|
-
if (r === e.v) {
|
|
821
|
-
// An eager adoption nothing wrote after: the backing is already
|
|
822
|
-
// the adopted object (shared ownership — never cloned).
|
|
823
|
-
e.pb = null;
|
|
824
|
-
e.wk = null;
|
|
825
|
-
} else if (e.ovl && (e.v !== n || !overlayRebuilds(e, r) && t?.T?.lt !== n)) {
|
|
815
|
+
if (r === e.v) ; else if (e.ovl && (e.v !== t || !overlayRebuilds(e, r) && n?.T?.lt !== t)) {
|
|
826
816
|
// Overlay flatten (#3044): the backing keeps its identity, so the
|
|
827
817
|
// `t.v === old` gate below skips path copying (the parent slot
|
|
828
818
|
// already points here) and the adopted-notify (setter
|
|
829
819
|
// notifications happened at write time). A backing privatized
|
|
830
820
|
// mid-batch is a fresh clone the parent already points at.
|
|
831
821
|
flattenOverlay(e, r);
|
|
832
|
-
} else if (e.v !==
|
|
822
|
+
} else if (e.v !== t) {
|
|
833
823
|
// Privatized mid-batch (a child's path copy repointed us): the
|
|
834
824
|
// owned backing takes the written keys.
|
|
835
825
|
privatizeCommitted(e);
|
|
836
|
-
const
|
|
826
|
+
const n = e.v;
|
|
837
827
|
const o = e.wk;
|
|
838
828
|
if (o !== null && o !== WK_ALL) {
|
|
839
|
-
for (let e = o; e !== undefined; e = e.u) for (const
|
|
840
|
-
if (hasOwn.call(r,
|
|
829
|
+
for (let e = o; e !== undefined; e = e.u) for (const t of e) {
|
|
830
|
+
if (hasOwn.call(r, t)) copyOwn(n, r, t); else delete n[t];
|
|
841
831
|
}
|
|
842
832
|
} else {
|
|
843
|
-
for (const e of Reflect.ownKeys(r))
|
|
844
|
-
|
|
845
|
-
if (
|
|
846
|
-
}
|
|
847
|
-
for (const e of Reflect.ownKeys(n)) {
|
|
848
|
-
if (!hasOwn.call(r, e)) delete t[e];
|
|
833
|
+
for (const e of Reflect.ownKeys(r)) copyOwn(n, r, e);
|
|
834
|
+
for (const e of Reflect.ownKeys(t)) {
|
|
835
|
+
if (!hasOwn.call(r, e)) delete n[e];
|
|
849
836
|
}
|
|
850
837
|
}
|
|
851
|
-
e.pb = null;
|
|
852
|
-
e.wk = null;
|
|
853
838
|
} else {
|
|
854
839
|
// The clone (or adopted object) becomes the committed backing
|
|
855
840
|
// (path copy below).
|
|
856
841
|
e.v = e.ovl ? materializePB(e) : r;
|
|
857
842
|
e.ch = e.v[$TARGET] !== undefined;
|
|
858
|
-
e.pb = null;
|
|
859
|
-
e.wk = null;
|
|
860
843
|
}
|
|
861
|
-
|
|
862
|
-
|
|
844
|
+
e.pb = null;
|
|
845
|
+
e.wk = null;
|
|
846
|
+
// the written-keys window closes with the commit
|
|
847
|
+
}
|
|
848
|
+
if (n !== null) {
|
|
863
849
|
// The node's committed value is the backing. A container nobody
|
|
864
850
|
// subscribes to was a transient staging home (INTERNALS §5: an
|
|
865
851
|
// observer-less write holds a transient record until the fold,
|
|
866
852
|
// discarded with it) — gone with the batch.
|
|
867
|
-
if (
|
|
853
|
+
if (n.o === null) e.k = null; else if (n.Ie instanceof LaneView) n.Ie.base = e.v; else n.Ie = e.v;
|
|
868
854
|
}
|
|
869
|
-
if (e.v !==
|
|
870
|
-
const
|
|
871
|
-
if (e.u.v[
|
|
855
|
+
if (e.v !== t && e.u) {
|
|
856
|
+
const n = parentSlotKey(e, t);
|
|
857
|
+
if (e.u.v[n] === t) {
|
|
872
858
|
privatizeCommitted(e.u);
|
|
873
|
-
|
|
874
|
-
e.u.v[t] = e.v;
|
|
859
|
+
e.u.v[n] = e.v;
|
|
875
860
|
}
|
|
876
861
|
}
|
|
877
862
|
}
|
|
@@ -881,28 +866,28 @@ function runDirect(e) {
|
|
|
881
866
|
// ---------------------------------------------------------------------------
|
|
882
867
|
// write-time notification (setter exit)
|
|
883
868
|
/** Devtools and attribution: the containers a setter replaced wholesale. */ function storePathOf(e) {
|
|
884
|
-
let
|
|
885
|
-
for (let
|
|
886
|
-
return
|
|
869
|
+
let t = "";
|
|
870
|
+
for (let n = e; n !== null; n = n.u) t = n.pk === null ? "store" + t : "." + String(n.pk) + t;
|
|
871
|
+
return t;
|
|
887
872
|
}
|
|
888
873
|
|
|
889
874
|
const REPLACED_CENSUS_MAX = 64;
|
|
890
875
|
|
|
891
|
-
function sameLeaf(e,
|
|
892
|
-
if (e ===
|
|
893
|
-
if (e === null ||
|
|
894
|
-
return unwrapValue(e) === unwrapValue(
|
|
876
|
+
function sameLeaf(e, t) {
|
|
877
|
+
if (e === t) return true;
|
|
878
|
+
if (e === null || t === null || typeof e !== "object" || typeof t !== "object") return false;
|
|
879
|
+
return unwrapValue(e) === unwrapValue(t) || targetsEqual(e, t);
|
|
895
880
|
}
|
|
896
881
|
|
|
897
|
-
function reportReplacedContainers(e,
|
|
898
|
-
const o = r ?? Reflect.ownKeys(
|
|
899
|
-
const i = Array.isArray(
|
|
882
|
+
function reportReplacedContainers(e, t, n, r) {
|
|
883
|
+
const o = r ?? Reflect.ownKeys(n);
|
|
884
|
+
const i = Array.isArray(n);
|
|
900
885
|
const l = storeRootOwner(e);
|
|
901
886
|
for (const r of o) {
|
|
902
887
|
if (i && r === "length" || r === $OWNER) continue;
|
|
903
|
-
if (e.del
|
|
904
|
-
const o = unwrapValue(
|
|
905
|
-
const u = unwrapValue(
|
|
888
|
+
if (e.del?.has(r)) continue;
|
|
889
|
+
const o = unwrapValue(t[r]);
|
|
890
|
+
const u = unwrapValue(n[r]);
|
|
906
891
|
if (o === null || u === null || typeof o !== "object" || typeof u !== "object" || o === u || targetsEqual(o, u)) continue;
|
|
907
892
|
const f = Array.isArray(u);
|
|
908
893
|
if (f !== Array.isArray(o)) continue;
|
|
@@ -912,13 +897,13 @@ function reportReplacedContainers(e, n, t, r) {
|
|
|
912
897
|
s = u.length;
|
|
913
898
|
if (s > REPLACED_CENSUS_MAX) continue;
|
|
914
899
|
const e = new Set;
|
|
915
|
-
for (const
|
|
916
|
-
for (const
|
|
900
|
+
for (const t of o) e.add(unwrapValue(t));
|
|
901
|
+
for (const t of u) if (e.has(unwrapValue(t))) a++;
|
|
917
902
|
} else {
|
|
918
903
|
const e = Object.keys(u);
|
|
919
904
|
s = e.length;
|
|
920
905
|
if (s > REPLACED_CENSUS_MAX) continue;
|
|
921
|
-
for (const
|
|
906
|
+
for (const t of e) if (sameLeaf(o[t], u[t])) a++;
|
|
922
907
|
}
|
|
923
908
|
attrHooks.storeReplaced(storePathOf(e) + "." + String(r), f, s, a, f ? o.length : Object.keys(o).length, l);
|
|
924
909
|
}
|
|
@@ -935,18 +920,18 @@ function reportReplacedContainers(e, n, t, r) {
|
|
|
935
920
|
return userWrite;
|
|
936
921
|
}
|
|
937
922
|
|
|
938
|
-
function heldDerivation(e,
|
|
923
|
+
function heldDerivation(e, t, n) {
|
|
939
924
|
if (e.fam === null || !userWrite) return false;
|
|
940
|
-
if (
|
|
925
|
+
if (t !== undefined) return (t.l & (CONFIG_HELD | CONFIG_MANUAL_WRITE)) === CONFIG_HELD && txOf(t) !== flushTransaction;
|
|
941
926
|
const r = e.k;
|
|
942
|
-
return r !== null && (r.l & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e,
|
|
927
|
+
return r !== null && (r.l & CONFIG_HELD) !== 0 && txOf(r) !== flushTransaction && heldKeyChanged(e, n);
|
|
943
928
|
}
|
|
944
929
|
|
|
945
930
|
/** Setter exit: one node write per changed observed key (the leaves stage
|
|
946
931
|
* like signals — holds and lanes engage here on later steps), presence and
|
|
947
932
|
* deep witnesses, and the container when membership changed. */ function notifyWrites(e) {
|
|
948
|
-
let
|
|
949
|
-
if (
|
|
933
|
+
let t = e.pb;
|
|
934
|
+
if (t === null) return;
|
|
950
935
|
// Optimistic channel: a user's writes on an optimistic family become
|
|
951
936
|
// guesses on the nodes (lanes); the draft is discarded, the committed
|
|
952
937
|
// backing never touched.
|
|
@@ -955,10 +940,10 @@ function heldDerivation(e, n, t) {
|
|
|
955
940
|
// are born from the committed frame, not the draft).
|
|
956
941
|
e.pb = null;
|
|
957
942
|
e.wk = null;
|
|
958
|
-
e.pb = optHooks.writes(e,
|
|
943
|
+
e.pb = optHooks.writes(e, t);
|
|
959
944
|
return;
|
|
960
945
|
}
|
|
961
|
-
const
|
|
946
|
+
const n = e.v;
|
|
962
947
|
const r = e.n;
|
|
963
948
|
// Written-keys bound: trap writes record their keys, so the notify visits
|
|
964
949
|
// O(written) nodes instead of every subscription on the record (a selection
|
|
@@ -974,9 +959,9 @@ function heldDerivation(e, n, t) {
|
|
|
974
959
|
// one did not is no second proposal — its node was told then, and telling
|
|
975
960
|
// a held node its unchanged value again would join this tick to the hold.
|
|
976
961
|
const o = e.wk;
|
|
977
|
-
const i = o === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v :
|
|
978
|
-
if (attrHooks !== null) reportReplacedContainers(e,
|
|
979
|
-
if (e.fam !== null && !heldDerivationHit && i !== null) for (const
|
|
962
|
+
const i = o === WK_ALL || e.a === true || !plainProto(e.ovl ? e.v : t) ? null : o;
|
|
963
|
+
if (attrHooks !== null) reportReplacedContainers(e, n, t, i);
|
|
964
|
+
if (e.fam !== null && !heldDerivationHit && i !== null) for (const t of i) if (heldDerivation(e, r?.[t], t)) {
|
|
980
965
|
heldDerivationHit = true;
|
|
981
966
|
break;
|
|
982
967
|
}
|
|
@@ -985,16 +970,16 @@ function heldDerivation(e, n, t) {
|
|
|
985
970
|
for (const i of o) {
|
|
986
971
|
const o = r[i];
|
|
987
972
|
if (o === undefined) continue;
|
|
988
|
-
if (o.acc === true || hasOwn.call(
|
|
973
|
+
if (o.acc === true || hasOwn.call(t, i) && lookupGetter.call(t, i) !== undefined) {
|
|
989
974
|
// Accessor keys: the node is linked for shape-change notification,
|
|
990
975
|
// its value is never served (the getter runs with the proxy
|
|
991
976
|
// receiver on read) — FORCE wakes the readers. A data descriptor
|
|
992
977
|
// falls through so the cleared-acc hot read serves it (#3949);
|
|
993
978
|
// an own getter's descriptor has no `value`, so the compare below
|
|
994
979
|
// would otherwise keep the getter's result (or FORCE) for `undefined`.
|
|
995
|
-
o.acc = isOwnAccessor(
|
|
996
|
-
const e = Object.getOwnPropertyDescriptor(
|
|
997
|
-
const r = Object.getOwnPropertyDescriptor(
|
|
980
|
+
o.acc = isOwnAccessor(t, i);
|
|
981
|
+
const e = Object.getOwnPropertyDescriptor(n, i);
|
|
982
|
+
const r = Object.getOwnPropertyDescriptor(t, i);
|
|
998
983
|
if (!r || r.get || r.set) {
|
|
999
984
|
if (e?.get !== r?.get || e?.set !== r?.set || e?.value !== r?.value) setSignal(o, () => FORCE);
|
|
1000
985
|
continue;
|
|
@@ -1002,7 +987,7 @@ function heldDerivation(e, n, t) {
|
|
|
1002
987
|
if (e?.get || e?.set || !isEqual(e?.value, r.value)) setSignal(o, () => r.value);
|
|
1003
988
|
continue;
|
|
1004
989
|
}
|
|
1005
|
-
const l = e.del
|
|
990
|
+
const l = e.del?.has(i) ? undefined : t[i];
|
|
1006
991
|
setSignal(o, () => l);
|
|
1007
992
|
if (e.fam !== null) {
|
|
1008
993
|
// The derive's write is a derivation; a user setter's is a proposal
|
|
@@ -1016,20 +1001,20 @@ function heldDerivation(e, n, t) {
|
|
|
1016
1001
|
}
|
|
1017
1002
|
const l = e.h;
|
|
1018
1003
|
if (l !== null) {
|
|
1019
|
-
const
|
|
1020
|
-
for (const r of
|
|
1021
|
-
const
|
|
1022
|
-
if (
|
|
1023
|
-
setSignal(
|
|
1024
|
-
if (e.fam !== null) holdWithDerive(e,
|
|
1004
|
+
const n = i ?? Reflect.ownKeys(l);
|
|
1005
|
+
for (const r of n) {
|
|
1006
|
+
const n = l[r];
|
|
1007
|
+
if (n !== undefined) {
|
|
1008
|
+
setSignal(n, r in t && !e.del?.has(r));
|
|
1009
|
+
if (e.fam !== null) holdWithDerive(e, n);
|
|
1025
1010
|
}
|
|
1026
1011
|
}
|
|
1027
1012
|
}
|
|
1028
1013
|
if (e.dk !== null) {
|
|
1029
|
-
if (e.del
|
|
1014
|
+
if (e.del?.size) bumpDeep(e); else for (const r of i ?? Reflect.ownKeys(t)) {
|
|
1030
1015
|
if (r === $OWNER) continue;
|
|
1031
|
-
const o =
|
|
1032
|
-
const i =
|
|
1016
|
+
const o = t[r];
|
|
1017
|
+
const i = n[r];
|
|
1033
1018
|
if (o !== null && typeof o === "object" ? !targetsEqual(i, o) : !isEqual(i, o)) {
|
|
1034
1019
|
bumpDeep(e);
|
|
1035
1020
|
break;
|
|
@@ -1044,52 +1029,52 @@ function heldDerivation(e, n, t) {
|
|
|
1044
1029
|
if (u !== null && (u.o !== null || u.l & CONFIG_OVERRIDE)) {
|
|
1045
1030
|
let r;
|
|
1046
1031
|
if (e.ovl) {
|
|
1047
|
-
r = e.del
|
|
1032
|
+
r = !!e.del?.size;
|
|
1048
1033
|
if (!r) {
|
|
1049
|
-
for (const e of Reflect.ownKeys(
|
|
1050
|
-
if (!hasOwn.call(
|
|
1034
|
+
for (const e of Reflect.ownKeys(t)) {
|
|
1035
|
+
if (!hasOwn.call(n, e)) {
|
|
1051
1036
|
r = true;
|
|
1052
1037
|
break;
|
|
1053
1038
|
}
|
|
1054
1039
|
}
|
|
1055
1040
|
}
|
|
1056
1041
|
} else {
|
|
1057
|
-
r = Array.isArray(
|
|
1042
|
+
r = Array.isArray(t) && Array.isArray(n) ? arrayStructureChanged(n, t) : membershipChanged(n, t);
|
|
1058
1043
|
}
|
|
1059
|
-
if (r) notifyContainer(u,
|
|
1044
|
+
if (r) notifyContainer(u, t);
|
|
1060
1045
|
}
|
|
1061
1046
|
}
|
|
1062
1047
|
|
|
1063
1048
|
/** Accessor-node wake sentinel: readers re-run and re-invoke the getter. */ const FORCE = Symbol();
|
|
1064
1049
|
|
|
1065
|
-
/** Two raws that wrap to the same target are the same logical value. */ function targetsEqual(e,
|
|
1066
|
-
if (e === null || typeof e !== "object" ||
|
|
1067
|
-
const
|
|
1068
|
-
return
|
|
1050
|
+
/** Two raws that wrap to the same target are the same logical value. */ function targetsEqual(e, t) {
|
|
1051
|
+
if (e === null || typeof e !== "object" || t === null || typeof t !== "object") return false;
|
|
1052
|
+
const n = lookupTarget(e, null);
|
|
1053
|
+
return n !== undefined && n === lookupTarget(t, null);
|
|
1069
1054
|
}
|
|
1070
1055
|
|
|
1071
|
-
/** Positional identity IS content for arrays (R15). */ function arrayStructureChanged(e,
|
|
1072
|
-
if (e.length !==
|
|
1073
|
-
for (let
|
|
1074
|
-
const r = e[
|
|
1075
|
-
const o = n
|
|
1056
|
+
/** Positional identity IS content for arrays (R15). */ function arrayStructureChanged(e, t) {
|
|
1057
|
+
if (e.length !== t.length) return true;
|
|
1058
|
+
for (let n = 0; n < t.length; n++) {
|
|
1059
|
+
const r = e[n];
|
|
1060
|
+
const o = t[n];
|
|
1076
1061
|
if (!isEqual(r, o) && !targetsEqual(r, o)) return true;
|
|
1077
1062
|
}
|
|
1078
1063
|
return false;
|
|
1079
1064
|
}
|
|
1080
1065
|
|
|
1081
|
-
function membershipChanged(e,
|
|
1082
|
-
const
|
|
1083
|
-
if (Reflect.ownKeys(e).length - +isOwned(e) !==
|
|
1084
|
-
for (const
|
|
1066
|
+
function membershipChanged(e, t) {
|
|
1067
|
+
const n = Reflect.ownKeys(t);
|
|
1068
|
+
if (Reflect.ownKeys(e).length - +isOwned(e) !== n.length - +isOwned(t)) return true;
|
|
1069
|
+
for (const t of n) if (t !== $OWNER && !(t in e)) return true;
|
|
1085
1070
|
return false;
|
|
1086
1071
|
}
|
|
1087
1072
|
|
|
1088
|
-
/** One leaf's notification for an adoption diff (descriptor-aware). */ function notifyKeyDiff(e,
|
|
1089
|
-
if (e.acc === true || o && hasOwn.call(r,
|
|
1090
|
-
e.acc = isOwnAccessor(r,
|
|
1091
|
-
const o = Object.getOwnPropertyDescriptor(
|
|
1092
|
-
const i = Object.getOwnPropertyDescriptor(r,
|
|
1073
|
+
/** One leaf's notification for an adoption diff (descriptor-aware). */ function notifyKeyDiff(e, t, n, r, o = true) {
|
|
1074
|
+
if (e.acc === true || o && hasOwn.call(r, t) && lookupGetter.call(r, t) !== undefined) {
|
|
1075
|
+
e.acc = isOwnAccessor(r, t);
|
|
1076
|
+
const o = Object.getOwnPropertyDescriptor(n, t);
|
|
1077
|
+
const i = Object.getOwnPropertyDescriptor(r, t);
|
|
1093
1078
|
// Data descriptors fall through, as in notifyWrites (#3949).
|
|
1094
1079
|
if (!i || i.get || i.set) {
|
|
1095
1080
|
if (o?.get !== i?.get || o?.set !== i?.set || o?.value !== i?.value) setSignal(e, () => FORCE);
|
|
@@ -1099,8 +1084,8 @@ function membershipChanged(e, n) {
|
|
|
1099
1084
|
const u = i?.value;
|
|
1100
1085
|
if (o?.get || o?.set || !isEqual(l, u) && !targetsEqual(l, u)) setSignal(e, typeof u === "function" ? () => u : u);
|
|
1101
1086
|
} else {
|
|
1102
|
-
const o = t
|
|
1103
|
-
const i = r[
|
|
1087
|
+
const o = n[t];
|
|
1088
|
+
const i = r[t];
|
|
1104
1089
|
if (!isEqual(o, i) && !targetsEqual(o, i)) setSignal(e, typeof i === "function" ? () => i : i);
|
|
1105
1090
|
}
|
|
1106
1091
|
}
|
|
@@ -1109,12 +1094,12 @@ function hasAccessorFlag(e) {
|
|
|
1109
1094
|
return e.acc === true;
|
|
1110
1095
|
}
|
|
1111
1096
|
|
|
1112
|
-
/** One leaf's notification when the caller already has both values. */ function notifyKeyValue(e,
|
|
1097
|
+
/** One leaf's notification when the caller already has both values. */ function notifyKeyValue(e, t, n, r, o, i) {
|
|
1113
1098
|
if (e.acc === true) {
|
|
1114
|
-
notifyKeyDiff(e,
|
|
1099
|
+
notifyKeyDiff(e, t, o, i, false);
|
|
1115
1100
|
return;
|
|
1116
1101
|
}
|
|
1117
|
-
if (!isEqual(
|
|
1102
|
+
if (!isEqual(n, r) && !targetsEqual(n, r)) setSignal(e, typeof r === "function" ? () => r : r);
|
|
1118
1103
|
}
|
|
1119
1104
|
|
|
1120
1105
|
/** The structural half of an adoption's notifications (reconcile does the
|
|
@@ -1136,28 +1121,28 @@ function hasAccessorFlag(e) {
|
|
|
1136
1121
|
* notification, and a landing whose truth changed no guessed key's value
|
|
1137
1122
|
* or presence (a newer question's rows beneath an optimistic push) still
|
|
1138
1123
|
* answers the arrangement — before, only the presence write reached the
|
|
1139
|
-
* lane, by accident of being unconditional. */ function notifyFoldTail(e,
|
|
1124
|
+
* lane, by accident of being unconditional. */ function notifyFoldTail(e, t, n) {
|
|
1140
1125
|
const r = e.h;
|
|
1141
1126
|
if (r !== null) {
|
|
1142
|
-
const e =
|
|
1143
|
-
for (const o of Reflect.ownKeys(r)) if (e || o in
|
|
1127
|
+
const e = t[$TARGET];
|
|
1128
|
+
for (const o of Reflect.ownKeys(r)) if (e || o in t !== o in n) setSignal(r[o], o in n);
|
|
1144
1129
|
}
|
|
1145
1130
|
const o = e.k;
|
|
1146
1131
|
if (o !== null && (o.o !== null || o.l & CONFIG_OVERRIDE)) {
|
|
1147
|
-
const e = Array.isArray(
|
|
1148
|
-
if (e) notifyContainer(o,
|
|
1132
|
+
const e = Array.isArray(n) && Array.isArray(t) ? arrayStructureChanged(t, n) : membershipChanged(t, n);
|
|
1133
|
+
if (e) notifyContainer(o, n);
|
|
1149
1134
|
}
|
|
1150
1135
|
}
|
|
1151
1136
|
|
|
1152
1137
|
/** An adoption's notifications: every node of the target, diffed `old` →
|
|
1153
1138
|
* `neu` (value nodes by key, presence by `in`, the container by
|
|
1154
|
-
* membership, the deep witness by identity). */ function notifyFold(e,
|
|
1155
|
-
if (e.dk !== null &&
|
|
1139
|
+
* membership, the deep witness by identity). */ function notifyFold(e, t, n) {
|
|
1140
|
+
if (e.dk !== null && t !== n) bumpDeep(e);
|
|
1156
1141
|
const r = e.n;
|
|
1157
1142
|
if (r !== null) {
|
|
1158
|
-
for (const e of Reflect.ownKeys(r)) notifyKeyDiff(r[e], e,
|
|
1143
|
+
for (const e of Reflect.ownKeys(r)) notifyKeyDiff(r[e], e, t, n);
|
|
1159
1144
|
}
|
|
1160
|
-
notifyFoldTail(e,
|
|
1145
|
+
notifyFoldTail(e, t, n);
|
|
1161
1146
|
}
|
|
1162
1147
|
|
|
1163
1148
|
// ---------------------------------------------------------------------------
|
|
@@ -1172,9 +1157,9 @@ const pendingNotify = new Set;
|
|
|
1172
1157
|
|
|
1173
1158
|
function scopeKey(e) {
|
|
1174
1159
|
if (e.fam !== null) return e.fam;
|
|
1175
|
-
let
|
|
1176
|
-
while (
|
|
1177
|
-
return
|
|
1160
|
+
let t = e;
|
|
1161
|
+
while (t.u !== null) t = t.u;
|
|
1162
|
+
return t;
|
|
1178
1163
|
}
|
|
1179
1164
|
|
|
1180
1165
|
function inDraft(e) {
|
|
@@ -1182,25 +1167,25 @@ function inDraft(e) {
|
|
|
1182
1167
|
}
|
|
1183
1168
|
|
|
1184
1169
|
/** Shallow store: values served verbatim (a nested store proxy still wraps
|
|
1185
|
-
* into the family so writes stay isolated). */ function serveShallow(e,
|
|
1186
|
-
if (
|
|
1187
|
-
return
|
|
1170
|
+
* into the family so writes stay isolated). */ function serveShallow(e, t, n) {
|
|
1171
|
+
if (n !== null && typeof n === "object" && n[$TARGET] !== undefined) return draftServe(e, wrap(n, e, t));
|
|
1172
|
+
return n;
|
|
1188
1173
|
}
|
|
1189
1174
|
|
|
1190
|
-
/** A child proxy served to a draft joins the draft's scope. */ function draftServe(e,
|
|
1175
|
+
/** A child proxy served to a draft joins the draft's scope. */ function draftServe(e, t) {
|
|
1191
1176
|
if (writeScopes !== null && inDraft(e)) {
|
|
1192
|
-
const e =
|
|
1193
|
-
if (e
|
|
1177
|
+
const e = t?.[$TARGET];
|
|
1178
|
+
if (e?.v !== undefined) writeScopes.add(scopeKey(e));
|
|
1194
1179
|
}
|
|
1195
|
-
return
|
|
1180
|
+
return t;
|
|
1196
1181
|
}
|
|
1197
1182
|
|
|
1198
1183
|
/** The committed backing: the container node's committed value while a
|
|
1199
1184
|
* batch is staged on it (an adoption swaps `t.v` eagerly — INTERNALS §3,
|
|
1200
1185
|
* "adoption is eager by contract" — and the node keeps the frame committed
|
|
1201
1186
|
* readers under a hold still see), `t.v` otherwise. */ function committed(e) {
|
|
1202
|
-
const
|
|
1203
|
-
if (
|
|
1187
|
+
const t = e.k;
|
|
1188
|
+
if (t !== null) return t.ue !== NOT_PENDING ? asBacking(t.Ie) : e.v;
|
|
1204
1189
|
// A node-less staging (`stageOn`): the fold queue remembers the frame.
|
|
1205
1190
|
return e.pb !== null ? preBatch(e) : e.v;
|
|
1206
1191
|
}
|
|
@@ -1228,15 +1213,15 @@ function inDraft(e) {
|
|
|
1228
1213
|
* or the staging a pass may see). An optimistic family's lane values are
|
|
1229
1214
|
* NOT composed here: a guessed key always has its node, and the node is
|
|
1230
1215
|
* what serves its value (`nodeValue`); presence and enumeration compose
|
|
1231
|
-
* per key / per walk (`optimisticHas`, `enumerationSource`). */ function readSource(e,
|
|
1216
|
+
* per key / per walk (`optimisticHas`, `enumerationSource`). */ function readSource(e, t, n = false) {
|
|
1232
1217
|
// An optimistic setter's read before its first write sees what the writer
|
|
1233
1218
|
// sees — the lanes' values and the tick's own guesses over the truth
|
|
1234
1219
|
// (#3665), never a staging awaiting its fold: the draft is born on the
|
|
1235
1220
|
// read (and notified at the setter's exit like any).
|
|
1236
|
-
if (userWrite && e.fam
|
|
1237
|
-
const
|
|
1221
|
+
if (userWrite && e.fam?.opt === true && !pendingNotify.has(e) && inDraft(e)) {
|
|
1222
|
+
const t = ensurePB(e);
|
|
1238
1223
|
pendingNotify.add(e);
|
|
1239
|
-
return
|
|
1224
|
+
return t;
|
|
1240
1225
|
}
|
|
1241
1226
|
const r = e.pb;
|
|
1242
1227
|
if (r === null) return e.v;
|
|
@@ -1249,12 +1234,12 @@ function inDraft(e) {
|
|
|
1249
1234
|
// "under the transaction and revealed with it"), not key by key. A
|
|
1250
1235
|
// user's setter reading its draft is no pass and joins nothing here.
|
|
1251
1236
|
if (writeOverride && !userWrite) {
|
|
1252
|
-
const
|
|
1253
|
-
if (
|
|
1254
|
-
if ((
|
|
1237
|
+
const n = t !== undefined ? e.n?.[t] : undefined;
|
|
1238
|
+
if (n !== undefined) {
|
|
1239
|
+
if ((n.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD) joinPassTx(txOf(n));
|
|
1255
1240
|
} else {
|
|
1256
|
-
const
|
|
1257
|
-
if (
|
|
1241
|
+
const n = e.k;
|
|
1242
|
+
if (n !== null && (n.l & (CONFIG_HELD | CONFIG_OVERRIDE)) === CONFIG_HELD && (t === undefined || heldKeyChanged(e, t))) joinPassTx(txOf(n));
|
|
1258
1243
|
}
|
|
1259
1244
|
}
|
|
1260
1245
|
return r;
|
|
@@ -1264,16 +1249,16 @@ function inDraft(e) {
|
|
|
1264
1249
|
// it — the container's frame is not consulted (a reader is not made a
|
|
1265
1250
|
// stale reader of the container's hold for a key it does not see through
|
|
1266
1251
|
// it).
|
|
1267
|
-
if (
|
|
1268
|
-
const
|
|
1269
|
-
if (
|
|
1252
|
+
if (t !== undefined) {
|
|
1253
|
+
const n = e.n?.[t];
|
|
1254
|
+
if (n?.l & CONFIG_OVERRIDE) return committed(e);
|
|
1270
1255
|
}
|
|
1271
1256
|
const i = o !== null && (o.l & CONFIG_HELD) !== 0;
|
|
1272
1257
|
// A held container: the hold's unit is the key (#3706). A key the HELD
|
|
1273
1258
|
// staging left unchanged reads the ambient view — a mainline layer's
|
|
1274
1259
|
// write above the hold publishes mainline (#3688, #3612); a key it
|
|
1275
1260
|
// changed reads the frame core serves this reader.
|
|
1276
|
-
if (i &&
|
|
1261
|
+
if (i && t !== undefined && !heldKeyChanged(e, t, n)) return r;
|
|
1277
1262
|
const l = stagingReader();
|
|
1278
1263
|
if (l === null) {
|
|
1279
1264
|
if (getObserver() !== null) return r;
|
|
@@ -1286,7 +1271,7 @@ function inDraft(e) {
|
|
|
1286
1271
|
// probe) judges the container like any node — for a key the batch
|
|
1287
1272
|
// changed; an unchanged key is committed and final (A22: pending is per
|
|
1288
1273
|
// key). A held container's committed frame is the node's.
|
|
1289
|
-
if (verdict === null || !i &&
|
|
1274
|
+
if (verdict === null || !i && t !== undefined && !keyChanged(e, t, n)) return i ? asBacking(o.Ie) : e.v;
|
|
1290
1275
|
return o === null ? committed(e) : asBacking(read(o));
|
|
1291
1276
|
}
|
|
1292
1277
|
// A pass reading a key the batch left unchanged: the same value in both
|
|
@@ -1304,7 +1289,7 @@ function inDraft(e) {
|
|
|
1304
1289
|
// per-key question is asked once per pass, not once per key (a
|
|
1305
1290
|
// `mapArray` over 1k shifted rows asked it 1k times).
|
|
1306
1291
|
if (l.R & REACTIVE_STAGED_READ && !(l.l & (CONFIG_VERDICT | CONFIG_CHILDREN_FORBIDDEN))) return r;
|
|
1307
|
-
if (
|
|
1292
|
+
if (t !== undefined && !keyChanged(e, t, n)) return committed(e);
|
|
1308
1293
|
// A node-less staging (`stageOn`): what `read()` of its node would do
|
|
1309
1294
|
// for this pass — a verdict reader sees the screen, any other derives
|
|
1310
1295
|
// from the staging and is marked as having read it.
|
|
@@ -1318,8 +1303,8 @@ function inDraft(e) {
|
|
|
1318
1303
|
}
|
|
1319
1304
|
|
|
1320
1305
|
/** Did the HELD staging (the container node's, not a mainline layer above
|
|
1321
|
-
* it) change `key`? */ function heldKeyChanged(e,
|
|
1322
|
-
return changedBetween(e, e.k.Ie, e.k.ue,
|
|
1306
|
+
* it) change `key`? */ function heldKeyChanged(e, t, n = true) {
|
|
1307
|
+
return changedBetween(e, e.k.Ie, e.k.ue, t, n) && !adoptChild(e, t);
|
|
1323
1308
|
}
|
|
1324
1309
|
|
|
1325
1310
|
/** The values of a held frame, built once per frame: neither the committed
|
|
@@ -1327,14 +1312,14 @@ function inDraft(e) {
|
|
|
1327
1312
|
* clone), so the set stays true for every first read of the hold. */ const frameRaws = new WeakMap;
|
|
1328
1313
|
|
|
1329
1314
|
function rawsOf(e) {
|
|
1330
|
-
let
|
|
1331
|
-
if (
|
|
1332
|
-
return
|
|
1315
|
+
let t = frameRaws.get(e);
|
|
1316
|
+
if (t === undefined) frameRaws.set(e, t = new Set(Object.values(e)));
|
|
1317
|
+
return t;
|
|
1333
1318
|
}
|
|
1334
1319
|
|
|
1335
1320
|
/** The same row at a slot, as the adoption pairs rows (reconcile.ts): two
|
|
1336
|
-
* objects whose keys agree — positionally for an unkeyed family. */ function sameRow(e,
|
|
1337
|
-
return e !== null && typeof e === "object" &&
|
|
1321
|
+
* objects whose keys agree — positionally for an unkeyed family. */ function sameRow(e, t, n) {
|
|
1322
|
+
return e !== null && typeof e === "object" && t !== null && typeof t === "object" && (n === null || sameKey(n(e), n(t)));
|
|
1338
1323
|
}
|
|
1339
1324
|
|
|
1340
1325
|
/** A key of a held adoption whose old and new values are the same row that
|
|
@@ -1344,27 +1329,27 @@ function rawsOf(e) {
|
|
|
1344
1329
|
* transaction. Both raws resolve to it, so the slot reads as unchanged and
|
|
1345
1330
|
* the row's own leaves decide: `id` the same in both frames holds no one,
|
|
1346
1331
|
* `version` holds. A record's keys pair by name; an array's by the family's
|
|
1347
|
-
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e,
|
|
1348
|
-
const
|
|
1349
|
-
const r = asBacking(
|
|
1350
|
-
const o = r[
|
|
1351
|
-
const i =
|
|
1332
|
+
* key — without a family the pairing is not known here and the hold stands. */ function adoptChild(e, t) {
|
|
1333
|
+
const n = e.k;
|
|
1334
|
+
const r = asBacking(n.Ie);
|
|
1335
|
+
const o = r[t];
|
|
1336
|
+
const i = n.ue[t];
|
|
1352
1337
|
const l = Array.isArray(r) ? e.fam?.key : null;
|
|
1353
1338
|
if (e.s || l === undefined || !sameRow(o, i, l) || !isWrappable(o) || !isWrappable(i) || Array.isArray(o) !== Array.isArray(i) || lookupTarget(o, e.fam) !== undefined || lookupTarget(i, e.fam) !== undefined ||
|
|
1354
1339
|
// A raw in both frames (the same objects swapped or reordered) is its
|
|
1355
1340
|
// own row: pairing it with another would alias the two after the commit.
|
|
1356
|
-
rawsOf(r).has(i) || rawsOf(
|
|
1341
|
+
rawsOf(r).has(i) || rawsOf(n.ue).has(o)) return false;
|
|
1357
1342
|
// A raw-marked value wraps to itself, a proxy to its own target: neither
|
|
1358
1343
|
// is a fresh target around `ov`.
|
|
1359
|
-
const u = wrap(o, e,
|
|
1360
|
-
if (u
|
|
1344
|
+
const u = wrap(o, e, t)[$TARGET];
|
|
1345
|
+
if (u?.v !== o) return false;
|
|
1361
1346
|
adoptPB(u, i, false);
|
|
1362
|
-
holdNode(getContainerNode(u), txOf(
|
|
1347
|
+
holdNode(getContainerNode(u), txOf(n));
|
|
1363
1348
|
return true;
|
|
1364
1349
|
}
|
|
1365
1350
|
|
|
1366
1351
|
/** A family target's unheld staging, read with no pass (a handler). */ function familyAhead(e) {
|
|
1367
|
-
return e.fam !== null && context === null && !(e.k
|
|
1352
|
+
return e.fam !== null && context === null && !(e.k?.l & CONFIG_HELD);
|
|
1368
1353
|
}
|
|
1369
1354
|
|
|
1370
1355
|
/** Did this batch change `key` — value (slot equality), presence, and (for
|
|
@@ -1378,69 +1363,69 @@ function rawsOf(e) {
|
|
|
1378
1363
|
* twice each) are the cost of the question on a wide container read under
|
|
1379
1364
|
* a staging (a keyed reconcile's 1k rows, read by `mapArray` in the flush
|
|
1380
1365
|
* that staged them: ~3× the tick). A container with accessors seen (`a`)
|
|
1381
|
-
* keeps the full test — a getter's answer is not a slot's. */ function keyChanged(e,
|
|
1382
|
-
return changedBetween(e, committed(e), e.pb,
|
|
1366
|
+
* keeps the full test — a getter's answer is not a slot's. */ function keyChanged(e, t, n = true) {
|
|
1367
|
+
return changedBetween(e, committed(e), e.pb, t, n);
|
|
1383
1368
|
}
|
|
1384
1369
|
|
|
1385
|
-
function changedBetween(e,
|
|
1386
|
-
if (e.ch ||
|
|
1387
|
-
if (e.del
|
|
1388
|
-
const i = hasOwn.call(
|
|
1389
|
-
if (i !== hasOwn.call(
|
|
1370
|
+
function changedBetween(e, t, n, r, o) {
|
|
1371
|
+
if (e.ch || n[$TARGET] !== undefined || Object.getPrototypeOf(n) !== Object.getPrototypeOf(t) || !plainProto(t)) return true;
|
|
1372
|
+
if (e.del?.has(r)) return r in t;
|
|
1373
|
+
const i = hasOwn.call(n, r) || e.ovl && hasOwn.call(e.v, r);
|
|
1374
|
+
if (i !== hasOwn.call(t, r)) return true;
|
|
1390
1375
|
if (!i) return false;
|
|
1391
|
-
if ((o || e.a) && (isOwnAccessor(
|
|
1392
|
-
const l =
|
|
1393
|
-
const u =
|
|
1376
|
+
if ((o || e.a) && (isOwnAccessor(t, r) || isOwnAccessor(n, r) || propertyIsEnumerable.call(t, r) !== propertyIsEnumerable.call(n, r))) return true;
|
|
1377
|
+
const l = n[r];
|
|
1378
|
+
const u = t[r];
|
|
1394
1379
|
return !isEqual(u, l) && !sameLogicalSlot(e, u, l);
|
|
1395
1380
|
}
|
|
1396
1381
|
|
|
1397
1382
|
/** Value of a key with a leaf: core's — `read()` links a tracked reader and,
|
|
1398
1383
|
* tracked or not, applies the hold and A28 rules a signal's read does. A
|
|
1399
|
-
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e,
|
|
1384
|
+
* projection's unheld staging is ahead for a handler (`familyAhead`). */ function nodeValue(e, t, n) {
|
|
1400
1385
|
let r = read(e);
|
|
1401
|
-
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && context === null &&
|
|
1402
|
-
return r === FORCE ?
|
|
1386
|
+
if (e.ue !== NOT_PENDING && !(e.l & CONFIG_HELD) && context === null && n.fam !== null) r = e.ue;
|
|
1387
|
+
return r === FORCE ? t : r;
|
|
1403
1388
|
}
|
|
1404
1389
|
|
|
1405
1390
|
/** Serve a present data key: link the leaf for a tracked reader (creating it
|
|
1406
|
-
* on first read), wrap a wrappable child through the per-node wrap cache. */ function serveDataKey(e,
|
|
1391
|
+
* on first read), wrap a wrappable child through the per-node wrap cache. */ function serveDataKey(e, t, n, r, o, i = -1) {
|
|
1407
1392
|
// Read-through (§7b): the inner store's node is the truth; the outer's is
|
|
1408
1393
|
// a subscription point only — unless it carries a guess (an optimistic
|
|
1409
1394
|
// store over a store: the guess is the outer's, shown over the inner).
|
|
1410
1395
|
const l = e.ch && r === e.v;
|
|
1411
|
-
let u =
|
|
1396
|
+
let u = n;
|
|
1412
1397
|
// A write-override read (a derive's draft) serves the backing: the derive
|
|
1413
1398
|
// composes on the truth, never on a lane's or a hold's frame.
|
|
1414
1399
|
if (!inDraft(e) && !writeOverride) {
|
|
1415
|
-
if (o === undefined && getObserver() !== null) o = getNode(e,
|
|
1400
|
+
if (o === undefined && getObserver() !== null) o = getNode(e, t, i);
|
|
1416
1401
|
if (o !== undefined) {
|
|
1417
|
-
const
|
|
1402
|
+
const t = nodeValue(o, n, e);
|
|
1418
1403
|
if (l) {
|
|
1419
1404
|
// The link's committed value follows the inner store's (what a lane
|
|
1420
1405
|
// over it covers — its reveal and revert compare against it, S4).
|
|
1421
|
-
o.Ie =
|
|
1422
|
-
if (o.l & CONFIG_OVERRIDE) u =
|
|
1423
|
-
} else u =
|
|
1406
|
+
o.Ie = n;
|
|
1407
|
+
if (o.l & CONFIG_OVERRIDE) u = t;
|
|
1408
|
+
} else u = t;
|
|
1424
1409
|
}
|
|
1425
1410
|
}
|
|
1426
|
-
if (e.s) return serveShallow(e,
|
|
1411
|
+
if (e.s) return serveShallow(e, t, u);
|
|
1427
1412
|
// A derive's draft wrapper answers `$TARGET` without being a proxy: the
|
|
1428
1413
|
// inner store's alias (#3767), resolved like a raw (#3859).
|
|
1429
|
-
if (e.ch && !l && u !== null && typeof u === "object" && u[$TARGET]?.px !== u) u = resolveChainedRaw(e,
|
|
1414
|
+
if (e.ch && !l && u !== null && typeof u === "object" && u[$TARGET]?.px !== u) u = resolveChainedRaw(e, t, u);
|
|
1430
1415
|
if (o !== undefined) {
|
|
1431
1416
|
if (o.pxv === u && u !== undefined) return draftServe(e, o.px);
|
|
1432
1417
|
if (!isWrappable(u)) return u;
|
|
1433
|
-
const
|
|
1434
|
-
o.px =
|
|
1418
|
+
const n = wrap(u, e, t);
|
|
1419
|
+
o.px = n;
|
|
1435
1420
|
o.pxv = u;
|
|
1436
|
-
return draftServe(e,
|
|
1421
|
+
return draftServe(e, n);
|
|
1437
1422
|
}
|
|
1438
1423
|
if (!isWrappable(u)) return u;
|
|
1439
|
-
return draftServe(e, wrap(u, e,
|
|
1424
|
+
return draftServe(e, wrap(u, e, t));
|
|
1440
1425
|
}
|
|
1441
1426
|
|
|
1442
|
-
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e,
|
|
1443
|
-
for (let
|
|
1427
|
+
/** Is `n` among `c`'s dependencies (any pass's — a kept tail included)? */ function linkedTo(e, t) {
|
|
1428
|
+
for (let n = e.C; n !== null; n = n.G) if (n.S === t) return true;
|
|
1444
1429
|
return false;
|
|
1445
1430
|
}
|
|
1446
1431
|
|
|
@@ -1452,14 +1437,14 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1452
1437
|
* window judges it; a pending source registers the reader for the landing)
|
|
1453
1438
|
* and the reader's height. The derive's own draft ops are exempt (the write
|
|
1454
1439
|
* override): the derive is the author. */ function pullFamily(e) {
|
|
1455
|
-
const
|
|
1456
|
-
if (
|
|
1440
|
+
const t = e.fam.node;
|
|
1441
|
+
if (t === null || writeOverride || inDraft(e)) return;
|
|
1457
1442
|
// A settled derive: the pull alone, no link (the barrier). A derive with a
|
|
1458
1443
|
// flight up (or errored): the reader observes the flight exactly as a
|
|
1459
1444
|
// reader of a memo would — linked, so it re-runs at the landing (the
|
|
1460
1445
|
// next settled pass pulls without linking and the stale link trims), a
|
|
1461
1446
|
// verdict reader registered, a tracked pass suspended.
|
|
1462
|
-
if (
|
|
1447
|
+
if (t.m & (STATUS_PENDING | STATUS_ERROR)) {
|
|
1463
1448
|
// A render effect outside the flight's own flush is the frame, not a
|
|
1464
1449
|
// derivation: it keeps what it shows and learns of the landing from the
|
|
1465
1450
|
// leaves the landing changes (unchanged leaves say nothing — it is not a
|
|
@@ -1469,8 +1454,8 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1469
1454
|
// another reason (a guess it read, the seam) that dropped the link would
|
|
1470
1455
|
// release the hold with the flight still up. An errored derive has no
|
|
1471
1456
|
// landing to learn of: the error is what the frame shows.
|
|
1472
|
-
const
|
|
1473
|
-
if (
|
|
1457
|
+
const n = context;
|
|
1458
|
+
if (n !== null && n.i === EFFECT_RENDER && !(t.m & STATUS_ERROR) && t.l & CONFIG_HELD && flushTransaction !== txOf(t) && !linkedTo(n, t)) return;
|
|
1474
1459
|
// An optimistic family carrying guesses that stand in for the flight
|
|
1475
1460
|
// (A17): the guesses are the display — a reader is served the leaves
|
|
1476
1461
|
// (committed plus the guesses) and does not suspend on the derive; a
|
|
@@ -1478,9 +1463,9 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1478
1463
|
// up). The family's, not the target's: the edit stands in for the one
|
|
1479
1464
|
// flight the family has.
|
|
1480
1465
|
const r = e.fam.overlaid;
|
|
1481
|
-
if (verdict === null &&
|
|
1482
|
-
read(
|
|
1483
|
-
} else untrack(() => read(
|
|
1466
|
+
if (verdict === null && t.m & STATUS_PENDING && r !== undefined && r.size !== 0) return;
|
|
1467
|
+
read(t);
|
|
1468
|
+
} else untrack(() => read(t));
|
|
1484
1469
|
}
|
|
1485
1470
|
|
|
1486
1471
|
/** A projection's writes are its derive's: a staging made while the derive
|
|
@@ -1488,31 +1473,31 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1488
1473
|
* callback's late write) belongs to the derive's transaction and reveals
|
|
1489
1474
|
* with it, never drained early — and answers the derive's question: a quiet
|
|
1490
1475
|
* re-ask's landing is quiet through its reveal (A19 exc. 2; the leaf's own
|
|
1491
|
-
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e,
|
|
1492
|
-
const
|
|
1493
|
-
if (
|
|
1494
|
-
|
|
1476
|
+
* classification, as core reads it — `heldNotFinal`). */ function holdWithDerive(e, t) {
|
|
1477
|
+
const n = e.fam?.node;
|
|
1478
|
+
if (n !== undefined && n !== null && !userWrite && // the derive's own write (its draft or landing), not a user setter's
|
|
1479
|
+
n.l & CONFIG_HELD && t.ue !== NOT_PENDING &&
|
|
1495
1480
|
// A node carrying a guess is the lane's: its staging is the truth beneath
|
|
1496
1481
|
// the guess (`setSignal` → `laneWrite`), never re-homed by the store.
|
|
1497
|
-
!(
|
|
1498
|
-
holdNode(
|
|
1499
|
-
|
|
1482
|
+
!(t.l & (CONFIG_HELD | CONFIG_OVERRIDE))) {
|
|
1483
|
+
holdNode(t, txOf(n));
|
|
1484
|
+
t.T.ut = n.T.ut;
|
|
1500
1485
|
}
|
|
1501
1486
|
}
|
|
1502
1487
|
|
|
1503
1488
|
/** A chained backing's child (§7b): a raw the inner family owns, or holds
|
|
1504
1489
|
* at this key, is served as the inner store's wrapper — never a fresh
|
|
1505
1490
|
* raw-keyed one — so the chained targets are the same objects across
|
|
1506
|
-
* settled and pending views. */ function resolveChainedRaw(e,
|
|
1491
|
+
* settled and pending views. */ function resolveChainedRaw(e, t, n) {
|
|
1507
1492
|
const r = e.v[$TARGET];
|
|
1508
1493
|
if (r.ch) {
|
|
1509
|
-
const e = resolveChainedRaw(r,
|
|
1510
|
-
return e ===
|
|
1494
|
+
const e = resolveChainedRaw(r, t, n);
|
|
1495
|
+
return e === n ? n : wrap(e, r, t);
|
|
1511
1496
|
}
|
|
1512
|
-
const o = lookupTarget(
|
|
1497
|
+
const o = lookupTarget(n, r.fam);
|
|
1513
1498
|
if (o !== undefined) return o.px;
|
|
1514
|
-
if ((r.v[
|
|
1515
|
-
return
|
|
1499
|
+
if ((r.v[t] === n || r.pb?.[t] === n) && isWrappable(n)) return wrap(n, r, t);
|
|
1500
|
+
return n;
|
|
1516
1501
|
}
|
|
1517
1502
|
|
|
1518
1503
|
/** The backing a structural read serves: a tracked reader subscribes to the
|
|
@@ -1522,17 +1507,17 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1522
1507
|
if (!inDraft(e) && !writeOverride && getObserver() !== null) {
|
|
1523
1508
|
// A container's arrangement guess is not a backing: the view composes
|
|
1524
1509
|
// over the base it is shown over.
|
|
1525
|
-
const
|
|
1526
|
-
return e.fam?.opt === true ? optHooks.view(e,
|
|
1510
|
+
const t = asBacking(read(getContainerNode(e)));
|
|
1511
|
+
return e.fam?.opt === true ? optHooks.view(e, t) : t;
|
|
1527
1512
|
}
|
|
1528
|
-
const
|
|
1529
|
-
return optRead(e) ? optHooks.view(e,
|
|
1513
|
+
const t = readSource(e);
|
|
1514
|
+
return optRead(e) ? optHooks.view(e, t) : t;
|
|
1530
1515
|
}
|
|
1531
1516
|
|
|
1532
1517
|
/** A read on an optimistic family that composes the lanes' values: not the
|
|
1533
1518
|
* draft's (it composed already), not a derive's write-override read (the
|
|
1534
1519
|
* derive composes on the truth). */ function optRead(e) {
|
|
1535
|
-
return e.fam
|
|
1520
|
+
return e.fam?.opt === true && !inDraft(e) && !writeOverride;
|
|
1536
1521
|
}
|
|
1537
1522
|
|
|
1538
1523
|
/** Pollution keys are never served from the prototype (core R30). */ const UNSAFE_KEYS = new Set([ "__proto__", "prototype", "constructor" ]);
|
|
@@ -1542,40 +1527,40 @@ function changedBetween(e, n, t, r, o) {
|
|
|
1542
1527
|
/** A trap write: the target is told at the setter's exit, the key recorded
|
|
1543
1528
|
* in this setter's `wk`. A setter's first write to a target still staged by
|
|
1544
1529
|
* an earlier setter of the batch opens a set of its own (the notify visits
|
|
1545
|
-
* it, D-8) and chains the earlier one behind for the fold's union. */ function touch(e,
|
|
1546
|
-
let
|
|
1530
|
+
* it, D-8) and chains the earlier one behind for the fold's union. */ function touch(e, t) {
|
|
1531
|
+
let n = e.wk;
|
|
1547
1532
|
if (!pendingNotify.has(e)) {
|
|
1548
1533
|
pendingNotify.add(e);
|
|
1549
|
-
if (
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
e.wk =
|
|
1534
|
+
if (n !== null && n !== WK_ALL) {
|
|
1535
|
+
n = new Set;
|
|
1536
|
+
n.u = e.wk;
|
|
1537
|
+
e.wk = n;
|
|
1553
1538
|
}
|
|
1554
1539
|
}
|
|
1555
|
-
if (
|
|
1540
|
+
if (n !== WK_ALL) (e.wk ??= new Set).add(t);
|
|
1556
1541
|
}
|
|
1557
1542
|
|
|
1558
1543
|
const traps = {
|
|
1559
|
-
get(e,
|
|
1544
|
+
get(e, t, n) {
|
|
1560
1545
|
// One typeof gates every brand-symbol compare off the hot string path.
|
|
1561
|
-
if (typeof
|
|
1562
|
-
if (
|
|
1563
|
-
if (
|
|
1564
|
-
if (
|
|
1546
|
+
if (typeof t !== "string") {
|
|
1547
|
+
if (t === $TARGET) return e;
|
|
1548
|
+
if (t === $PROXY) return n;
|
|
1549
|
+
if (t === $OWNER) return undefined;
|
|
1565
1550
|
// ownership stamp: never a user key
|
|
1566
|
-
if (
|
|
1551
|
+
if (t === $RECORD) return undefined;
|
|
1567
1552
|
// a store is no view (see `viewOf`)
|
|
1568
1553
|
// refresh()/isPending resolve the projection computed through $REFRESH.
|
|
1569
|
-
if (
|
|
1570
|
-
if (
|
|
1554
|
+
if (t === $REFRESH) return e.fam?.node ?? undefined;
|
|
1555
|
+
if (t === $TRACK) {
|
|
1571
1556
|
if (e.fam !== null) pullFamily(e);
|
|
1572
1557
|
if (!inDraft(e) && getObserver() !== null) {
|
|
1573
1558
|
read(getContainerNode(e));
|
|
1574
1559
|
// Structural chaining (§7b): a chained backing's $TRACK reads
|
|
1575
1560
|
// through to the INNER store's container — structural
|
|
1576
1561
|
// notifications land on the source's own node.
|
|
1577
|
-
const
|
|
1578
|
-
if (
|
|
1562
|
+
const t = readSource(e);
|
|
1563
|
+
if (t[$TARGET] !== undefined) t[$TRACK];
|
|
1579
1564
|
}
|
|
1580
1565
|
return undefined;
|
|
1581
1566
|
}
|
|
@@ -1583,7 +1568,7 @@ const traps = {
|
|
|
1583
1568
|
}
|
|
1584
1569
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1585
1570
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1586
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e,
|
|
1571
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, t);
|
|
1587
1572
|
if (e.fam !== null) pullFamily(e);
|
|
1588
1573
|
// Hot inline case: existing PLAIN node (non-accessor) read outside any
|
|
1589
1574
|
// draft — the dbmon/uibench effect re-read shape. Core's `read()` serves
|
|
@@ -1595,28 +1580,28 @@ const traps = {
|
|
|
1595
1580
|
// a chained target's node carrying a GUESS is the value (A17 — the base
|
|
1596
1581
|
// beneath it, and any hold there, is not this reader's concern; a link
|
|
1597
1582
|
// without one reads through below).
|
|
1598
|
-
const r = e.n?.[
|
|
1583
|
+
const r = e.n?.[t];
|
|
1599
1584
|
if (writeScopes === null && !writeOverride) {
|
|
1600
|
-
const
|
|
1601
|
-
if (
|
|
1602
|
-
const r = nodeValue(
|
|
1585
|
+
const n = r;
|
|
1586
|
+
if (n !== undefined && n.acc !== true && (!e.ch || n.l & CONFIG_OVERRIDE)) {
|
|
1587
|
+
const r = nodeValue(n, undefined, e);
|
|
1603
1588
|
if (r === null || typeof r !== "object") return r;
|
|
1604
|
-
if (e.s) return serveShallow(e,
|
|
1605
|
-
if (
|
|
1589
|
+
if (e.s) return serveShallow(e, t, r);
|
|
1590
|
+
if (n.pxv === r) return n.px;
|
|
1606
1591
|
if (isWrappable(r)) {
|
|
1607
|
-
const o = wrap(r, e,
|
|
1608
|
-
|
|
1609
|
-
|
|
1592
|
+
const o = wrap(r, e, t);
|
|
1593
|
+
n.px = o;
|
|
1594
|
+
n.pxv = r;
|
|
1610
1595
|
return o;
|
|
1611
1596
|
}
|
|
1612
1597
|
return r;
|
|
1613
1598
|
}
|
|
1614
1599
|
}
|
|
1615
|
-
const o = readSource(e,
|
|
1600
|
+
const o = readSource(e, t);
|
|
1616
1601
|
// Overlay delete (#3044): a prototype overlay cannot shadow a delete, so
|
|
1617
1602
|
// deleted keys are tracked aside and read as absent in the pending view.
|
|
1618
|
-
if (
|
|
1619
|
-
if (!inDraft(e) && getObserver() !== null) read(getNode(e,
|
|
1603
|
+
if (o === e.pb && e.del?.has(t)) {
|
|
1604
|
+
if (!inDraft(e) && getObserver() !== null) read(getNode(e, t));
|
|
1620
1605
|
return undefined;
|
|
1621
1606
|
}
|
|
1622
1607
|
// Accessor keys serve through Reflect.get with the PROXY receiver
|
|
@@ -1630,14 +1615,14 @@ const traps = {
|
|
|
1630
1615
|
{
|
|
1631
1616
|
let l;
|
|
1632
1617
|
if (r !== undefined) l = r.acc === true; else if (!inDraft(e) && getObserver() !== null) {
|
|
1633
|
-
l = isOwnAccessor(o,
|
|
1618
|
+
l = isOwnAccessor(o, t);
|
|
1634
1619
|
if (o === (e.pb ?? e.v)) i = l ? 1 : 0;
|
|
1635
1620
|
} else l = false;
|
|
1636
1621
|
if (l) {
|
|
1637
|
-
if (!inDraft(e) && getObserver() !== null) read(r ?? getNode(e,
|
|
1638
|
-
const l = Reflect.get(o,
|
|
1639
|
-
if (e.s) return serveShallow(e,
|
|
1640
|
-
return isWrappable(l) ? draftServe(e, wrap(l, e,
|
|
1622
|
+
if (!inDraft(e) && getObserver() !== null) read(r ?? getNode(e, t, i));
|
|
1623
|
+
const l = Reflect.get(o, t, n);
|
|
1624
|
+
if (e.s) return serveShallow(e, t, l);
|
|
1625
|
+
return isWrappable(l) ? draftServe(e, wrap(l, e, t)) : l;
|
|
1641
1626
|
}
|
|
1642
1627
|
}
|
|
1643
1628
|
// Plain-data fast path: no descriptor allocation per read. Inherited
|
|
@@ -1647,78 +1632,78 @@ const traps = {
|
|
|
1647
1632
|
// either layer — a genuine prototype method is one that is own on
|
|
1648
1633
|
// NEITHER.
|
|
1649
1634
|
const l = e.ovl && o === e.pb;
|
|
1650
|
-
if ((
|
|
1651
|
-
let u = o[
|
|
1652
|
-
if (u === undefined ? !hasOwn.call(o,
|
|
1635
|
+
if ((t === "constructor" || t === "__proto__" || t === "prototype") && !hasOwn.call(o, t) && !(l && hasOwn.call(e.v, t))) return undefined;
|
|
1636
|
+
let u = o[t];
|
|
1637
|
+
if (u === undefined ? !hasOwn.call(o, t) && !(l && hasOwn.call(e.v, t)) : false) {
|
|
1653
1638
|
// Inherited: prototype getters/methods run with the proxy receiver.
|
|
1654
|
-
u = Reflect.get(o,
|
|
1639
|
+
u = Reflect.get(o, t, n);
|
|
1655
1640
|
if (typeof u === "function") return u;
|
|
1656
1641
|
// proto methods untracked
|
|
1657
1642
|
// Read-through (§7b): the inner store answers an absent key too — the
|
|
1658
1643
|
// outer's node is a subscription point unless it carries a guess.
|
|
1659
|
-
if (e.ch && o === e.v) return serveDataKey(e,
|
|
1644
|
+
if (e.ch && o === e.v) return serveDataKey(e, t, u, o, r, i);
|
|
1660
1645
|
// Reading a currently-absent own key subscribes to it (R12) — for any
|
|
1661
1646
|
// target OUTSIDE its own draft scope, even mid-setter (#3037, above).
|
|
1662
1647
|
if (u === undefined && !inDraft(e)) {
|
|
1663
|
-
if (getObserver() !== null) read(getNode(e,
|
|
1664
|
-
const
|
|
1665
|
-
if (
|
|
1666
|
-
const r = nodeValue(
|
|
1667
|
-
if (e.s) return serveShallow(e,
|
|
1668
|
-
return isWrappable(r) ? draftServe(e, wrap(r, e,
|
|
1648
|
+
if (getObserver() !== null) read(getNode(e, t, i));
|
|
1649
|
+
const n = e.n?.[t];
|
|
1650
|
+
if (n) {
|
|
1651
|
+
const r = nodeValue(n, undefined, e);
|
|
1652
|
+
if (e.s) return serveShallow(e, t, r);
|
|
1653
|
+
return isWrappable(r) ? draftServe(e, wrap(r, e, t)) : r;
|
|
1669
1654
|
}
|
|
1670
1655
|
}
|
|
1671
|
-
if (e.s) return serveShallow(e,
|
|
1672
|
-
return isWrappable(u) ? draftServe(e, wrap(u, e,
|
|
1656
|
+
if (e.s) return serveShallow(e, t, u);
|
|
1657
|
+
return isWrappable(u) ? draftServe(e, wrap(u, e, t)) : u;
|
|
1673
1658
|
}
|
|
1674
|
-
if (typeof u === "function" && !hasOwn.call(o,
|
|
1659
|
+
if (typeof u === "function" && !hasOwn.call(o, t) && !(l && hasOwn.call(e.v, t))) return u;
|
|
1675
1660
|
// proto method
|
|
1676
|
-
return serveDataKey(e,
|
|
1661
|
+
return serveDataKey(e, t, u, o, r, i);
|
|
1677
1662
|
},
|
|
1678
|
-
has(e,
|
|
1679
|
-
if (
|
|
1680
|
-
if (
|
|
1663
|
+
has(e, t) {
|
|
1664
|
+
if (t === $TARGET || t === $PROXY || t === $TRACK) return true;
|
|
1665
|
+
if (t === $OWNER || t === $RECORD) return false;
|
|
1681
1666
|
// (The witness before the pull: a mark on an uninitialized derived
|
|
1682
1667
|
// store is witnessed, then the pull throws — loading, declared pending.)
|
|
1683
|
-
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e,
|
|
1668
|
+
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, t);
|
|
1684
1669
|
if (e.fam !== null) pullFamily(e);
|
|
1685
|
-
const
|
|
1670
|
+
const n = readSource(e, t);
|
|
1686
1671
|
// A tracked reader's presence node answers (born from the two frames;
|
|
1687
1672
|
// core decides which it sees); everyone else reads the backing. A
|
|
1688
1673
|
// chained target's node is a subscription point (§7b): the inner store
|
|
1689
1674
|
// answers through the backing — unless the outer node carries a guess.
|
|
1690
1675
|
if (!inDraft(e) && !writeOverride) {
|
|
1691
1676
|
if (getObserver() !== null) {
|
|
1692
|
-
const r = getHasNode(e,
|
|
1677
|
+
const r = getHasNode(e, t);
|
|
1693
1678
|
const o = read(r);
|
|
1694
|
-
if (!e.ch ||
|
|
1695
|
-
const i =
|
|
1679
|
+
if (!e.ch || n !== e.v) return !!o;
|
|
1680
|
+
const i = t in n;
|
|
1696
1681
|
r.Ie = i;
|
|
1697
1682
|
// the link follows the inner store (S4)
|
|
1698
1683
|
if (r.l & CONFIG_OVERRIDE) return !!o;
|
|
1699
1684
|
return i;
|
|
1700
1685
|
} else if (optRead(e)) {
|
|
1701
|
-
const
|
|
1702
|
-
if (
|
|
1686
|
+
const n = optHooks.has(e, t);
|
|
1687
|
+
if (n !== undefined) return n;
|
|
1703
1688
|
}
|
|
1704
1689
|
}
|
|
1705
|
-
return
|
|
1690
|
+
return t in n && !(n === e.pb && e.del?.has(t));
|
|
1706
1691
|
},
|
|
1707
1692
|
ownKeys(e) {
|
|
1708
1693
|
if (verdict !== null && affectsHooks !== null && getObserver() === null) affectsHooks.io(e, undefined);
|
|
1709
1694
|
if (e.fam !== null) pullFamily(e);
|
|
1710
1695
|
return visibleKeys(e, enumerationSource(e));
|
|
1711
1696
|
},
|
|
1712
|
-
getOwnPropertyDescriptor(e,
|
|
1713
|
-
if (
|
|
1714
|
-
const
|
|
1715
|
-
if (verdict !== null && affectsHooks !== null &&
|
|
1697
|
+
getOwnPropertyDescriptor(e, t) {
|
|
1698
|
+
if (t === $OWNER || t === $RECORD) return undefined;
|
|
1699
|
+
const n = getObserver();
|
|
1700
|
+
if (verdict !== null && affectsHooks !== null && n === null) affectsHooks.io(e, t);
|
|
1716
1701
|
if (e.fam !== null) pullFamily(e);
|
|
1717
1702
|
// An enumerator (spread, Object.entries) already holds the container
|
|
1718
1703
|
// node and reads its frame; a descriptor read on its own tracks
|
|
1719
1704
|
// presence (R13).
|
|
1720
|
-
const r =
|
|
1721
|
-
const o = r && observerHoldsContainer(e,
|
|
1705
|
+
const r = n !== null && !inDraft(e) && !writeOverride;
|
|
1706
|
+
const o = r && observerHoldsContainer(e, n);
|
|
1722
1707
|
let i;
|
|
1723
1708
|
if (o) i = enumerationSource(e); else if (r) {
|
|
1724
1709
|
// The presence node's frame (core decides which) is the one described
|
|
@@ -1727,32 +1712,31 @@ const traps = {
|
|
|
1727
1712
|
// serves this reader (`readSource`: a held container's changed key —
|
|
1728
1713
|
// enumerability, accessor-ness — reads committed for a stale reader,
|
|
1729
1714
|
// the staging for a member; #3706 contrast).
|
|
1730
|
-
const
|
|
1731
|
-
const r = read(
|
|
1732
|
-
i = readSource(e,
|
|
1733
|
-
if (!r && (!e.ch ||
|
|
1734
|
-
} else i = readSource(e,
|
|
1715
|
+
const n = getHasNode(e, t);
|
|
1716
|
+
const r = read(n);
|
|
1717
|
+
i = readSource(e, t, true);
|
|
1718
|
+
if (!r && (!e.ch || n.l & CONFIG_OVERRIDE || !(t in i))) return undefined;
|
|
1719
|
+
} else i = readSource(e, t, true);
|
|
1735
1720
|
let l;
|
|
1736
1721
|
if (!o && optRead(e)) {
|
|
1737
|
-
const
|
|
1738
|
-
if (
|
|
1739
|
-
l =
|
|
1722
|
+
const n = optHooks.descriptor(e, t);
|
|
1723
|
+
if (n === null) return undefined;
|
|
1724
|
+
l = n;
|
|
1740
1725
|
}
|
|
1741
|
-
l ??= visibleDescriptor(e, i,
|
|
1726
|
+
l ??= visibleDescriptor(e, i, t);
|
|
1742
1727
|
if (l === undefined) return undefined;
|
|
1743
|
-
if (!(
|
|
1728
|
+
if (!(t === "length" && Array.isArray(e))) l.configurable = true;
|
|
1744
1729
|
return l;
|
|
1745
1730
|
},
|
|
1746
|
-
set(e,
|
|
1731
|
+
set(e, t, n) {
|
|
1747
1732
|
const r = inDraft(e);
|
|
1748
|
-
|
|
1749
|
-
if (!r && !o) {
|
|
1733
|
+
if (!r && !writeOverride) {
|
|
1750
1734
|
return true;
|
|
1751
1735
|
}
|
|
1752
|
-
if (
|
|
1736
|
+
if (t === "__proto__") return true;
|
|
1753
1737
|
// pollution guard (core R30)
|
|
1754
|
-
const
|
|
1755
|
-
const
|
|
1738
|
+
const o = e.s ? n : unwrapValue(n);
|
|
1739
|
+
const i = ensurePB(e);
|
|
1756
1740
|
// #4146 (R1: node existence is unobservable; #3706: the hold's unit is
|
|
1757
1741
|
// the key): a user setter writing a key a HELD container's staging left
|
|
1758
1742
|
// unchanged would write it into the held staging — read as the hold's
|
|
@@ -1763,65 +1747,57 @@ const traps = {
|
|
|
1763
1747
|
// (1)), and the write stages on it (A28; a write by the holder joins it,
|
|
1764
1748
|
// A34) — as it would with a reader's leaf. Kept while the container is
|
|
1765
1749
|
// held (`deferredReleases`).
|
|
1766
|
-
if (userWrite && e.k?.l & CONFIG_HELD) deferredReleases.add(getNode(e,
|
|
1767
|
-
if (Array.isArray(
|
|
1768
|
-
touch(e,
|
|
1769
|
-
if (
|
|
1750
|
+
if (userWrite && e.k?.l & CONFIG_HELD) deferredReleases.add(getNode(e, t));
|
|
1751
|
+
if (Array.isArray(i)) {
|
|
1752
|
+
touch(e, t);
|
|
1753
|
+
if (t === "length") e.wk = WK_ALL; else touch(e, "length");
|
|
1770
1754
|
} else {
|
|
1771
|
-
touch(e,
|
|
1772
|
-
if (!(
|
|
1755
|
+
touch(e, t);
|
|
1756
|
+
if (!(t in i)) e.kc++;
|
|
1773
1757
|
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1758
|
+
const l = UNSAFE_KEYS.has(t);
|
|
1759
|
+
if (l || e.ovl && e.sc !== 2 && !hasOwn.call(i, t)) {
|
|
1760
|
+
Object.defineProperty(i, t, {
|
|
1761
|
+
value: o,
|
|
1777
1762
|
writable: true,
|
|
1778
1763
|
enumerable: true,
|
|
1779
1764
|
configurable: true
|
|
1780
1765
|
});
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
if (e.
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
enumerable: true,
|
|
1789
|
-
configurable: true
|
|
1790
|
-
});
|
|
1791
|
-
} else l[n] = i;
|
|
1792
|
-
if (e.del !== null) e.del.delete(n);
|
|
1793
|
-
if (e.s && i !== null && typeof i === "object") markRawOne(i);
|
|
1794
|
-
if (o) notifyWrites(e);
|
|
1795
|
-
return true;
|
|
1766
|
+
} else i[t] = o;
|
|
1767
|
+
e.del?.delete(t);
|
|
1768
|
+
if (l) return true;
|
|
1769
|
+
if (e.s && o !== null && typeof o === "object") markRawOne(o);
|
|
1770
|
+
if (!r) notifyWrites(e);
|
|
1771
|
+
// a write-override write
|
|
1772
|
+
return true;
|
|
1796
1773
|
},
|
|
1797
|
-
defineProperty(e,
|
|
1774
|
+
defineProperty(e, t, n) {
|
|
1798
1775
|
const r = inDraft(e);
|
|
1799
|
-
|
|
1800
|
-
if (!r && !o) {
|
|
1776
|
+
if (!r && !writeOverride) {
|
|
1801
1777
|
return true;
|
|
1802
1778
|
}
|
|
1803
|
-
if (
|
|
1804
|
-
if (
|
|
1805
|
-
if ("value" in
|
|
1806
|
-
...
|
|
1807
|
-
value: unwrapValue(
|
|
1779
|
+
if (t === "__proto__") return true;
|
|
1780
|
+
if (n.get || n.set) e.a = true;
|
|
1781
|
+
if ("value" in n) n = {
|
|
1782
|
+
...n,
|
|
1783
|
+
value: unwrapValue(n.value)
|
|
1808
1784
|
};
|
|
1809
|
-
const
|
|
1810
|
-
if (e.a || !(
|
|
1811
|
-
touch(e,
|
|
1812
|
-
Object.defineProperty(
|
|
1813
|
-
|
|
1814
|
-
if (
|
|
1815
|
-
|
|
1785
|
+
const o = ensurePB(e);
|
|
1786
|
+
if (e.a || !(n.enumerable && n.writable && n.configurable)) e.sc = 1;
|
|
1787
|
+
touch(e, t);
|
|
1788
|
+
Object.defineProperty(o, t, n);
|
|
1789
|
+
e.del?.delete(t);
|
|
1790
|
+
if (!r) notifyWrites(e);
|
|
1791
|
+
// a write-override write
|
|
1792
|
+
return true;
|
|
1816
1793
|
},
|
|
1817
|
-
deleteProperty(e,
|
|
1818
|
-
const
|
|
1819
|
-
|
|
1820
|
-
if (!t && !r) {
|
|
1794
|
+
deleteProperty(e, t) {
|
|
1795
|
+
const n = inDraft(e);
|
|
1796
|
+
if (!n && !writeOverride) {
|
|
1821
1797
|
return true;
|
|
1822
1798
|
}
|
|
1823
|
-
const
|
|
1824
|
-
touch(e,
|
|
1799
|
+
const r = ensurePB(e);
|
|
1800
|
+
touch(e, t);
|
|
1825
1801
|
// The key count (`kc`, the overlay gate and the rebuild cap) follows a
|
|
1826
1802
|
// delete the way it follows an add: an own key of the draft leaving — a
|
|
1827
1803
|
// clone's key, or an overlay's own add — is one fewer now. An overlay's
|
|
@@ -1831,34 +1807,35 @@ const traps = {
|
|
|
1831
1807
|
// one" shape drifted the count up by one per write until a three-key
|
|
1832
1808
|
// object crossed the overlay threshold and paid a rebuild on every
|
|
1833
1809
|
// commit after.
|
|
1834
|
-
const
|
|
1835
|
-
if (hasOwn.call(
|
|
1836
|
-
delete
|
|
1837
|
-
|
|
1810
|
+
const o = e.ovl && hasOwn.call(e.v, t);
|
|
1811
|
+
if (hasOwn.call(r, t)) {
|
|
1812
|
+
delete r[t];
|
|
1813
|
+
o || e.kc--;
|
|
1838
1814
|
}
|
|
1839
|
-
if (
|
|
1840
|
-
if (
|
|
1841
|
-
|
|
1815
|
+
if (o) (e.del ??= new Set).add(t);
|
|
1816
|
+
if (!n) notifyWrites(e);
|
|
1817
|
+
// a write-override write
|
|
1818
|
+
return true;
|
|
1842
1819
|
}
|
|
1843
1820
|
};
|
|
1844
1821
|
|
|
1845
1822
|
/** Is `obs` subscribed to the container BY THIS PASS (its enumerator holds
|
|
1846
1823
|
* it)? A link left from a previous pass is stale — the reader must subscribe
|
|
1847
|
-
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e,
|
|
1848
|
-
const
|
|
1849
|
-
if (
|
|
1850
|
-
const r =
|
|
1851
|
-
return r !== null && r.u ===
|
|
1824
|
+
* to the key's presence node or a later add never re-runs it. */ function observerHoldsContainer(e, t) {
|
|
1825
|
+
const n = e.k;
|
|
1826
|
+
if (n === null) return false;
|
|
1827
|
+
const r = n.Ct;
|
|
1828
|
+
return r !== null && r.u === t && (!(t.R & REACTIVE_RECOMPUTING_DEPS) || r.Te === t.Ne);
|
|
1852
1829
|
}
|
|
1853
1830
|
|
|
1854
|
-
function storeSetter(e,
|
|
1831
|
+
function storeSetter(e, t, n = true) {
|
|
1855
1832
|
if (writeHook !== null) writeHook(e);
|
|
1856
1833
|
const r = e[$TARGET];
|
|
1857
1834
|
const o = writeScopes;
|
|
1858
1835
|
writeScopes = new Set;
|
|
1859
1836
|
writeScopes.add(scopeKey(r));
|
|
1860
1837
|
const i = userWrite;
|
|
1861
|
-
userWrite =
|
|
1838
|
+
userWrite = n;
|
|
1862
1839
|
writing++;
|
|
1863
1840
|
let l;
|
|
1864
1841
|
try {
|
|
@@ -1866,7 +1843,7 @@ function storeSetter(e, n, t = true) {
|
|
|
1866
1843
|
// (draft reads never self-track, proj R2), while EXTERNAL reads (signals
|
|
1867
1844
|
// inside a projection derive) must keep tracking — they are the derive's
|
|
1868
1845
|
// dependencies.
|
|
1869
|
-
l =
|
|
1846
|
+
l = t(e);
|
|
1870
1847
|
} finally {
|
|
1871
1848
|
writing--;
|
|
1872
1849
|
writeScopes = o;
|
|
@@ -1875,10 +1852,10 @@ function storeSetter(e, n, t = true) {
|
|
|
1875
1852
|
if (writing === 0 && pendingNotify.size) {
|
|
1876
1853
|
const e = [ ...pendingNotify ];
|
|
1877
1854
|
pendingNotify.clear();
|
|
1878
|
-
for (const
|
|
1855
|
+
for (const t of e) notifyWrites(t);
|
|
1879
1856
|
// A direct commit (a projection's creation run) folds at the setter's
|
|
1880
1857
|
// exit, unqueued: the backing is the pre-batch one (nothing swapped).
|
|
1881
|
-
if (directCommit) for (const
|
|
1858
|
+
if (directCommit) for (const t of e) if (t.pb !== null) foldTarget(t, t.v);
|
|
1882
1859
|
}
|
|
1883
1860
|
userWrite = i;
|
|
1884
1861
|
}
|
|
@@ -1886,13 +1863,13 @@ function storeSetter(e, n, t = true) {
|
|
|
1886
1863
|
// A returned replacement: on an optimistic family a user's is itself an
|
|
1887
1864
|
// optimistic edit — diffed against the view as guesses (`next` parity);
|
|
1888
1865
|
// otherwise it is adopted as the incoming truth.
|
|
1889
|
-
if (
|
|
1866
|
+
if (n && r.fam?.opt === true && !writeOverride) optHooks.writes(r, unwrapValue(l)); else adoptPB(r, unwrapValue(l));
|
|
1890
1867
|
}
|
|
1891
1868
|
// A34 (3), #3612 — the derived store's twin of `setMemo`: a user write to
|
|
1892
1869
|
// a derivation another transaction holds is nobody's proposal; it becomes
|
|
1893
1870
|
// the draft's prior state and the hold re-derives over it (the writes
|
|
1894
1871
|
// joined the hold through `setSignal`; the derive re-runs under it).
|
|
1895
|
-
if (
|
|
1872
|
+
if (n && heldDerivationHit) {
|
|
1896
1873
|
heldDerivationHit = false;
|
|
1897
1874
|
const e = r.fam.node;
|
|
1898
1875
|
if (!(e.R & REACTIVE_DIRTY)) {
|
|
@@ -1903,12 +1880,12 @@ function storeSetter(e, n, t = true) {
|
|
|
1903
1880
|
}
|
|
1904
1881
|
}
|
|
1905
1882
|
|
|
1906
|
-
function createStore(e,
|
|
1907
|
-
const
|
|
1883
|
+
function createStore(e, t) {
|
|
1884
|
+
const n = t?.shallow === true;
|
|
1908
1885
|
const r = wrap(e);
|
|
1909
1886
|
// Dependency edges (store/edges.ts): the root is named like a plain signal.
|
|
1910
1887
|
if (signalHook !== null) signalHook(r[$TARGET]);
|
|
1911
|
-
if (
|
|
1888
|
+
if (n) {
|
|
1912
1889
|
r[$TARGET].s = true;
|
|
1913
1890
|
markRawIngest(e);
|
|
1914
1891
|
}
|
|
@@ -1920,7 +1897,7 @@ function createStore(e, n) {
|
|
|
1920
1897
|
// disabled, buys nothing.
|
|
1921
1898
|
if (attrHooks !== null) {
|
|
1922
1899
|
storeOwners.set(r[$TARGET], e);
|
|
1923
|
-
nameStore(r,
|
|
1900
|
+
nameStore(r, t?.name, t?.loc);
|
|
1924
1901
|
}
|
|
1925
1902
|
}
|
|
1926
1903
|
const setter = e => storeSetter(r, e);
|
|
@@ -1930,47 +1907,47 @@ function createStore(e, n) {
|
|
|
1930
1907
|
/** True when `proxy` is a SHALLOW store (children served verbatim, slots
|
|
1931
1908
|
* replaced by reference — #2932). The list driver uses this to choose the
|
|
1932
1909
|
* slot-patch channel (collected row bodies) over per-record registration. */ function storeIsShallow(e) {
|
|
1933
|
-
const
|
|
1934
|
-
return
|
|
1910
|
+
const t = e?.[$TARGET];
|
|
1911
|
+
return t?.s === true;
|
|
1935
1912
|
}
|
|
1936
1913
|
|
|
1937
1914
|
/** True when `proxy` belongs to a projection family (S3). */ function storeHasFamily(e) {
|
|
1938
|
-
const
|
|
1939
|
-
return
|
|
1915
|
+
const t = e?.[$TARGET];
|
|
1916
|
+
return t?.fam != null;
|
|
1940
1917
|
}
|
|
1941
1918
|
|
|
1942
1919
|
/** True when `proxy` belongs to an OPTIMISTIC family (S4). */ function storeHasOptimisticFamily(e) {
|
|
1943
|
-
const
|
|
1944
|
-
return
|
|
1920
|
+
const t = e?.[$TARGET];
|
|
1921
|
+
return t?.fam?.opt === true;
|
|
1945
1922
|
}
|
|
1946
1923
|
|
|
1947
1924
|
// ---------------------------------------------------------------------------
|
|
1948
1925
|
// enumeration views (one visibility rule for the traps AND the deep walk)
|
|
1949
|
-
function visibleKeys(e,
|
|
1950
|
-
let
|
|
1951
|
-
if (e.ovl &&
|
|
1952
|
-
|
|
1926
|
+
function visibleKeys(e, t) {
|
|
1927
|
+
let n;
|
|
1928
|
+
if (e.ovl && t === e.pb) {
|
|
1929
|
+
n = Reflect.ownKeys(e.v);
|
|
1953
1930
|
const r = e.del;
|
|
1954
|
-
if (r
|
|
1955
|
-
for (const r of Reflect.ownKeys(
|
|
1956
|
-
if (!hasOwn.call(e.v, r))
|
|
1931
|
+
if (r?.size) n = n.filter(e => !r.has(e));
|
|
1932
|
+
for (const r of Reflect.ownKeys(t)) {
|
|
1933
|
+
if (!hasOwn.call(e.v, r)) n.push(r);
|
|
1957
1934
|
}
|
|
1958
|
-
} else
|
|
1935
|
+
} else n = Reflect.ownKeys(t);
|
|
1959
1936
|
// The ownership stamp is the last symbol on an owned backing: hide it.
|
|
1960
|
-
for (let e =
|
|
1961
|
-
if (
|
|
1962
|
-
|
|
1937
|
+
for (let e = n.length - 1; e >= 0 && typeof n[e] === "symbol"; e--) {
|
|
1938
|
+
if (n[e] === $OWNER) {
|
|
1939
|
+
n.splice(e, 1);
|
|
1963
1940
|
break;
|
|
1964
1941
|
}
|
|
1965
1942
|
}
|
|
1966
|
-
return
|
|
1943
|
+
return n;
|
|
1967
1944
|
}
|
|
1968
1945
|
|
|
1969
|
-
function visibleDescriptor(e,
|
|
1970
|
-
let r = Object.getOwnPropertyDescriptor(
|
|
1971
|
-
if (e.ovl &&
|
|
1972
|
-
if (e.del
|
|
1973
|
-
if (r === undefined) r = Object.getOwnPropertyDescriptor(e.v,
|
|
1946
|
+
function visibleDescriptor(e, t, n) {
|
|
1947
|
+
let r = Object.getOwnPropertyDescriptor(t, n);
|
|
1948
|
+
if (e.ovl && t === e.pb) {
|
|
1949
|
+
if (e.del?.has(n)) return undefined;
|
|
1950
|
+
if (r === undefined) r = Object.getOwnPropertyDescriptor(e.v, n);
|
|
1974
1951
|
}
|
|
1975
1952
|
return r;
|
|
1976
1953
|
}
|
|
@@ -1979,33 +1956,33 @@ function visibleDescriptor(e, n, t) {
|
|
|
1979
1956
|
// deep / snapshot
|
|
1980
1957
|
/** Tracking snapshot: subscribes every reachable record's container and deep
|
|
1981
1958
|
* witness (one node per record, not one per path), then snapshots. */ function deep(e) {
|
|
1982
|
-
const
|
|
1983
|
-
if (
|
|
1984
|
-
const
|
|
1985
|
-
const childTarget = (e,
|
|
1986
|
-
let r = lookupTarget(
|
|
1959
|
+
const t = e?.[$TARGET];
|
|
1960
|
+
if (t?.px !== e) return e;
|
|
1961
|
+
const n = new Set;
|
|
1962
|
+
const childTarget = (e, t, n) => {
|
|
1963
|
+
let r = lookupTarget(t, e.fam);
|
|
1987
1964
|
if (r === undefined) {
|
|
1988
|
-
if (!isWrappable(
|
|
1989
|
-
wrap(
|
|
1990
|
-
r = lookupTarget(
|
|
1965
|
+
if (!isWrappable(t)) return undefined;
|
|
1966
|
+
wrap(t, e, n);
|
|
1967
|
+
r = lookupTarget(t, e.fam) ?? t[$TARGET];
|
|
1991
1968
|
}
|
|
1992
1969
|
return r;
|
|
1993
1970
|
};
|
|
1994
1971
|
const walkT = e => {
|
|
1995
1972
|
if (e.fam !== null) pullFamily(e);
|
|
1996
|
-
const
|
|
1997
|
-
if (
|
|
1998
|
-
|
|
1973
|
+
const t = enumerationSource(e);
|
|
1974
|
+
if (n.has(t)) return;
|
|
1975
|
+
n.add(t);
|
|
1999
1976
|
read(getDeepNode(e));
|
|
2000
1977
|
// Through a chain (#3323): every inner record's container AND deep
|
|
2001
1978
|
// witness — base writes bump the inner witnesses.
|
|
2002
|
-
for (let
|
|
2003
|
-
|
|
2004
|
-
read(getContainerNode(
|
|
2005
|
-
read(getDeepNode(
|
|
1979
|
+
for (let t = e; t.ch; ) {
|
|
1980
|
+
t = t.v[$TARGET];
|
|
1981
|
+
read(getContainerNode(t));
|
|
1982
|
+
read(getDeepNode(t));
|
|
2006
1983
|
}
|
|
2007
|
-
for (const
|
|
2008
|
-
const r = visibleDescriptor(e,
|
|
1984
|
+
for (const n of visibleKeys(e, t)) {
|
|
1985
|
+
const r = visibleDescriptor(e, t, n);
|
|
2009
1986
|
if (r === undefined) continue;
|
|
2010
1987
|
if (r.get || r.set) {
|
|
2011
1988
|
e.a = true;
|
|
@@ -2014,38 +1991,38 @@ function visibleDescriptor(e, n, t) {
|
|
|
2014
1991
|
}
|
|
2015
1992
|
let o = r.value;
|
|
2016
1993
|
if (o === null || typeof o !== "object") continue;
|
|
2017
|
-
if (e.ch && o[$TARGET] === undefined) o = resolveChainedRaw(e,
|
|
2018
|
-
const i = childTarget(e, o,
|
|
1994
|
+
if (e.ch && o[$TARGET] === undefined) o = resolveChainedRaw(e, n, o);
|
|
1995
|
+
const i = childTarget(e, o, n);
|
|
2019
1996
|
if (i === undefined) continue;
|
|
2020
1997
|
// raw-marked: leaf by contract
|
|
2021
1998
|
walkT(i);
|
|
2022
1999
|
}
|
|
2023
2000
|
};
|
|
2024
|
-
walkT(
|
|
2001
|
+
walkT(t);
|
|
2025
2002
|
return snapshot(e);
|
|
2026
2003
|
}
|
|
2027
2004
|
|
|
2028
2005
|
/** Non-tracking snapshot: source identity for unowned subtrees (zero copy),
|
|
2029
2006
|
* a copy for owned ones (they are by definition modified relative to the
|
|
2030
2007
|
* source). Sees the pending backing (R27). */ function snapshot(e) {
|
|
2031
|
-
const
|
|
2032
|
-
return snapshotWalk(e, new Map,
|
|
2008
|
+
const t = e?.[$TARGET];
|
|
2009
|
+
return snapshotWalk(e, new Map, t?.fam ?? null);
|
|
2033
2010
|
}
|
|
2034
2011
|
|
|
2035
|
-
function snapshotWalk(e,
|
|
2012
|
+
function snapshotWalk(e, t, n) {
|
|
2036
2013
|
if (e === null || typeof e !== "object") return e;
|
|
2037
2014
|
let r = e;
|
|
2038
2015
|
for (let e = true; ;e = false) {
|
|
2039
|
-
const
|
|
2040
|
-
let o =
|
|
2041
|
-
if (o === undefined &&
|
|
2016
|
+
const t = r?.[$TARGET]?.v !== undefined;
|
|
2017
|
+
let o = t ? r[$TARGET] : undefined;
|
|
2018
|
+
if (o === undefined && n !== null) o = lookupTarget(r, n);
|
|
2042
2019
|
if (o === undefined) o = lookupTarget(r, null);
|
|
2043
2020
|
if (o === undefined) break;
|
|
2044
|
-
if (e && !
|
|
2045
|
-
const e =
|
|
2021
|
+
if (e && !t && n !== null && o.fam !== n) {
|
|
2022
|
+
const e = n.map.get(o.px);
|
|
2046
2023
|
if (e !== undefined) o = e;
|
|
2047
2024
|
}
|
|
2048
|
-
if (o.fam !== null)
|
|
2025
|
+
if (o.fam !== null) n = o.fam;
|
|
2049
2026
|
// R27: a snapshot sees the batch's pending backing — a held one (another
|
|
2050
2027
|
// transaction's future) only from its own draft.
|
|
2051
2028
|
let i = o.v;
|
|
@@ -2058,12 +2035,12 @@ function snapshotWalk(e, n, t) {
|
|
|
2058
2035
|
r = i;
|
|
2059
2036
|
}
|
|
2060
2037
|
if (!isWrappable(r)) return r;
|
|
2061
|
-
const o =
|
|
2038
|
+
const o = t.get(r);
|
|
2062
2039
|
if (o !== undefined) return o;
|
|
2063
2040
|
if (isOwned(r)) {
|
|
2064
2041
|
const e = Array.isArray(r);
|
|
2065
2042
|
const o = e ? [] : Object.create(Object.getPrototypeOf(r));
|
|
2066
|
-
|
|
2043
|
+
t.set(r, o);
|
|
2067
2044
|
for (const i of Reflect.ownKeys(r)) {
|
|
2068
2045
|
if (e && i === "length" || i === $OWNER) continue;
|
|
2069
2046
|
const l = Object.getOwnPropertyDescriptor(r, i);
|
|
@@ -2073,7 +2050,7 @@ function snapshotWalk(e, n, t) {
|
|
|
2073
2050
|
continue;
|
|
2074
2051
|
}
|
|
2075
2052
|
const u = l.value;
|
|
2076
|
-
const f = u !== null && typeof u === "object" ? snapshotWalk(u,
|
|
2053
|
+
const f = u !== null && typeof u === "object" ? snapshotWalk(u, t, n) : u;
|
|
2077
2054
|
if (l.enumerable && l.writable && l.configurable) o[i] = f; else Object.defineProperty(o, i, {
|
|
2078
2055
|
...l,
|
|
2079
2056
|
value: f
|
|
@@ -2082,18 +2059,18 @@ function snapshotWalk(e, n, t) {
|
|
|
2082
2059
|
if (e && o.length !== r.length) o.length = r.length;
|
|
2083
2060
|
return o;
|
|
2084
2061
|
}
|
|
2085
|
-
|
|
2062
|
+
t.set(r, r);
|
|
2086
2063
|
let i = null;
|
|
2087
2064
|
for (const e of Reflect.ownKeys(r)) {
|
|
2088
2065
|
const o = Object.getOwnPropertyDescriptor(r, e);
|
|
2089
2066
|
if (!o || o.get || o.set) continue;
|
|
2090
2067
|
const l = o.value;
|
|
2091
2068
|
if (l === null || typeof l !== "object") continue;
|
|
2092
|
-
const u = snapshotWalk(l,
|
|
2069
|
+
const u = snapshotWalk(l, t, n);
|
|
2093
2070
|
if (u !== l) {
|
|
2094
2071
|
if (i === null) {
|
|
2095
2072
|
i = Array.isArray(r) ? [ ...r ] : Object.create(Object.getPrototypeOf(r), Object.getOwnPropertyDescriptors(r));
|
|
2096
|
-
|
|
2073
|
+
t.set(r, i);
|
|
2097
2074
|
}
|
|
2098
2075
|
i[e] = u;
|
|
2099
2076
|
}
|