@solidjs/signals 2.0.0-rc.4 → 2.0.0-rc.6
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 +1618 -232
- package/dist/node.cjs +2262 -1199
- package/dist/prod/boundaries.js +4 -1
- package/dist/prod/core/async.js +152 -101
- package/dist/prod/core/constants.js +55 -1
- package/dist/prod/core/core.js +305 -234
- package/dist/prod/core/effect.js +28 -28
- package/dist/prod/core/error.js +13 -1
- package/dist/prod/core/external.js +2 -2
- package/dist/prod/core/graph.js +27 -27
- package/dist/prod/core/heap.js +30 -30
- package/dist/prod/core/lanes.js +32 -32
- package/dist/prod/core/optimistic.js +57 -57
- package/dist/prod/core/owner.js +34 -34
- package/dist/prod/core/scheduler.js +346 -152
- package/dist/prod/core/verdict.js +122 -65
- package/dist/prod/index.js +3 -3
- package/dist/prod/map.js +106 -106
- package/dist/prod/signals.js +321 -26
- package/dist/prod/store/next/optimistic.js +363 -151
- package/dist/prod/store/next/patch.js +6 -6
- package/dist/prod/store/next/projection.js +25 -21
- package/dist/prod/store/next/store.js +223 -113
- package/dist/prod/store/store.js +2 -2
- package/dist/types/core/async.d.ts +2 -0
- package/dist/types/core/attribution-hooks.d.ts +11 -0
- package/dist/types/core/attribution.d.ts +57 -4
- package/dist/types/core/constants.d.ts +54 -0
- package/dist/types/core/core.d.ts +19 -20
- package/dist/types/core/dev.d.ts +8 -2
- package/dist/types/core/error.d.ts +9 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/scheduler.d.ts +39 -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 -11
- package/dist/types/store/next/optimistic.d.ts +13 -10
- package/dist/types/store/next/projection.d.ts +1 -1
- package/dist/types/store/next/store.d.ts +22 -0
- package/dist/types/store/next/target.d.ts +25 -0
- package/dist/types-cjs/core/async.d.cts +2 -0
- package/dist/types-cjs/core/attribution-hooks.d.cts +11 -0
- package/dist/types-cjs/core/attribution.d.cts +57 -4
- package/dist/types-cjs/core/constants.d.cts +54 -0
- package/dist/types-cjs/core/core.d.cts +19 -20
- package/dist/types-cjs/core/dev.d.cts +8 -2
- package/dist/types-cjs/core/error.d.cts +9 -0
- package/dist/types-cjs/core/index.d.cts +2 -2
- package/dist/types-cjs/core/scheduler.d.cts +39 -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 -11
- package/dist/types-cjs/store/next/optimistic.d.cts +13 -10
- package/dist/types-cjs/store/next/projection.d.cts +1 -1
- package/dist/types-cjs/store/next/store.d.cts +22 -0
- package/dist/types-cjs/store/next/target.d.cts +25 -0
- package/package.json +1 -1
package/dist/prod/boundaries.js
CHANGED
|
@@ -561,7 +561,10 @@ function flattenArray(e, t = [], r) {
|
|
|
561
561
|
} while (typeof n === "function" && !n.length);
|
|
562
562
|
}
|
|
563
563
|
if (Array.isArray(n)) {
|
|
564
|
-
|
|
564
|
+
// OR, don't overwrite: an accessor already pushed under doNotUnwrap
|
|
565
|
+
// still needs the resolving wrapper even when a later sibling
|
|
566
|
+
// fragment contains no functions (#3133).
|
|
567
|
+
s = flattenArray(n, t, r) || s;
|
|
565
568
|
} else if (r?.skipNonRendered && (n == null || n === true || n === false || n === "")) {
|
|
566
569
|
// skip
|
|
567
570
|
} else t.push(n);
|
package/dist/prod/core/async.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
3
|
import { untrack, ext, statusNotifierOf, setSignal, context } from "./core.js";
|
|
4
4
|
|
|
@@ -28,13 +28,15 @@ function addPendingSource(e, n) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function removePendingSource(e, n) {
|
|
31
|
-
|
|
32
|
-
if (
|
|
31
|
+
const t = e.o?.le;
|
|
32
|
+
if (!t?.delete(n)) return false;
|
|
33
|
+
if (!t.size) e.o.le = undefined;
|
|
33
34
|
return true;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
function clearPendingSources(e) {
|
|
37
|
-
|
|
38
|
+
// This set is node-owned and never shared; dropping the sole reference
|
|
39
|
+
// releases the set and every entry without a redundant clear() walk.
|
|
38
40
|
if (e.o !== null) e.o.le = undefined;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -140,40 +142,44 @@ function settleErroredDependents(e, n) {
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
function settlePendingSource(e) {
|
|
145
|
+
// The normal landing path already cleared the source's own set. Superseded
|
|
146
|
+
// re-parks arrive here with an abandoned self entry, which must retire in
|
|
147
|
+
// the same walk as its propagated copies.
|
|
148
|
+
removePendingSource(e, e);
|
|
143
149
|
let n = false;
|
|
144
150
|
let t;
|
|
145
151
|
const r = new Set;
|
|
146
152
|
// Companion updates no-op without the verdict layer (null hook).
|
|
147
153
|
const o = GlobalQueue.de;
|
|
148
|
-
const settle =
|
|
149
|
-
if (r.has(
|
|
150
|
-
r.add(
|
|
151
|
-
|
|
152
|
-
const
|
|
154
|
+
const settle = i => {
|
|
155
|
+
if (r.has(i) || !removePendingSource(i, e)) return;
|
|
156
|
+
r.add(i);
|
|
157
|
+
i.Te = clock;
|
|
158
|
+
const l = i.o?.le?.values().next().value;
|
|
153
159
|
// STATUS_ERROR + pending sources only coexist via an errored loading
|
|
154
160
|
// window's park (notifyStatus(STATUS_ERROR) clears pending sources
|
|
155
161
|
// otherwise): the settled error stays the answer through the settle —
|
|
156
162
|
// nulling it here would have reads throw `null` until the re-enqueued
|
|
157
163
|
// retry lands, or lose it entirely if that retry parks again (#2989).
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
160
|
-
if (!
|
|
161
|
-
o
|
|
164
|
+
const s = i.S & STATUS_ERROR;
|
|
165
|
+
if (l) {
|
|
166
|
+
if (!s) setPendingError(i, l);
|
|
167
|
+
o?.(i);
|
|
162
168
|
} else {
|
|
163
|
-
|
|
164
|
-
if (!
|
|
165
|
-
o
|
|
166
|
-
if (
|
|
167
|
-
enqueueSub(
|
|
169
|
+
i.S &= ~STATUS_PENDING;
|
|
170
|
+
if (!s) setPendingError(i);
|
|
171
|
+
o?.(i);
|
|
172
|
+
if (i.o?.fe) {
|
|
173
|
+
enqueueSub(i);
|
|
168
174
|
n = true;
|
|
169
175
|
}
|
|
170
|
-
if (
|
|
176
|
+
if (i.o !== null) i.o.fe = false;
|
|
171
177
|
// Fully settled with nobody watching: release candidate (#2934). Checked
|
|
172
178
|
// again at release time — deferred so unobserved() can't unlink subs
|
|
173
179
|
// lists this walk is still iterating.
|
|
174
|
-
if (!
|
|
180
|
+
if (!i.u && i.T & CONFIG_AUTO_DISPOSE) (t ??= []).push(i);
|
|
175
181
|
}
|
|
176
|
-
forEachDependent(
|
|
182
|
+
forEachDependent(i, settle);
|
|
177
183
|
};
|
|
178
184
|
forEachDependent(e, settle);
|
|
179
185
|
// Release before the flush schedule below: unobserved() pulls the node back
|
|
@@ -187,6 +193,14 @@ function isThenable(e) {
|
|
|
187
193
|
return e != null && typeof e === "object" && typeof e.then === "function";
|
|
188
194
|
}
|
|
189
195
|
|
|
196
|
+
/** Fire and clear a node's iterator-flight cancellation hook (#3122). */ function releaseFlightTeardown(e) {
|
|
197
|
+
const n = e.o?.Ee;
|
|
198
|
+
if (n != null) {
|
|
199
|
+
e.o.Ee = null;
|
|
200
|
+
n();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
190
204
|
function handleAsync(e, n, t) {
|
|
191
205
|
let r = false;
|
|
192
206
|
let o = false;
|
|
@@ -197,13 +211,18 @@ function handleAsync(e, n, t) {
|
|
|
197
211
|
});
|
|
198
212
|
}
|
|
199
213
|
if (!o && !r) {
|
|
200
|
-
if (e.o !== null) e.o.
|
|
214
|
+
if (e.o !== null) e.o.Ie = null;
|
|
201
215
|
// A sync landing is the first real answer for a loadingValue node.
|
|
202
|
-
e.
|
|
216
|
+
e.Ne = false;
|
|
203
217
|
return n;
|
|
204
218
|
}
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
// Flight replacement relies on recompute's supersede release for iterator
|
|
220
|
+
// teardown (#3122): every handleAsync call — including the projection
|
|
221
|
+
// self-registration — runs during a recompute of `el`, which has already
|
|
222
|
+
// fired _flightTeardown. A future non-recompute registration path must
|
|
223
|
+
// release it here before overwriting _inFlight.
|
|
224
|
+
ext(e).Ie = n;
|
|
225
|
+
let i;
|
|
207
226
|
// Settle-time transition re-entry. The loading rail is invisible to
|
|
208
227
|
// transactions (#2933): a boundary-caught first load never registers as an
|
|
209
228
|
// async reporter, so its settle — the boundary's fallback -> content
|
|
@@ -216,30 +235,34 @@ function handleAsync(e, n, t) {
|
|
|
216
235
|
// are the transaction's reveal machinery and always re-enter.
|
|
217
236
|
const settleTransition = () => {
|
|
218
237
|
const n = resolveTransition(e);
|
|
219
|
-
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n).
|
|
238
|
+
if (n && e.S & STATUS_UNINITIALIZED && !currentTransition(n)._e.has(e)) {
|
|
220
239
|
// Drop the stale stamp too: the plain settle write (setSignal) and the
|
|
221
240
|
// stash-path restamp both re-enter the transaction through it.
|
|
222
|
-
e.
|
|
241
|
+
e.Ae = null;
|
|
223
242
|
return;
|
|
224
243
|
}
|
|
225
244
|
globalQueue.initTransition(n);
|
|
226
245
|
};
|
|
227
246
|
const handleError = t => {
|
|
228
|
-
if (e.o?.
|
|
247
|
+
if (e.o?.Ie !== n) return;
|
|
229
248
|
// NotReadyError from rejected promises should be treated as pending, not error
|
|
230
249
|
let r = t instanceof NotReadyError;
|
|
231
|
-
if (r && e.
|
|
250
|
+
if (r && e.Ne) {
|
|
232
251
|
// Loading window: the flight died waiting on an unready source. Keep
|
|
233
252
|
// serving commit #0 — same parking as recompute's catch for sync
|
|
234
253
|
// dependency throws. The dead flight is released so the clock-gated
|
|
235
254
|
// error-retry pull (updateIfNecessary) can also re-ask.
|
|
236
|
-
if (e.o !== null) e.o.
|
|
255
|
+
if (e.o !== null) e.o.Ie = null;
|
|
237
256
|
parkLoadingWindow(e, t);
|
|
238
257
|
e.Te = clock;
|
|
239
258
|
return;
|
|
240
259
|
}
|
|
241
260
|
settleTransition();
|
|
242
261
|
notifyStatus(e, r ? STATUS_PENDING : STATUS_ERROR, t);
|
|
262
|
+
// A NotReady rejection is a landing into another pending source. The
|
|
263
|
+
// rejected flight will never settle its self entry, so transfer ownership
|
|
264
|
+
// after notifyStatus has propagated the replacement source.
|
|
265
|
+
if (r) settlePendingSource(e);
|
|
243
266
|
e.Te = clock;
|
|
244
267
|
// A real error settles derivatively-pending dependents (notifyStatus
|
|
245
268
|
// cleared their pending sources), so stranded lazy ones release here —
|
|
@@ -247,21 +270,29 @@ function handleAsync(e, n, t) {
|
|
|
247
270
|
if (!r) releaseSettledDependents(e);
|
|
248
271
|
};
|
|
249
272
|
const asyncWrite = (r, o) => {
|
|
250
|
-
if (e.o?.
|
|
273
|
+
if (e.o?.Ie !== n) return;
|
|
251
274
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
252
275
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
253
276
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
254
277
|
if (e.ie & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
255
278
|
settleTransition();
|
|
256
|
-
const
|
|
279
|
+
const i = !!(e.S & STATUS_UNINITIALIZED);
|
|
280
|
+
// Captured before clearStatus wipes it: a quiet re-ask's landing may be
|
|
281
|
+
// transition-held below, and the displayed value keeps answering the same
|
|
282
|
+
// question until the hold commits — the classification must survive to
|
|
283
|
+
// that reveal or companion synchronization briefly classifies the held
|
|
284
|
+
// old value as pending, a one-frame pulse to direct observers (#3178).
|
|
285
|
+
// A truthy capture implies `_x` exists, so the restore writes it directly.
|
|
286
|
+
const l = e.o?.De;
|
|
257
287
|
trimStaleDeps(e);
|
|
258
288
|
clearStatus(e);
|
|
259
|
-
|
|
260
|
-
|
|
289
|
+
if (l) e.o.De = true;
|
|
290
|
+
const s = resolveLane(e);
|
|
291
|
+
if (s) s.Oe.delete(e);
|
|
261
292
|
if (t) {
|
|
262
293
|
t(r);
|
|
263
|
-
if (
|
|
264
|
-
} else if (e.o?.
|
|
294
|
+
if (i) clearStatus(e, true);
|
|
295
|
+
} else if (e.o?.Pe !== undefined) {
|
|
265
296
|
// Optimistic node — resting OR covered by an active override — holds
|
|
266
297
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
267
298
|
// so the commit clears STATUS_UNINITIALIZED (#2806) and elevation to
|
|
@@ -271,8 +302,8 @@ function handleAsync(e, n, t) {
|
|
|
271
302
|
// (A17 — every reader sees the override); the revert reveals whatever
|
|
272
303
|
// has committed by then, so corrections reveal atomically with their
|
|
273
304
|
// transition rather than escaping it.
|
|
274
|
-
if (e.
|
|
275
|
-
e.
|
|
305
|
+
if (e.Re === NOT_PENDING) queuePendingNode(e);
|
|
306
|
+
e.Re = r;
|
|
276
307
|
// The hold is a companion-visible write like any other (A13/A19): the
|
|
277
308
|
// clearStatus() above computed its verdict before the hold existed, so
|
|
278
309
|
// isPending must re-derive (the value is not final until commit — V1)
|
|
@@ -280,24 +311,35 @@ function handleAsync(e, n, t) {
|
|
|
280
311
|
// only notified when the hold is visible to them: under an active
|
|
281
312
|
// override every reader sees the override (A17), so waking subs would
|
|
282
313
|
// re-show an unchanged view — the revert is the notification point.
|
|
283
|
-
GlobalQueue.
|
|
314
|
+
GlobalQueue.Ue?.(e, r);
|
|
284
315
|
if (!hasActiveOverride(e)) {
|
|
285
316
|
insertSubs(e);
|
|
317
|
+
} else if (e.T & CONFIG_AUTHORITATIVE_OBSERVED) {
|
|
318
|
+
// A17 silence is stated over ordinary readers; an authoritative-view
|
|
319
|
+
// reader (until()'s predicate) observed this node PAST its override
|
|
320
|
+
// and is waiting for exactly this staged truth. Without the wake the
|
|
321
|
+
// hold deadlocks: the landing waits on the transaction, the
|
|
322
|
+
// transaction on the action, the action on an until() that was never
|
|
323
|
+
// re-notified (#3164). Same selective wake as the equal-landing
|
|
324
|
+
// branch in recompute(). Optional call: the bit implies the
|
|
325
|
+
// optimistic engine WAS consulted, but the hook only installs with
|
|
326
|
+
// it — a bare-core build must not crash here.
|
|
327
|
+
GlobalQueue.he?.(e);
|
|
286
328
|
}
|
|
287
329
|
e.Te = clock;
|
|
288
|
-
} else if (
|
|
330
|
+
} else if (s) {
|
|
289
331
|
// Route through lane's effect queue for independent flushing
|
|
290
|
-
const n = e.
|
|
332
|
+
const n = e.ge;
|
|
291
333
|
const t = e.be;
|
|
292
|
-
const o = e.
|
|
334
|
+
const o = e.pe;
|
|
293
335
|
try {
|
|
294
|
-
if (!n &&
|
|
336
|
+
if (!n && i || !o || !o(r, t)) {
|
|
295
337
|
e.be = r;
|
|
296
338
|
e.Te = clock;
|
|
297
339
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
298
340
|
// _pendingSignal update is a no-op repeat of the clearStatus() call above
|
|
299
341
|
// (computePendingState doesn't read _value).
|
|
300
|
-
GlobalQueue.
|
|
342
|
+
GlobalQueue.Ue?.(e, r);
|
|
301
343
|
insertSubs(e, true);
|
|
302
344
|
}
|
|
303
345
|
} catch (n) {
|
|
@@ -321,8 +363,12 @@ function handleAsync(e, n, t) {
|
|
|
321
363
|
// (`_pendingValue` set above or inside setSignal) is not — the verdict's
|
|
322
364
|
// held-value branch is window-gated, and commitPendingNode closes the
|
|
323
365
|
// window when the hold commits, so no one-frame isPending pulse can leak
|
|
324
|
-
// to live observers between the landing and its commit (#2990).
|
|
325
|
-
|
|
366
|
+
// to live observers between the landing and its commit (#2990). The
|
|
367
|
+
// quiet re-ask classification follows the same schedule (#3178).
|
|
368
|
+
if (e.Re === NOT_PENDING) {
|
|
369
|
+
e.Ne = false;
|
|
370
|
+
if (l) e.o.De = false;
|
|
371
|
+
}
|
|
326
372
|
settlePendingSource(e);
|
|
327
373
|
schedule();
|
|
328
374
|
flush();
|
|
@@ -355,18 +401,23 @@ function handleAsync(e, n, t) {
|
|
|
355
401
|
// meaningful only in the live posture.
|
|
356
402
|
const consumeIterator = (t, r) => {
|
|
357
403
|
const o = t[Symbol.asyncIterator]();
|
|
358
|
-
let
|
|
359
|
-
let
|
|
360
|
-
let
|
|
404
|
+
let l = false;
|
|
405
|
+
let s = false;
|
|
406
|
+
let u = !r;
|
|
361
407
|
const close = () => {
|
|
362
|
-
if (
|
|
363
|
-
|
|
408
|
+
if (s) return;
|
|
409
|
+
s = true;
|
|
364
410
|
try {
|
|
365
411
|
const e = o.return?.();
|
|
366
412
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
367
413
|
} catch {}
|
|
368
414
|
};
|
|
369
415
|
r ? r(close) : cleanup(close);
|
|
416
|
+
// Flight-identity cancellation (#3122): the registration above is the
|
|
417
|
+
// owner-death backstop, but its disposal list can be zombie-deferred
|
|
418
|
+
// until the SUPERSEDING flight settles. The teardown slot fires at the
|
|
419
|
+
// _inFlight release sites so supersede stops this stream immediately.
|
|
420
|
+
ext(e).Ee = close;
|
|
370
421
|
// Release check before each next pull: an unobserved lazy node must tear
|
|
371
422
|
// down (its close above runs via disposal, closing the iterator) instead
|
|
372
423
|
// of pumping the stream forever with zero subscribers (#2935).
|
|
@@ -392,18 +443,18 @@ function handleAsync(e, n, t) {
|
|
|
392
443
|
// stash: it must write through the async path or the value is
|
|
393
444
|
// silently dropped. (The deferred posture never has a caller, so
|
|
394
445
|
// initialRead starts false there and everything writes through.)
|
|
395
|
-
if (c &&
|
|
446
|
+
if (c && u) {
|
|
396
447
|
t = r;
|
|
397
448
|
f = true;
|
|
398
|
-
if (r.done)
|
|
399
|
-
} else if (e.o?.
|
|
449
|
+
if (r.done) s = true;
|
|
450
|
+
} else if (e.o?.Ie !== n) {
|
|
400
451
|
return;
|
|
401
452
|
} else if (!r.done) {
|
|
402
|
-
|
|
453
|
+
l = true;
|
|
403
454
|
asyncWrite(r.value, iterateOrRelease);
|
|
404
455
|
} else {
|
|
405
|
-
|
|
406
|
-
if (
|
|
456
|
+
s = true;
|
|
457
|
+
if (l) {
|
|
407
458
|
schedule();
|
|
408
459
|
flush();
|
|
409
460
|
} else {
|
|
@@ -413,11 +464,11 @@ function handleAsync(e, n, t) {
|
|
|
413
464
|
settleAutodispose();
|
|
414
465
|
}
|
|
415
466
|
}, t => {
|
|
416
|
-
if (c &&
|
|
467
|
+
if (c && u) {
|
|
417
468
|
r = t;
|
|
418
469
|
a = true;
|
|
419
|
-
} else if (e.o?.
|
|
420
|
-
|
|
470
|
+
} else if (e.o?.Ie === n) {
|
|
471
|
+
s = true;
|
|
421
472
|
handleError(t);
|
|
422
473
|
settleAutodispose();
|
|
423
474
|
}
|
|
@@ -425,27 +476,27 @@ function handleAsync(e, n, t) {
|
|
|
425
476
|
c = false;
|
|
426
477
|
if (a) {
|
|
427
478
|
// Match the promise branch, but only rethrow during the initial read.
|
|
428
|
-
|
|
479
|
+
s = true;
|
|
429
480
|
handleError(r);
|
|
430
|
-
if (
|
|
481
|
+
if (u) throw r;
|
|
431
482
|
return true;
|
|
432
483
|
}
|
|
433
484
|
if (f && !t.done) {
|
|
434
|
-
|
|
435
|
-
|
|
485
|
+
i = t.value;
|
|
486
|
+
l = true;
|
|
436
487
|
return iterate();
|
|
437
488
|
}
|
|
438
489
|
return f && t.done;
|
|
439
490
|
};
|
|
440
491
|
const f = iterate();
|
|
441
492
|
// Later iterate() calls run from asyncWrite, where rethrowing would be unhandled.
|
|
442
|
-
|
|
443
|
-
return
|
|
493
|
+
u = false;
|
|
494
|
+
return l || f;
|
|
444
495
|
};
|
|
445
496
|
// Landed-synchronously verdict for a LIVE iterator drain; null when no live
|
|
446
497
|
// drain ran (plain promise flight, or a deferred flatten). Drives the
|
|
447
498
|
// shared NotReady/loading tail below.
|
|
448
|
-
let
|
|
499
|
+
let l = null;
|
|
449
500
|
// Flatten one async level: a thenable that RESOLVES to an AsyncIterable —
|
|
450
501
|
// the shape every async stub returning a stream produces — consumes as the
|
|
451
502
|
// stream itself rather than settling on the iterable object. One level
|
|
@@ -460,11 +511,11 @@ function handleAsync(e, n, t) {
|
|
|
460
511
|
}
|
|
461
512
|
if (!t) return false;
|
|
462
513
|
const r = consumeIterator(e, n);
|
|
463
|
-
if (!n)
|
|
514
|
+
if (!n) l = r;
|
|
464
515
|
return true;
|
|
465
516
|
};
|
|
466
517
|
if (o) {
|
|
467
|
-
let t = false, r = false, o,
|
|
518
|
+
let t = false, r = false, o, l = true;
|
|
468
519
|
// Close registration for the flattening path. Consumption starts in a
|
|
469
520
|
// microtask where the ambient owner is gone (or worse, someone else's),
|
|
470
521
|
// so `cleanup()` can't be used — the close targets el's disposal list
|
|
@@ -476,18 +527,18 @@ function handleAsync(e, n, t) {
|
|
|
476
527
|
// becomes disposal-bearing, which is exactly the class a directly
|
|
477
528
|
// returned iterable already occupies.
|
|
478
529
|
const registerDeferredClose = n => {
|
|
479
|
-
if (!e.
|
|
530
|
+
if (!e.Ge) e.Ge = n; else if (Array.isArray(e.Ge)) e.Ge.push(n); else e.Ge = [ e.Ge, n ];
|
|
480
531
|
};
|
|
481
532
|
n.then(r => {
|
|
482
|
-
if (
|
|
483
|
-
|
|
533
|
+
if (l) {
|
|
534
|
+
i = r;
|
|
484
535
|
t = true;
|
|
485
|
-
} else if (e.o?.
|
|
536
|
+
} else if (e.o?.Ie === n && !(e.ie & REACTIVE_DISPOSED) && flattenIfIterable(r, registerDeferredClose)) ; else {
|
|
486
537
|
asyncWrite(r);
|
|
487
538
|
settleAutodispose();
|
|
488
539
|
}
|
|
489
540
|
}, e => {
|
|
490
|
-
if (
|
|
541
|
+
if (l) {
|
|
491
542
|
o = e;
|
|
492
543
|
r = true;
|
|
493
544
|
} else {
|
|
@@ -495,7 +546,7 @@ function handleAsync(e, n, t) {
|
|
|
495
546
|
settleAutodispose();
|
|
496
547
|
}
|
|
497
548
|
});
|
|
498
|
-
|
|
549
|
+
l = false;
|
|
499
550
|
if (r) {
|
|
500
551
|
// Settle through the same status path an async rejection uses, then
|
|
501
552
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
@@ -507,30 +558,30 @@ function handleAsync(e, n, t) {
|
|
|
507
558
|
// is opened — first-flight work on a loadingValue node is loading-class
|
|
508
559
|
// (invisible to boundaries and transitions); the flight itself is
|
|
509
560
|
// already registered in _inFlight and lands through asyncWrite.
|
|
510
|
-
if (e.
|
|
561
|
+
if (e.Ne) return e.be;
|
|
511
562
|
globalQueue.initTransition(resolveTransition(e));
|
|
512
563
|
throw new NotReadyError(context);
|
|
513
|
-
} else if (!flattenIfIterable(
|
|
564
|
+
} else if (!flattenIfIterable(i)) {
|
|
514
565
|
// Synchronously-resolved promise: the first real answer landed.
|
|
515
|
-
e.
|
|
566
|
+
e.Ne = false;
|
|
516
567
|
}
|
|
517
568
|
// A sync-resolved promise holding an AsyncIterable flattened LIVE (we
|
|
518
569
|
// are still inside the synchronous read): full initial-drain semantics
|
|
519
570
|
// apply and the shared tail below settles the verdict.
|
|
520
571
|
}
|
|
521
572
|
if (r) flattenIfIterable(n);
|
|
522
|
-
if (
|
|
523
|
-
if (!
|
|
573
|
+
if (l !== null) {
|
|
574
|
+
if (!l) {
|
|
524
575
|
// Loading window: serve commit #0 (see the promise branch above).
|
|
525
|
-
if (e.
|
|
576
|
+
if (e.Ne) return e.be;
|
|
526
577
|
globalQueue.initTransition(resolveTransition(e));
|
|
527
578
|
throw new NotReadyError(context);
|
|
528
579
|
}
|
|
529
580
|
// A sync first yield (or immediate empty completion) is the first real
|
|
530
581
|
// answer; async yields clear inside asyncWrite.
|
|
531
|
-
e.
|
|
582
|
+
e.Ne = false;
|
|
532
583
|
}
|
|
533
|
-
return
|
|
584
|
+
return i;
|
|
534
585
|
}
|
|
535
586
|
|
|
536
587
|
function clearStatus(e, n = false) {
|
|
@@ -539,13 +590,13 @@ function clearStatus(e, n = false) {
|
|
|
539
590
|
// The pending window is over; its quiet classification dies with it.
|
|
540
591
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
541
592
|
// plain store to an existing slot — no shape change.)
|
|
542
|
-
if (e.o !== null) e.o.
|
|
593
|
+
if (e.o !== null) e.o.De = false;
|
|
543
594
|
e.S = n ? 0 : e.S & STATUS_UNINITIALIZED;
|
|
544
595
|
if (e.o?._) setPendingError(e);
|
|
545
596
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
546
597
|
// once the verdict layer created them, which installs the hooks).
|
|
547
|
-
if (e.o?.
|
|
548
|
-
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.
|
|
598
|
+
if (e.o?.ye || e.o?.Ce) GlobalQueue.de(e);
|
|
599
|
+
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.me !== null) GlobalQueue.me(e);
|
|
549
600
|
const t = statusNotifierOf(e);
|
|
550
601
|
if (t) t.call(e);
|
|
551
602
|
}
|
|
@@ -553,30 +604,30 @@ function clearStatus(e, n = false) {
|
|
|
553
604
|
function notifyStatus(e, n, t, r, o) {
|
|
554
605
|
// Wrap regular errors to track source node
|
|
555
606
|
if (n === STATUS_ERROR && !(t instanceof StatusError) && !(t instanceof NotReadyError)) t = new StatusError(e, t);
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
const
|
|
607
|
+
const i = n === STATUS_PENDING && t instanceof NotReadyError ? t.source : undefined;
|
|
608
|
+
const l = i === e;
|
|
609
|
+
const s = n === STATUS_PENDING && e.o?.Pe !== undefined && !l;
|
|
610
|
+
const u = s && hasActiveOverride(e);
|
|
560
611
|
if (!r) {
|
|
561
|
-
if (n === STATUS_PENDING &&
|
|
562
|
-
addPendingSource(e,
|
|
612
|
+
if (n === STATUS_PENDING && i) {
|
|
613
|
+
addPendingSource(e, i);
|
|
563
614
|
e.S = STATUS_PENDING | e.S & STATUS_UNINITIALIZED;
|
|
564
615
|
// Preserve the current source on this propagation so render-effect notification
|
|
565
616
|
// can register every distinct pending source with the transition.
|
|
566
|
-
setPendingError(e,
|
|
617
|
+
setPendingError(e, i, t);
|
|
567
618
|
} else {
|
|
568
619
|
clearPendingSources(e);
|
|
569
620
|
e.S = n | (n !== STATUS_ERROR ? e.S & STATUS_UNINITIALIZED : 0);
|
|
570
621
|
ext(e)._ = t;
|
|
571
622
|
}
|
|
572
|
-
GlobalQueue.de
|
|
573
|
-
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.
|
|
623
|
+
GlobalQueue.de?.(e);
|
|
624
|
+
if (e.o?.i && e.T & CONFIG_CHILD_COMPANIONS && GlobalQueue.me !== null) GlobalQueue.me(e);
|
|
574
625
|
}
|
|
575
626
|
if (o && !r) {
|
|
576
627
|
assignOrMergeLane(e, o);
|
|
577
628
|
}
|
|
578
|
-
const f = r ||
|
|
579
|
-
const a = r ||
|
|
629
|
+
const f = r || u;
|
|
630
|
+
const a = r || s ? undefined : o;
|
|
580
631
|
const c = statusNotifierOf(e);
|
|
581
632
|
if (c) {
|
|
582
633
|
if (r && n === STATUS_PENDING) {
|
|
@@ -591,21 +642,21 @@ function notifyStatus(e, n, t, r, o) {
|
|
|
591
642
|
}
|
|
592
643
|
forEachDependent(e, (e, r) => {
|
|
593
644
|
e.Te = clock;
|
|
594
|
-
if (n === STATUS_PENDING &&
|
|
645
|
+
if (n === STATUS_PENDING && i && !e.o?.le?.has(i) || n !== STATUS_PENDING && (e.o?._ !== t || e.o?.le)) {
|
|
595
646
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
596
647
|
// It exists so the observer re-runs when the source settles, but it must
|
|
597
648
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
598
649
|
// read swallows those, and the async path must match. Re-run the observer
|
|
599
650
|
// so `isPending` re-evaluates (to not-pending) instead of forwarding.
|
|
600
|
-
if (r.
|
|
651
|
+
if (r.ve && n !== STATUS_PENDING && !(t instanceof NotReadyError)) {
|
|
601
652
|
enqueueSub(e);
|
|
602
653
|
schedule();
|
|
603
654
|
return;
|
|
604
655
|
}
|
|
605
|
-
if (!f && !e.
|
|
656
|
+
if (!f && !e.Ae) queuePendingNode(e);
|
|
606
657
|
notifyStatus(e, n, t, f, a);
|
|
607
658
|
}
|
|
608
659
|
});
|
|
609
660
|
}
|
|
610
661
|
|
|
611
|
-
export { addPendingSource, clearStatus, forEachDependent, handleAsync, isThenable, notifyStatus, parkLoadingWindow, releaseSettledDependents, setPendingError, settleErroredDependents, settlePendingSource };
|
|
662
|
+
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 };
|