@solidjs/signals 2.0.0-beta.32 → 2.0.0-beta.34
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 +235 -87
- package/dist/node.cjs +758 -635
- package/dist/prod/core/async.js +108 -54
- package/dist/prod/core/core.js +241 -204
- package/dist/prod/core/effect.js +27 -27
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +32 -32
- package/dist/prod/core/heap.js +36 -36
- package/dist/prod/core/lanes.js +14 -14
- package/dist/prod/core/optimistic.js +39 -39
- package/dist/prod/core/owner.js +53 -44
- package/dist/prod/core/scheduler.js +76 -72
- package/dist/prod/core/verdict.js +59 -48
- package/dist/prod/map.js +6 -6
- package/dist/prod/signals.js +2 -34
- package/dist/prod/store/optimistic.js +37 -32
- package/dist/prod/store/projection.js +41 -6
- package/dist/prod/store/store.js +7 -7
- package/dist/types/core/async.d.ts +9 -0
- package/dist/types/core/types.d.ts +24 -0
- package/dist/types/signals.d.ts +60 -4
- package/dist/types/store/store.d.ts +14 -0
- package/dist/types-cjs/core/async.d.cts +9 -0
- package/dist/types-cjs/core/types.d.cts +24 -0
- package/dist/types-cjs/signals.d.cts +60 -4
- package/dist/types-cjs/store/store.d.cts +14 -0
- package/package.json +1 -1
package/dist/prod/core/async.js
CHANGED
|
@@ -38,6 +38,22 @@ function clearPendingSources(e) {
|
|
|
38
38
|
e.oe = undefined;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A loading-window node hit an unready source (sync throw in recompute, or a
|
|
43
|
+
* NotReadyError-rejected flight): register for the source's settle — the
|
|
44
|
+
* settlePendingSource walk runs off `_pendingSources` + `_blocked` alone —
|
|
45
|
+
* with NO read-visible pending status, no downstream propagation, no
|
|
46
|
+
* transition, no lane registration. Commit #0 keeps serving.
|
|
47
|
+
*/ function parkLoadingWindow(e, n) {
|
|
48
|
+
e.ue = true;
|
|
49
|
+
if (n.source) addPendingSource(e, n.source);
|
|
50
|
+
// A settled error is the node's answer ("the error stays the answer until
|
|
51
|
+
// this retry can actually run") — the park must not replace it: reads
|
|
52
|
+
// throw `_error` while STATUS_ERROR is set, and overwriting it here leaks
|
|
53
|
+
// a pending-class NotReadyError from a read-invisible park (#2989).
|
|
54
|
+
if (!(e.S & STATUS_ERROR)) setPendingError(e, n.source, n);
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
function setPendingError(e, n, t) {
|
|
42
58
|
if (!n) {
|
|
43
59
|
e._ = null;
|
|
@@ -54,10 +70,10 @@ function setPendingError(e, n, t) {
|
|
|
54
70
|
}
|
|
55
71
|
|
|
56
72
|
function forEachDependent(e, n) {
|
|
57
|
-
for (let t = e.o; t !== null; t = t.
|
|
73
|
+
for (let t = e.o; t !== null; t = t.le) n(t.fe, t);
|
|
58
74
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
59
|
-
for (let t = e.u ?? null; t !== null; t = t.
|
|
60
|
-
for (let e = t.o; e !== null; e = e.
|
|
75
|
+
for (let t = e.u ?? null; t !== null; t = t.ae) {
|
|
76
|
+
for (let e = t.o; e !== null; e = e.le) n(e.fe, e);
|
|
61
77
|
}
|
|
62
78
|
}
|
|
63
79
|
|
|
@@ -72,7 +88,7 @@ function forEachDependent(e, n) {
|
|
|
72
88
|
// callbacks handle their own release (settleAutodispose in handleAsync); this
|
|
73
89
|
// covers derivatively-pending dependents, which have no callbacks of their own.
|
|
74
90
|
function releaseIfSettledUnobserved(e) {
|
|
75
|
-
e.
|
|
91
|
+
e.ce && e.T & CONFIG_AUTO_DISPOSE && !e.o && !(e.se & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
// Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
|
|
@@ -128,24 +144,30 @@ function settlePendingSource(e) {
|
|
|
128
144
|
let t;
|
|
129
145
|
const r = new Set;
|
|
130
146
|
// Companion updates no-op without the verdict layer (null hook).
|
|
131
|
-
const o = GlobalQueue.
|
|
147
|
+
const o = GlobalQueue.Se;
|
|
132
148
|
const settle = u => {
|
|
133
149
|
if (r.has(u) || !removePendingSource(u, e)) return;
|
|
134
150
|
r.add(u);
|
|
135
|
-
u.
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
151
|
+
u.de = clock;
|
|
152
|
+
const i = u.oe?.values().next().value;
|
|
153
|
+
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
154
|
+
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
155
|
+
// otherwise): the settled error stays the answer through the settle —
|
|
156
|
+
// nulling it here would have reads throw `null` until the re-enqueued
|
|
157
|
+
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
158
|
+
const l = u.S & STATUS_ERROR;
|
|
159
|
+
if (i) {
|
|
160
|
+
if (!l) setPendingError(u, i);
|
|
139
161
|
o !== null && o(u);
|
|
140
162
|
} else {
|
|
141
163
|
u.S &= ~STATUS_PENDING;
|
|
142
|
-
setPendingError(u);
|
|
164
|
+
if (!l) setPendingError(u);
|
|
143
165
|
o !== null && o(u);
|
|
144
|
-
if (u.
|
|
166
|
+
if (u.ue) {
|
|
145
167
|
enqueueSub(u);
|
|
146
168
|
n = true;
|
|
147
169
|
}
|
|
148
|
-
u.
|
|
170
|
+
u.ue = false;
|
|
149
171
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
150
172
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
151
173
|
// lists this walk is still iterating.
|
|
@@ -176,6 +198,8 @@ function handleAsync(e, n, t) {
|
|
|
176
198
|
}
|
|
177
199
|
if (!o && !r) {
|
|
178
200
|
e.Te = null;
|
|
201
|
+
// A sync landing is the first real answer for a loadingValue node.
|
|
202
|
+
e.Ee = false;
|
|
179
203
|
return n;
|
|
180
204
|
}
|
|
181
205
|
e.Te = n;
|
|
@@ -192,21 +216,31 @@ function handleAsync(e, n, t) {
|
|
|
192
216
|
// are the transaction's reveal machinery and always re-enter.
|
|
193
217
|
const settleTransition = () => {
|
|
194
218
|
const n = resolveTransition(e);
|
|
195
|
-
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).
|
|
219
|
+
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).Ie.has(e)) {
|
|
196
220
|
// Drop the stale stamp too: the plain settle write (setSignal) and the
|
|
197
221
|
// stash-path restamp both re-enter the transaction through it.
|
|
198
|
-
e.
|
|
222
|
+
e.Ne = null;
|
|
199
223
|
return;
|
|
200
224
|
}
|
|
201
225
|
globalQueue.initTransition(n);
|
|
202
226
|
};
|
|
203
227
|
const handleError = t => {
|
|
204
228
|
if (e.Te !== n) return;
|
|
205
|
-
settleTransition();
|
|
206
229
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
207
|
-
|
|
230
|
+
let r = t instanceof NotReadyError;
|
|
231
|
+
if (r && e.Ee) {
|
|
232
|
+
// Loading window: the flight died waiting on an unready source. Keep
|
|
233
|
+
// serving commit #0 — same parking as recompute's catch for sync
|
|
234
|
+
// dependency throws. The dead flight is released so the clock-gated
|
|
235
|
+
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
236
|
+
e.Te = null;
|
|
237
|
+
parkLoadingWindow(e, t);
|
|
238
|
+
e.de = clock;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
settleTransition();
|
|
208
242
|
notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
|
|
209
|
-
e.
|
|
243
|
+
e.de = clock;
|
|
210
244
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
211
245
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
212
246
|
// the error twin of settlePendingSource's release (#2934).
|
|
@@ -222,12 +256,12 @@ function handleAsync(e, n, t) {
|
|
|
222
256
|
const u = !!(e.S & STATUS_UNINITIALIZED);
|
|
223
257
|
trimStaleDeps(e);
|
|
224
258
|
clearStatus(e);
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
259
|
+
const i = resolveLane(e);
|
|
260
|
+
if (i) i.Ae.delete(e);
|
|
227
261
|
if (t) {
|
|
228
262
|
t(r);
|
|
229
263
|
if (u) clearStatus(e, true);
|
|
230
|
-
} else if (e.
|
|
264
|
+
} else if (e._e !== undefined) {
|
|
231
265
|
// Optimistic node — resting OR covered by an active override — holds
|
|
232
266
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
233
267
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -246,22 +280,22 @@ function handleAsync(e, n, t) {
|
|
|
246
280
|
// only notified when the hold is visible to them: under an active
|
|
247
281
|
// override every reader sees the override (A17), so waking subs would
|
|
248
282
|
// re-show an unchanged view — the revert is the notification point.
|
|
249
|
-
GlobalQueue.
|
|
283
|
+
GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
|
|
250
284
|
if (!hasActiveOverride(e)) insertSubs(e);
|
|
251
|
-
e.
|
|
252
|
-
} else if (
|
|
285
|
+
e.de = clock;
|
|
286
|
+
} else if (i) {
|
|
253
287
|
// Route through lane's effect queue for independent flushing
|
|
254
|
-
const n = e.
|
|
255
|
-
const t = e.
|
|
256
|
-
const o = e.
|
|
288
|
+
const n = e.Re;
|
|
289
|
+
const t = e.Ue;
|
|
290
|
+
const o = e.be;
|
|
257
291
|
try {
|
|
258
292
|
if (!n && u || !o || !o(r, t)) {
|
|
259
|
-
e.
|
|
260
|
-
e.
|
|
293
|
+
e.Ue = r;
|
|
294
|
+
e.de = clock;
|
|
261
295
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
262
296
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
263
297
|
// (computePendingState doesn't read _value).
|
|
264
|
-
GlobalQueue.
|
|
298
|
+
GlobalQueue.Pe !== null && GlobalQueue.Pe(e, r);
|
|
265
299
|
insertSubs(e, true);
|
|
266
300
|
}
|
|
267
301
|
} catch (n) {
|
|
@@ -280,6 +314,13 @@ function handleAsync(e, n, t) {
|
|
|
280
314
|
notifyStatus(e, STATUS_ERROR, n);
|
|
281
315
|
}
|
|
282
316
|
}
|
|
317
|
+
// First real answer landing: the window closes when the answer becomes
|
|
318
|
+
// OBSERVABLE. A direct commit is observable now; a transition-held write
|
|
319
|
+
// (`_pendingValue` set above or inside setSignal) is not — the verdict's
|
|
320
|
+
// held-value branch is window-gated, and commitPendingNode closes the
|
|
321
|
+
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
322
|
+
// to live observers between the landing and its commit (#2990).
|
|
323
|
+
if (e.De === NOT_PENDING) e.Ee = false;
|
|
283
324
|
settlePendingSource(e);
|
|
284
325
|
schedule();
|
|
285
326
|
flush();
|
|
@@ -300,9 +341,9 @@ function handleAsync(e, n, t) {
|
|
|
300
341
|
return false;
|
|
301
342
|
};
|
|
302
343
|
if (o) {
|
|
303
|
-
let t = false, r = false, o,
|
|
344
|
+
let t = false, r = false, o, i = true;
|
|
304
345
|
n.then(e => {
|
|
305
|
-
if (
|
|
346
|
+
if (i) {
|
|
306
347
|
u = e;
|
|
307
348
|
t = true;
|
|
308
349
|
} else {
|
|
@@ -310,7 +351,7 @@ function handleAsync(e, n, t) {
|
|
|
310
351
|
settleAutodispose();
|
|
311
352
|
}
|
|
312
353
|
}, e => {
|
|
313
|
-
if (
|
|
354
|
+
if (i) {
|
|
314
355
|
o = e;
|
|
315
356
|
r = true;
|
|
316
357
|
} else {
|
|
@@ -318,7 +359,7 @@ function handleAsync(e, n, t) {
|
|
|
318
359
|
settleAutodispose();
|
|
319
360
|
}
|
|
320
361
|
});
|
|
321
|
-
|
|
362
|
+
i = false;
|
|
322
363
|
if (r) {
|
|
323
364
|
// Settle through the same status path an async rejection uses, then
|
|
324
365
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
@@ -326,15 +367,23 @@ function handleAsync(e, n, t) {
|
|
|
326
367
|
handleError(o);
|
|
327
368
|
throw o;
|
|
328
369
|
} else if (!t) {
|
|
370
|
+
// Loading window: serve commit #0 instead of suspending. No transition
|
|
371
|
+
// is opened — first-flight work on a loadingValue node is loading-class
|
|
372
|
+
// (invisible to boundaries and transitions); the flight itself is
|
|
373
|
+
// already registered in _inFlight and lands through asyncWrite.
|
|
374
|
+
if (e.Ee) return e.Ue;
|
|
329
375
|
globalQueue.initTransition(resolveTransition(e));
|
|
330
376
|
throw new NotReadyError(context);
|
|
377
|
+
} else {
|
|
378
|
+
// Synchronously-resolved promise: the first real answer landed.
|
|
379
|
+
e.Ee = false;
|
|
331
380
|
}
|
|
332
381
|
}
|
|
333
382
|
if (r) {
|
|
334
383
|
const t = n[Symbol.asyncIterator]();
|
|
335
384
|
let r = false;
|
|
336
385
|
let o = false;
|
|
337
|
-
let
|
|
386
|
+
let i = true;
|
|
338
387
|
cleanup(() => {
|
|
339
388
|
if (o) return;
|
|
340
389
|
o = true;
|
|
@@ -350,7 +399,7 @@ function handleAsync(e, n, t) {
|
|
|
350
399
|
if (!settleAutodispose()) iterate();
|
|
351
400
|
};
|
|
352
401
|
const iterate = () => {
|
|
353
|
-
let l,
|
|
402
|
+
let l, s, f = false, a = false, c = true;
|
|
354
403
|
// Protocol tolerance, matching `for await`: `await` unwraps whatever
|
|
355
404
|
// next() returns — a thenable OR a bare IteratorResult. Real producers
|
|
356
405
|
// use the bare form as a promise-free fast path when a value is already
|
|
@@ -367,7 +416,7 @@ function handleAsync(e, n, t) {
|
|
|
367
416
|
// sync-thenable producer mid-stream — has no caller reading the
|
|
368
417
|
// stash: it must write through the async path or the value is
|
|
369
418
|
// silently dropped.
|
|
370
|
-
if (c &&
|
|
419
|
+
if (c && i) {
|
|
371
420
|
l = t;
|
|
372
421
|
f = true;
|
|
373
422
|
if (t.done) o = true;
|
|
@@ -389,7 +438,7 @@ function handleAsync(e, n, t) {
|
|
|
389
438
|
}
|
|
390
439
|
}, t => {
|
|
391
440
|
if (c) {
|
|
392
|
-
|
|
441
|
+
s = t;
|
|
393
442
|
a = true;
|
|
394
443
|
} else if (e.Te === n) {
|
|
395
444
|
o = true;
|
|
@@ -401,8 +450,8 @@ function handleAsync(e, n, t) {
|
|
|
401
450
|
if (a) {
|
|
402
451
|
// Match the promise branch, but only rethrow during the initial read.
|
|
403
452
|
o = true;
|
|
404
|
-
handleError(
|
|
405
|
-
if (
|
|
453
|
+
handleError(s);
|
|
454
|
+
if (i) throw s;
|
|
406
455
|
return true;
|
|
407
456
|
}
|
|
408
457
|
if (f && !l.done) {
|
|
@@ -414,28 +463,33 @@ function handleAsync(e, n, t) {
|
|
|
414
463
|
};
|
|
415
464
|
const l = iterate();
|
|
416
465
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
417
|
-
|
|
466
|
+
i = false;
|
|
418
467
|
if (!r && !l) {
|
|
468
|
+
// Loading window: serve commit #0 (see the promise branch above).
|
|
469
|
+
if (e.Ee) return e.Ue;
|
|
419
470
|
globalQueue.initTransition(resolveTransition(e));
|
|
420
471
|
throw new NotReadyError(context);
|
|
421
472
|
}
|
|
473
|
+
// A sync first yield (or immediate empty completion) is the first real
|
|
474
|
+
// answer; async yields clear inside asyncWrite.
|
|
475
|
+
e.Ee = false;
|
|
422
476
|
}
|
|
423
477
|
return u;
|
|
424
478
|
}
|
|
425
479
|
|
|
426
480
|
function clearStatus(e, n = false) {
|
|
427
481
|
if (e.oe) clearPendingSources(e);
|
|
428
|
-
if (e.
|
|
482
|
+
if (e.ue) e.ue = false;
|
|
429
483
|
// The pending window is over; its quiet classification dies with it.
|
|
430
484
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
431
485
|
// plain store to an existing slot — no shape change.)
|
|
432
|
-
e.
|
|
486
|
+
e.he = false;
|
|
433
487
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
434
488
|
if (e._) setPendingError(e);
|
|
435
489
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
436
490
|
// once the verdict layer created them, which installs the hooks).
|
|
437
|
-
if (e.
|
|
438
|
-
if (e.u && GlobalQueue.
|
|
491
|
+
if (e.ge || e.pe) GlobalQueue.Se(e);
|
|
492
|
+
if (e.u && GlobalQueue.Oe !== null) GlobalQueue.Oe(e);
|
|
439
493
|
if (e.i) e.i();
|
|
440
494
|
}
|
|
441
495
|
|
|
@@ -443,9 +497,9 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
443
497
|
// Wrap regular errors to track source node
|
|
444
498
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
445
499
|
const u = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
446
|
-
const
|
|
447
|
-
const l = n === STATUS_PENDING && e.
|
|
448
|
-
const
|
|
500
|
+
const i = u === e;
|
|
501
|
+
const l = n === STATUS_PENDING && e._e !== undefined && !i;
|
|
502
|
+
const s = l && hasActiveOverride(e);
|
|
449
503
|
if (!r) {
|
|
450
504
|
if (n === STATUS_PENDING && u) {
|
|
451
505
|
addPendingSource(e, u);
|
|
@@ -458,13 +512,13 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
458
512
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
459
513
|
e._ = t;
|
|
460
514
|
}
|
|
461
|
-
GlobalQueue.
|
|
462
|
-
if (e.u && GlobalQueue.
|
|
515
|
+
GlobalQueue.Se !== null && GlobalQueue.Se(e);
|
|
516
|
+
if (e.u && GlobalQueue.Oe !== null) GlobalQueue.Oe(e);
|
|
463
517
|
}
|
|
464
518
|
if (o && !r) {
|
|
465
519
|
assignOrMergeLane(e, o);
|
|
466
520
|
}
|
|
467
|
-
const f = r ||
|
|
521
|
+
const f = r || s;
|
|
468
522
|
const a = r || l ? undefined : o;
|
|
469
523
|
if (e.i) {
|
|
470
524
|
if (r && n === STATUS_PENDING) {
|
|
@@ -478,22 +532,22 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
478
532
|
return;
|
|
479
533
|
}
|
|
480
534
|
forEachDependent(e, (e, r) => {
|
|
481
|
-
e.
|
|
535
|
+
e.de = clock;
|
|
482
536
|
if (n === STATUS_PENDING && u && !e.oe?.has(u) || n !== STATUS_PENDING && (e._ !== t || e.oe)) {
|
|
483
537
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
484
538
|
// It exists so the observer re-runs when the source settles, but it must
|
|
485
539
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
486
540
|
// read swallows those, and the async path must match. Re-run the observer
|
|
487
541
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
488
|
-
if (r.
|
|
542
|
+
if (r.Ge && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
489
543
|
enqueueSub(e);
|
|
490
544
|
schedule();
|
|
491
545
|
return;
|
|
492
546
|
}
|
|
493
|
-
if (!f && !e.
|
|
547
|
+
if (!f && !e.Ne) queuePendingNode(e);
|
|
494
548
|
notifyStatus(e, n, t, f, a);
|
|
495
549
|
}
|
|
496
550
|
});
|
|
497
551
|
}
|
|
498
552
|
|
|
499
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|
|
553
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|