@solidjs/signals 2.0.0-beta.19 → 2.0.0-beta.21
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/README.md +5 -1
- package/dist/dev.js +753 -378
- package/dist/node.cjs +1947 -1607
- package/dist/prod/affects.js +17 -21
- package/dist/prod/core/action.js +66 -15
- package/dist/prod/core/async.js +57 -71
- package/dist/prod/core/constants.js +15 -1
- package/dist/prod/core/core.js +39 -39
- package/dist/prod/core/effect.js +14 -14
- package/dist/prod/core/graph.js +1 -1
- package/dist/prod/core/heap.js +11 -3
- package/dist/prod/core/lanes.js +23 -13
- package/dist/prod/core/optimistic.js +107 -99
- package/dist/prod/core/owner.js +23 -23
- package/dist/prod/core/scheduler.js +188 -181
- package/dist/prod/core/verdict.js +29 -15
- package/dist/prod/map.js +196 -167
- package/dist/prod/signals.js +1 -1
- package/dist/prod/store/optimistic.js +103 -68
- package/dist/prod/store/reconcile.js +80 -46
- package/dist/prod/store/store.js +204 -73
- package/dist/prod/store/utils.js +61 -42
- package/dist/types/core/action.d.ts +35 -6
- package/dist/types/core/constants.d.ts +12 -0
- package/dist/types/core/dev.d.ts +7 -0
- package/dist/types/core/lanes.d.ts +2 -0
- package/dist/types/core/scheduler.d.ts +2 -6
- package/dist/types/core/types.d.ts +9 -1
- package/dist/types/store/store.d.ts +8 -2
- package/dist/types-cjs/core/action.d.cts +35 -6
- package/dist/types-cjs/core/constants.d.cts +12 -0
- package/dist/types-cjs/core/dev.d.cts +7 -0
- package/dist/types-cjs/core/lanes.d.cts +2 -0
- package/dist/types-cjs/core/scheduler.d.cts +2 -6
- package/dist/types-cjs/core/types.d.cts +9 -1
- package/dist/types-cjs/store/store.d.cts +8 -2
- package/package.json +1 -1
package/dist/prod/affects.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { addPendingSource, setPendingError, settlePendingSource, forEachDependent, notifyStatus } from "./core/async.js";
|
|
2
2
|
|
|
3
|
-
import { $REFRESH
|
|
3
|
+
import { STATUS_PENDING, $REFRESH } from "./core/constants.js";
|
|
4
4
|
|
|
5
5
|
import { NotReadyError } from "./core/error.js";
|
|
6
6
|
|
|
7
7
|
import "./core/core.js";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { GlobalQueue, shiftAffectsMarks, globalQueue, schedule } from "./core/scheduler.js";
|
|
10
10
|
|
|
11
11
|
import "./core/verdict.js";
|
|
12
12
|
|
|
@@ -68,7 +68,7 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
68
68
|
const t = getAffectsSentinel(e);
|
|
69
69
|
const r = new NotReadyError(t);
|
|
70
70
|
forEachDependent(e, e => {
|
|
71
|
-
if (e.m
|
|
71
|
+
if (!e.m?.has(t)) {
|
|
72
72
|
notifyStatus(e, STATUS_PENDING, r);
|
|
73
73
|
}
|
|
74
74
|
});
|
|
@@ -84,7 +84,7 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
84
84
|
let r = false;
|
|
85
85
|
for (let f = 0; f < t.length; f++) {
|
|
86
86
|
const o = t[f];
|
|
87
|
-
if (!o.
|
|
87
|
+
if (!o.M) continue;
|
|
88
88
|
const s = getAffectsSentinel(o);
|
|
89
89
|
if (addPendingSource(e, s)) {
|
|
90
90
|
e.i |= STATUS_PENDING;
|
|
@@ -92,7 +92,7 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
92
92
|
r = true;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
if (r && GlobalQueue.
|
|
95
|
+
if (r && GlobalQueue.P !== null) GlobalQueue.P(e);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
@@ -101,24 +101,24 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
101
101
|
* bumps the refcount and pokes the node's verdict companions so an
|
|
102
102
|
* already-materialized `false` flips reactively.
|
|
103
103
|
*/ function markAffects(e) {
|
|
104
|
-
e.
|
|
104
|
+
e.M = (e.M || 0) + 1;
|
|
105
105
|
shiftAffectsMarks(1);
|
|
106
106
|
// Companions only exist once the verdict layer (isPending/latest) loaded;
|
|
107
107
|
// without them there is no materialized verdict to flip.
|
|
108
|
-
if (e.
|
|
108
|
+
if (e.M === 1 && GlobalQueue.P !== null) GlobalQueue.P(e);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Registers one `affects()` mark on a node: counts it, records the
|
|
113
113
|
* registration with the current transaction (after initTransition the queue's
|
|
114
|
-
*
|
|
114
|
+
* batch IS the active transition, mirroring `_optimisticNodes`), and
|
|
115
115
|
* propagates STATUS_PENDING downstream on the status rails so everything
|
|
116
116
|
* DERIVED from the marked data reads pending too. Propagation runs on every
|
|
117
117
|
* registration (not just the first): subscribers gained since an earlier
|
|
118
118
|
* overlapping registration get covered, and dedup stops re-descent early.
|
|
119
119
|
*/ function registerAffectsMark(e) {
|
|
120
120
|
markAffects(e);
|
|
121
|
-
globalQueue.N.push(e);
|
|
121
|
+
globalQueue.N._.push(e);
|
|
122
122
|
propagateAffectsMark(e);
|
|
123
123
|
schedule();
|
|
124
124
|
}
|
|
@@ -131,8 +131,8 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
131
131
|
* can't open a fresh override window that would itself need settlement.
|
|
132
132
|
*/ function releaseAffectsMark(e) {
|
|
133
133
|
shiftAffectsMarks(-1);
|
|
134
|
-
e.
|
|
135
|
-
if (!e.
|
|
134
|
+
e.M--;
|
|
135
|
+
if (!e.M) {
|
|
136
136
|
const t = e.t;
|
|
137
137
|
if (t) settlePendingSource(e, t, true);
|
|
138
138
|
GlobalQueue.R !== null && GlobalQueue.R(e);
|
|
@@ -156,12 +156,12 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
156
156
|
* keeps normal suspension semantics. Core's read path reaches this through
|
|
157
157
|
* `GlobalQueue._onlyMarkPending`, gated on the `activeAffectsMarks` counter.
|
|
158
158
|
*/ function onlyMarkPending(e) {
|
|
159
|
-
const t = e.
|
|
159
|
+
const t = e.m;
|
|
160
160
|
if (t) {
|
|
161
161
|
for (const e of t) if (!e.l) return false;
|
|
162
162
|
return true;
|
|
163
163
|
}
|
|
164
|
-
return
|
|
164
|
+
return false;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/**
|
|
@@ -175,14 +175,10 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
175
175
|
* the very status it reports on. Reached through
|
|
176
176
|
* `GlobalQueue._collectMarkSources`, gated on `activeAffectsMarks`.
|
|
177
177
|
*/ function collectMarkSources(e, t) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const e = r.l;
|
|
181
|
-
if (e && e.P) t.push(e);
|
|
182
|
-
} else if (e.M) {
|
|
183
|
-
for (const r of e.M) {
|
|
178
|
+
if (e.m) {
|
|
179
|
+
for (const r of e.m) {
|
|
184
180
|
const e = r.l;
|
|
185
|
-
if (e && e.
|
|
181
|
+
if (e && e.M) t.push(e);
|
|
186
182
|
}
|
|
187
183
|
}
|
|
188
184
|
}
|
package/dist/prod/core/action.js
CHANGED
|
@@ -4,23 +4,51 @@ import { isThenable } from "./async.js";
|
|
|
4
4
|
|
|
5
5
|
import "./core.js";
|
|
6
6
|
|
|
7
|
-
function restoreTransition(e,
|
|
7
|
+
function restoreTransition(e, r) {
|
|
8
8
|
globalQueue.initTransition(e);
|
|
9
|
-
const t =
|
|
9
|
+
const t = r();
|
|
10
10
|
flush();
|
|
11
11
|
return t;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* The primitive for mutations: imperative async workflows whose *writes span
|
|
16
|
+
* an async gap* — optimistic write, server round-trip, reconciling write —
|
|
17
|
+
* where intermediate state must not leak and failure must revert cleanly
|
|
18
|
+
* (pair with `createOptimistic` / `createOptimisticStore`).
|
|
19
|
+
*
|
|
20
|
+
* Navigation-shaped updates do not need an action. A plain setter call is
|
|
21
|
+
* enough: reads pull the async, and downstream async computeds hold their
|
|
22
|
+
* previous values per-node until the new ones are ready (`isPending` /
|
|
23
|
+
* `latest` expose the in-flight state). Reach for `action` only when writes
|
|
24
|
+
* happen *after* async work, not merely upstream of it.
|
|
25
|
+
*
|
|
26
|
+
* Framework-level actions (router form actions, server actions) are
|
|
27
|
+
* specializations of this primitive: they are actions in exactly this sense —
|
|
28
|
+
* the same transactional semantics — with form binding, serialization, and
|
|
29
|
+
* submission tracking layered on top. The shared name is deliberate.
|
|
30
|
+
*
|
|
15
31
|
* Wraps a generator function so each invocation runs as a single transaction
|
|
16
32
|
* (a "transition") that batches every signal/store write between yields. The
|
|
17
33
|
* surrounding UI sees one atomic update per yielded step; nothing is committed
|
|
18
34
|
* until the action either completes or the next `yield` resolves.
|
|
19
35
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
36
|
+
* `yield` is the transaction-safe suspension point: the action waits for a
|
|
37
|
+
* yielded promise and re-enters the transaction before running the code after
|
|
38
|
+
* it. A plain `await` does NOT — the runtime has no hook into an async
|
|
39
|
+
* generator's internal await continuations, so writes to fresh signals
|
|
40
|
+
* between an `await` and the next `yield` escape the transaction and commit
|
|
41
|
+
* immediately. `await` is still the ergonomic choice for typed results; just
|
|
42
|
+
* put a bare `yield` before any writes that follow it:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* const saved = await api.createTodo(text); // typed result
|
|
46
|
+
* yield; // re-enter the transaction before writing
|
|
47
|
+
* setTodos(t => { ... });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* (For the same reason, don't call `flush()` inside an action body — it
|
|
51
|
+
* drains the transaction mid-step.)
|
|
24
52
|
*
|
|
25
53
|
* Each call returns a `Promise` that resolves with the generator's return
|
|
26
54
|
* value, or rejects if it throws. Pair with `createOptimistic` /
|
|
@@ -31,10 +59,11 @@ function restoreTransition(e, n) {
|
|
|
31
59
|
* ```ts
|
|
32
60
|
* const [todos, setTodos] = createOptimisticStore<Todo[]>([]);
|
|
33
61
|
*
|
|
34
|
-
* const addTodo = action(function* (text: string) {
|
|
62
|
+
* const addTodo = action(async function* (text: string) {
|
|
35
63
|
* const tempId = crypto.randomUUID();
|
|
36
64
|
* setTodos(t => { t.push({ id: tempId, text, pending: true }); }); // optimistic
|
|
37
|
-
* const saved =
|
|
65
|
+
* const saved = await api.createTodo(text); // network round-trip, typed
|
|
66
|
+
* yield; // re-enter the transaction
|
|
38
67
|
* setTodos(t => {
|
|
39
68
|
* const i = t.findIndex(x => x.id === tempId);
|
|
40
69
|
* if (i >= 0) t[i] = saved;
|
|
@@ -45,23 +74,23 @@ function restoreTransition(e, n) {
|
|
|
45
74
|
* await addTodo("buy milk");
|
|
46
75
|
* ```
|
|
47
76
|
*/ function action(e) {
|
|
48
|
-
return (...
|
|
49
|
-
const i = e(...
|
|
77
|
+
return (...r) => new Promise((t, n) => {
|
|
78
|
+
const i = e(...r);
|
|
50
79
|
globalQueue.initTransition();
|
|
51
80
|
let o = activeTransition;
|
|
52
81
|
o.Te.push(i);
|
|
53
|
-
const done = (e,
|
|
82
|
+
const done = (e, r, s = false) => {
|
|
54
83
|
o = currentTransition(o);
|
|
55
84
|
const u = o.Te.indexOf(i);
|
|
56
85
|
if (u >= 0) o.Te.splice(u, 1);
|
|
57
86
|
setActiveTransition(o);
|
|
58
87
|
schedule();
|
|
59
|
-
s ? r
|
|
88
|
+
s ? n(r) : t(e);
|
|
60
89
|
};
|
|
61
|
-
const step = (e,
|
|
90
|
+
const step = (e, r) => {
|
|
62
91
|
let t;
|
|
63
92
|
try {
|
|
64
|
-
t =
|
|
93
|
+
t = r ? i.throw(e) : i.next(e);
|
|
65
94
|
} catch (e) {
|
|
66
95
|
return done(undefined, e, true);
|
|
67
96
|
}
|
|
@@ -73,7 +102,29 @@ function restoreTransition(e, n) {
|
|
|
73
102
|
};
|
|
74
103
|
const run = e => {
|
|
75
104
|
if (e.done) return done(e.value);
|
|
76
|
-
|
|
105
|
+
// Thenable assimilation can itself throw synchronously (a `then`
|
|
106
|
+
// getter, or a `then()` method that throws — #2918). Match `await`
|
|
107
|
+
// semantics: the failure is thrown back into the generator at the
|
|
108
|
+
// yield point (catchable there); if uncaught, step()'s guard settles
|
|
109
|
+
// the action so its iterator never leaks in the transition. The
|
|
110
|
+
// settled flag implements A+ 2.3.3.3.4.1: a throw after the thenable
|
|
111
|
+
// already called a callback is ignored.
|
|
112
|
+
let r = false;
|
|
113
|
+
try {
|
|
114
|
+
if (isThenable(e.value)) return void e.value.then(e => {
|
|
115
|
+
if (r) return;
|
|
116
|
+
r = true;
|
|
117
|
+
restoreTransition(o, () => step(e));
|
|
118
|
+
}, e => {
|
|
119
|
+
if (r) return;
|
|
120
|
+
r = true;
|
|
121
|
+
restoreTransition(o, () => step(e, true));
|
|
122
|
+
});
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (r) return;
|
|
125
|
+
r = true;
|
|
126
|
+
return void restoreTransition(o, () => step(e, true));
|
|
127
|
+
}
|
|
77
128
|
restoreTransition(o, () => step(e.value));
|
|
78
129
|
};
|
|
79
130
|
step();
|
package/dist/prod/core/async.js
CHANGED
|
@@ -14,42 +14,28 @@ import { resolveTransition, hasActiveOverride, assignOrMergeLane, resolveLane }
|
|
|
14
14
|
|
|
15
15
|
import { cleanup } from "./owner.js";
|
|
16
16
|
|
|
17
|
-
import { globalQueue, GlobalQueue, clock, schedule,
|
|
17
|
+
import { globalQueue, GlobalQueue, clock, schedule, flush, queuePendingNode, insertSubs } from "./scheduler.js";
|
|
18
18
|
|
|
19
|
+
// The lazily-created Set is the ONE container for pending sources. Its
|
|
20
|
+
// predecessor — a singular slot promoted to a Set on the second source —
|
|
21
|
+
// created dual state whose migration invariant was easy to break: a third
|
|
22
|
+
// overlapping source landed beside the Set and removePendingSource refused
|
|
23
|
+
// to clear it, stranding the Set members' pending forever (#2893).
|
|
19
24
|
function addPendingSource(e, n) {
|
|
20
|
-
if (e.m
|
|
21
|
-
|
|
22
|
-
// from migration until removePendingSource collapses back to one entry.
|
|
23
|
-
// Landing a third source in the singular slot instead created dual state
|
|
24
|
-
// that removePendingSource refused to clear, stranding the Set members'
|
|
25
|
-
// pending forever (#2893).
|
|
26
|
-
if (e.M) e.M.add(n); else if (!e.m) e.m = n; else {
|
|
27
|
-
e.M = new Set([ e.m, n ]);
|
|
28
|
-
e.m = undefined;
|
|
29
|
-
}
|
|
25
|
+
if (e.m?.has(n)) return false;
|
|
26
|
+
(e.m ??= new Set).add(n);
|
|
30
27
|
return true;
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
function removePendingSource(e, n) {
|
|
34
|
-
if (e.m)
|
|
35
|
-
|
|
36
|
-
e.m = undefined;
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
if (!e.M?.delete(n)) return false;
|
|
40
|
-
if (e.M.size === 1) {
|
|
41
|
-
e.m = e.M.values().next().value;
|
|
42
|
-
e.M = undefined;
|
|
43
|
-
} else if (e.M.size === 0) {
|
|
44
|
-
e.M = undefined;
|
|
45
|
-
}
|
|
31
|
+
if (!e.m?.delete(n)) return false;
|
|
32
|
+
if (e.m.size === 0) e.m = undefined;
|
|
46
33
|
return true;
|
|
47
34
|
}
|
|
48
35
|
|
|
49
36
|
function clearPendingSources(e) {
|
|
37
|
+
e.m?.clear();
|
|
50
38
|
e.m = undefined;
|
|
51
|
-
e.M?.clear();
|
|
52
|
-
e.M = undefined;
|
|
53
39
|
}
|
|
54
40
|
|
|
55
41
|
function setPendingError(e, n, r) {
|
|
@@ -84,21 +70,21 @@ function settlePendingSource(e, n = e,
|
|
|
84
70
|
// open a fresh transition-scoped override window that nothing reverts.
|
|
85
71
|
r = false) {
|
|
86
72
|
let t = false;
|
|
87
|
-
const
|
|
73
|
+
const o = new Set;
|
|
88
74
|
// Companion updates no-op without the verdict layer (null hooks).
|
|
89
|
-
const
|
|
75
|
+
const u = r ? GlobalQueue.R : GlobalQueue.P;
|
|
90
76
|
const settle = e => {
|
|
91
|
-
if (
|
|
92
|
-
|
|
77
|
+
if (o.has(e) || !removePendingSource(e, n)) return;
|
|
78
|
+
o.add(e);
|
|
93
79
|
e.Ie = clock;
|
|
94
|
-
const r = e.m
|
|
80
|
+
const r = e.m?.values().next().value;
|
|
95
81
|
if (r) {
|
|
96
82
|
setPendingError(e, r);
|
|
97
|
-
|
|
83
|
+
u !== null && u(e);
|
|
98
84
|
} else {
|
|
99
85
|
e.i &= ~STATUS_PENDING;
|
|
100
86
|
setPendingError(e);
|
|
101
|
-
|
|
87
|
+
u !== null && u(e);
|
|
102
88
|
if (e.Re) {
|
|
103
89
|
enqueueSub(e);
|
|
104
90
|
t = true;
|
|
@@ -118,19 +104,19 @@ function isThenable(e) {
|
|
|
118
104
|
|
|
119
105
|
function handleAsync(e, n, r) {
|
|
120
106
|
let t = false;
|
|
121
|
-
let
|
|
107
|
+
let o = false;
|
|
122
108
|
if (typeof n === "object" && n !== null) {
|
|
123
109
|
untrack(() => {
|
|
124
110
|
t = n[Symbol.asyncIterator];
|
|
125
|
-
|
|
111
|
+
o = !t && isThenable(n);
|
|
126
112
|
});
|
|
127
113
|
}
|
|
128
|
-
if (!
|
|
114
|
+
if (!o && !t) {
|
|
129
115
|
e.Ae = null;
|
|
130
116
|
return n;
|
|
131
117
|
}
|
|
132
118
|
e.Ae = n;
|
|
133
|
-
let
|
|
119
|
+
let u;
|
|
134
120
|
const handleError = r => {
|
|
135
121
|
if (e.Ae !== n) return;
|
|
136
122
|
globalQueue.initTransition(resolveTransition(e));
|
|
@@ -138,21 +124,21 @@ function handleAsync(e, n, r) {
|
|
|
138
124
|
notifyStatus(e, r instanceof NotReadyError ? STATUS_PENDING : STATUS_ERROR, r);
|
|
139
125
|
e.Ie = clock;
|
|
140
126
|
};
|
|
141
|
-
const asyncWrite = (t,
|
|
127
|
+
const asyncWrite = (t, o) => {
|
|
142
128
|
if (e.Ae !== n) return;
|
|
143
129
|
// If the node was dirtied by a newer write (optimistic override or regular),
|
|
144
130
|
// skip this stale async result — the upcoming flush will recompute the node
|
|
145
131
|
// with the new value, creating a fresh Promise that supersedes this one.
|
|
146
132
|
if (e.u & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY)) return;
|
|
147
133
|
globalQueue.initTransition(resolveTransition(e));
|
|
148
|
-
const
|
|
134
|
+
const u = !!(e.i & STATUS_UNINITIALIZED);
|
|
149
135
|
trimStaleDeps(e);
|
|
150
136
|
clearStatus(e);
|
|
151
137
|
const l = resolveLane(e);
|
|
152
138
|
if (l) l.Pe.delete(e);
|
|
153
139
|
if (r) {
|
|
154
140
|
r(t);
|
|
155
|
-
if (
|
|
141
|
+
if (u) clearStatus(e, true);
|
|
156
142
|
} else if (e.be !== undefined) {
|
|
157
143
|
// Optimistic node — resting OR covered by an active override — holds
|
|
158
144
|
// through the shared pending-node path, exactly like a plain async memo,
|
|
@@ -179,9 +165,9 @@ function handleAsync(e, n, r) {
|
|
|
179
165
|
// Route through lane's effect queue for independent flushing
|
|
180
166
|
const n = e.De;
|
|
181
167
|
const r = e.Ue;
|
|
182
|
-
const
|
|
168
|
+
const o = e.Ge;
|
|
183
169
|
try {
|
|
184
|
-
if (!n &&
|
|
170
|
+
if (!n && u || !o || !o(t, r)) {
|
|
185
171
|
e.Ue = t;
|
|
186
172
|
e.Ie = clock;
|
|
187
173
|
// The latest() shadow write gives latest() effects independent lanes; the
|
|
@@ -209,18 +195,18 @@ function handleAsync(e, n, r) {
|
|
|
209
195
|
settlePendingSource(e);
|
|
210
196
|
schedule();
|
|
211
197
|
flush();
|
|
212
|
-
|
|
198
|
+
o?.();
|
|
213
199
|
};
|
|
214
|
-
if (
|
|
215
|
-
let r = false, t = false,
|
|
200
|
+
if (o) {
|
|
201
|
+
let r = false, t = false, o, l = true;
|
|
216
202
|
n.then(e => {
|
|
217
203
|
if (l) {
|
|
218
|
-
|
|
204
|
+
u = e;
|
|
219
205
|
r = true;
|
|
220
206
|
} else asyncWrite(e);
|
|
221
207
|
}, e => {
|
|
222
208
|
if (l) {
|
|
223
|
-
|
|
209
|
+
o = e;
|
|
224
210
|
t = true;
|
|
225
211
|
} else handleError(e);
|
|
226
212
|
});
|
|
@@ -229,8 +215,8 @@ function handleAsync(e, n, r) {
|
|
|
229
215
|
// Settle through the same status path an async rejection uses, then
|
|
230
216
|
// unwind the in-progress synchronous read so the errored node isn't
|
|
231
217
|
// momentarily read as `undefined`.
|
|
232
|
-
handleError(
|
|
233
|
-
throw
|
|
218
|
+
handleError(o);
|
|
219
|
+
throw o;
|
|
234
220
|
} else if (!r) {
|
|
235
221
|
globalQueue.initTransition(resolveTransition(e));
|
|
236
222
|
throw new NotReadyError(context);
|
|
@@ -239,11 +225,11 @@ function handleAsync(e, n, r) {
|
|
|
239
225
|
if (t) {
|
|
240
226
|
const r = n[Symbol.asyncIterator]();
|
|
241
227
|
let t = false;
|
|
242
|
-
let
|
|
228
|
+
let o = false;
|
|
243
229
|
let l = true;
|
|
244
230
|
cleanup(() => {
|
|
245
|
-
if (
|
|
246
|
-
|
|
231
|
+
if (o) return;
|
|
232
|
+
o = true;
|
|
247
233
|
try {
|
|
248
234
|
const e = r.return?.();
|
|
249
235
|
if (isThenable(e)) e.then(undefined, () => {});
|
|
@@ -255,14 +241,14 @@ function handleAsync(e, n, r) {
|
|
|
255
241
|
if (c) {
|
|
256
242
|
i = r;
|
|
257
243
|
f = true;
|
|
258
|
-
if (r.done)
|
|
244
|
+
if (r.done) o = true;
|
|
259
245
|
} else if (e.Ae !== n) {
|
|
260
246
|
return;
|
|
261
247
|
} else if (!r.done) {
|
|
262
248
|
t = true;
|
|
263
249
|
asyncWrite(r.value, iterate);
|
|
264
250
|
} else {
|
|
265
|
-
|
|
251
|
+
o = true;
|
|
266
252
|
if (t) {
|
|
267
253
|
schedule();
|
|
268
254
|
flush();
|
|
@@ -276,20 +262,20 @@ function handleAsync(e, n, r) {
|
|
|
276
262
|
s = r;
|
|
277
263
|
a = true;
|
|
278
264
|
} else if (e.Ae === n) {
|
|
279
|
-
|
|
265
|
+
o = true;
|
|
280
266
|
handleError(r);
|
|
281
267
|
}
|
|
282
268
|
});
|
|
283
269
|
c = false;
|
|
284
270
|
if (a) {
|
|
285
271
|
// Match the promise branch, but only rethrow during the initial read.
|
|
286
|
-
|
|
272
|
+
o = true;
|
|
287
273
|
handleError(s);
|
|
288
274
|
if (l) throw s;
|
|
289
275
|
return true;
|
|
290
276
|
}
|
|
291
277
|
if (f && !i.done) {
|
|
292
|
-
|
|
278
|
+
u = i.value;
|
|
293
279
|
t = true;
|
|
294
280
|
return iterate();
|
|
295
281
|
}
|
|
@@ -303,11 +289,11 @@ function handleAsync(e, n, r) {
|
|
|
303
289
|
throw new NotReadyError(context);
|
|
304
290
|
}
|
|
305
291
|
}
|
|
306
|
-
return
|
|
292
|
+
return u;
|
|
307
293
|
}
|
|
308
294
|
|
|
309
295
|
function clearStatus(e, n = false) {
|
|
310
|
-
if (e.m
|
|
296
|
+
if (e.m) clearPendingSources(e);
|
|
311
297
|
if (e.Re) e.Re = false;
|
|
312
298
|
// The pending window is over; its quiet classification dies with it.
|
|
313
299
|
// (Unconditional: _reask is baked into the node literals, so this is a
|
|
@@ -317,50 +303,50 @@ function clearStatus(e, n = false) {
|
|
|
317
303
|
if (e.k) setPendingError(e);
|
|
318
304
|
// Update pending signal for isPending() reactivity (companions only exist
|
|
319
305
|
// once the verdict layer created them, which installs the hooks).
|
|
320
|
-
if (e.ye || e.pe) GlobalQueue.
|
|
306
|
+
if (e.ye || e.pe) GlobalQueue.P(e);
|
|
321
307
|
if (e.G && GlobalQueue.me !== null) GlobalQueue.me(e);
|
|
322
308
|
if (e.C) e.C();
|
|
323
309
|
}
|
|
324
310
|
|
|
325
|
-
function notifyStatus(e, n, r, t,
|
|
311
|
+
function notifyStatus(e, n, r, t, o) {
|
|
326
312
|
// Wrap regular errors to track source node
|
|
327
313
|
if (n === STATUS_ERROR && !(r instanceof StatusError) && !(r instanceof NotReadyError)) r = new StatusError(e, r);
|
|
328
|
-
const
|
|
314
|
+
const u = n === STATUS_PENDING && r instanceof NotReadyError ? r.source : undefined;
|
|
329
315
|
// Mark-sourced propagation must not capture subscribers into the marking
|
|
330
316
|
// action's transaction (#2893): they carry no held value needing a
|
|
331
317
|
// transition-scheduled commit, and stamping `_transition` on them would
|
|
332
318
|
// freeze unrelated writes that share a downstream memo until the action
|
|
333
319
|
// settles. Real async keeps queuing (its commits ride the transition).
|
|
334
|
-
const l =
|
|
320
|
+
const l = u?.l !== undefined;
|
|
335
321
|
// A real error is a settled verdict a mark must not erase (#2893): landing
|
|
336
322
|
// STATUS_PENDING here would clobber `_error` with the sentinel's
|
|
337
323
|
// NotReadyError, and unlike real async there is no arriving value whose
|
|
338
324
|
// recompute would surface the error again. Descent stops too — everything
|
|
339
325
|
// downstream holds the propagated error for the same reason.
|
|
340
326
|
if (l && e.i & STATUS_ERROR) return;
|
|
341
|
-
const i =
|
|
327
|
+
const i = u === e;
|
|
342
328
|
const s = n === STATUS_PENDING && e.be !== undefined && !i;
|
|
343
329
|
const f = s && hasActiveOverride(e);
|
|
344
330
|
if (!t) {
|
|
345
|
-
if (n === STATUS_PENDING &&
|
|
346
|
-
addPendingSource(e,
|
|
331
|
+
if (n === STATUS_PENDING && u) {
|
|
332
|
+
addPendingSource(e, u);
|
|
347
333
|
e.i = STATUS_PENDING | e.i & STATUS_UNINITIALIZED;
|
|
348
334
|
// Preserve the current source on this propagation so render-effect notification
|
|
349
335
|
// can register every distinct pending source with the transition.
|
|
350
|
-
setPendingError(e,
|
|
336
|
+
setPendingError(e, u, r);
|
|
351
337
|
} else {
|
|
352
338
|
clearPendingSources(e);
|
|
353
339
|
e.i = n | (n !== STATUS_ERROR ? e.i & STATUS_UNINITIALIZED : 0);
|
|
354
340
|
e.k = r;
|
|
355
341
|
}
|
|
356
|
-
GlobalQueue.
|
|
342
|
+
GlobalQueue.P !== null && GlobalQueue.P(e);
|
|
357
343
|
if (e.G && GlobalQueue.me !== null) GlobalQueue.me(e);
|
|
358
344
|
}
|
|
359
|
-
if (
|
|
360
|
-
assignOrMergeLane(e,
|
|
345
|
+
if (o && !t) {
|
|
346
|
+
assignOrMergeLane(e, o);
|
|
361
347
|
}
|
|
362
348
|
const a = t || f;
|
|
363
|
-
const c = t || s ? undefined :
|
|
349
|
+
const c = t || s ? undefined : o;
|
|
364
350
|
if (e.C) {
|
|
365
351
|
if (t && n === STATUS_PENDING) {
|
|
366
352
|
return;
|
|
@@ -374,7 +360,7 @@ function notifyStatus(e, n, r, t, u) {
|
|
|
374
360
|
}
|
|
375
361
|
forEachDependent(e, (e, t) => {
|
|
376
362
|
e.Ie = clock;
|
|
377
|
-
if (n === STATUS_PENDING &&
|
|
363
|
+
if (n === STATUS_PENDING && u && !e.m?.has(u) || n !== STATUS_PENDING && (e.k !== r || e.m)) {
|
|
378
364
|
// A pending-observer link is the subscription an `isPending` read created.
|
|
379
365
|
// It exists so the observer re-runs when the source settles, but it must
|
|
380
366
|
// not carry a real (non-NotReadyError) error — the synchronous `isPending`
|
|
@@ -61,6 +61,20 @@ const NOT_PENDING = {};
|
|
|
61
61
|
|
|
62
62
|
const NO_SNAPSHOT = {};
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Stand-in stored in `_overrideValue` for an optimistic write of literal
|
|
66
|
+
* `undefined` (#2898). The slot doubles as the optimistic-node brand
|
|
67
|
+
* (`undefined` = not optimistic, `NOT_PENDING` = at rest), so the raw value
|
|
68
|
+
* would erase the node's optimistic identity: the write turns invisible and
|
|
69
|
+
* follow-up writes route off the optimistic path and commit permanently.
|
|
70
|
+
* Same shape as NO_SNAPSHOT. Sites that surface the override VALUE unwrap
|
|
71
|
+
* via `visibleOverrideValue`; slot identity tests stay raw.
|
|
72
|
+
*/ const OVERRIDE_UNDEFINED = {};
|
|
73
|
+
|
|
74
|
+
/** Unwrap an active override's stored value for surfacing to readers (#2898). */ function unwrapOverride(E) {
|
|
75
|
+
return E === OVERRIDE_UNDEFINED ? undefined : E;
|
|
76
|
+
}
|
|
77
|
+
|
|
64
78
|
const STORE_SNAPSHOT_PROPS = "sp";
|
|
65
79
|
|
|
66
80
|
const SUPPORTS_PROXY = typeof Proxy === "function";
|
|
@@ -75,4 +89,4 @@ const defaultContext = {};
|
|
|
75
89
|
* @internal
|
|
76
90
|
*/ const $REFRESH = Symbol("refresh");
|
|
77
91
|
|
|
78
|
-
export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, CONFIG_OWNED_WRITE, CONFIG_SYNC, CONFIG_TRANSPARENT, EFFECT_RENDER, EFFECT_TRACKED, EFFECT_USER, NOT_PENDING, NO_SNAPSHOT, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_DISPOSED, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_LAZY, REACTIVE_MANUAL_WRITE, 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 };
|
|
92
|
+
export { $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_CHILDREN_FORBIDDEN, CONFIG_IN_SNAPSHOT_SCOPE, CONFIG_NO_SNAPSHOT, 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_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 };
|