@solidjs/signals 2.0.0-beta.20 → 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/README.md +5 -1
- package/dist/dev.js +267 -307
- package/dist/node.cjs +1125 -1168
- package/dist/prod/affects.js +71 -163
- package/dist/prod/boundaries.js +141 -137
- package/dist/prod/core/action.js +50 -12
- package/dist/prod/core/async.js +83 -99
- package/dist/prod/core/core.js +255 -298
- 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 +58 -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 +217 -211
- package/dist/prod/core/verdict.js +144 -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/action.d.ts +16 -0
- 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/action.d.cts +16 -0
- 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
package/dist/prod/affects.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { forEachDependent } from "./core/async.js";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { $REFRESH, STATUS_PENDING } 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 { GlobalQueue,
|
|
9
|
+
import { GlobalQueue, globalQueue, schedule, shiftAffectsMarks } from "./core/scheduler.js";
|
|
10
10
|
|
|
11
11
|
import "./core/verdict.js";
|
|
12
12
|
|
|
@@ -17,126 +17,76 @@ import "./core/invariants.js";
|
|
|
17
17
|
import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* The
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* re-ask of the same node (the whole point of declaring one).
|
|
30
|
-
* - A landing on the marked node settles only the node's OWN source entry;
|
|
31
|
-
* the sentinel entry survives until the mark's transaction releases it.
|
|
32
|
-
* - The sentinel itself never carries `STATUS_PENDING`, so
|
|
33
|
-
* `transitionComplete` never counts a mark as a blocker of its own
|
|
34
|
-
* transaction (release happens AT settle — self-blocking would deadlock),
|
|
35
|
-
* and reads of the marked node never throw (marks are value-transparent
|
|
36
|
-
* at the source; pendingness is what propagates).
|
|
37
|
-
*/ function getAffectsSentinel(e) {
|
|
38
|
-
return e.t ??= {
|
|
39
|
-
o: undefined,
|
|
40
|
-
// Brand + backref: lets the scheduler's settlement checks recognize
|
|
41
|
-
// mark-sourced pending (which must never block its own transaction —
|
|
42
|
-
// release happens AT settle).
|
|
43
|
-
l: e,
|
|
44
|
-
u: 0,
|
|
45
|
-
i: 0,
|
|
46
|
-
A: false,
|
|
47
|
-
k: undefined,
|
|
48
|
-
p: null,
|
|
49
|
-
S: null
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Push a live mark's pendingness downstream from the marked node through the
|
|
55
|
-
* normal status rails. Runs on every registration (dedup in `notifyStatus`
|
|
56
|
-
* stops re-descent at already-covered subscribers). Subscribers that
|
|
57
|
-
* recompute mid-window shed this via `clearStatus` and re-acquire it through
|
|
58
|
-
* the read path (`applyAffectsReads` for direct readers of the marked node,
|
|
59
|
-
* `collectMarkSources` transitively) — the mark analogue of real async's
|
|
60
|
-
* re-throw-on-read. Subscribers are deliberately NOT queued as pending nodes
|
|
61
|
-
* (#2893): they hold no value needing a transition-scheduled commit, and
|
|
62
|
-
* queueing would stamp the marking action's transaction onto them — from then
|
|
63
|
-
* on ANY write dirtying one of them (including writes to unmarked signals
|
|
64
|
-
* that merely share a downstream memo) would be captured and frozen until the
|
|
65
|
-
* action settles.
|
|
66
|
-
*/ function propagateAffectsMark(e) {
|
|
67
|
-
if (!e.p && !e.G) return;
|
|
68
|
-
const t = getAffectsSentinel(e);
|
|
69
|
-
const r = new NotReadyError(t);
|
|
70
|
-
forEachDependent(e, e => {
|
|
71
|
-
if (!e.m?.has(t)) {
|
|
72
|
-
notifyStatus(e, STATUS_PENDING, r);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
20
|
+
* The counting half of a mark, shared by direct registration and store-scope
|
|
21
|
+
* inheritance (a node created inside a live keyless mark's identity scope).
|
|
22
|
+
* A mark is ONLY this count: coverage of everything derived from the node is
|
|
23
|
+
* pull-derived by the verdict layer's `markWalk` (dep-graph reachability), so
|
|
24
|
+
* nothing is stored downstream and nothing can be stranded or stripped by
|
|
25
|
+
* mid-window recomputes.
|
|
26
|
+
*/ function markAffects(e) {
|
|
27
|
+
e.t = (e.t || 0) + 1;
|
|
28
|
+
shiftAffectsMarks(1);
|
|
75
29
|
}
|
|
76
30
|
|
|
77
31
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
32
|
+
* Boundary visual channel: within a transaction, a live mark holds Loading
|
|
33
|
+
* fallbacks / reveal ordering the way real in-flight async would — but the
|
|
34
|
+
* notification is tagged visibility-only at the source (`_markVisual`), so
|
|
35
|
+
* the root queue never registers reporters from it: marks are invisible to
|
|
36
|
+
* ALL completion and settlement accounting by construction. Boundaries hold
|
|
37
|
+
* the marked node in `_sources` while `_affectsCount` is live; the release
|
|
38
|
+
* sweep (finalizePureQueue re-checks boundary children after mark release)
|
|
39
|
+
* is the display-state update point. Ambient marks release inside the same
|
|
40
|
+
* flush that would surface them, before effects run — verdict-only, netting
|
|
41
|
+
* no visual change.
|
|
42
|
+
*/ function notifyMarkBoundaries(e) {
|
|
43
|
+
if (!e.o && !e.u) return;
|
|
44
|
+
const r = new NotReadyError(e);
|
|
45
|
+
r.l = true;
|
|
46
|
+
const t = new Set;
|
|
47
|
+
const visit = e => {
|
|
48
|
+
if (t.has(e)) return;
|
|
49
|
+
t.add(e);
|
|
50
|
+
// Display consumers (render effects, boundary computeds) act on the
|
|
51
|
+
// notification; descent stops there, exactly like the status rails.
|
|
52
|
+
if (e.i) {
|
|
53
|
+
e.i(STATUS_PENDING, r);
|
|
54
|
+
return;
|
|
93
55
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* The counting half of a mark, shared by direct registration and store-scope
|
|
100
|
-
* inheritance (a node created inside a live keyless mark's identity scope):
|
|
101
|
-
* bumps the refcount and pokes the node's verdict companions so an
|
|
102
|
-
* already-materialized `false` flips reactively.
|
|
103
|
-
*/ function markAffects(e) {
|
|
104
|
-
e.M = (e.M || 0) + 1;
|
|
105
|
-
shiftAffectsMarks(1);
|
|
106
|
-
// Companions only exist once the verdict layer (isPending/latest) loaded;
|
|
107
|
-
// without them there is no materialized verdict to flip.
|
|
108
|
-
if (e.M === 1 && GlobalQueue.P !== null) GlobalQueue.P(e);
|
|
56
|
+
forEachDependent(e, visit);
|
|
57
|
+
};
|
|
58
|
+
forEachDependent(e, visit);
|
|
109
59
|
}
|
|
110
60
|
|
|
111
61
|
/**
|
|
112
62
|
* Registers one `affects()` mark on a node: counts it, records the
|
|
113
63
|
* registration with the current transaction (after initTransition the queue's
|
|
114
|
-
* batch IS the active transition, mirroring `_optimisticNodes`),
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* registration (not just the first): subscribers gained since an
|
|
118
|
-
* overlapping registration get covered, and dedup stops re-descent
|
|
64
|
+
* batch IS the active transition, mirroring `_optimisticNodes`), re-derives
|
|
65
|
+
* every downstream verdict companion (the mark channel's only push — verdict
|
|
66
|
+
* pokes, not state), and notifies boundary display state. Both walks run on
|
|
67
|
+
* every registration (not just the first): subscribers gained since an
|
|
68
|
+
* earlier overlapping registration get covered, and dedup stops re-descent.
|
|
119
69
|
*/ function registerAffectsMark(e) {
|
|
120
70
|
markAffects(e);
|
|
121
|
-
globalQueue.
|
|
122
|
-
|
|
71
|
+
globalQueue.m.A.push(e);
|
|
72
|
+
// Companions only exist once the verdict layer (isPending/latest) loaded;
|
|
73
|
+
// without them there is no materialized verdict to poke.
|
|
74
|
+
GlobalQueue.k !== null && GlobalQueue.k(e);
|
|
75
|
+
notifyMarkBoundaries(e);
|
|
123
76
|
schedule();
|
|
124
77
|
}
|
|
125
78
|
|
|
126
79
|
/**
|
|
127
|
-
* Releases one registration. When the node's last mark drops,
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* can't open a fresh override window that would itself need settlement.
|
|
80
|
+
* Releases one registration. When the node's last mark drops, re-derives
|
|
81
|
+
* every downstream verdict through the settlement snap (committed, not
|
|
82
|
+
* transition-scoped — release runs inside queue finalization, where a
|
|
83
|
+
* setSignal would open a fresh override window that nothing settles).
|
|
132
84
|
*/ function releaseAffectsMark(e) {
|
|
133
85
|
shiftAffectsMarks(-1);
|
|
134
|
-
e.
|
|
135
|
-
if (!e.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
GlobalQueue.R !== null && GlobalQueue.R(e);
|
|
139
|
-
GlobalQueue.T?.(e);
|
|
86
|
+
e.t--;
|
|
87
|
+
if (!e.t) {
|
|
88
|
+
GlobalQueue.k !== null && GlobalQueue.k(e, true);
|
|
89
|
+
GlobalQueue.p?.(e);
|
|
140
90
|
}
|
|
141
91
|
}
|
|
142
92
|
|
|
@@ -144,73 +94,31 @@ import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
|
144
94
|
* Releases one batch of affects marks (a settling transaction's, or the
|
|
145
95
|
* ambient batch at a plain flush end).
|
|
146
96
|
*/ function releaseAffectsMarks(e) {
|
|
147
|
-
for (let
|
|
97
|
+
for (let r = 0; r < e.length; r++) releaseAffectsMark(e[r]);
|
|
148
98
|
e.length = 0;
|
|
149
99
|
}
|
|
150
100
|
|
|
151
|
-
/**
|
|
152
|
-
* True when a node's pending status comes ONLY from affects() sentinels. A
|
|
153
|
-
* mark is a promise of change, not an absence of value: reads of mark-pended
|
|
154
|
-
* derived nodes stay value-transparent (verdicts report pending; the read
|
|
155
|
-
* path must not suspend). Any real async source among the pending sources
|
|
156
|
-
* keeps normal suspension semantics. Core's read path reaches this through
|
|
157
|
-
* `GlobalQueue._onlyMarkPending`, gated on the `activeAffectsMarks` counter.
|
|
158
|
-
*/ function onlyMarkPending(e) {
|
|
159
|
-
const t = e.m;
|
|
160
|
-
if (t) {
|
|
161
|
-
for (const e of t) if (!e.l) return false;
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Collect the still-live marked nodes behind a pended owner's sentinel
|
|
169
|
-
* sources into a recompute's `affectsReads`. This is the transitive half of
|
|
170
|
-
* read-path re-establishment (#2893): real async re-establishes at every
|
|
171
|
-
* derivation level because its re-throw on read re-registers the source, but
|
|
172
|
-
* mark-pended reads are value-transparent — without this, pendingness dies on
|
|
173
|
-
* the first mid-window recompute past depth one, and the isPending() probe
|
|
174
|
-
* itself (whose prepare step recomputes retryable NotReady holders) strips
|
|
175
|
-
* the very status it reports on. Reached through
|
|
176
|
-
* `GlobalQueue._collectMarkSources`, gated on `activeAffectsMarks`.
|
|
177
|
-
*/ function collectMarkSources(e, t) {
|
|
178
|
-
if (e.m) {
|
|
179
|
-
for (const r of e.m) {
|
|
180
|
-
const e = r.l;
|
|
181
|
-
if (e && e.M) t.push(e);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
101
|
// Late installation (same pattern as `GlobalQueue._update`): the mark engine
|
|
187
102
|
// lives with the feature so graphs that never declare a mark never ship it.
|
|
188
|
-
// Each call site is gated by state only this module creates (
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
GlobalQueue.j = applyAffectsReads;
|
|
103
|
+
// Each call site is gated by state only this module creates (a non-empty
|
|
104
|
+
// `_affectsNodes` batch, a live scope in the store's `affectsScopes`), so the
|
|
105
|
+
// hooks are installed before the first time any of them can fire.
|
|
106
|
+
GlobalQueue.G = releaseAffectsMarks;
|
|
193
107
|
|
|
194
|
-
GlobalQueue.
|
|
108
|
+
GlobalQueue.M = markAffects;
|
|
195
109
|
|
|
196
|
-
GlobalQueue.
|
|
110
|
+
GlobalQueue.h = releaseAffectsMark;
|
|
197
111
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
function affects(e, t) {
|
|
205
|
-
const r = e?.[$TARGET];
|
|
206
|
-
if (r) {
|
|
207
|
-
const e = getStoreAffectsNodes(r, t);
|
|
208
|
-
for (let t = 0; t < e.length; t++) registerAffectsMark(e[t]);
|
|
112
|
+
function affects(e, r) {
|
|
113
|
+
const t = e?.[$TARGET];
|
|
114
|
+
if (t) {
|
|
115
|
+
const e = getStoreAffectsNodes(t, r);
|
|
116
|
+
for (let r = 0; r < e.length; r++) registerAffectsMark(e[r]);
|
|
209
117
|
return;
|
|
210
118
|
}
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
registerAffectsMark(
|
|
119
|
+
const o = e?.[$REFRESH];
|
|
120
|
+
if (o) {
|
|
121
|
+
registerAffectsMark(o);
|
|
214
122
|
return;
|
|
215
123
|
}
|
|
216
124
|
}
|