@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.4
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 +1454 -118
- package/dist/node.cjs +2709 -1396
- package/dist/prod/affects.js +13 -12
- package/dist/prod/boundaries.js +39 -34
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +48 -46
- package/dist/prod/core/core.js +99 -67
- package/dist/prod/core/effect.js +25 -28
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +85 -49
- package/dist/prod/core/heap.js +10 -10
- package/dist/prod/core/lanes.js +19 -19
- package/dist/prod/core/optimistic.js +66 -41
- package/dist/prod/core/owner.js +13 -13
- package/dist/prod/core/scheduler.js +131 -85
- package/dist/prod/core/verdict.js +36 -15
- package/dist/prod/index.js +4 -0
- package/dist/prod/map.js +101 -101
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/index.js +2 -0
- package/dist/prod/store/next/optimistic.js +65 -11
- package/dist/prod/store/next/patch-hooks.js +13 -0
- package/dist/prod/store/next/patch.js +614 -0
- package/dist/prod/store/next/projection.js +107 -45
- package/dist/prod/store/next/reconcile.js +307 -120
- package/dist/prod/store/next/store.js +321 -92
- package/dist/prod/store/next/target.js +13 -4
- package/dist/prod/store/store.js +5 -5
- package/dist/types/core/core.d.ts +15 -1
- package/dist/types/core/dev.d.ts +8 -0
- package/dist/types/core/graph.d.ts +22 -0
- package/dist/types/core/invariants.d.ts +1 -1
- package/dist/types/core/scheduler.d.ts +12 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/patch-hooks.d.ts +41 -0
- package/dist/types/store/next/patch.d.ts +91 -0
- package/dist/types/store/next/reconcile.d.ts +14 -0
- package/dist/types/store/next/store.d.ts +30 -2
- package/dist/types/store/next/target.d.ts +58 -8
- package/dist/types-cjs/core/core.d.cts +15 -1
- package/dist/types-cjs/core/dev.d.cts +8 -0
- package/dist/types-cjs/core/graph.d.cts +22 -0
- package/dist/types-cjs/core/invariants.d.cts +1 -1
- package/dist/types-cjs/core/scheduler.d.cts +12 -0
- package/dist/types-cjs/store/index.d.cts +2 -0
- package/dist/types-cjs/store/next/patch-hooks.d.cts +41 -0
- package/dist/types-cjs/store/next/patch.d.cts +91 -0
- package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/next/store.d.cts +30 -2
- package/dist/types-cjs/store/next/target.d.cts +58 -8
- package/package.json +14 -14
- package/dist/types/store/optimistic.d.ts +0 -45
- package/dist/types/store/projection.d.ts +0 -70
- package/dist/types/store/reconcile.d.ts +0 -46
- package/dist/types-cjs/store/optimistic.d.cts +0 -45
- package/dist/types-cjs/store/projection.d.cts +0 -70
- package/dist/types-cjs/store/reconcile.d.cts +0 -46
|
@@ -6,6 +6,8 @@ import { DEV } from "./dev.js";
|
|
|
6
6
|
|
|
7
7
|
import { NotReadyError } from "./error.js";
|
|
8
8
|
|
|
9
|
+
import { sweepDormant } from "./graph.js";
|
|
10
|
+
|
|
9
11
|
import { runHeap, deleteFromHeap, enqueueSub } from "./heap.js";
|
|
10
12
|
|
|
11
13
|
import { activeLanes, assignOrMergeLane, findLane } from "./lanes.js";
|
|
@@ -60,8 +62,8 @@ let projectionWriteActive = false;
|
|
|
60
62
|
const transientStoreNodes = new Set;
|
|
61
63
|
|
|
62
64
|
function canUseSimpleSyncFlush(e) {
|
|
63
|
-
const t = e.
|
|
64
|
-
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Ke.length === 0 && t.
|
|
65
|
+
const t = e.m;
|
|
66
|
+
return transitions.size === 0 && activeLanes.size === 0 && e.Qt.length === 0 && t.Ke.length === 0 && t.A.length === 0 && t.Tn.size === 0 && transientStoreNodes.size === 0;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
function sweepTransientStoreNodes() {
|
|
@@ -109,21 +111,21 @@ function setProjectionWriteActive(e) {
|
|
|
109
111
|
yt: [],
|
|
110
112
|
Ne: new Map,
|
|
111
113
|
Ke: [],
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
A: [],
|
|
115
|
+
Tn: new Set,
|
|
116
|
+
ue: [],
|
|
115
117
|
bt: {
|
|
116
118
|
Lt: [ [], [] ],
|
|
117
119
|
Qt: []
|
|
118
120
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
fn: false,
|
|
122
|
+
cn: new Set
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
function mergeTransitionState(e, t) {
|
|
125
|
-
t.
|
|
126
|
-
e.
|
|
127
|
+
t.fn = e;
|
|
128
|
+
e.ue.push(...t.ue);
|
|
127
129
|
for (const i of activeLanes) if (i._e === t) i._e = e;
|
|
128
130
|
if (t.Ke.length) {
|
|
129
131
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
@@ -132,20 +134,40 @@ function mergeTransitionState(e, t) {
|
|
|
132
134
|
e.Ke.push(...t.Ke);
|
|
133
135
|
t.Ke.length = 0;
|
|
134
136
|
}
|
|
135
|
-
if (t.
|
|
137
|
+
if (t.A.length) {
|
|
136
138
|
// Move (don't copy): the global queue's batch may still be the outgoing
|
|
137
139
|
// transition, and the adoption pass in initTransition would re-push its
|
|
138
140
|
// contents into the target — double-releasing every mark.
|
|
139
|
-
e.
|
|
140
|
-
t.
|
|
141
|
+
e.A.push(...t.A);
|
|
142
|
+
t.A.length = 0;
|
|
143
|
+
}
|
|
144
|
+
for (const i of t.Tn) e.Tn.add(i);
|
|
145
|
+
// Patch-channel stash (store/next/patch.ts): entries held for the outgoing
|
|
146
|
+
// transition must ride the merge like every other per-transition
|
|
147
|
+
// collection — releaseBatch only reads the COMMITTING transition's stash,
|
|
148
|
+
// so a stranded sidecar would silently drop its patches. Move (don't
|
|
149
|
+
// copy), same aliasing rule as the collections above. The field is an
|
|
150
|
+
// expando so this module stays free of patch imports (pay-for-use).
|
|
151
|
+
const i = t.Mt;
|
|
152
|
+
if (i !== undefined) {
|
|
153
|
+
t.Mt = undefined;
|
|
154
|
+
let n = e.Mt;
|
|
155
|
+
if (n !== undefined) n.push(...i); else n = e.Mt = i;
|
|
156
|
+
// Retarget the entries' coalescing stamps to the surviving stash
|
|
157
|
+
// (opaque backref contract with store/next/patch.ts): without this a
|
|
158
|
+
// post-merge emission misses the stamp and pushes a SECOND entry —
|
|
159
|
+
// the record's patch applies twice at commit (re-audit 5, P1-2).
|
|
160
|
+
for (let e = 0; e < i.length; e++) {
|
|
161
|
+
const t = i[e].pc;
|
|
162
|
+
if (t !== undefined && t.qe === i[e]) t.qa = n;
|
|
163
|
+
}
|
|
141
164
|
}
|
|
142
|
-
for (const i of t.cn) e.cn.add(i);
|
|
143
165
|
for (const [i, n] of t.Ne) {
|
|
144
166
|
let t = e.Ne.get(i);
|
|
145
167
|
if (!t) e.Ne.set(i, t = new Set);
|
|
146
168
|
for (const e of n) t.add(e);
|
|
147
169
|
}
|
|
148
|
-
for (const i of t.
|
|
170
|
+
for (const i of t.cn) e.cn.add(i);
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
function schedule() {
|
|
@@ -155,7 +177,7 @@ function schedule() {
|
|
|
155
177
|
}
|
|
156
178
|
if (scheduled) return;
|
|
157
179
|
scheduled = true;
|
|
158
|
-
if (!syncDepth && !globalQueue.
|
|
180
|
+
if (!syncDepth && !globalQueue.En && !projectionWriteActive) queueMicrotask(flush);
|
|
159
181
|
}
|
|
160
182
|
|
|
161
183
|
/**
|
|
@@ -189,24 +211,24 @@ function notifyHalted() {
|
|
|
189
211
|
let queueRunToken = 0;
|
|
190
212
|
|
|
191
213
|
class Queue {
|
|
192
|
-
|
|
214
|
+
ke=null;
|
|
193
215
|
Lt=[ [], [] ];
|
|
194
216
|
Qt=[];
|
|
195
|
-
|
|
217
|
+
Vt=0;
|
|
196
218
|
created=clock;
|
|
197
219
|
addChild(e) {
|
|
198
220
|
this.Qt.push(e);
|
|
199
|
-
e.
|
|
221
|
+
e.ke = this;
|
|
200
222
|
}
|
|
201
223
|
removeChild(e) {
|
|
202
224
|
const t = this.Qt.indexOf(e);
|
|
203
225
|
if (t >= 0) {
|
|
204
226
|
this.Qt.splice(t, 1);
|
|
205
|
-
e.
|
|
227
|
+
e.ke = null;
|
|
206
228
|
}
|
|
207
229
|
}
|
|
208
230
|
notify(e, t, i, n) {
|
|
209
|
-
if (this.
|
|
231
|
+
if (this.ke) return this.ke.notify(e, t, i, n);
|
|
210
232
|
return false;
|
|
211
233
|
}
|
|
212
234
|
run(e) {
|
|
@@ -226,8 +248,8 @@ class Queue {
|
|
|
226
248
|
const i = ++queueRunToken;
|
|
227
249
|
for (let n = 0; n < t.length; ) {
|
|
228
250
|
const s = t[n];
|
|
229
|
-
if (s.
|
|
230
|
-
s.
|
|
251
|
+
if (s.Vt !== i) {
|
|
252
|
+
s.Vt = i;
|
|
231
253
|
s.run?.(e);
|
|
232
254
|
if (t[n] !== s) {
|
|
233
255
|
n = 0;
|
|
@@ -242,7 +264,7 @@ class Queue {
|
|
|
242
264
|
// Route to lane's effect queue if we're in an optimistic recomputation
|
|
243
265
|
if (currentOptimisticLane) {
|
|
244
266
|
const i = findLane(currentOptimisticLane);
|
|
245
|
-
i.
|
|
267
|
+
i.rn[e - 1].push(t);
|
|
246
268
|
} else {
|
|
247
269
|
this.Lt[e - 1].push(t);
|
|
248
270
|
}
|
|
@@ -278,25 +300,25 @@ class Queue {
|
|
|
278
300
|
}
|
|
279
301
|
|
|
280
302
|
class GlobalQueue extends Queue {
|
|
281
|
-
|
|
303
|
+
En=false;
|
|
282
304
|
// The current transaction-shaped batch: a plain ambient batch while no
|
|
283
305
|
// transition is active, the active transition itself after initTransition.
|
|
284
|
-
|
|
306
|
+
m=createBatch();
|
|
285
307
|
static Ce;
|
|
286
308
|
static Fe;
|
|
287
309
|
static tt;
|
|
288
|
-
static
|
|
310
|
+
static Bt=null;
|
|
289
311
|
// Store-side hook: drops a keyless affects() mark's identity scope when the
|
|
290
312
|
// carrier node's last registration releases (wired by store.ts, mirroring
|
|
291
313
|
// _clearOptimisticStore).
|
|
292
|
-
static
|
|
314
|
+
static p=null;
|
|
293
315
|
// affects()-side hooks (wired by affects.ts, mirroring _update): the mark
|
|
294
316
|
// engine — count/register/release — lives with the feature. Every call site
|
|
295
317
|
// is gated by state only that module creates, so `!` invocations are safe
|
|
296
318
|
// once the gate holds.
|
|
319
|
+
static G=null;
|
|
297
320
|
static M=null;
|
|
298
|
-
static
|
|
299
|
-
static j=null;
|
|
321
|
+
static N=null;
|
|
300
322
|
// External-source bridge (wired by enableExternalSource(); null while no
|
|
301
323
|
// config is active — including after _resetExternalSourceConfig()).
|
|
302
324
|
static Rt=null;
|
|
@@ -315,11 +337,11 @@ class GlobalQueue extends Queue {
|
|
|
315
337
|
static Dt=null;
|
|
316
338
|
static Ht=null;
|
|
317
339
|
static Je=null;
|
|
318
|
-
static
|
|
319
|
-
static
|
|
340
|
+
static k=null;
|
|
341
|
+
static wt=null;
|
|
320
342
|
// Re-asks probes whose verdict was provisionally suppressed by a fresh read
|
|
321
343
|
// of a held value, once the transaction gains an async blocker (#3028).
|
|
322
|
-
static
|
|
344
|
+
static jt=null;
|
|
323
345
|
// Optimistic-engine hooks (wired by core/optimistic.ts via
|
|
324
346
|
// installOptimisticEngine(), called from verdict.ts / createOptimistic /
|
|
325
347
|
// createOptimisticStore — every module that can create optimistic state).
|
|
@@ -327,39 +349,51 @@ class GlobalQueue extends Queue {
|
|
|
327
349
|
// `_overrideValue` slot, a lane in `activeLanes`, an `_optimisticNodes`
|
|
328
350
|
// entry, or a non-null `currentOptimisticLane`, so `!` invocations are safe
|
|
329
351
|
// once the gate holds.
|
|
330
|
-
static
|
|
331
|
-
static fn=null;
|
|
352
|
+
static kt=null;
|
|
332
353
|
static dn=null;
|
|
333
|
-
static
|
|
334
|
-
static
|
|
354
|
+
static In=null;
|
|
355
|
+
static Nn=null;
|
|
356
|
+
static _n=null;
|
|
357
|
+
/** Patch-channel optimistic drain (next/patch.ts): optimistic emissions
|
|
358
|
+
* apply at lane-effect timing — visible in flight, unlike the regular
|
|
359
|
+
* effect queues an action stashes. Injected; null when unused. */
|
|
360
|
+
static ln=null;
|
|
335
361
|
static Ft=null;
|
|
336
362
|
static ht=null;
|
|
337
363
|
static gt=null;
|
|
338
364
|
static je=null;
|
|
339
365
|
static $e=null;
|
|
340
366
|
static ze=null;
|
|
341
|
-
static
|
|
367
|
+
static An=null;
|
|
342
368
|
flush() {
|
|
343
|
-
if (this.
|
|
369
|
+
if (this.En) return;
|
|
344
370
|
// Fast drain: nothing in flight but plain pending commits — no dirty
|
|
345
371
|
// computeds, no queued effects, no child queues, no transitions/lanes/
|
|
346
372
|
// optimistic state. Commit and go; anything a commit hook schedules
|
|
347
373
|
// (companion snaps, store folds notifying subs) re-arms `scheduled`
|
|
348
374
|
// below and the outer drain loop takes the full spine next round.
|
|
349
375
|
if (activeTransition === null && dirtyQueue.EE < dirtyQueue.xe && this.Lt[0].length === 0 && this.Lt[1].length === 0 && this.Qt.length === 0 && canUseSimpleSyncFlush(this)) {
|
|
350
|
-
this.
|
|
376
|
+
this.En = true;
|
|
351
377
|
try {
|
|
378
|
+
// Sweep first: unobserved() pulls swept nodes out of the dirty heap,
|
|
379
|
+
// so a dormant memo dirtied in the same tick is reclaimed instead of
|
|
380
|
+
// recomputed (matching the old inline dispose-on-read counts).
|
|
381
|
+
sweepDormant();
|
|
352
382
|
commitPendingNodes();
|
|
353
383
|
} finally {
|
|
354
|
-
this.
|
|
384
|
+
this.En = false;
|
|
355
385
|
}
|
|
356
386
|
clock++;
|
|
357
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.Lt[0].length !== 0 || this.Lt[1].length !== 0 || this.
|
|
387
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.Lt[0].length !== 0 || this.Lt[1].length !== 0 || this.m.yt.length !== 0;
|
|
358
388
|
return;
|
|
359
389
|
}
|
|
360
|
-
this.
|
|
390
|
+
this.En = true;
|
|
361
391
|
try {
|
|
362
392
|
if (false) ;
|
|
393
|
+
// Before runHeap for the same reason as the fast drain above; late
|
|
394
|
+
// subscribers (an effect reading a swept memo this flush) revive it,
|
|
395
|
+
// which is the pay-for-use contract.
|
|
396
|
+
sweepDormant();
|
|
363
397
|
runHeap(dirtyQueue, GlobalQueue.Ce);
|
|
364
398
|
if (activeTransition) {
|
|
365
399
|
const e = transitionComplete(activeTransition);
|
|
@@ -373,32 +407,32 @@ class GlobalQueue extends Queue {
|
|
|
373
407
|
// zombies here. Height-adjust entries still process normally: a
|
|
374
408
|
// dirtied one keeps its height flag and falls through to runHeap's
|
|
375
409
|
// adjustHeight path on the next pass of the bucket.
|
|
376
|
-
runHeap(zombieQueue, this.
|
|
410
|
+
runHeap(zombieQueue, this.m === e ? cancelZombieRecompute : GlobalQueue.Ce);
|
|
377
411
|
// Detach: the stashed transition keeps its batch; ambient work that
|
|
378
412
|
// follows lands in a fresh one. If the batch is already a separate
|
|
379
413
|
// ambient one — action done() restored activeTransition without
|
|
380
414
|
// adopting the batch, and an ordinary write landed there before
|
|
381
415
|
// the scheduled flush (#2916) — keep it: replacing it would strand
|
|
382
416
|
// its queued pending nodes with held _pendingValues forever.
|
|
383
|
-
if (this.
|
|
417
|
+
if (this.m === e) currentBatch = this.m = createBatch();
|
|
384
418
|
// Run lane effects immediately (before stashing) - lanes with no pending async
|
|
385
419
|
if (activeLanes.size) {
|
|
386
|
-
GlobalQueue.
|
|
387
|
-
GlobalQueue.
|
|
420
|
+
GlobalQueue._n(EFFECT_RENDER);
|
|
421
|
+
GlobalQueue._n(EFFECT_USER);
|
|
388
422
|
}
|
|
389
423
|
this.stashQueues(e.bt);
|
|
390
424
|
clock++;
|
|
391
425
|
// A kept ambient batch may hold pending nodes (#2916): stay
|
|
392
426
|
// scheduled so the outer drain loop commits them via the plain
|
|
393
427
|
// flush path instead of leaving them until the next natural flush.
|
|
394
|
-
scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.
|
|
428
|
+
scheduled = dirtyQueue.EE >= dirtyQueue.xe || this.m.yt.length > 0;
|
|
395
429
|
reassignPendingTransition(e.yt);
|
|
396
430
|
activeTransition = null;
|
|
397
431
|
finalizePureQueue(null, true);
|
|
398
432
|
return;
|
|
399
433
|
}
|
|
400
434
|
const t = activeTransition;
|
|
401
|
-
const i = this.
|
|
435
|
+
const i = this.m;
|
|
402
436
|
i !== t && i.yt.push(...t.yt);
|
|
403
437
|
this.restoreQueues(t.bt);
|
|
404
438
|
transitions.delete(t);
|
|
@@ -412,9 +446,9 @@ class GlobalQueue extends Queue {
|
|
|
412
446
|
const e = createBatch();
|
|
413
447
|
e.yt = i.yt;
|
|
414
448
|
e.Ke = i.Ke;
|
|
415
|
-
e.
|
|
416
|
-
e.
|
|
417
|
-
currentBatch = this.
|
|
449
|
+
e.A = i.A;
|
|
450
|
+
e.Tn = i.Tn;
|
|
451
|
+
currentBatch = this.m = e;
|
|
418
452
|
}
|
|
419
453
|
} else {
|
|
420
454
|
if (canUseSimpleSyncFlush(this)) {
|
|
@@ -432,15 +466,15 @@ class GlobalQueue extends Queue {
|
|
|
432
466
|
// Check if finalization added items to the heap (from optimistic reversion)
|
|
433
467
|
scheduled = dirtyQueue.EE >= dirtyQueue.xe;
|
|
434
468
|
// Run lane effects first (for ready lanes), then regular effects
|
|
435
|
-
activeLanes.size && GlobalQueue.
|
|
469
|
+
activeLanes.size && GlobalQueue._n(EFFECT_RENDER);
|
|
436
470
|
this.run(EFFECT_RENDER);
|
|
437
|
-
activeLanes.size && GlobalQueue.
|
|
471
|
+
activeLanes.size && GlobalQueue._n(EFFECT_USER);
|
|
438
472
|
this.run(EFFECT_USER);
|
|
439
473
|
if (false) ;
|
|
440
474
|
if (false && !scheduled && !activeTransition && transitions.size === 0 && activeLanes.size === 0) ;
|
|
441
475
|
if (false) ;
|
|
442
476
|
} finally {
|
|
443
|
-
this.
|
|
477
|
+
this.En = false;
|
|
444
478
|
}
|
|
445
479
|
}
|
|
446
480
|
notify(e, t, i, n) {
|
|
@@ -452,7 +486,7 @@ class GlobalQueue extends Queue {
|
|
|
452
486
|
// channel) updates display state on its way up but must be invisible
|
|
453
487
|
// to completion accounting BY CONSTRUCTION: it never registers a
|
|
454
488
|
// reporter and never counts toward the loading-boundary diagnostic.
|
|
455
|
-
if (t?.
|
|
489
|
+
if (t?.l) return true;
|
|
456
490
|
if (activeTransition && t) {
|
|
457
491
|
const i = t.source;
|
|
458
492
|
let n = activeTransition.Ne.get(i);
|
|
@@ -461,7 +495,7 @@ class GlobalQueue extends Queue {
|
|
|
461
495
|
n.add(e);
|
|
462
496
|
if (n.size !== s) {
|
|
463
497
|
schedule();
|
|
464
|
-
GlobalQueue.
|
|
498
|
+
GlobalQueue.jt?.(activeTransition);
|
|
465
499
|
}
|
|
466
500
|
}
|
|
467
501
|
}
|
|
@@ -483,7 +517,7 @@ class GlobalQueue extends Queue {
|
|
|
483
517
|
}
|
|
484
518
|
transitions.add(activeTransition);
|
|
485
519
|
activeTransition.Te = clock;
|
|
486
|
-
const t = this.
|
|
520
|
+
const t = this.m;
|
|
487
521
|
if (t !== activeTransition) {
|
|
488
522
|
// Adopt the ambient batch into the transaction, then make the
|
|
489
523
|
// transaction the batch so later registrations land there directly.
|
|
@@ -502,15 +536,15 @@ class GlobalQueue extends Queue {
|
|
|
502
536
|
i._e = activeTransition;
|
|
503
537
|
activeTransition.Ke.push(i);
|
|
504
538
|
}
|
|
505
|
-
if (t.
|
|
506
|
-
for (const e of t.
|
|
539
|
+
if (t.A.length) activeTransition.A.push(...t.A);
|
|
540
|
+
for (const e of t.Tn) activeTransition.Tn.add(e);
|
|
507
541
|
// Gated readers recorded against the ambient batch move with it: their
|
|
508
542
|
// replay-at-commit now happens at the transaction's completion.
|
|
509
|
-
if (t.
|
|
510
|
-
for (const e of t.
|
|
511
|
-
t.
|
|
543
|
+
if (t.cn.size) {
|
|
544
|
+
for (const e of t.cn) activeTransition.cn.add(e);
|
|
545
|
+
t.cn.clear();
|
|
512
546
|
}
|
|
513
|
-
currentBatch = this.
|
|
547
|
+
currentBatch = this.m = activeTransition;
|
|
514
548
|
}
|
|
515
549
|
for (const e of activeLanes) {
|
|
516
550
|
if (!e._e) e._e = activeTransition;
|
|
@@ -554,8 +588,8 @@ function insertSubs(e, t = false) {
|
|
|
554
588
|
const n = (i & CONFIG_HAS_LANE ? e.o?.Be : undefined) || currentOptimisticLane;
|
|
555
589
|
const s = (i & CONFIG_HAS_SNAPSHOT) !== 0 && e.o?.Qe !== undefined;
|
|
556
590
|
const o = reaskArmed;
|
|
557
|
-
for (let i = e.u; i !== null; i = i.
|
|
558
|
-
const e = i.
|
|
591
|
+
for (let i = e.u; i !== null; i = i.ae) {
|
|
592
|
+
const e = i.ce;
|
|
559
593
|
// A value-change notification is a new question for the subscriber: any
|
|
560
594
|
// pending re-ask mark (refresh) it carried is superseded.
|
|
561
595
|
if (o) e.ie &= ~REACTIVE_REASK;
|
|
@@ -567,7 +601,7 @@ function insertSubs(e, t = false) {
|
|
|
567
601
|
// pass either re-reads them fresh or trims them), and neither does the
|
|
568
602
|
// tail link: it is the read IN FLIGHT — read() links before it pulls, so
|
|
569
603
|
// this very commit is what that read returns.
|
|
570
|
-
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.
|
|
604
|
+
if (e.ie & REACTIVE_RECOMPUTING_DEPS && i.nn === e.Ze && i !== e.Ye) e.ie |= REACTIVE_MISSED_WAKE;
|
|
571
605
|
if (s && e.T & CONFIG_IN_SNAPSHOT_SCOPE) {
|
|
572
606
|
e.ie |= REACTIVE_SNAPSHOT_STALE;
|
|
573
607
|
continue;
|
|
@@ -586,7 +620,7 @@ function insertSubs(e, t = false) {
|
|
|
586
620
|
|
|
587
621
|
function commitPendingNode(e) {
|
|
588
622
|
const t = e;
|
|
589
|
-
if (!t.
|
|
623
|
+
if (!t.oe) {
|
|
590
624
|
if (e.Pe !== NOT_PENDING) {
|
|
591
625
|
e.be = e.Pe;
|
|
592
626
|
e.Pe = NOT_PENDING;
|
|
@@ -621,6 +655,17 @@ function setStoreCommitHook(e) {
|
|
|
621
655
|
storeCommitHook = e;
|
|
622
656
|
}
|
|
623
657
|
|
|
658
|
+
/** Patch-channel release hook (next/patch.ts): transition-stamped patch
|
|
659
|
+
* emissions are released when THEIR batch commits. Transitions never
|
|
660
|
+
* abort: failed actions still commit (only optimistic overrides revert),
|
|
661
|
+
* and merged-away transitions hand their stash to the survivor
|
|
662
|
+
* (mergeTransitionState) — every stash drains exactly once. Injected like
|
|
663
|
+
* storeCommitHook to stay tree-shakeable. */ let patchCommitHook = null;
|
|
664
|
+
|
|
665
|
+
function setPatchCommitHook(e) {
|
|
666
|
+
patchCommitHook = e;
|
|
667
|
+
}
|
|
668
|
+
|
|
624
669
|
function commitPendingNodes() {
|
|
625
670
|
const e = currentBatch.yt;
|
|
626
671
|
for (let t = 0; t < e.length; t++) {
|
|
@@ -628,6 +673,7 @@ function commitPendingNodes() {
|
|
|
628
673
|
}
|
|
629
674
|
e.length = 0;
|
|
630
675
|
storeCommitHook?.();
|
|
676
|
+
patchCommitHook?.(currentBatch);
|
|
631
677
|
}
|
|
632
678
|
|
|
633
679
|
function finalizePureQueue(e = null, t = false) {
|
|
@@ -641,20 +687,20 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
641
687
|
if (i) {
|
|
642
688
|
if (n) commitPendingNodes();
|
|
643
689
|
// The settling batch: the completing transaction's, or the ambient one.
|
|
644
|
-
const t = e ?? globalQueue.
|
|
690
|
+
const t = e ?? globalQueue.m;
|
|
645
691
|
// Optimistic reversion: a non-empty batch means _optimisticWrite ran,
|
|
646
692
|
// which installed the engine's hooks.
|
|
647
|
-
if (t.Ke.length) GlobalQueue.
|
|
693
|
+
if (t.Ke.length) GlobalQueue.dn(t.Ke);
|
|
648
694
|
// Replay entanglement: subs recorded by the read-time gate get rescheduled
|
|
649
695
|
// so they re-run with the now-committed values visible. The ambient batch
|
|
650
696
|
// replays too — laneReadsCommitted records readers whose committed-view
|
|
651
697
|
// read hid a same-tick plain write that just committed above (#2963).
|
|
652
|
-
if (t.
|
|
653
|
-
for (const e of t.
|
|
698
|
+
if (t.cn.size) {
|
|
699
|
+
for (const e of t.cn) {
|
|
654
700
|
if (e.ie & REACTIVE_DISPOSED) continue;
|
|
655
701
|
enqueueSub(e);
|
|
656
702
|
}
|
|
657
|
-
t.
|
|
703
|
+
t.cn.clear();
|
|
658
704
|
// A completing transition keeps the outer flush loop alive by itself;
|
|
659
705
|
// the ambient batch needs the re-arm or the replay sits in the heap
|
|
660
706
|
// until the next unrelated write.
|
|
@@ -666,18 +712,18 @@ function finalizePureQueue(e = null, t = false) {
|
|
|
666
712
|
// held boundary display state through the visual channel, and their
|
|
667
713
|
// release is the display-state update point — re-run the boundary sweep
|
|
668
714
|
// (the earlier sweep above ran while the marks were still live).
|
|
669
|
-
if (t.
|
|
670
|
-
GlobalQueue.
|
|
715
|
+
if (t.A.length) {
|
|
716
|
+
GlobalQueue.G(t.A);
|
|
671
717
|
if (globalQueue.Qt.length) checkBoundaryChildren(globalQueue);
|
|
672
718
|
}
|
|
673
719
|
// A non-empty set means trackOptimisticStore ran, which installed the
|
|
674
720
|
// hook; the hook iterates, clears, and schedules (keeping the loop out of
|
|
675
721
|
// core lets esbuild shake it — rollup already folds the null guard). The
|
|
676
722
|
// completing transition scopes the clear to its own layer keys (#2899).
|
|
677
|
-
if (t.
|
|
723
|
+
if (t.Tn.size) GlobalQueue.Bt(t.Tn, e);
|
|
678
724
|
sweepTransientStoreNodes();
|
|
679
725
|
// Lanes only enter activeLanes through the engine's getOrCreateLane.
|
|
680
|
-
if (activeLanes.size) GlobalQueue.
|
|
726
|
+
if (activeLanes.size) GlobalQueue.Nn(e);
|
|
681
727
|
}
|
|
682
728
|
}
|
|
683
729
|
|
|
@@ -717,7 +763,7 @@ const globalQueue = new GlobalQueue;
|
|
|
717
763
|
// property hop through `_batch` was a measured instruction-count regression
|
|
718
764
|
// (CodSpeed update1to1, PR #2905). The field stays authoritative for
|
|
719
765
|
// cross-module readers; every `_batch` assignment updates both.
|
|
720
|
-
let currentBatch = globalQueue.
|
|
766
|
+
let currentBatch = globalQueue.m;
|
|
721
767
|
|
|
722
768
|
function flush(e) {
|
|
723
769
|
if (e) {
|
|
@@ -734,7 +780,7 @@ function flush(e) {
|
|
|
734
780
|
}
|
|
735
781
|
}
|
|
736
782
|
}
|
|
737
|
-
if (globalQueue.
|
|
783
|
+
if (globalQueue.En) {
|
|
738
784
|
return;
|
|
739
785
|
}
|
|
740
786
|
if (halted) return;
|
|
@@ -756,15 +802,15 @@ function reporterBlocksSource(e, t) {
|
|
|
756
802
|
let e = i.ut;
|
|
757
803
|
while (e) {
|
|
758
804
|
if (e === t || e.lt === t) return true;
|
|
759
|
-
e = e.o?.
|
|
805
|
+
e = e.o?.Et;
|
|
760
806
|
}
|
|
761
807
|
}
|
|
762
808
|
return !!(e.S & STATUS_PENDING && e.o?._ instanceof NotReadyError && e.o?._.source === t);
|
|
763
809
|
}
|
|
764
810
|
|
|
765
811
|
function transitionComplete(e) {
|
|
766
|
-
if (e.
|
|
767
|
-
if (e.
|
|
812
|
+
if (e.fn) return true;
|
|
813
|
+
if (e.ue.length) return false;
|
|
768
814
|
let t = true;
|
|
769
815
|
for (const [i, n] of e.Ne) {
|
|
770
816
|
let s = false;
|
|
@@ -783,13 +829,13 @@ function transitionComplete(e) {
|
|
|
783
829
|
// Override blockage lives with the engine (absent hook = "no optimistic
|
|
784
830
|
// blockage"); the hook's loops over _optimisticNodes/_optimisticStores are
|
|
785
831
|
// no-ops when the transition holds neither, so no pre-check is needed.
|
|
786
|
-
if (t && GlobalQueue.
|
|
787
|
-
t && (e.
|
|
832
|
+
if (t && GlobalQueue.In?.(e)) t = false;
|
|
833
|
+
t && (e.fn = true);
|
|
788
834
|
return t;
|
|
789
835
|
}
|
|
790
836
|
|
|
791
837
|
function currentTransition(e) {
|
|
792
|
-
while (e.
|
|
838
|
+
while (e.fn && typeof e.fn === "object") e = e.fn;
|
|
793
839
|
return e;
|
|
794
840
|
}
|
|
795
841
|
|
|
@@ -803,4 +849,4 @@ function runInTransition(e, t) {
|
|
|
803
849
|
}
|
|
804
850
|
}
|
|
805
851
|
|
|
806
|
-
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, insertSubs, notifyEpoch, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runInTransition, schedule, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
|
|
852
|
+
export { GlobalQueue, Queue, activeAffectsMarks, activeLanes, activeTransition, armReaskClear, assignOrMergeLane, bumpNotifyEpoch, clock, currentTransition, dirtyQueue, enforceLoadingBoundary, finalizePureQueue, findLane, flush, globalQueue, haltReactivity, insertSubs, notifyEpoch, patchCommitHook, projectionWriteActive, queuePendingNode, reaskArmed, resetErrorHalt, runInTransition, schedule, setPatchCommitHook, setProjectionWriteActive, setStoreCommitHook, shiftAffectsMarks, storeCommitHook, zombieQueue };
|
|
@@ -50,7 +50,7 @@ let pendingProbe = null;
|
|
|
50
50
|
const t = e.lt;
|
|
51
51
|
if (!t) return;
|
|
52
52
|
t.T |= CONFIG_CHILD_COMPANIONS;
|
|
53
|
-
(ext(t).
|
|
53
|
+
(ext(t).It ??= new Set).add(e);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function getPendingSignal(e) {
|
|
@@ -63,7 +63,7 @@ function getPendingSignal(e) {
|
|
|
63
63
|
ext(e).Ge = t;
|
|
64
64
|
e.T |= CONFIG_HAS_COMPANIONS;
|
|
65
65
|
markFirewallChildCompanions(e);
|
|
66
|
-
ext(t).
|
|
66
|
+
ext(t).Et = e;
|
|
67
67
|
if (computePendingState(e)) setSignal(t, true);
|
|
68
68
|
}
|
|
69
69
|
return t;
|
|
@@ -153,8 +153,8 @@ function computePendingState(e) {
|
|
|
153
153
|
// so the one walk covers direct marks, derivation, and companion chains.
|
|
154
154
|
if (markCovered(e)) return true;
|
|
155
155
|
const n = e.lt;
|
|
156
|
-
if (e.o?.
|
|
157
|
-
const t = e.o?.
|
|
156
|
+
if (e.o?.Et) {
|
|
157
|
+
const t = e.o?.Et;
|
|
158
158
|
const n = t.lt || t;
|
|
159
159
|
return newQuestionInFlight(n);
|
|
160
160
|
}
|
|
@@ -185,7 +185,7 @@ function updatePendingSignal(e) {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
function updateChildCompanions(e) {
|
|
188
|
-
const t = e.o?.
|
|
188
|
+
const t = e.o?.It;
|
|
189
189
|
if (t === undefined) return;
|
|
190
190
|
for (const e of t) updatePendingSignal(e);
|
|
191
191
|
}
|
|
@@ -205,8 +205,8 @@ function updateChildCompanions(e) {
|
|
|
205
205
|
if (i.has(e)) return;
|
|
206
206
|
i.add(e);
|
|
207
207
|
if (e.o?.Ge || e.o?.ge) n(e);
|
|
208
|
-
for (let t = e.u; t !== null; t = t.
|
|
209
|
-
for (let t = e.o?.
|
|
208
|
+
for (let t = e.u; t !== null; t = t.ae) visit(t.ce);
|
|
209
|
+
for (let t = e.o?.i ?? null; t !== null; t = t.Se) {
|
|
210
210
|
visit(t);
|
|
211
211
|
}
|
|
212
212
|
};
|
|
@@ -273,6 +273,13 @@ function snapCompanionsToState(e) {
|
|
|
273
273
|
|
|
274
274
|
function getLatestValueComputed(e) {
|
|
275
275
|
let t = e.o?.ge;
|
|
276
|
+
// A shadow disposed while unobserved (its gated reader unmounted at a
|
|
277
|
+
// landing) is a corpse: sync writes into it equality-swallow against its
|
|
278
|
+
// frozen _value, and a later read revives it via recompute — clearing
|
|
279
|
+
// DISPOSED and re-deriving from the committed view, so the banner showed
|
|
280
|
+
// the previous transition's target (#3041 follow-up). Treat it as absent;
|
|
281
|
+
// recreation backfills from the in-flight write below.
|
|
282
|
+
if (t && t.ie & REACTIVE_DISPOSED) t = undefined;
|
|
276
283
|
if (!t) {
|
|
277
284
|
const n = latestReadActive;
|
|
278
285
|
setLatestReadActive(false);
|
|
@@ -285,7 +292,7 @@ function getLatestValueComputed(e) {
|
|
|
285
292
|
ext(e).ge = t;
|
|
286
293
|
e.T |= CONFIG_HAS_COMPANIONS;
|
|
287
294
|
markFirewallChildCompanions(e);
|
|
288
|
-
ext(t).
|
|
295
|
+
ext(t).Et = e;
|
|
289
296
|
// Parent-child lane relationship
|
|
290
297
|
// Backfill an in-flight write (mirrors getPendingSignal): the companion is
|
|
291
298
|
// created lazily, possibly after the write was processed — syncCompanions
|
|
@@ -341,7 +348,7 @@ function getLatestValueComputed(e) {
|
|
|
341
348
|
|
|
342
349
|
/** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
|
|
343
350
|
setPendingCheckActive(false);
|
|
344
|
-
if (typeof e.
|
|
351
|
+
if (typeof e.oe === "function") prepareComputed(e, true);
|
|
345
352
|
const r = n.S;
|
|
346
353
|
if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
|
|
347
354
|
if (tracking && e !== t) link(e, t);
|
|
@@ -362,8 +369,22 @@ function getLatestValueComputed(e) {
|
|
|
362
369
|
* (#3028).
|
|
363
370
|
*/ function heldAwaitingAsync(e) {
|
|
364
371
|
const t = e._e;
|
|
365
|
-
const n = t ? currentTransition(t) :
|
|
366
|
-
if (!n || n.
|
|
372
|
+
const n = t ? currentTransition(t) : activeTransition;
|
|
373
|
+
if (!n || n.fn) return false;
|
|
374
|
+
// A plain staged write (a signal/store leaf — no _fn) held while an action
|
|
375
|
+
// is still running is an INPUT to a computation still in flight (#3078):
|
|
376
|
+
// the pairing rule must not suppress the verdict, or a memo recomputing
|
|
377
|
+
// mid-action reads the staged value, gets told "not pending", and
|
|
378
|
+
// disagrees with a direct isPending() probe for the whole action window.
|
|
379
|
+
// A computed's staged value is the opposite case — a LANDED answer
|
|
380
|
+
// awaiting reveal — where the pairing rule stands even inside an open
|
|
381
|
+
// action (#2831: a reader that saw the new value must not also see
|
|
382
|
+
// pending); still-computing answers are covered by the reporter scan.
|
|
383
|
+
if (n.ue.length && !e.oe) return true;
|
|
384
|
+
// A node not yet stamped with a transition only qualifies through the
|
|
385
|
+
// action check above; the reporter scan below is for transition-held
|
|
386
|
+
// writes whose source async is still computing.
|
|
387
|
+
if (!t) return false;
|
|
367
388
|
for (const [e, t] of n.Ne) {
|
|
368
389
|
if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
|
|
369
390
|
}
|
|
@@ -422,7 +443,7 @@ function isPending(e) {
|
|
|
422
443
|
// this flush (a downstream async pends and holds it), the suppression was
|
|
423
444
|
// wrong and the wrapper must re-ask (#3028). Remember who to wake — the
|
|
424
445
|
// async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
|
|
425
|
-
if (!i.found && i.suppressed.length && context && typeof context.
|
|
446
|
+
if (!i.found && i.suppressed.length && context && typeof context.oe === "function") {
|
|
426
447
|
for (const e of i.suppressed) {
|
|
427
448
|
let t = suppressedProbes.get(e);
|
|
428
449
|
if (!t) suppressedProbes.set(e, t = new Set);
|
|
@@ -467,10 +488,10 @@ GlobalQueue.Ht = recordFreshRead;
|
|
|
467
488
|
|
|
468
489
|
GlobalQueue.Je = applyReask;
|
|
469
490
|
|
|
470
|
-
GlobalQueue.
|
|
491
|
+
GlobalQueue.k = repollDownstreamVerdicts;
|
|
471
492
|
|
|
472
|
-
GlobalQueue.
|
|
493
|
+
GlobalQueue.wt = witnessAffects;
|
|
473
494
|
|
|
474
|
-
GlobalQueue.
|
|
495
|
+
GlobalQueue.jt = wakeSuppressedProbes;
|
|
475
496
|
|
|
476
497
|
export { isPending, latest };
|
package/dist/prod/index.js
CHANGED
|
@@ -38,6 +38,10 @@ 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
|
+
export { storeHasFamily, storeHasOptimisticFamily, storeIsShallow } from "./store/next/store.js";
|
|
44
|
+
|
|
41
45
|
export { storePath } from "./store/storePath.js";
|
|
42
46
|
|
|
43
47
|
const DEV = undefined;
|