@solidjs/signals 2.0.0-beta.17 → 2.0.0-beta.19
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 +4 -3
- package/dist/dev.js +2956 -646
- package/dist/node.cjs +6396 -3974
- package/dist/prod/affects.js +222 -0
- package/dist/prod/boundaries.js +568 -0
- package/dist/prod/core/action.js +83 -0
- package/dist/prod/core/async.js +394 -0
- package/dist/prod/core/constants.js +78 -0
- package/dist/prod/core/context.js +67 -0
- package/dist/prod/core/core.js +772 -0
- package/dist/prod/core/dev.js +3 -0
- package/dist/prod/core/effect.js +145 -0
- package/dist/prod/core/error.js +59 -0
- package/dist/prod/core/external.js +98 -0
- package/dist/prod/core/graph.js +91 -0
- package/dist/prod/core/heap.js +132 -0
- package/dist/prod/core/invariants.js +42 -0
- package/dist/prod/core/lanes.js +130 -0
- package/dist/prod/core/optimistic.js +265 -0
- package/dist/prod/core/owner.js +293 -0
- package/dist/prod/core/scheduler.js +689 -0
- package/dist/prod/core/verdict.js +293 -0
- package/dist/prod/index.js +45 -0
- package/dist/prod/map.js +264 -0
- package/dist/prod/signals.js +389 -0
- package/dist/prod/store/optimistic.js +149 -0
- package/dist/prod/store/projection.js +184 -0
- package/dist/prod/store/reconcile.js +392 -0
- package/dist/prod/store/store.js +739 -0
- package/dist/prod/store/storePath.js +103 -0
- package/dist/prod/store/utils.js +297 -0
- package/dist/types/affects.d.ts +47 -0
- package/dist/types/boundaries.d.ts +8 -8
- package/dist/types/core/async.d.ts +5 -2
- package/dist/types/core/constants.d.ts +8 -0
- package/dist/types/core/core.d.ts +12 -69
- package/dist/types/core/dev.d.ts +1 -1
- package/dist/types/core/external.d.ts +0 -30
- package/dist/types/core/heap.d.ts +8 -0
- package/dist/types/core/index.d.ts +3 -2
- package/dist/types/core/invariants.d.ts +10 -0
- package/dist/types/core/optimistic.d.ts +6 -0
- package/dist/types/core/owner.d.ts +8 -0
- package/dist/types/core/scheduler.d.ts +53 -5
- package/dist/types/core/types.d.ts +22 -5
- package/dist/types/core/verdict.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/store/projection.d.ts +0 -1
- package/dist/types/store/store.d.ts +32 -14
- package/dist/types-cjs/affects.d.cts +47 -0
- package/dist/types-cjs/boundaries.d.cts +8 -8
- package/dist/types-cjs/core/async.d.cts +5 -2
- package/dist/types-cjs/core/constants.d.cts +8 -0
- package/dist/types-cjs/core/core.d.cts +12 -69
- package/dist/types-cjs/core/dev.d.cts +1 -1
- package/dist/types-cjs/core/external.d.cts +0 -30
- package/dist/types-cjs/core/heap.d.cts +8 -0
- package/dist/types-cjs/core/index.d.cts +3 -2
- package/dist/types-cjs/core/invariants.d.cts +10 -0
- package/dist/types-cjs/core/optimistic.d.cts +6 -0
- package/dist/types-cjs/core/owner.d.cts +8 -0
- package/dist/types-cjs/core/scheduler.d.cts +53 -5
- package/dist/types-cjs/core/types.d.cts +22 -5
- package/dist/types-cjs/core/verdict.d.cts +2 -0
- package/dist/types-cjs/index.d.cts +1 -0
- package/dist/types-cjs/store/projection.d.cts +0 -1
- package/dist/types-cjs/store/store.d.cts +32 -14
- package/package.json +8 -6
- package/dist/prod.js +0 -4406
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { forEachDependent, notifyStatus, addPendingSource, setPendingError, settlePendingSource } from "./core/async.js";
|
|
2
|
+
|
|
3
|
+
import { $REFRESH, STATUS_PENDING } from "./core/constants.js";
|
|
4
|
+
|
|
5
|
+
import { NotReadyError } from "./core/error.js";
|
|
6
|
+
|
|
7
|
+
import "./core/core.js";
|
|
8
|
+
|
|
9
|
+
import { globalQueue, schedule, GlobalQueue, shiftAffectsMarks } from "./core/scheduler.js";
|
|
10
|
+
|
|
11
|
+
import "./core/verdict.js";
|
|
12
|
+
|
|
13
|
+
import "./core/effect.js";
|
|
14
|
+
|
|
15
|
+
import "./core/invariants.js";
|
|
16
|
+
|
|
17
|
+
import { $TARGET, getStoreAffectsNodes } from "./store/store.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The pending-source identity of a live `affects()` mark on `node` (lazy,
|
|
21
|
+
* one per node, shared by overlapping registrations via the refcount).
|
|
22
|
+
*
|
|
23
|
+
* A mark rides the SAME status rails as real in-flight async — downstream
|
|
24
|
+
* subscribers hold the sentinel in `_pendingSources` — but under its own
|
|
25
|
+
* identity so the two channels can't clear each other:
|
|
26
|
+
* - `_reask` is permanently `false`: a mark is by definition a declared
|
|
27
|
+
* value change, so `quietPending` never silences a window it participates
|
|
28
|
+
* in — even when the mark rides over an otherwise-quiet `refresh()`
|
|
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 !== t && !e.M?.has(t)) {
|
|
72
|
+
notifyStatus(e, STATUS_PENDING, r);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Re-establish mark pendingness on a computed that read marked sources
|
|
79
|
+
* during its recompute (`clearStatus` at the top of the commit path wiped
|
|
80
|
+
* any sentinel entries it held). Called by `recompute` after the commit —
|
|
81
|
+
* not before, because setting `_error` earlier would make the commit path
|
|
82
|
+
* treat the node as errored and skip the value write.
|
|
83
|
+
*/ function applyAffectsReads(e, t) {
|
|
84
|
+
let r = false;
|
|
85
|
+
for (let f = 0; f < t.length; f++) {
|
|
86
|
+
const o = t[f];
|
|
87
|
+
if (!o.P) continue;
|
|
88
|
+
const s = getAffectsSentinel(o);
|
|
89
|
+
if (addPendingSource(e, s)) {
|
|
90
|
+
e.i |= STATUS_PENDING;
|
|
91
|
+
setPendingError(e, s);
|
|
92
|
+
r = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (r && GlobalQueue._ !== null) GlobalQueue._(e);
|
|
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.P = (e.P || 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.P === 1 && GlobalQueue._ !== null) GlobalQueue._(e);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Registers one `affects()` mark on a node: counts it, records the
|
|
113
|
+
* registration with the current transaction (after initTransition the queue's
|
|
114
|
+
* array aliases the active transition's, mirroring `_optimisticNodes`), and
|
|
115
|
+
* propagates STATUS_PENDING downstream on the status rails so everything
|
|
116
|
+
* DERIVED from the marked data reads pending too. Propagation runs on every
|
|
117
|
+
* registration (not just the first): subscribers gained since an earlier
|
|
118
|
+
* overlapping registration get covered, and dedup stops re-descent early.
|
|
119
|
+
*/ function registerAffectsMark(e) {
|
|
120
|
+
markAffects(e);
|
|
121
|
+
globalQueue.N.push(e);
|
|
122
|
+
propagateAffectsMark(e);
|
|
123
|
+
schedule();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Releases one registration. When the node's last mark drops, settles the
|
|
128
|
+
* mark's sentinel out of every downstream `_pendingSources` (waking blocked
|
|
129
|
+
* nodes and re-deriving verdicts along the walk). Companion writes go through
|
|
130
|
+
* the settlement snap (committed, not transition-scoped) so releasing a mark
|
|
131
|
+
* can't open a fresh override window that would itself need settlement.
|
|
132
|
+
*/ function releaseAffectsMark(e) {
|
|
133
|
+
shiftAffectsMarks(-1);
|
|
134
|
+
e.P--;
|
|
135
|
+
if (!e.P) {
|
|
136
|
+
const t = e.t;
|
|
137
|
+
if (t) settlePendingSource(e, t, true);
|
|
138
|
+
GlobalQueue.R !== null && GlobalQueue.R(e);
|
|
139
|
+
GlobalQueue.T?.(e);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Releases one batch of affects marks (a settling transaction's, or the
|
|
145
|
+
* ambient batch at a plain flush end).
|
|
146
|
+
*/ function releaseAffectsMarks(e) {
|
|
147
|
+
for (let t = 0; t < e.length; t++) releaseAffectsMark(e[t]);
|
|
148
|
+
e.length = 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
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 !!e.m?.l;
|
|
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
|
+
const r = e.m;
|
|
179
|
+
if (r) {
|
|
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) {
|
|
184
|
+
const e = r.l;
|
|
185
|
+
if (e && e.P) t.push(e);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Late installation (same pattern as `GlobalQueue._update`): the mark engine
|
|
191
|
+
// lives with the feature so graphs that never declare a mark never ship it.
|
|
192
|
+
// Each call site is gated by state only this module creates (`markedReads`
|
|
193
|
+
// collection under `activeAffectsMarks`, a non-empty `_affectsNodes` batch,
|
|
194
|
+
// a live scope in the store's `affectsScopes`), so the hooks are installed
|
|
195
|
+
// before the first time any of them can fire.
|
|
196
|
+
GlobalQueue.j = applyAffectsReads;
|
|
197
|
+
|
|
198
|
+
GlobalQueue.h = releaseAffectsMarks;
|
|
199
|
+
|
|
200
|
+
GlobalQueue.D = markAffects;
|
|
201
|
+
|
|
202
|
+
GlobalQueue.F = releaseAffectsMark;
|
|
203
|
+
|
|
204
|
+
GlobalQueue.$ = onlyMarkPending;
|
|
205
|
+
|
|
206
|
+
GlobalQueue.I = collectMarkSources;
|
|
207
|
+
|
|
208
|
+
function affects(e, t) {
|
|
209
|
+
const r = e?.[$TARGET];
|
|
210
|
+
if (r) {
|
|
211
|
+
const e = getStoreAffectsNodes(r, t);
|
|
212
|
+
for (let t = 0; t < e.length; t++) registerAffectsMark(e[t]);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const f = e?.[$REFRESH];
|
|
216
|
+
if (f) {
|
|
217
|
+
registerAffectsMark(f);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export { affects };
|