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