@solidjs/signals 2.0.0-beta.21 → 2.0.0-beta.22
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 +182 -298
- package/dist/node.cjs +1055 -1166
- package/dist/prod/affects.js +71 -163
- package/dist/prod/boundaries.js +141 -137
- package/dist/prod/core/action.js +3 -3
- package/dist/prod/core/async.js +83 -99
- package/dist/prod/core/core.js +254 -297
- package/dist/prod/core/effect.js +46 -46
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +54 -46
- package/dist/prod/core/heap.js +50 -50
- package/dist/prod/core/lanes.js +34 -34
- package/dist/prod/core/optimistic.js +64 -106
- package/dist/prod/core/owner.js +52 -52
- package/dist/prod/core/scheduler.js +209 -210
- package/dist/prod/core/verdict.js +129 -78
- package/dist/prod/map.js +101 -101
- package/dist/prod/signals.js +23 -5
- package/dist/prod/store/optimistic.js +11 -11
- package/dist/prod/store/projection.js +2 -2
- package/dist/prod/store/store.js +14 -14
- package/dist/types/affects.d.ts +9 -9
- package/dist/types/core/async.d.ts +1 -1
- package/dist/types/core/error.d.ts +7 -0
- package/dist/types/core/scheduler.d.ts +1 -6
- package/dist/types/core/types.d.ts +6 -11
- package/dist/types-cjs/affects.d.cts +9 -9
- package/dist/types-cjs/core/async.d.cts +1 -1
- package/dist/types-cjs/core/error.d.cts +7 -0
- package/dist/types-cjs/core/scheduler.d.cts +1 -6
- package/dist/types-cjs/core/types.d.cts +6 -11
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE } from "./constants.js";
|
|
1
|
+
import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_MANUAL_WRITE, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
|
|
2
2
|
|
|
3
3
|
import { setSignal, prepareComputed, read, context, stale, currentOptimisticLane, tracking, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
|
|
4
4
|
|
|
@@ -14,7 +14,7 @@ import { findLane, hasActiveOverride } from "./lanes.js";
|
|
|
14
14
|
|
|
15
15
|
import { installOptimisticEngine } from "./optimistic.js";
|
|
16
16
|
|
|
17
|
-
import { GlobalQueue, clock, insertSubs, schedule, activeTransition } from "./scheduler.js";
|
|
17
|
+
import { GlobalQueue, clock, insertSubs, schedule, activeTransition, activeAffectsMarks } from "./scheduler.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* The isPending()/latest() verdict layer, moved out of core.ts. Importing this
|
|
@@ -32,21 +32,21 @@ let pendingProbe = null;
|
|
|
32
32
|
* Get or create the pending signal for a node (lazy).
|
|
33
33
|
* Used by isPending() to track pending state reactively.
|
|
34
34
|
*/ function getPendingSignal(e) {
|
|
35
|
-
if (!e.
|
|
35
|
+
if (!e.ge) {
|
|
36
36
|
// Start false, write true if pending - ensures reversion returns to false
|
|
37
|
-
e.
|
|
37
|
+
e.ge = optimisticSignal(false, {
|
|
38
38
|
ownedWrite: true
|
|
39
39
|
});
|
|
40
|
-
e.
|
|
41
|
-
if (computePendingState(e)) setSignal(e.
|
|
40
|
+
e.ge.nn = e;
|
|
41
|
+
if (computePendingState(e)) setSignal(e.ge, true);
|
|
42
42
|
}
|
|
43
|
-
return e.
|
|
43
|
+
return e.ge;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function collectPendingSources(e) {
|
|
47
47
|
if (!pendingProbe) return;
|
|
48
48
|
pendingProbe.sources.add(e);
|
|
49
|
-
const t = e.
|
|
49
|
+
const t = e.nt || e;
|
|
50
50
|
if (t !== e) pendingProbe.sources.add(t);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -60,65 +60,116 @@ function collectPendingSources(e) {
|
|
|
60
60
|
pendingProbe?.sources.add(e);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* The affects() coverage walk — the read half of the dedicated mark channel.
|
|
65
|
+
* A node is covered by a live mark iff it carries one (`_affectsCount`) or
|
|
66
|
+
* derives, through its CURRENT deps (hopping store firewalls), from a node
|
|
67
|
+
* that does. Pull-based coverage means graph rewires, mid-window recomputes,
|
|
68
|
+
* and probe-triggered recomputes can never strand or strip a mark — there is
|
|
69
|
+
* nothing stored downstream to corrupt. Probe-created links
|
|
70
|
+
* (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
|
|
71
|
+
* inherits the coverage it reports on.
|
|
72
|
+
*/ function markWalk(e, t) {
|
|
73
|
+
if (e.t) return true;
|
|
74
|
+
// A real error outranks an inherited mark (A16/A24c): an errored node
|
|
75
|
+
// answers probes with its error, not a coverage verdict, and coverage does
|
|
76
|
+
// not flow through it — matching the rails' behavior, where propagation
|
|
77
|
+
// stopped at errored nodes. A DIRECT mark on an errored node still reads
|
|
78
|
+
// pending (the count check above), also matching.
|
|
79
|
+
if (e.S & STATUS_ERROR) return false;
|
|
80
|
+
if (t.has(e)) return false;
|
|
81
|
+
t.add(e);
|
|
82
|
+
const n = e.nt;
|
|
83
|
+
if (n && markWalk(n, t)) return true;
|
|
84
|
+
// Mid-recompute (the clearStatus companion poke runs before
|
|
85
|
+
// trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
|
|
86
|
+
// pass's dependency set — walking past it would read dropped deps and
|
|
87
|
+
// latch a stale verdict on the companion.
|
|
88
|
+
const i = e;
|
|
89
|
+
const r = i.se & REACTIVE_RECOMPUTING_DEPS ? i.qe : undefined;
|
|
90
|
+
if (r !== null) {
|
|
91
|
+
for (let e = i.Xe ?? null; e !== null; e = e.et) {
|
|
92
|
+
if (!e.De && markWalk(e.tt, t)) return true;
|
|
93
|
+
if (e === r) break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Gated entry: apps with no live mark pay one integer compare. */ function markCovered(e) {
|
|
100
|
+
return activeAffectsMarks !== 0 && markWalk(e, new Set);
|
|
101
|
+
}
|
|
102
|
+
|
|
63
103
|
function quietPending(e) {
|
|
64
|
-
if (e.
|
|
65
|
-
for (const t of e.
|
|
104
|
+
if (e.oe) {
|
|
105
|
+
for (const t of e.oe) if (!t.be) return false;
|
|
66
106
|
return true;
|
|
67
107
|
}
|
|
68
|
-
return e.
|
|
108
|
+
return e.be;
|
|
69
109
|
}
|
|
70
110
|
|
|
71
111
|
function newQuestionInFlight(e) {
|
|
72
|
-
return !!(e.
|
|
112
|
+
return !!(e.S & STATUS_PENDING) && !(e.S & STATUS_UNINITIALIZED) && !quietPending(e);
|
|
73
113
|
}
|
|
74
114
|
|
|
75
115
|
function computePendingState(e) {
|
|
76
116
|
const t = e;
|
|
77
|
-
if (t.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
117
|
+
if (t.se & REACTIVE_DISPOSED) return false;
|
|
118
|
+
// Mark coverage is transitive by dep-graph reachability: a latest() shadow
|
|
119
|
+
// reaches its owner (and a store leaf its firewall) through its own deps,
|
|
120
|
+
// so the one walk covers direct marks, derivation, and companion chains.
|
|
121
|
+
if (markCovered(e)) return true;
|
|
122
|
+
const n = e.nt;
|
|
123
|
+
if (e.nn) {
|
|
124
|
+
const t = e.nn;
|
|
125
|
+
const n = t.nt || t;
|
|
84
126
|
return newQuestionInFlight(n);
|
|
85
127
|
}
|
|
86
|
-
if (n && e.
|
|
87
|
-
return !!(n.
|
|
128
|
+
if (n && e.Ne !== NOT_PENDING && !hasActiveOverride(e)) {
|
|
129
|
+
return !!(n.se & REACTIVE_MANUAL_WRITE) || !n.Te && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
|
|
88
130
|
}
|
|
89
|
-
if (e.
|
|
90
|
-
if (hasActiveOverride(e)) return !e.
|
|
131
|
+
if (e.Ne !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED)) {
|
|
132
|
+
if (hasActiveOverride(e)) return !e.Pe || !e.Pe(e.Ne, unwrapOverride(e.Ee));
|
|
91
133
|
return true;
|
|
92
134
|
}
|
|
93
135
|
return newQuestionInFlight(t);
|
|
94
136
|
}
|
|
95
137
|
|
|
96
138
|
function syncCompanions(e, t) {
|
|
97
|
-
if (e.
|
|
98
|
-
if (e.
|
|
139
|
+
if (e.ge) updatePendingSignal(e);
|
|
140
|
+
if (e.he) setSignal(e.he, t);
|
|
99
141
|
}
|
|
100
142
|
|
|
101
143
|
function updatePendingSignal(e) {
|
|
102
|
-
if (e.
|
|
103
|
-
setSignal(e.
|
|
144
|
+
if (e.ge) {
|
|
145
|
+
setSignal(e.ge, computePendingState(e));
|
|
104
146
|
}
|
|
105
|
-
if (e.
|
|
147
|
+
if (e.he) updatePendingSignal(e.he);
|
|
106
148
|
}
|
|
107
149
|
|
|
108
150
|
function updateChildCompanions(e) {
|
|
109
|
-
for (let t = e.
|
|
110
|
-
if (t.
|
|
151
|
+
for (let t = e.u; t !== null; t = t.fe) {
|
|
152
|
+
if (t.ge || t.he) updatePendingSignal(t);
|
|
111
153
|
}
|
|
112
154
|
}
|
|
113
155
|
|
|
114
|
-
|
|
115
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Re-derive every verdict companion downstream of `el` (subs + firewall
|
|
158
|
+
* children, dedup'd). The affects() channel's poke walk: registration and
|
|
159
|
+
* re-ask flips use the live write path (companion setSignal — its own lane
|
|
160
|
+
* lets the wake escape an incomplete transition's effect stash, #2887);
|
|
161
|
+
* mark release passes `snap` because it runs inside queue finalization,
|
|
162
|
+
* where companion writes must land committed (a setSignal there would open
|
|
163
|
+
* a fresh override window that nothing settles).
|
|
164
|
+
*/ function repollDownstreamVerdicts(e, t = false) {
|
|
165
|
+
const n = t ? snapCompanionsToState : updatePendingSignal;
|
|
166
|
+
const i = new Set;
|
|
116
167
|
const visit = e => {
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
if (e.
|
|
120
|
-
for (let t = e.
|
|
121
|
-
for (let t = e.
|
|
168
|
+
if (i.has(e)) return;
|
|
169
|
+
i.add(e);
|
|
170
|
+
if (e.ge || e.he) n(e);
|
|
171
|
+
for (let t = e.o; t !== null; t = t.ue) visit(t.le);
|
|
172
|
+
for (let t = e.u ?? null; t !== null; t = t.fe) {
|
|
122
173
|
visit(t);
|
|
123
174
|
}
|
|
124
175
|
};
|
|
@@ -126,21 +177,21 @@ function repollDownstreamVerdicts(e) {
|
|
|
126
177
|
}
|
|
127
178
|
|
|
128
179
|
function snapCompanionsToState(e) {
|
|
129
|
-
const t = e.
|
|
130
|
-
if (t && (t.
|
|
180
|
+
const t = e.ge;
|
|
181
|
+
if (t && (t.Ee === undefined || t.Ee === NOT_PENDING)) {
|
|
131
182
|
const n = computePendingState(e);
|
|
132
|
-
if (t.
|
|
133
|
-
t.
|
|
134
|
-
t.
|
|
135
|
-
t.
|
|
183
|
+
if (t.Re !== n || t.Ne !== NOT_PENDING) {
|
|
184
|
+
t.Re = n;
|
|
185
|
+
t.Ne = NOT_PENDING;
|
|
186
|
+
t.ce = clock;
|
|
136
187
|
insertSubs(t);
|
|
137
188
|
schedule();
|
|
138
189
|
}
|
|
139
190
|
}
|
|
140
|
-
const n = e.
|
|
141
|
-
if (n && !(n.
|
|
142
|
-
if ((n.
|
|
143
|
-
n.
|
|
191
|
+
const n = e.he;
|
|
192
|
+
if (n && !(n.se & REACTIVE_DISPOSED)) {
|
|
193
|
+
if ((n.Ee === undefined || n.Ee === NOT_PENDING) && n.Ne === NOT_PENDING && !Object.is(n.Re, e.Re) && !(n.se & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
|
|
194
|
+
n.se |= REACTIVE_DIRTY;
|
|
144
195
|
insertIntoHeap(n, queueFor(n));
|
|
145
196
|
insertSubs(n);
|
|
146
197
|
schedule();
|
|
@@ -150,7 +201,7 @@ function snapCompanionsToState(e) {
|
|
|
150
201
|
}
|
|
151
202
|
|
|
152
203
|
function getLatestValueComputed(e) {
|
|
153
|
-
if (!e.
|
|
204
|
+
if (!e.he) {
|
|
154
205
|
const t = latestReadActive;
|
|
155
206
|
setLatestReadActive(false);
|
|
156
207
|
const n = pendingCheckActive;
|
|
@@ -158,21 +209,21 @@ function getLatestValueComputed(e) {
|
|
|
158
209
|
const i = context;
|
|
159
210
|
setContextInternal(null);
|
|
160
211
|
// Detach from owner so it isn't disposed with effects
|
|
161
|
-
e.
|
|
162
|
-
e.
|
|
212
|
+
e.he = optimisticComputed(() => read(e));
|
|
213
|
+
e.he.nn = e;
|
|
163
214
|
// Parent-child lane relationship
|
|
164
215
|
setContextInternal(i);
|
|
165
216
|
setPendingCheckActive(n);
|
|
166
217
|
setLatestReadActive(t);
|
|
167
218
|
}
|
|
168
|
-
return e.
|
|
219
|
+
return e.he;
|
|
169
220
|
}
|
|
170
221
|
|
|
171
222
|
/** The latest()-mode read path, installed as GlobalQueue._latestRead. */ function latestRead(e) {
|
|
172
223
|
const t = getLatestValueComputed(e);
|
|
173
224
|
const n = latestReadActive;
|
|
174
225
|
setLatestReadActive(false);
|
|
175
|
-
const i = e.
|
|
226
|
+
const i = e.Ee !== undefined && e.Ee !== NOT_PENDING ? unwrapOverride(e.Ee) : e.Re;
|
|
176
227
|
let r;
|
|
177
228
|
try {
|
|
178
229
|
// An untracked latest() read has no reading context, so read() never
|
|
@@ -181,22 +232,22 @@ function getLatestValueComputed(e) {
|
|
|
181
232
|
// until the flush (#2922). Mirror the tracked-read pull here: mark the
|
|
182
233
|
// queued staleness through the graph, then bring the shadow up to date.
|
|
183
234
|
const e = queueFor(t);
|
|
184
|
-
if (t.
|
|
235
|
+
if (t.He >= e.Fe && !(t.se & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
|
|
185
236
|
markHeap(e);
|
|
186
237
|
prepareComputed(t, true);
|
|
187
238
|
}
|
|
188
239
|
r = read(t);
|
|
189
240
|
} catch (t) {
|
|
190
|
-
if (t instanceof NotReadyError && (!context || !(e.
|
|
241
|
+
if (t instanceof NotReadyError && (!context || !(e.S & STATUS_UNINITIALIZED))) return i;
|
|
191
242
|
throw t;
|
|
192
243
|
} finally {
|
|
193
244
|
setLatestReadActive(n);
|
|
194
245
|
}
|
|
195
|
-
if (t.
|
|
196
|
-
if (stale && currentOptimisticLane && t.
|
|
197
|
-
const e = findLane(t.
|
|
246
|
+
if (t.S & STATUS_PENDING) return i;
|
|
247
|
+
if (stale && currentOptimisticLane && t.je) {
|
|
248
|
+
const e = findLane(t.je);
|
|
198
249
|
const n = findLane(currentOptimisticLane);
|
|
199
|
-
if (e !== n && e.
|
|
250
|
+
if (e !== n && e.de.size > 0) {
|
|
200
251
|
return i;
|
|
201
252
|
}
|
|
202
253
|
}
|
|
@@ -204,18 +255,18 @@ function getLatestValueComputed(e) {
|
|
|
204
255
|
// speculative value in _pendingValue; a contextless read() only surfaces
|
|
205
256
|
// _value. Overrides stay authoritative (A17), and stale readers keep the
|
|
206
257
|
// other transition's committed view, matching read()'s own selection.
|
|
207
|
-
if (t.
|
|
258
|
+
if (t.Ne !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t.Ue && activeTransition !== t.Ue)) return t.Ne;
|
|
208
259
|
return r;
|
|
209
260
|
}
|
|
210
261
|
|
|
211
262
|
/** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
|
|
212
263
|
setPendingCheckActive(false);
|
|
213
|
-
if (typeof e.
|
|
214
|
-
const r = n.
|
|
264
|
+
if (typeof e.ke === "function") prepareComputed(e, true);
|
|
265
|
+
const r = n.S;
|
|
215
266
|
if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
|
|
216
267
|
if (tracking && e !== t) link(e, t);
|
|
217
268
|
setPendingCheckActive(true);
|
|
218
|
-
throw n.
|
|
269
|
+
throw n._;
|
|
219
270
|
}
|
|
220
271
|
collectPendingSources(e);
|
|
221
272
|
if (i) collectPendingSources(i);
|
|
@@ -223,14 +274,14 @@ function getLatestValueComputed(e) {
|
|
|
223
274
|
}
|
|
224
275
|
|
|
225
276
|
function recordFreshRead(e, t) {
|
|
226
|
-
if (pendingProbe !== null && e.
|
|
277
|
+
if (pendingProbe !== null && e.Ne !== NOT_PENDING && t === e.Ne) pendingProbe.freshReads.add(e);
|
|
227
278
|
}
|
|
228
279
|
|
|
229
280
|
function applyReask(e, t) {
|
|
230
|
-
const n = !!(e.
|
|
231
|
-
const i = t && !(n && !e.
|
|
232
|
-
const r = n && e.
|
|
233
|
-
e.
|
|
281
|
+
const n = !!(e.S & STATUS_PENDING);
|
|
282
|
+
const i = t && !(n && !e.be);
|
|
283
|
+
const r = n && e.be !== i;
|
|
284
|
+
e.be = i;
|
|
234
285
|
return r;
|
|
235
286
|
}
|
|
236
287
|
|
|
@@ -270,7 +321,7 @@ function isPending(e) {
|
|
|
270
321
|
} catch (e) {
|
|
271
322
|
collectPending();
|
|
272
323
|
if (e instanceof NotReadyError) {
|
|
273
|
-
const t = !!(e.source?.
|
|
324
|
+
const t = !!(e.source?.S & STATUS_UNINITIALIZED);
|
|
274
325
|
if (i.found && !t) return true;
|
|
275
326
|
if (context && t) throw e;
|
|
276
327
|
}
|
|
@@ -284,24 +335,24 @@ function isPending(e) {
|
|
|
284
335
|
// Hook installation (same late-binding pattern as GlobalQueue._update /
|
|
285
336
|
// _propagateAffects): core call sites fire these behind the same guards the
|
|
286
337
|
// direct calls used, so behavior is identical once this module loads.
|
|
287
|
-
GlobalQueue.
|
|
338
|
+
GlobalQueue.Ie = syncCompanions;
|
|
288
339
|
|
|
289
|
-
GlobalQueue.
|
|
340
|
+
GlobalQueue.ae = updatePendingSignal;
|
|
290
341
|
|
|
291
|
-
GlobalQueue.
|
|
342
|
+
GlobalQueue._e = updateChildCompanions;
|
|
292
343
|
|
|
293
|
-
GlobalQueue.
|
|
344
|
+
GlobalQueue.un = snapCompanionsToState;
|
|
294
345
|
|
|
295
|
-
GlobalQueue.
|
|
346
|
+
GlobalQueue.St = latestRead;
|
|
296
347
|
|
|
297
|
-
GlobalQueue.
|
|
348
|
+
GlobalQueue.dt = pendingCheckRead;
|
|
298
349
|
|
|
299
|
-
GlobalQueue.
|
|
350
|
+
GlobalQueue.Rt = recordFreshRead;
|
|
300
351
|
|
|
301
|
-
GlobalQueue.
|
|
352
|
+
GlobalQueue.ze = applyReask;
|
|
302
353
|
|
|
303
|
-
GlobalQueue.
|
|
354
|
+
GlobalQueue.k = repollDownstreamVerdicts;
|
|
304
355
|
|
|
305
|
-
GlobalQueue.
|
|
356
|
+
GlobalQueue.Lt = witnessAffects;
|
|
306
357
|
|
|
307
358
|
export { isPending, latest };
|