@solidjs/signals 2.0.0-rc.5 → 2.0.0-rc.7
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.js +2467 -1322
- package/dist/node.cjs +2037 -2428
- package/dist/node.dev.cjs +13724 -0
- package/dist/prod/boundaries.js +3 -1
- package/dist/prod/core/async.js +47 -20
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +9 -1
- package/dist/prod/core/context.js +10 -16
- package/dist/prod/core/core.js +260 -188
- package/dist/prod/core/dev.js +2 -0
- package/dist/prod/core/effect.js +41 -32
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +35 -31
- package/dist/prod/core/heap.js +34 -40
- package/dist/prod/core/lanes.js +44 -30
- package/dist/prod/core/optimistic.js +55 -45
- package/dist/prod/core/owner.js +35 -35
- package/dist/prod/core/scheduler.js +176 -177
- package/dist/prod/core/verdict.js +53 -52
- package/dist/prod/index.js +0 -2
- package/dist/prod/map.js +104 -94
- package/dist/prod/signals.js +119 -35
- package/dist/prod/store/next/optimistic.js +217 -163
- package/dist/prod/store/next/projection.js +2 -2
- package/dist/prod/store/next/reconcile.js +140 -288
- package/dist/prod/store/next/store.js +338 -252
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/attribution-hooks.d.ts +75 -0
- package/dist/types/core/attribution.d.ts +313 -9
- package/dist/types/core/constants.d.ts +8 -0
- package/dist/types/core/core.d.ts +12 -3
- package/dist/types/core/dev.d.ts +56 -9
- package/dist/types/core/heap.d.ts +5 -3
- package/dist/types/core/invariants.d.ts +1 -1
- package/dist/types/core/lanes.d.ts +10 -0
- package/dist/types/core/scheduler.d.ts +14 -4
- package/dist/types/signals.d.ts +10 -11
- package/dist/types/store/index.d.ts +3 -6
- package/dist/types/store/next/optimistic.d.ts +4 -2
- package/dist/types/store/next/reconcile.d.ts +5 -12
- package/dist/types/store/next/store.d.ts +3 -9
- package/dist/types/store/next/target.d.ts +30 -46
- package/dist/types/store/store.d.ts +14 -9
- package/dist/types-cjs/core/attribution-hooks.d.cts +75 -0
- package/dist/types-cjs/core/attribution.d.cts +313 -9
- package/dist/types-cjs/core/constants.d.cts +8 -0
- package/dist/types-cjs/core/core.d.cts +12 -3
- package/dist/types-cjs/core/dev.d.cts +56 -9
- package/dist/types-cjs/core/heap.d.cts +5 -3
- package/dist/types-cjs/core/invariants.d.cts +1 -1
- package/dist/types-cjs/core/lanes.d.cts +10 -0
- package/dist/types-cjs/core/scheduler.d.cts +14 -4
- package/dist/types-cjs/signals.d.cts +10 -11
- package/dist/types-cjs/store/index.d.cts +3 -6
- package/dist/types-cjs/store/next/optimistic.d.cts +4 -2
- package/dist/types-cjs/store/next/reconcile.d.cts +5 -12
- package/dist/types-cjs/store/next/store.d.cts +3 -9
- package/dist/types-cjs/store/next/target.d.cts +30 -46
- package/dist/types-cjs/store/store.d.cts +14 -9
- package/package.json +3 -2
- package/dist/prod/store/next/patch-hooks.js +0 -13
- package/dist/prod/store/next/patch.js +0 -614
- package/dist/types/store/next/patch-hooks.d.ts +0 -41
- package/dist/types/store/next/patch.d.ts +0 -91
- package/dist/types-cjs/store/next/patch-hooks.d.cts +0 -41
- package/dist/types-cjs/store/next/patch.d.cts +0 -91
|
@@ -10,7 +10,7 @@ import { insertIntoHeap, queueFor, markHeap, enqueueSub } from "./heap.js";
|
|
|
10
10
|
|
|
11
11
|
import "./invariants.js";
|
|
12
12
|
|
|
13
|
-
import { findLane, hasActiveOverride, assignOrMergeLane } from "./lanes.js";
|
|
13
|
+
import { findLane, laneHeld, hasActiveOverride, assignOrMergeLane } from "./lanes.js";
|
|
14
14
|
|
|
15
15
|
import { installOptimisticEngine } from "./optimistic.js";
|
|
16
16
|
|
|
@@ -47,10 +47,10 @@ let pendingProbe = null;
|
|
|
47
47
|
* O(all-leaves-ever-read)-per-update pathology). Entries are permanent like
|
|
48
48
|
* the companions themselves; a store with no leaf-level isPending()/latest()
|
|
49
49
|
* reads never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
|
|
50
|
-
const t = e.
|
|
50
|
+
const t = e.rt;
|
|
51
51
|
if (!t) return;
|
|
52
52
|
t.T |= CONFIG_CHILD_COMPANIONS;
|
|
53
|
-
(ext(t).
|
|
53
|
+
(ext(t).dt ??= new Set).add(e);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function getPendingSignal(e) {
|
|
@@ -63,7 +63,7 @@ function getPendingSignal(e) {
|
|
|
63
63
|
ext(e).ye = t;
|
|
64
64
|
e.T |= CONFIG_HAS_COMPANIONS;
|
|
65
65
|
markFirewallChildCompanions(e);
|
|
66
|
-
ext(t).
|
|
66
|
+
ext(t).St = e;
|
|
67
67
|
if (computePendingState(e)) setSignal(t, true);
|
|
68
68
|
}
|
|
69
69
|
return t;
|
|
@@ -72,7 +72,7 @@ function getPendingSignal(e) {
|
|
|
72
72
|
function collectPendingSources(e) {
|
|
73
73
|
if (!pendingProbe) return;
|
|
74
74
|
pendingProbe.sources.add(e);
|
|
75
|
-
const t = e.
|
|
75
|
+
const t = e.rt || e;
|
|
76
76
|
if (t !== e) pendingProbe.sources.add(t);
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -105,33 +105,29 @@ function collectPendingSources(e) {
|
|
|
105
105
|
if (e.S & STATUS_ERROR) return false;
|
|
106
106
|
if (t.has(e)) return false;
|
|
107
107
|
t.add(e);
|
|
108
|
-
const n = e.
|
|
108
|
+
const n = e.rt;
|
|
109
109
|
if (n && markWalk(n, t)) return true;
|
|
110
110
|
// Mid-recompute (the clearStatus companion poke runs before
|
|
111
111
|
// trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
|
|
112
112
|
// pass's dependency set — walking past it would read dropped deps and
|
|
113
113
|
// latch a stale verdict on the companion.
|
|
114
114
|
const i = e;
|
|
115
|
-
const r = i.ie & REACTIVE_RECOMPUTING_DEPS ? i.
|
|
115
|
+
const r = i.ie & REACTIVE_RECOMPUTING_DEPS ? i.Be : undefined;
|
|
116
116
|
if (r !== null) {
|
|
117
|
-
for (let e = i.
|
|
118
|
-
if (!e.ve && markWalk(e.
|
|
117
|
+
for (let e = i.lt ?? null; e !== null; e = e.ot) {
|
|
118
|
+
if (!e.ve && markWalk(e.st, t)) return true;
|
|
119
119
|
if (e === r) break;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
return false;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
/** Gated entry: apps with no live mark pay one integer compare. */ function markCovered(e) {
|
|
126
|
-
return activeAffectsMarks !== 0 && markWalk(e, new Set);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
125
|
function quietPending(e) {
|
|
130
126
|
if (e.o?.le) {
|
|
131
|
-
for (const t of e.o.le) if (!t.o?.
|
|
127
|
+
for (const t of e.o.le) if (!t.o?.De) return false;
|
|
132
128
|
return true;
|
|
133
129
|
}
|
|
134
|
-
return e.o?.
|
|
130
|
+
return e.o?.De ?? false;
|
|
135
131
|
}
|
|
136
132
|
|
|
137
133
|
// NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
|
|
@@ -151,23 +147,28 @@ function computePendingState(e) {
|
|
|
151
147
|
// Mark coverage is transitive by dep-graph reachability: a latest() shadow
|
|
152
148
|
// reaches its owner (and a store leaf its firewall) through its own deps,
|
|
153
149
|
// so the one walk covers direct marks, derivation, and companion chains.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
150
|
+
// Gated: apps with no live mark pay one integer compare.
|
|
151
|
+
if (activeAffectsMarks !== 0 && markWalk(e, new Set)) return true;
|
|
152
|
+
const n = e.rt;
|
|
153
|
+
if (e.o?.St) {
|
|
154
|
+
const t = e.o?.St;
|
|
155
|
+
const n = t.rt || t;
|
|
159
156
|
return newQuestionInFlight(n);
|
|
160
157
|
}
|
|
161
|
-
if (n && e.
|
|
158
|
+
if (n && e.Re !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
162
159
|
return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ie && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
|
|
163
160
|
}
|
|
164
161
|
// `!comp._loading`: a hold created while the loading window is still open is
|
|
165
162
|
// the window's own landing in flight to its commit — verdict-quiet like the
|
|
166
163
|
// rest of the window (the UNINITIALIZED check suppresses exactly this frame
|
|
167
164
|
// for windowless first loads; born-committed nodes need their own gate, #2990).
|
|
168
|
-
if (e.
|
|
169
|
-
if (hasActiveOverride(e)) return !e.
|
|
170
|
-
|
|
165
|
+
if (e.Re !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ne) {
|
|
166
|
+
if (hasActiveOverride(e)) return !e.pe || !e.pe(e.Re, unwrapOverride(e.o?.Pe));
|
|
167
|
+
// A quiet re-ask's held landing still answers the same question: the
|
|
168
|
+
// classification survives the landing (asyncWrite) and dies with the
|
|
169
|
+
// commit (commitPendingNode) — verdict-quiet through the reveal, like
|
|
170
|
+
// the loading window above (#3178).
|
|
171
|
+
if (!t.o?.De) return true;
|
|
171
172
|
}
|
|
172
173
|
return newQuestionInFlight(t);
|
|
173
174
|
}
|
|
@@ -185,7 +186,7 @@ function updatePendingSignal(e) {
|
|
|
185
186
|
}
|
|
186
187
|
|
|
187
188
|
function updateChildCompanions(e) {
|
|
188
|
-
const t = e.o?.
|
|
189
|
+
const t = e.o?.dt;
|
|
189
190
|
if (t === undefined) return;
|
|
190
191
|
for (const e of t) updatePendingSignal(e);
|
|
191
192
|
}
|
|
@@ -235,11 +236,11 @@ function updateChildCompanions(e) {
|
|
|
235
236
|
}
|
|
236
237
|
if (s !== e) continue;
|
|
237
238
|
suppressedProbes.delete(n);
|
|
238
|
-
const o = n.o?.ye?.o
|
|
239
|
+
const o = n.o?.ye?.o?.$e;
|
|
239
240
|
for (const e of i) {
|
|
240
241
|
if (e.ie & REACTIVE_DISPOSED) continue;
|
|
241
242
|
e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
|
|
242
|
-
if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o
|
|
243
|
+
if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.$e = undefined;
|
|
243
244
|
enqueueSub(e);
|
|
244
245
|
t = true;
|
|
245
246
|
}
|
|
@@ -250,18 +251,18 @@ function updateChildCompanions(e) {
|
|
|
250
251
|
function snapCompanionsToState(e) {
|
|
251
252
|
suppressedProbes.size !== 0 && suppressedProbes.delete(e);
|
|
252
253
|
const t = e.o?.ye;
|
|
253
|
-
if (t && (t.o?.
|
|
254
|
+
if (t && (t.o?.Pe === undefined || t.o?.Pe === NOT_PENDING)) {
|
|
254
255
|
const n = computePendingState(e);
|
|
255
|
-
if (t.be !== n || t.
|
|
256
|
+
if (t.be !== n || t.Re !== NOT_PENDING) {
|
|
256
257
|
t.be = n;
|
|
257
|
-
t.
|
|
258
|
+
t.Re = NOT_PENDING;
|
|
258
259
|
insertSubs(t);
|
|
259
260
|
schedule();
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
const n = e.o?.Ce;
|
|
263
264
|
if (n && !(n.ie & REACTIVE_DISPOSED)) {
|
|
264
|
-
if ((n.o?.
|
|
265
|
+
if ((n.o?.Pe === undefined || n.o?.Pe === NOT_PENDING) && n.Re === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
265
266
|
n.ie |= REACTIVE_DIRTY;
|
|
266
267
|
insertIntoHeap(n, queueFor(n));
|
|
267
268
|
insertSubs(n);
|
|
@@ -292,13 +293,13 @@ function getLatestValueComputed(e) {
|
|
|
292
293
|
ext(e).Ce = t;
|
|
293
294
|
e.T |= CONFIG_HAS_COMPANIONS;
|
|
294
295
|
markFirewallChildCompanions(e);
|
|
295
|
-
ext(t).
|
|
296
|
+
ext(t).St = e;
|
|
296
297
|
// Parent-child lane relationship
|
|
297
298
|
// Backfill an in-flight write (mirrors getPendingSignal): the companion is
|
|
298
299
|
// created lazily, possibly after the write was processed — syncCompanions
|
|
299
300
|
// only pushes into companions that already exist, so the first latest()
|
|
300
301
|
// read inside a held transition showed the committed value (#3041).
|
|
301
|
-
if (e.
|
|
302
|
+
if (e.Re !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Re);
|
|
302
303
|
setContextInternal(r);
|
|
303
304
|
setPendingCheckActive(i);
|
|
304
305
|
setLatestReadActive(n);
|
|
@@ -310,7 +311,7 @@ function getLatestValueComputed(e) {
|
|
|
310
311
|
const t = getLatestValueComputed(e);
|
|
311
312
|
const n = latestReadActive;
|
|
312
313
|
setLatestReadActive(false);
|
|
313
|
-
const i = e.o?.
|
|
314
|
+
const i = e.o?.Pe !== undefined && e.o?.Pe !== NOT_PENDING ? unwrapOverride(e.o?.Pe) : e.be;
|
|
314
315
|
let r;
|
|
315
316
|
try {
|
|
316
317
|
// An untracked latest() read has no reading context, so read() never
|
|
@@ -319,7 +320,7 @@ function getLatestValueComputed(e) {
|
|
|
319
320
|
// until the flush (#2922). Mirror the tracked-read pull here: mark the
|
|
320
321
|
// queued staleness through the graph, then bring the shadow up to date.
|
|
321
322
|
const e = queueFor(t);
|
|
322
|
-
if (t.
|
|
323
|
+
if (t.qe >= e.Me && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
|
|
323
324
|
markHeap(e);
|
|
324
325
|
// Suspend probe collection during the pull (mirrors pendingCheckRead's
|
|
325
326
|
// prepare): a probe through latest() answers for the SHADOW — the
|
|
@@ -346,10 +347,10 @@ function getLatestValueComputed(e) {
|
|
|
346
347
|
setLatestReadActive(n);
|
|
347
348
|
}
|
|
348
349
|
if (t.S & STATUS_PENDING) return i;
|
|
349
|
-
if (stale && currentOptimisticLane && t.o
|
|
350
|
-
const e = findLane(t.o
|
|
350
|
+
if (stale && currentOptimisticLane && t.o?.$e) {
|
|
351
|
+
const e = findLane(t.o?.$e);
|
|
351
352
|
const n = findLane(currentOptimisticLane);
|
|
352
|
-
if (e !== n && e
|
|
353
|
+
if (e !== n && laneHeld(e)) {
|
|
353
354
|
return i;
|
|
354
355
|
}
|
|
355
356
|
}
|
|
@@ -357,7 +358,7 @@ function getLatestValueComputed(e) {
|
|
|
357
358
|
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
358
359
|
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
359
360
|
// other transition's committed view, matching read()'s own selection.
|
|
360
|
-
if (t.
|
|
361
|
+
if (t.Re !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ae && activeTransition !== t.Ae)) return t.Re;
|
|
361
362
|
return r;
|
|
362
363
|
}
|
|
363
364
|
|
|
@@ -371,9 +372,9 @@ function getLatestValueComputed(e) {
|
|
|
371
372
|
* so the `_fn` check is belt-and-braces for this call site.
|
|
372
373
|
*/ function latestShadowWithInitializedParent(e) {
|
|
373
374
|
if (typeof e.oe !== "function") return false;
|
|
374
|
-
const t = e.o?.
|
|
375
|
+
const t = e.o?.St;
|
|
375
376
|
if (t === undefined) return false;
|
|
376
|
-
const n = t.
|
|
377
|
+
const n = t.rt || t;
|
|
377
378
|
return !(n.S & STATUS_UNINITIALIZED);
|
|
378
379
|
}
|
|
379
380
|
|
|
@@ -433,7 +434,7 @@ function getLatestValueComputed(e) {
|
|
|
433
434
|
}
|
|
434
435
|
|
|
435
436
|
function recordFreshRead(e, t) {
|
|
436
|
-
if (pendingProbe !== null && e.
|
|
437
|
+
if (pendingProbe !== null && e.Re !== NOT_PENDING && t === e.Re) {
|
|
437
438
|
if (heldAwaitingAsync(e)) return;
|
|
438
439
|
pendingProbe.freshReads.add(e);
|
|
439
440
|
}
|
|
@@ -441,10 +442,10 @@ function recordFreshRead(e, t) {
|
|
|
441
442
|
|
|
442
443
|
function applyReask(e, t) {
|
|
443
444
|
const n = !!(e.S & STATUS_PENDING);
|
|
444
|
-
const i = t && !(n && !e.o?.
|
|
445
|
-
const r = n && (e.o?.
|
|
445
|
+
const i = t && !(n && !e.o?.De);
|
|
446
|
+
const r = n && (e.o?.De ?? false) !== i;
|
|
446
447
|
// Allocation-free for the quiet case: false is the extension default.
|
|
447
|
-
if (i) ext(e).
|
|
448
|
+
if (i) ext(e).De = true; else if (e.o !== null) e.o.De = false;
|
|
448
449
|
return r;
|
|
449
450
|
}
|
|
450
451
|
|
|
@@ -525,7 +526,7 @@ function isPending(e) {
|
|
|
525
526
|
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
526
527
|
// _propagateAffects): core call sites fire these behind the same guards the
|
|
527
528
|
// direct calls used, so behavior is identical once this module loads.
|
|
528
|
-
GlobalQueue.
|
|
529
|
+
GlobalQueue.Ue = syncCompanions;
|
|
529
530
|
|
|
530
531
|
GlobalQueue.de = updatePendingSignal;
|
|
531
532
|
|
|
@@ -533,18 +534,18 @@ GlobalQueue.me = updateChildCompanions;
|
|
|
533
534
|
|
|
534
535
|
GlobalQueue.un = snapCompanionsToState;
|
|
535
536
|
|
|
536
|
-
GlobalQueue.
|
|
537
|
+
GlobalQueue.Vt = latestRead;
|
|
537
538
|
|
|
538
|
-
GlobalQueue.
|
|
539
|
+
GlobalQueue.bt = pendingCheckRead;
|
|
539
540
|
|
|
540
|
-
GlobalQueue.
|
|
541
|
+
GlobalQueue.Lt = recordFreshRead;
|
|
541
542
|
|
|
542
|
-
GlobalQueue.
|
|
543
|
+
GlobalQueue.nt = applyReask;
|
|
543
544
|
|
|
544
545
|
GlobalQueue.k = repollDownstreamVerdicts;
|
|
545
546
|
|
|
546
|
-
GlobalQueue.
|
|
547
|
+
GlobalQueue.zt = witnessAffects;
|
|
547
548
|
|
|
548
|
-
GlobalQueue.
|
|
549
|
+
GlobalQueue.Kt = wakeSuppressedProbes;
|
|
549
550
|
|
|
550
551
|
export { isPending, latest };
|
package/dist/prod/index.js
CHANGED
|
@@ -38,8 +38,6 @@ export { createProjectionNext as createProjection } from "./store/next/projectio
|
|
|
38
38
|
|
|
39
39
|
export { merge, omit } from "./store/utils.js";
|
|
40
40
|
|
|
41
|
-
export { patchableRaw, registerPatch, registerRowOps, registerSlotPatchNext as registerSlotPatch } from "./store/next/patch.js";
|
|
42
|
-
|
|
43
41
|
export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store/next/store.js";
|
|
44
42
|
|
|
45
43
|
export { storePath } from "./store/storePath.js";
|
package/dist/prod/map.js
CHANGED
|
@@ -6,6 +6,8 @@ import "./core/scheduler.js";
|
|
|
6
6
|
|
|
7
7
|
import { CONFIG_AUTO_DISPOSE } from "./core/constants.js";
|
|
8
8
|
|
|
9
|
+
import { attrHooks } from "./core/attribution-hooks.js";
|
|
10
|
+
|
|
9
11
|
import "./core/invariants.js";
|
|
10
12
|
|
|
11
13
|
import "./core/verdict.js";
|
|
@@ -23,23 +25,23 @@ function mapArray(t, s, i) {
|
|
|
23
25
|
const r = s.length > 1;
|
|
24
26
|
const n = s;
|
|
25
27
|
const h = {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Zt: [],
|
|
28
|
+
$t: createOwner(),
|
|
29
|
+
Jt: 0,
|
|
30
|
+
Xt: t,
|
|
31
|
+
Yt: [],
|
|
32
|
+
Zt: n,
|
|
32
33
|
ts: [],
|
|
33
|
-
ss:
|
|
34
|
+
ss: [],
|
|
35
|
+
Ht: e,
|
|
34
36
|
es: e || i?.keyed === false ? [] : undefined,
|
|
35
37
|
rs: r && i?.keyed !== false ? [] : undefined,
|
|
36
38
|
ns: i?.keyed === false,
|
|
37
39
|
hs: i?.fallback
|
|
38
40
|
};
|
|
39
|
-
const o = computed(updateKeyedMap.bind(h));
|
|
41
|
+
const o = computed(updateKeyedMap.bind(h), undefined);
|
|
40
42
|
// Untracked reads inside the internal owner resolve via _parentComputed; routing
|
|
41
43
|
// them through node lets store-proxy lookups see pending writes (not stale _value).
|
|
42
|
-
h.
|
|
44
|
+
h.$t.Pt = o;
|
|
43
45
|
o.T &= ~CONFIG_AUTO_DISPOSE;
|
|
44
46
|
return accessor(o);
|
|
45
47
|
}
|
|
@@ -58,163 +60,171 @@ const pureOptions = {
|
|
|
58
60
|
// strong-abort ordering: removed rows now dispose AFTER the pass's new rows
|
|
59
61
|
// are created (you cannot destroy state before knowing the pass will land).
|
|
60
62
|
function updateKeyedMap() {
|
|
61
|
-
const t = this.
|
|
63
|
+
const t = this.Xt() || [], s = t.length;
|
|
62
64
|
t[$TRACK];
|
|
63
65
|
// top level tracking
|
|
64
|
-
runWithOwner(this
|
|
66
|
+
runWithOwner(this.$t, () => {
|
|
65
67
|
let i, e, r, n,
|
|
66
68
|
// Mappers write freshly-created row/index signals into the STAGE
|
|
67
69
|
// arrays (`rows`/`indexes`), never into `this._rows`/`this._indexes`.
|
|
68
70
|
h = this.es ? this.ns ? () => {
|
|
69
71
|
r[e] = signal(t[e], pureOptions);
|
|
70
|
-
return this.
|
|
72
|
+
return this.Zt(accessor(r[e]), e);
|
|
71
73
|
} : () => {
|
|
72
74
|
r[e] = signal(t[e], pureOptions);
|
|
73
75
|
n && (n[e] = signal(e, pureOptions));
|
|
74
|
-
return this.
|
|
76
|
+
return this.Zt(accessor(r[e]), n ? accessor(n[e]) : undefined);
|
|
75
77
|
} : this.rs ? () => {
|
|
76
78
|
const s = t[e];
|
|
77
79
|
n[e] = signal(e, pureOptions);
|
|
78
|
-
return this.
|
|
80
|
+
return this.Zt(s, accessor(n[e]));
|
|
79
81
|
} : () => {
|
|
80
82
|
const s = t[e];
|
|
81
|
-
return this.
|
|
83
|
+
return this.Zt(s);
|
|
82
84
|
};
|
|
83
85
|
// fast path for empty arrays
|
|
84
86
|
if (s === 0) {
|
|
85
|
-
if (this
|
|
86
|
-
this.
|
|
87
|
+
if (this.Jt !== 0) {
|
|
88
|
+
this.$t.dispose(false);
|
|
89
|
+
this.ss = [];
|
|
90
|
+
this.Yt = [];
|
|
87
91
|
this.ts = [];
|
|
88
|
-
this.
|
|
89
|
-
this.Zt = [];
|
|
90
|
-
this.$t = 0;
|
|
92
|
+
this.Jt = 0;
|
|
91
93
|
this.es && (this.es = []);
|
|
92
94
|
this.rs && (this.rs = []);
|
|
93
95
|
}
|
|
94
|
-
if (this.hs && !this.
|
|
96
|
+
if (this.hs && !this.ts[0]) {
|
|
95
97
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
96
98
|
// dispose it before re-creating
|
|
97
|
-
this.
|
|
98
|
-
this.
|
|
99
|
+
this.ss[0]?.dispose();
|
|
100
|
+
this.ts[0] = runWithOwner(this.ss[0] = createOwner(), this.hs);
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
// fast path for new create
|
|
102
|
-
else if (this
|
|
104
|
+
else if (this.Jt === 0) {
|
|
103
105
|
const o = new Array(s);
|
|
104
|
-
const
|
|
106
|
+
const a = new Array(s);
|
|
105
107
|
r = this.es && new Array(s);
|
|
106
108
|
n = this.rs && new Array(s);
|
|
107
109
|
try {
|
|
108
|
-
for (e = 0; e < s; e++) o[e] = runWithOwner(
|
|
110
|
+
for (e = 0; e < s; e++) o[e] = runWithOwner(a[e] = createOwner(), h);
|
|
109
111
|
} catch (t) {
|
|
110
|
-
for (i = 0; i <= e; i++)
|
|
112
|
+
for (i = 0; i <= e; i++) a[i]?.dispose();
|
|
111
113
|
throw t;
|
|
112
114
|
}
|
|
113
115
|
// commit
|
|
114
|
-
if (this.
|
|
116
|
+
if (this.ss[0]) this.ss[0].dispose();
|
|
115
117
|
// previous fallback
|
|
116
|
-
this.
|
|
117
|
-
this.
|
|
118
|
+
this.ts = o;
|
|
119
|
+
this.ss = a;
|
|
118
120
|
r && (this.es = r);
|
|
119
121
|
n && (this.rs = n);
|
|
120
|
-
this.
|
|
121
|
-
this
|
|
122
|
+
this.Yt = t.slice(0);
|
|
123
|
+
this.Jt = s;
|
|
122
124
|
} else {
|
|
123
|
-
let o,
|
|
125
|
+
let o, a, c, f, u, p, l, d, w,
|
|
126
|
+
// Dev (attribution engine installed): the items behind the exited and
|
|
127
|
+
// entered rows, for the list-identity census.
|
|
128
|
+
m, O;
|
|
124
129
|
// skip common prefix
|
|
125
|
-
for (o = 0,
|
|
130
|
+
for (o = 0, a = Math.min(this.Jt, s); o < a && (this.Yt[o] === t[o] || this.es && compare(this.Ht, this.Yt[o], t[o])); o++) {
|
|
126
131
|
if (this.es) setSignal(this.es[o], t[o]);
|
|
127
132
|
}
|
|
128
133
|
// skip common suffix — counted only; retained entries land in one pass
|
|
129
134
|
// at commit instead of being staged and copied twice
|
|
130
|
-
for (
|
|
131
|
-
|
|
135
|
+
for (a = this.Jt - 1, c = s - 1; a >= o && c >= o && (this.Yt[a] === t[c] || this.es && compare(this.Ht, this.Yt[a], t[c])); a--,
|
|
136
|
+
c--) ;
|
|
132
137
|
// no structural change (every position matched in place at equal
|
|
133
138
|
// length — the common post-reconcile shape): keep the same mapped
|
|
134
139
|
// array identity so downstream consumers don't re-run at all
|
|
135
|
-
if (o === s && this
|
|
136
|
-
this.
|
|
140
|
+
if (o === s && this.Jt === s) {
|
|
141
|
+
this.Yt = t.slice(0);
|
|
137
142
|
return;
|
|
138
143
|
}
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
144
|
+
const _ = s - this.Jt;
|
|
145
|
+
const y = new Array(s);
|
|
146
|
+
const A = new Array(s);
|
|
142
147
|
r = this.es ? new Array(s) : undefined;
|
|
143
148
|
n = this.rs ? new Array(s) : undefined;
|
|
144
149
|
// 0) prepare a map of all indices in the changed window of newItems,
|
|
145
150
|
// scanning backwards so we encounter them in natural order
|
|
146
151
|
p = new Map;
|
|
147
|
-
|
|
148
|
-
for (e =
|
|
152
|
+
l = new Array(c + 1);
|
|
153
|
+
for (e = c; e >= o; e--) {
|
|
149
154
|
f = t[e];
|
|
150
|
-
u = this.
|
|
155
|
+
u = this.Ht ? this.Ht(f) : f;
|
|
151
156
|
i = p.get(u);
|
|
152
|
-
|
|
157
|
+
l[e] = i === undefined ? -1 : i;
|
|
153
158
|
p.set(u, e);
|
|
154
159
|
}
|
|
155
160
|
// 1) step through the old changed window and see if items can be found
|
|
156
161
|
// in the new set; if so, stage them at their new positions; if not,
|
|
157
162
|
// queue them for disposal at commit
|
|
158
|
-
for (i = o; i <=
|
|
159
|
-
f = this.
|
|
160
|
-
u = this.
|
|
163
|
+
for (i = o; i <= a; i++) {
|
|
164
|
+
f = this.Yt[i];
|
|
165
|
+
u = this.Ht ? this.Ht(f) : f;
|
|
161
166
|
e = p.get(u);
|
|
162
167
|
if (e !== undefined && e !== -1) {
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
y[e] = this.ts[i];
|
|
169
|
+
A[e] = this.ss[i];
|
|
165
170
|
r && (r[e] = this.es[i]);
|
|
166
171
|
n && (n[e] = this.rs[i]);
|
|
167
|
-
e =
|
|
172
|
+
e = l[e];
|
|
168
173
|
p.set(u, e);
|
|
169
|
-
} else
|
|
174
|
+
} else {
|
|
175
|
+
(d ??= []).push(this.ss[i]);
|
|
176
|
+
if (false && attrHooks !== null) ;
|
|
177
|
+
}
|
|
170
178
|
}
|
|
171
179
|
// 2) create new rows into the temp arrays; an abort disposes only these
|
|
172
180
|
try {
|
|
173
|
-
for (e = o; e <=
|
|
174
|
-
if (
|
|
175
|
-
(
|
|
176
|
-
|
|
181
|
+
for (e = o; e <= c; e++) {
|
|
182
|
+
if (A[e] !== undefined) continue;
|
|
183
|
+
(w ??= []).push(A[e] = createOwner());
|
|
184
|
+
if (false && attrHooks !== null) ;
|
|
185
|
+
y[e] = runWithOwner(A[e], h);
|
|
177
186
|
}
|
|
178
187
|
} catch (t) {
|
|
179
|
-
if (
|
|
188
|
+
if (w) for (i = 0; i < w.length; i++) w[i].dispose();
|
|
180
189
|
throw t;
|
|
181
190
|
}
|
|
182
191
|
// 3) commit: land the retained prefix and suffix plus the staged window
|
|
183
192
|
// into the fresh arrays, swap them in (new identity for downstream
|
|
184
193
|
// change propagation), then dispose exited rows
|
|
185
194
|
for (i = 0; i < o; i++) {
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
y[i] = this.ts[i];
|
|
196
|
+
A[i] = this.ss[i];
|
|
188
197
|
r && (r[i] = this.es[i]);
|
|
189
198
|
n && (n[i] = this.rs[i]);
|
|
190
199
|
}
|
|
191
|
-
for (e = o; e <=
|
|
200
|
+
for (e = o; e <= c; e++) {
|
|
192
201
|
if (r) setSignal(r[e], t[e]);
|
|
193
202
|
if (n) setSignal(n[e], e);
|
|
194
203
|
}
|
|
195
|
-
for (e =
|
|
196
|
-
|
|
197
|
-
|
|
204
|
+
for (e = c + 1; e < s; e++) {
|
|
205
|
+
y[e] = this.ts[e - _];
|
|
206
|
+
A[e] = this.ss[e - _];
|
|
198
207
|
if (r) {
|
|
199
|
-
r[e] = this.es[e -
|
|
208
|
+
r[e] = this.es[e - _];
|
|
200
209
|
setSignal(r[e], t[e]);
|
|
201
210
|
}
|
|
202
211
|
if (n) {
|
|
203
|
-
n[e] = this.rs[e -
|
|
204
|
-
if (
|
|
212
|
+
n[e] = this.rs[e - _];
|
|
213
|
+
if (_ !== 0) setSignal(n[e], e);
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
|
-
this.
|
|
208
|
-
this.
|
|
216
|
+
this.ts = y;
|
|
217
|
+
this.ss = A;
|
|
209
218
|
r && (this.es = r);
|
|
210
219
|
n && (this.rs = n);
|
|
211
|
-
this
|
|
220
|
+
this.Jt = s;
|
|
212
221
|
// save a copy of the mapped items for the next update
|
|
213
|
-
this.
|
|
214
|
-
if (
|
|
222
|
+
this.Yt = t.slice(0);
|
|
223
|
+
if (d) for (i = 0; i < d.length; i++) d[i].dispose();
|
|
224
|
+
if (false && attrHooks !== null && m !== undefined && O !== undefined) ;
|
|
215
225
|
}
|
|
216
226
|
});
|
|
217
|
-
return this.
|
|
227
|
+
return this.ts;
|
|
218
228
|
}
|
|
219
229
|
|
|
220
230
|
/**
|
|
@@ -234,13 +244,13 @@ function updateKeyedMap() {
|
|
|
234
244
|
*/ function repeat(t, s, i) {
|
|
235
245
|
const e = s;
|
|
236
246
|
const r = {
|
|
237
|
-
|
|
238
|
-
|
|
247
|
+
$t: createOwner(),
|
|
248
|
+
Jt: 0,
|
|
239
249
|
cs: 0,
|
|
240
250
|
fs: t,
|
|
241
|
-
|
|
251
|
+
Zt: e,
|
|
252
|
+
ss: [],
|
|
242
253
|
ts: [],
|
|
243
|
-
Zt: [],
|
|
244
254
|
us: i?.from,
|
|
245
255
|
hs: i?.fallback
|
|
246
256
|
};
|
|
@@ -248,7 +258,7 @@ function updateKeyedMap() {
|
|
|
248
258
|
// Same as mapArray: untracked reads inside the internal owner resolve via
|
|
249
259
|
// _parentComputed, so async reads in row callbacks register with the node
|
|
250
260
|
// (pending tracking + post-settle retry) instead of vanishing.
|
|
251
|
-
r.
|
|
261
|
+
r.$t.Pt = n;
|
|
252
262
|
n.T &= ~CONFIG_AUTO_DISPOSE;
|
|
253
263
|
return accessor(n);
|
|
254
264
|
}
|
|
@@ -263,26 +273,26 @@ function updateKeyedMap() {
|
|
|
263
273
|
function updateRepeat() {
|
|
264
274
|
const t = this.fs();
|
|
265
275
|
const s = this.us?.() || 0;
|
|
266
|
-
runWithOwner(this
|
|
276
|
+
runWithOwner(this.$t, () => {
|
|
267
277
|
if (t === 0) {
|
|
268
|
-
if (this
|
|
269
|
-
this.
|
|
278
|
+
if (this.Jt !== 0) {
|
|
279
|
+
this.$t.dispose(false);
|
|
280
|
+
this.ss = [];
|
|
270
281
|
this.ts = [];
|
|
271
|
-
this.
|
|
272
|
-
this.$t = 0;
|
|
282
|
+
this.Jt = 0;
|
|
273
283
|
// Reset offset to match the cleared data (#2767, repro 2).
|
|
274
284
|
this.cs = 0;
|
|
275
285
|
}
|
|
276
|
-
if (this.hs && !this.
|
|
286
|
+
if (this.hs && !this.ts[0]) {
|
|
277
287
|
// an aborted fallback attempt leaves an owner without a mapping;
|
|
278
288
|
// dispose it before re-creating
|
|
279
|
-
this.
|
|
280
|
-
this.
|
|
289
|
+
this.ss[0]?.dispose();
|
|
290
|
+
this.ts[0] = runWithOwner(this.ss[0] = createOwner(), this.hs);
|
|
281
291
|
}
|
|
282
292
|
return;
|
|
283
293
|
}
|
|
284
294
|
const i = s + t;
|
|
285
|
-
const e = this.cs + this
|
|
295
|
+
const e = this.cs + this.Jt;
|
|
286
296
|
// Retained overlap [keepStart, keepEnd) in global indexes; empty when the
|
|
287
297
|
// windows are disjoint or when coming from empty/fallback.
|
|
288
298
|
const r = Math.max(s, this.cs);
|
|
@@ -290,26 +300,26 @@ function updateRepeat() {
|
|
|
290
300
|
const h = new Array(t);
|
|
291
301
|
const o = new Array(t);
|
|
292
302
|
for (let t = r; t < n; t++) {
|
|
293
|
-
o[t - s] = this.
|
|
294
|
-
h[t - s] = this.
|
|
303
|
+
o[t - s] = this.ss[t - this.cs];
|
|
304
|
+
h[t - s] = this.ts[t - this.cs];
|
|
295
305
|
}
|
|
296
306
|
try {
|
|
297
307
|
for (let t = s; t < i; t++) {
|
|
298
308
|
if (t >= r && t < n) continue;
|
|
299
|
-
h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.
|
|
309
|
+
h[t - s] = runWithOwner(o[t - s] = createOwner(), () => this.Zt(t));
|
|
300
310
|
}
|
|
301
311
|
} catch (t) {
|
|
302
312
|
for (let t = s; t < i; t++) if ((t < r || t >= n) && o[t - s]) o[t - s].dispose();
|
|
303
313
|
throw t;
|
|
304
314
|
}
|
|
305
315
|
// commit: dispose the previous fallback or the rows leaving the window
|
|
306
|
-
if (this
|
|
307
|
-
this.
|
|
308
|
-
this.
|
|
316
|
+
if (this.Jt === 0) this.ss[0]?.dispose(); else for (let t = this.cs; t < e; t++) if (t < s || t >= i) this.ss[t - this.cs].dispose();
|
|
317
|
+
this.ts = h;
|
|
318
|
+
this.ss = o;
|
|
309
319
|
this.cs = s;
|
|
310
|
-
this
|
|
320
|
+
this.Jt = t;
|
|
311
321
|
});
|
|
312
|
-
return this.
|
|
322
|
+
return this.ts;
|
|
313
323
|
}
|
|
314
324
|
|
|
315
325
|
function compare(t, s, i) {
|