@solidjs/signals 2.0.0-rc.3 → 2.0.0-rc.5
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 +2494 -271
- package/dist/node.cjs +3691 -1595
- package/dist/prod/affects.js +13 -12
- package/dist/prod/boundaries.js +43 -35
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +137 -106
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +354 -247
- package/dist/prod/core/effect.js +47 -50
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +88 -52
- package/dist/prod/core/heap.js +38 -38
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +61 -58
- package/dist/prod/core/owner.js +38 -38
- package/dist/prod/core/scheduler.js +401 -174
- package/dist/prod/core/verdict.js +132 -65
- package/dist/prod/index.js +7 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +253 -25
- package/dist/prod/store/index.js +2 -0
- package/dist/prod/store/next/optimistic.js +314 -121
- 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 +23 -19
- package/dist/prod/store/next/reconcile.js +307 -120
- package/dist/prod/store/next/store.js +440 -117
- package/dist/prod/store/next/target.js +13 -4
- package/dist/prod/store/store.js +5 -5
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution.d.ts +9 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +34 -21
- package/dist/types/core/dev.d.ts +8 -0
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/graph.d.ts +22 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +46 -0
- package/dist/types/core/types.d.ts +12 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/signals.d.ts +108 -0
- package/dist/types/store/index.d.ts +2 -0
- package/dist/types/store/next/optimistic.d.ts +13 -10
- 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/projection.d.ts +1 -1
- package/dist/types/store/next/reconcile.d.ts +14 -0
- package/dist/types/store/next/store.d.ts +52 -2
- package/dist/types/store/next/target.d.ts +73 -8
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution.d.cts +9 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +34 -21
- package/dist/types-cjs/core/dev.d.cts +8 -0
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/graph.d.cts +22 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +46 -0
- package/dist/types-cjs/core/types.d.cts +12 -0
- package/dist/types-cjs/index.d.cts +3 -3
- package/dist/types-cjs/signals.d.cts +108 -0
- package/dist/types-cjs/store/index.d.cts +2 -0
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- 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/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/reconcile.d.cts +14 -0
- package/dist/types-cjs/store/next/store.d.cts +52 -2
- package/dist/types-cjs/store/next/target.d.cts +73 -8
- package/package.json +2 -2
package/dist/prod/core/async.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
|
|
1
|
+
import { STATUS_UNINITIALIZED, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, STATUS_PENDING, REACTIVE_DIRTY, REACTIVE_OPTIMISTIC_DIRTY, NOT_PENDING, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_AUTO_DISPOSE, REACTIVE_ZOMBIE, REACTIVE_DISPOSED } from "./constants.js";
|
|
2
2
|
|
|
3
|
-
import { untrack, ext, setSignal, context } from "./core.js";
|
|
3
|
+
import { untrack, ext, statusNotifierOf, setSignal, context } from "./core.js";
|
|
4
4
|
|
|
5
5
|
import "./invariants.js";
|
|
6
6
|
|
|
@@ -45,7 +45,7 @@ function clearPendingSources(e) {
|
|
|
45
45
|
* with NO read-visible pending status, no downstream propagation, no
|
|
46
46
|
* transition, no lane registration. Commit #0 keeps serving.
|
|
47
47
|
*/ function parkLoadingWindow(e, n) {
|
|
48
|
-
ext(e).
|
|
48
|
+
ext(e).fe = true;
|
|
49
49
|
if (n.source) addPendingSource(e, n.source);
|
|
50
50
|
// A settled error is the node's answer ("the error stays the answer until
|
|
51
51
|
// this retry can actually run") — the park must not replace it: reads
|
|
@@ -70,10 +70,10 @@ function setPendingError(e, n, t) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function forEachDependent(e, n) {
|
|
73
|
-
for (let t = e.u; t !== null; t = t.
|
|
73
|
+
for (let t = e.u; t !== null; t = t.ae) n(t.ce, t);
|
|
74
74
|
// `?? null`: affects() marks route plain signals (no `_child` slot) through here.
|
|
75
|
-
for (let t = e.o?.
|
|
76
|
-
for (let e = t.u; e !== null; e = e.
|
|
75
|
+
for (let t = e.o?.i ?? null; t !== null; t = t.Se) {
|
|
76
|
+
for (let e = t.u; e !== null; e = e.ae) n(e.ce, e);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -88,7 +88,7 @@ function forEachDependent(e, n) {
|
|
|
88
88
|
// callbacks handle their own release (settleAutodispose in handleAsync); this
|
|
89
89
|
// covers derivatively-pending dependents, which have no callbacks of their own.
|
|
90
90
|
function releaseIfSettledUnobserved(e) {
|
|
91
|
-
e.
|
|
91
|
+
e.oe && e.T & CONFIG_AUTO_DISPOSE && !e.u && !(e.ie & REACTIVE_ZOMBIE) && !(e.S & STATUS_PENDING) && unobserved(e);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Error-path sweep: notifyStatus(STATUS_ERROR) clears dependents' pending
|
|
@@ -145,35 +145,35 @@ function settlePendingSource(e) {
|
|
|
145
145
|
const r = new Set;
|
|
146
146
|
// Companion updates no-op without the verdict layer (null hook).
|
|
147
147
|
const o = GlobalQueue.de;
|
|
148
|
-
const settle =
|
|
149
|
-
if (r.has(
|
|
150
|
-
r.add(
|
|
151
|
-
|
|
152
|
-
const
|
|
148
|
+
const settle = i => {
|
|
149
|
+
if (r.has(i) || !removePendingSource(i, e)) return;
|
|
150
|
+
r.add(i);
|
|
151
|
+
i.Te = clock;
|
|
152
|
+
const l = i.o?.le?.values().next().value;
|
|
153
153
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
154
154
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
155
155
|
// otherwise): the settled error stays the answer through the settle —
|
|
156
156
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
157
157
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
160
|
-
if (!
|
|
161
|
-
o
|
|
158
|
+
const s = i.S & STATUS_ERROR;
|
|
159
|
+
if (l) {
|
|
160
|
+
if (!s) setPendingError(i, l);
|
|
161
|
+
o?.(i);
|
|
162
162
|
} else {
|
|
163
|
-
|
|
164
|
-
if (!
|
|
165
|
-
o
|
|
166
|
-
if (
|
|
167
|
-
enqueueSub(
|
|
163
|
+
i.S &= ~STATUS_PENDING;
|
|
164
|
+
if (!s) setPendingError(i);
|
|
165
|
+
o?.(i);
|
|
166
|
+
if (i.o?.fe) {
|
|
167
|
+
enqueueSub(i);
|
|
168
168
|
n = true;
|
|
169
169
|
}
|
|
170
|
-
if (
|
|
170
|
+
if (i.o !== null) i.o.fe = false;
|
|
171
171
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
172
172
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
173
173
|
// lists this walk is still iterating.
|
|
174
|
-
if (!
|
|
174
|
+
if (!i.u && i.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(i);
|
|
175
175
|
}
|
|
176
|
-
forEachDependent(
|
|
176
|
+
forEachDependent(i, settle);
|
|
177
177
|
};
|
|
178
178
|
forEachDependent(e, settle);
|
|
179
179
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
@@ -187,6 +187,14 @@ function isThenable(e) {
|
|
|
187
187
|
return e != null && typeof e === "object" && typeof e.then === "function";
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
191
|
+
const n = e.o?.Ee;
|
|
192
|
+
if (n != null) {
|
|
193
|
+
e.o.Ee = null;
|
|
194
|
+
n();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
190
198
|
function handleAsync(e, n, t) {
|
|
191
199
|
let r = false;
|
|
192
200
|
let o = false;
|
|
@@ -197,13 +205,18 @@ function handleAsync(e, n, t) {
|
|
|
197
205
|
});
|
|
198
206
|
}
|
|
199
207
|
if (!o && !r) {
|
|
200
|
-
if (e.o !== null) e.o.
|
|
208
|
+
if (e.o !== null) e.o.Ie = null;
|
|
201
209
|
// A sync landing is the first real answer for a loadingValue node.
|
|
202
|
-
e.
|
|
210
|
+
e.Ne = false;
|
|
203
211
|
return n;
|
|
204
212
|
}
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
// Flight replacement relies on recompute's supersede release for iterator
|
|
214
|
+
// teardown (#3122): every handleAsync call — including the projection
|
|
215
|
+
// self-registration — runs during a recompute of `el`, which has already
|
|
216
|
+
// fired _flightTeardown. A future non-recompute registration path must
|
|
217
|
+
// release it here before overwriting _inFlight.
|
|
218
|
+
ext(e).Ie = n;
|
|
219
|
+
let i;
|
|
207
220
|
// Settle-time transition re-entry. The loading rail is invisible to
|
|
208
221
|
// transactions (#2933): a boundary-caught first load never registers as an
|
|
209
222
|
// async reporter, so its settle — the boundary's fallback -> content
|
|
@@ -216,24 +229,24 @@ function handleAsync(e, n, t) {
|
|
|
216
229
|
// are the transaction's reveal machinery and always re-enter.
|
|
217
230
|
const settleTransition = () => {
|
|
218
231
|
const n = resolveTransition(e);
|
|
219
|
-
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).
|
|
232
|
+
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n)._e.has(e)) {
|
|
220
233
|
// Drop the stale stamp too: the plain settle write (setSignal) and the
|
|
221
234
|
// stash-path restamp both re-enter the transaction through it.
|
|
222
|
-
e.
|
|
235
|
+
e.Ae = null;
|
|
223
236
|
return;
|
|
224
237
|
}
|
|
225
238
|
globalQueue.initTransition(n);
|
|
226
239
|
};
|
|
227
240
|
const handleError = t => {
|
|
228
|
-
if (e.o?.
|
|
241
|
+
if (e.o?.Ie !== n) return;
|
|
229
242
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
230
243
|
let r = t instanceof NotReadyError;
|
|
231
|
-
if (r && e.
|
|
244
|
+
if (r && e.Ne) {
|
|
232
245
|
// Loading window: the flight died waiting on an unready source. Keep
|
|
233
246
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
234
247
|
// dependency throws. The dead flight is released so the clock-gated
|
|
235
248
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
236
|
-
if (e.o !== null) e.o.
|
|
249
|
+
if (e.o !== null) e.o.Ie = null;
|
|
237
250
|
parkLoadingWindow(e, t);
|
|
238
251
|
e.Te = clock;
|
|
239
252
|
return;
|
|
@@ -247,21 +260,21 @@ function handleAsync(e, n, t) {
|
|
|
247
260
|
if (!r) releaseSettledDependents(e);
|
|
248
261
|
};
|
|
249
262
|
const asyncWrite = (r, o) => {
|
|
250
|
-
if (e.o?.
|
|
263
|
+
if (e.o?.Ie !== n) return;
|
|
251
264
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
252
265
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
253
266
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
254
267
|
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
255
268
|
settleTransition();
|
|
256
|
-
const
|
|
269
|
+
const i = !!(e.S & STATUS_UNINITIALIZED);
|
|
257
270
|
trimStaleDeps(e);
|
|
258
271
|
clearStatus(e);
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
272
|
+
const l = resolveLane(e);
|
|
273
|
+
if (l) l.De.delete(e);
|
|
261
274
|
if (t) {
|
|
262
275
|
t(r);
|
|
263
|
-
if (
|
|
264
|
-
} else if (e.o?.
|
|
276
|
+
if (i) clearStatus(e, true);
|
|
277
|
+
} else if (e.o?.Oe !== undefined) {
|
|
265
278
|
// Optimistic node — resting OR covered by an active override — holds
|
|
266
279
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
267
280
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -280,24 +293,35 @@ function handleAsync(e, n, t) {
|
|
|
280
293
|
// only notified when the hold is visible to them: under an active
|
|
281
294
|
// override every reader sees the override (A17), so waking subs would
|
|
282
295
|
// re-show an unchanged view — the revert is the notification point.
|
|
283
|
-
GlobalQueue.
|
|
296
|
+
GlobalQueue.Re?.(e, r);
|
|
284
297
|
if (!hasActiveOverride(e)) {
|
|
285
298
|
insertSubs(e);
|
|
299
|
+
} else if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) {
|
|
300
|
+
// A17 silence is stated over ordinary readers; an authoritative-view
|
|
301
|
+
// reader (until()'s predicate) observed this node PAST its override
|
|
302
|
+
// and is waiting for exactly this staged truth. Without the wake the
|
|
303
|
+
// hold deadlocks: the landing waits on the transaction, the
|
|
304
|
+
// transaction on the action, the action on an until() that was never
|
|
305
|
+
// re-notified (#3164). Same selective wake as the equal-landing
|
|
306
|
+
// branch in recompute(). Optional call: the bit implies the
|
|
307
|
+
// optimistic engine WAS consulted, but the hook only installs with
|
|
308
|
+
// it — a bare-core build must not crash here.
|
|
309
|
+
GlobalQueue.Ue?.(e);
|
|
286
310
|
}
|
|
287
311
|
e.Te = clock;
|
|
288
|
-
} else if (
|
|
312
|
+
} else if (l) {
|
|
289
313
|
// Route through lane's effect queue for independent flushing
|
|
290
|
-
const n = e.
|
|
314
|
+
const n = e.he;
|
|
291
315
|
const t = e.be;
|
|
292
|
-
const o = e.
|
|
316
|
+
const o = e.ge;
|
|
293
317
|
try {
|
|
294
|
-
if (!n &&
|
|
318
|
+
if (!n && i || !o || !o(r, t)) {
|
|
295
319
|
e.be = r;
|
|
296
320
|
e.Te = clock;
|
|
297
321
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
298
322
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
299
323
|
// (computePendingState doesn't read _value).
|
|
300
|
-
GlobalQueue.
|
|
324
|
+
GlobalQueue.Re?.(e, r);
|
|
301
325
|
insertSubs(e, true);
|
|
302
326
|
}
|
|
303
327
|
} catch (n) {
|
|
@@ -322,7 +346,7 @@ function handleAsync(e, n, t) {
|
|
|
322
346
|
// held-value branch is window-gated, and commitPendingNode closes the
|
|
323
347
|
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
324
348
|
// to live observers between the landing and its commit (#2990).
|
|
325
|
-
if (e.Pe === NOT_PENDING) e.
|
|
349
|
+
if (e.Pe === NOT_PENDING) e.Ne = false;
|
|
326
350
|
settlePendingSource(e);
|
|
327
351
|
schedule();
|
|
328
352
|
flush();
|
|
@@ -355,18 +379,23 @@ function handleAsync(e, n, t) {
|
|
|
355
379
|
// meaningful only in the live posture.
|
|
356
380
|
const consumeIterator = (t, r) => {
|
|
357
381
|
const o = t[Symbol.asyncIterator]();
|
|
358
|
-
let
|
|
359
|
-
let
|
|
360
|
-
let
|
|
382
|
+
let l = false;
|
|
383
|
+
let s = false;
|
|
384
|
+
let u = !r;
|
|
361
385
|
const close = () => {
|
|
362
|
-
if (
|
|
363
|
-
|
|
386
|
+
if (s) return;
|
|
387
|
+
s = true;
|
|
364
388
|
try {
|
|
365
389
|
const e = o.return?.();
|
|
366
390
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
367
391
|
} catch {}
|
|
368
392
|
};
|
|
369
393
|
r ? r(close) : cleanup(close);
|
|
394
|
+
// Flight-identity cancellation (#3122): the registration above is the
|
|
395
|
+
// owner-death backstop, but its disposal list can be zombie-deferred
|
|
396
|
+
// until the SUPERSEDING flight settles. The teardown slot fires at the
|
|
397
|
+
// _inFlight release sites so supersede stops this stream immediately.
|
|
398
|
+
ext(e).Ee = close;
|
|
370
399
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
371
400
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
372
401
|
// of pumping the stream forever with zero subscribers (#2935).
|
|
@@ -392,18 +421,18 @@ function handleAsync(e, n, t) {
|
|
|
392
421
|
// stash: it must write through the async path or the value is
|
|
393
422
|
// silently dropped. (The deferred posture never has a caller, so
|
|
394
423
|
// initialRead starts false there and everything writes through.)
|
|
395
|
-
if (c &&
|
|
424
|
+
if (c && u) {
|
|
396
425
|
t = r;
|
|
397
426
|
f = true;
|
|
398
|
-
if (r.done)
|
|
399
|
-
} else if (e.o?.
|
|
427
|
+
if (r.done) s = true;
|
|
428
|
+
} else if (e.o?.Ie !== n) {
|
|
400
429
|
return;
|
|
401
430
|
} else if (!r.done) {
|
|
402
|
-
|
|
431
|
+
l = true;
|
|
403
432
|
asyncWrite(r.value, iterateOrRelease);
|
|
404
433
|
} else {
|
|
405
|
-
|
|
406
|
-
if (
|
|
434
|
+
s = true;
|
|
435
|
+
if (l) {
|
|
407
436
|
schedule();
|
|
408
437
|
flush();
|
|
409
438
|
} else {
|
|
@@ -413,11 +442,11 @@ function handleAsync(e, n, t) {
|
|
|
413
442
|
settleAutodispose();
|
|
414
443
|
}
|
|
415
444
|
}, t => {
|
|
416
|
-
if (c &&
|
|
445
|
+
if (c && u) {
|
|
417
446
|
r = t;
|
|
418
447
|
a = true;
|
|
419
|
-
} else if (e.o?.
|
|
420
|
-
|
|
448
|
+
} else if (e.o?.Ie === n) {
|
|
449
|
+
s = true;
|
|
421
450
|
handleError(t);
|
|
422
451
|
settleAutodispose();
|
|
423
452
|
}
|
|
@@ -425,27 +454,27 @@ function handleAsync(e, n, t) {
|
|
|
425
454
|
c = false;
|
|
426
455
|
if (a) {
|
|
427
456
|
// Match the promise branch, but only rethrow during the initial read.
|
|
428
|
-
|
|
457
|
+
s = true;
|
|
429
458
|
handleError(r);
|
|
430
|
-
if (
|
|
459
|
+
if (u) throw r;
|
|
431
460
|
return true;
|
|
432
461
|
}
|
|
433
462
|
if (f && !t.done) {
|
|
434
|
-
|
|
435
|
-
|
|
463
|
+
i = t.value;
|
|
464
|
+
l = true;
|
|
436
465
|
return iterate();
|
|
437
466
|
}
|
|
438
467
|
return f && t.done;
|
|
439
468
|
};
|
|
440
469
|
const f = iterate();
|
|
441
470
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
442
|
-
|
|
443
|
-
return
|
|
471
|
+
u = false;
|
|
472
|
+
return l || f;
|
|
444
473
|
};
|
|
445
474
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
446
475
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
447
476
|
// shared NotReady/loading tail below.
|
|
448
|
-
let
|
|
477
|
+
let l = null;
|
|
449
478
|
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
450
479
|
// the shape every async stub returning a stream produces — consumes as the
|
|
451
480
|
// stream itself rather than settling on the iterable object. One level
|
|
@@ -460,11 +489,11 @@ function handleAsync(e, n, t) {
|
|
|
460
489
|
}
|
|
461
490
|
if (!t) return false;
|
|
462
491
|
const r = consumeIterator(e, n);
|
|
463
|
-
if (!n)
|
|
492
|
+
if (!n) l = r;
|
|
464
493
|
return true;
|
|
465
494
|
};
|
|
466
495
|
if (o) {
|
|
467
|
-
let t = false, r = false, o,
|
|
496
|
+
let t = false, r = false, o, l = true;
|
|
468
497
|
// Close registration for the flattening path. Consumption starts in a
|
|
469
498
|
// microtask where the ambient owner is gone (or worse, someone else's),
|
|
470
499
|
// so `cleanup()` can't be used — the close targets el's disposal list
|
|
@@ -476,18 +505,18 @@ function handleAsync(e, n, t) {
|
|
|
476
505
|
// becomes disposal-bearing, which is exactly the class a directly
|
|
477
506
|
// returned iterable already occupies.
|
|
478
507
|
const registerDeferredClose = n => {
|
|
479
|
-
if (!e.
|
|
508
|
+
if (!e.pe) e.pe = n; else if (Array.isArray(e.pe)) e.pe.push(n); else e.pe = [ e.pe, n ];
|
|
480
509
|
};
|
|
481
510
|
n.then(r => {
|
|
482
|
-
if (
|
|
483
|
-
|
|
511
|
+
if (l) {
|
|
512
|
+
i = r;
|
|
484
513
|
t = true;
|
|
485
|
-
} else if (e.o?.
|
|
514
|
+
} else if (e.o?.Ie === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
|
|
486
515
|
asyncWrite(r);
|
|
487
516
|
settleAutodispose();
|
|
488
517
|
}
|
|
489
518
|
}, e => {
|
|
490
|
-
if (
|
|
519
|
+
if (l) {
|
|
491
520
|
o = e;
|
|
492
521
|
r = true;
|
|
493
522
|
} else {
|
|
@@ -495,7 +524,7 @@ function handleAsync(e, n, t) {
|
|
|
495
524
|
settleAutodispose();
|
|
496
525
|
}
|
|
497
526
|
});
|
|
498
|
-
|
|
527
|
+
l = false;
|
|
499
528
|
if (r) {
|
|
500
529
|
// Settle through the same status path an async rejection uses, then
|
|
501
530
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
@@ -507,103 +536,105 @@ function handleAsync(e, n, t) {
|
|
|
507
536
|
// is opened — first-flight work on a loadingValue node is loading-class
|
|
508
537
|
// (invisible to boundaries and transitions); the flight itself is
|
|
509
538
|
// already registered in _inFlight and lands through asyncWrite.
|
|
510
|
-
if (e.
|
|
539
|
+
if (e.Ne) return e.be;
|
|
511
540
|
globalQueue.initTransition(resolveTransition(e));
|
|
512
541
|
throw new NotReadyError(context);
|
|
513
|
-
} else if (!flattenIfIterable(
|
|
542
|
+
} else if (!flattenIfIterable(i)) {
|
|
514
543
|
// Synchronously-resolved promise: the first real answer landed.
|
|
515
|
-
e.
|
|
544
|
+
e.Ne = false;
|
|
516
545
|
}
|
|
517
546
|
// A sync-resolved promise holding an AsyncIterable flattened LIVE (we
|
|
518
547
|
// are still inside the synchronous read): full initial-drain semantics
|
|
519
548
|
// apply and the shared tail below settles the verdict.
|
|
520
549
|
}
|
|
521
550
|
if (r) flattenIfIterable(n);
|
|
522
|
-
if (
|
|
523
|
-
if (!
|
|
551
|
+
if (l !== null) {
|
|
552
|
+
if (!l) {
|
|
524
553
|
// Loading window: serve commit #0 (see the promise branch above).
|
|
525
|
-
if (e.
|
|
554
|
+
if (e.Ne) return e.be;
|
|
526
555
|
globalQueue.initTransition(resolveTransition(e));
|
|
527
556
|
throw new NotReadyError(context);
|
|
528
557
|
}
|
|
529
558
|
// A sync first yield (or immediate empty completion) is the first real
|
|
530
559
|
// answer; async yields clear inside asyncWrite.
|
|
531
|
-
e.
|
|
560
|
+
e.Ne = false;
|
|
532
561
|
}
|
|
533
|
-
return
|
|
562
|
+
return i;
|
|
534
563
|
}
|
|
535
564
|
|
|
536
565
|
function clearStatus(e, n = false) {
|
|
537
566
|
if (e.o?.le) clearPendingSources(e);
|
|
538
|
-
if (e.o?.
|
|
567
|
+
if (e.o?.fe) if (e.o !== null) e.o.fe = false;
|
|
539
568
|
// The pending window is over; its quiet classification dies with it.
|
|
540
569
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
541
570
|
// plain store to an existing slot — no shape change.)
|
|
542
|
-
if (e.o !== null) e.o.
|
|
571
|
+
if (e.o !== null) e.o.Ge = false;
|
|
543
572
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
544
573
|
if (e.o?._) setPendingError(e);
|
|
545
574
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
546
575
|
// once the verdict layer created them, which installs the hooks).
|
|
547
|
-
if (e.o?.
|
|
548
|
-
if (e.o?.
|
|
549
|
-
|
|
576
|
+
if (e.o?.ye || e.o?.Ce) GlobalQueue.de(e);
|
|
577
|
+
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.me !== null) GlobalQueue.me(e);
|
|
578
|
+
const t = statusNotifierOf(e);
|
|
579
|
+
if (t) t.call(e);
|
|
550
580
|
}
|
|
551
581
|
|
|
552
582
|
function notifyStatus(e, n, t, r, o) {
|
|
553
583
|
// Wrap regular errors to track source node
|
|
554
584
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
555
|
-
const
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const
|
|
585
|
+
const i = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
586
|
+
const l = i === e;
|
|
587
|
+
const s = n === STATUS_PENDING && e.o?.Oe !== undefined && !l;
|
|
588
|
+
const u = s && hasActiveOverride(e);
|
|
559
589
|
if (!r) {
|
|
560
|
-
if (n === STATUS_PENDING &&
|
|
561
|
-
addPendingSource(e,
|
|
590
|
+
if (n === STATUS_PENDING && i) {
|
|
591
|
+
addPendingSource(e, i);
|
|
562
592
|
e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
|
|
563
593
|
// Preserve the current source on this propagation so render-effect notification
|
|
564
594
|
// can register every distinct pending source with the transition.
|
|
565
|
-
setPendingError(e,
|
|
595
|
+
setPendingError(e, i, t);
|
|
566
596
|
} else {
|
|
567
597
|
clearPendingSources(e);
|
|
568
598
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
569
599
|
ext(e)._ = t;
|
|
570
600
|
}
|
|
571
|
-
GlobalQueue.de
|
|
572
|
-
if (e.o?.
|
|
601
|
+
GlobalQueue.de?.(e);
|
|
602
|
+
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.me !== null) GlobalQueue.me(e);
|
|
573
603
|
}
|
|
574
604
|
if (o && !r) {
|
|
575
605
|
assignOrMergeLane(e, o);
|
|
576
606
|
}
|
|
577
|
-
const f = r ||
|
|
578
|
-
const a = r ||
|
|
579
|
-
|
|
607
|
+
const f = r || u;
|
|
608
|
+
const a = r || s ? undefined : o;
|
|
609
|
+
const c = statusNotifierOf(e);
|
|
610
|
+
if (c) {
|
|
580
611
|
if (r && n === STATUS_PENDING) {
|
|
581
612
|
return;
|
|
582
613
|
}
|
|
583
614
|
if (f) {
|
|
584
|
-
|
|
615
|
+
c.call(e, n, t);
|
|
585
616
|
} else {
|
|
586
|
-
|
|
617
|
+
c.call(e);
|
|
587
618
|
}
|
|
588
619
|
return;
|
|
589
620
|
}
|
|
590
621
|
forEachDependent(e, (e, r) => {
|
|
591
622
|
e.Te = clock;
|
|
592
|
-
if (n === STATUS_PENDING &&
|
|
623
|
+
if (n === STATUS_PENDING && i && !e.o?.le?.has(i) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
|
|
593
624
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
594
625
|
// It exists so the observer re-runs when the source settles, but it must
|
|
595
626
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
596
627
|
// read swallows those, and the async path must match. Re-run the observer
|
|
597
628
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
598
|
-
if (r.
|
|
629
|
+
if (r.ve && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
599
630
|
enqueueSub(e);
|
|
600
631
|
schedule();
|
|
601
632
|
return;
|
|
602
633
|
}
|
|
603
|
-
if (!f && !e.
|
|
634
|
+
if (!f && !e.Ae) queuePendingNode(e);
|
|
604
635
|
notifyStatus(e, n, t, f, a);
|
|
605
636
|
}
|
|
606
637
|
});
|
|
607
638
|
}
|
|
608
639
|
|
|
609
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|
|
640
|
+
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseFlightTeardown, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|
|
@@ -83,6 +83,60 @@ const CONFIG_HAS_LANE = 1 << 10;
|
|
|
83
83
|
* moved into the cold extension (§12), and an unconditional `_x` deref per
|
|
84
84
|
* marked node measurably taxed the propagation hot path (diamond -22%). */ const CONFIG_FW_CHILDREN = 1 << 12;
|
|
85
85
|
|
|
86
|
+
/** Authoritative-view reader (`until()`): while this node computes, reads
|
|
87
|
+
* dodge active optimistic OVERRIDES only — the predicate must observe
|
|
88
|
+
* arriving truth, never the caller's own tentative writes (which would
|
|
89
|
+
* trivially satisfy it). Everything else reads normally, INCLUDING
|
|
90
|
+
* transition-staged `_pendingValue`: staged data is authoritative (optimism
|
|
91
|
+
* lives only in override slots), and a hold that refused staged reads would
|
|
92
|
+
* deadlock on data the open transaction itself is holding (a refresh the
|
|
93
|
+
* action issued lands staged and cannot commit until the hold releases).
|
|
94
|
+
* read() checks the bit on the reading computation (`context`) directly — no
|
|
95
|
+
* ambient flag — so a shared computed the predicate pulls recomputes as
|
|
96
|
+
* itself (no bit) under the normal view, and its cache never forks. */ const CONFIG_AUTHORITATIVE_READ = 1 << 13;
|
|
97
|
+
|
|
98
|
+
/** Sticky mark: an authoritative-view reader read this node PAST an active
|
|
99
|
+
* override. The ack shape — an authoritative arrival EQUAL to the override —
|
|
100
|
+
* rides paths that are deliberately silent under A17 (every ordinary reader
|
|
101
|
+
* sees the override, so an equal landing changes nothing for them). A marked
|
|
102
|
+
* node notifies those readers on such paths anyway, so the landed truth is
|
|
103
|
+
* seen without re-firing ordinary subscribers. Never cleared — only nodes an
|
|
104
|
+
* until() predicate observed mid-override pay. */ const CONFIG_AUTHORITATIVE_OBSERVED = 1 << 14;
|
|
105
|
+
|
|
106
|
+
/** Promise-delivery effect (resolve()/until()): commits its computed value
|
|
107
|
+
* directly even when recomputing under its own held transition. These
|
|
108
|
+
* effects deliver applies on a microtask (#2930) instead of the stashed
|
|
109
|
+
* effect queues, so the value must ride the same immediate schedule — a
|
|
110
|
+
* staged value with an immediate apply delivers stale state (resolve) or
|
|
111
|
+
* deadlocks the hold (until). Safe because the node is a private leaf: no
|
|
112
|
+
* subscriber reads an effect's value, only its own apply does. */ const CONFIG_DIRECT_COMMIT = 1 << 15;
|
|
113
|
+
|
|
114
|
+
/** Fresh-pull reader (awaitable `refresh()`'s waiter effect): a read of a
|
|
115
|
+
* dirty source recomputes it inline even when the height gate defers to the
|
|
116
|
+
* flush. Closes the same-flush ordering race where a waiter created
|
|
117
|
+
* alongside a refresh() mark read the PRE-re-ask value as settled and
|
|
118
|
+
* delivered stale; with the pull, the waiter either parks on the re-ask's
|
|
119
|
+
* pending window (async — woken by the settle walk, which runs on every
|
|
120
|
+
* landing including equal-value ones) or serves its sync answer. resolve()
|
|
121
|
+
* deliberately keeps that race — its contract is "first settled value"
|
|
122
|
+
* (#2930), not "next quiescent state". */ const CONFIG_FRESH_READ = 1 << 16;
|
|
123
|
+
|
|
124
|
+
/** HELD truth (#3164): this node's staged `_pendingValue` is confirming
|
|
125
|
+
* truth riding a transaction that retains optimism, revealed only at that
|
|
126
|
+
* transaction's settle. Two arming sites, one meaning: the store fold
|
|
127
|
+
* (a landing staged into the retaining transaction) and until()'s
|
|
128
|
+
* flip-entanglement (a foreign carrier's staged write, stolen when it
|
|
129
|
+
* flipped the awaited predicate truthy). Until the reveal, ordinary
|
|
130
|
+
* readers — lane and speculative recomputes included — keep committed:
|
|
131
|
+
* the staging notified subscribers as a plain write, so without the mask
|
|
132
|
+
* a mid-hold recompute composes live optimism with the confirming truth,
|
|
133
|
+
* a frame no timeline contains (GabbeV's union tear). Authoritative
|
|
134
|
+
* readers (until()'s predicate) and latest() tunnel through — the
|
|
135
|
+
* exemption that keeps holds deadlock-free. Override-covered nodes never
|
|
136
|
+
* arm: the override is their display and its revert their notification
|
|
137
|
+
* (A17). Cleared at commit (the commit IS the reveal); subscribers masked
|
|
138
|
+
* during the hold are woken by finalizePureQueue's post-revert pass. */ const CONFIG_HELD_TRUTH = 1 << 17;
|
|
139
|
+
|
|
86
140
|
const STATUS_PENDING = 1 << 0;
|
|
87
141
|
|
|
88
142
|
const STATUS_ERROR = 1 << 1;
|
|
@@ -127,4 +181,4 @@ const defaultContext = {};
|
|
|
127
181
|
* @internal
|
|
128
182
|
*/ const $REFRESH = Symbol("refresh");
|
|
129
183
|
|
|
130
|
-
export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
|
|
184
|
+
export { $REFRESH, CONFIG_AUTHORITATIVE_OBSERVED, CONFIG_AUTHORITATIVE_READ, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_CHILD_COMPANIONS, CONFIG_DIRECT_COMMIT, CONFIG_FRESH_READ, CONFIG_FW_CHILDREN, CONFIG_HAS_COMPANIONS, CONFIG_HAS_LANE, CONFIG_HAS_SNAPSHOT, CONFIG_HELD_TRUTH, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OPTIMISTIC, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, OVERRIDE_UNDEFINED, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, REACTIVE_MISSED_WAKE, REACTIVE_NONE, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_SNAPSHOT_STALE, REACTIVE_ZOMBIE, STATUS_ERROR, STATUS_PENDING, STATUS_UNINITIALIZED, STORE_SNAPSHOT_PROPS, SUPPORTS_PROXY, defaultContext, unwrapOverride };
|