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