@solidjs/signals 2.0.0-beta.18 → 2.0.0-beta.20

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.
Files changed (68) hide show
  1. package/dist/dev.js +3276 -928
  2. package/dist/node.cjs +6646 -4189
  3. package/dist/prod/affects.js +218 -0
  4. package/dist/prod/boundaries.js +568 -0
  5. package/dist/prod/core/action.js +96 -0
  6. package/dist/prod/core/async.js +380 -0
  7. package/dist/prod/core/constants.js +92 -0
  8. package/dist/prod/core/context.js +67 -0
  9. package/dist/prod/core/core.js +772 -0
  10. package/dist/prod/core/dev.js +3 -0
  11. package/dist/prod/core/effect.js +145 -0
  12. package/dist/prod/core/error.js +59 -0
  13. package/dist/prod/core/external.js +98 -0
  14. package/dist/prod/core/graph.js +91 -0
  15. package/dist/prod/core/heap.js +132 -0
  16. package/dist/prod/core/invariants.js +42 -0
  17. package/dist/prod/core/lanes.js +140 -0
  18. package/dist/prod/core/optimistic.js +273 -0
  19. package/dist/prod/core/owner.js +293 -0
  20. package/dist/prod/core/scheduler.js +689 -0
  21. package/dist/prod/core/verdict.js +292 -0
  22. package/dist/prod/index.js +45 -0
  23. package/dist/prod/map.js +293 -0
  24. package/dist/prod/signals.js +389 -0
  25. package/dist/prod/store/optimistic.js +184 -0
  26. package/dist/prod/store/projection.js +184 -0
  27. package/dist/prod/store/reconcile.js +426 -0
  28. package/dist/prod/store/store.js +870 -0
  29. package/dist/prod/store/storePath.js +103 -0
  30. package/dist/prod/store/utils.js +316 -0
  31. package/dist/types/affects.d.ts +1 -1
  32. package/dist/types/boundaries.d.ts +6 -6
  33. package/dist/types/core/action.d.ts +19 -6
  34. package/dist/types/core/async.d.ts +4 -38
  35. package/dist/types/core/constants.d.ts +12 -0
  36. package/dist/types/core/core.d.ts +12 -78
  37. package/dist/types/core/dev.d.ts +7 -0
  38. package/dist/types/core/external.d.ts +0 -30
  39. package/dist/types/core/heap.d.ts +8 -0
  40. package/dist/types/core/index.d.ts +3 -2
  41. package/dist/types/core/lanes.d.ts +2 -0
  42. package/dist/types/core/optimistic.d.ts +6 -0
  43. package/dist/types/core/owner.d.ts +8 -0
  44. package/dist/types/core/scheduler.d.ts +40 -39
  45. package/dist/types/core/types.d.ts +9 -1
  46. package/dist/types/core/verdict.d.ts +2 -0
  47. package/dist/types/store/projection.d.ts +0 -1
  48. package/dist/types/store/store.d.ts +8 -2
  49. package/dist/types-cjs/affects.d.cts +1 -1
  50. package/dist/types-cjs/boundaries.d.cts +6 -6
  51. package/dist/types-cjs/core/action.d.cts +19 -6
  52. package/dist/types-cjs/core/async.d.cts +4 -38
  53. package/dist/types-cjs/core/constants.d.cts +12 -0
  54. package/dist/types-cjs/core/core.d.cts +12 -78
  55. package/dist/types-cjs/core/dev.d.cts +7 -0
  56. package/dist/types-cjs/core/external.d.cts +0 -30
  57. package/dist/types-cjs/core/heap.d.cts +8 -0
  58. package/dist/types-cjs/core/index.d.cts +3 -2
  59. package/dist/types-cjs/core/lanes.d.cts +2 -0
  60. package/dist/types-cjs/core/optimistic.d.cts +6 -0
  61. package/dist/types-cjs/core/owner.d.cts +8 -0
  62. package/dist/types-cjs/core/scheduler.d.cts +40 -39
  63. package/dist/types-cjs/core/types.d.cts +9 -1
  64. package/dist/types-cjs/core/verdict.d.cts +2 -0
  65. package/dist/types-cjs/store/projection.d.cts +0 -1
  66. package/dist/types-cjs/store/store.d.cts +8 -2
  67. package/package.json +8 -6
  68. package/dist/prod.js +0 -4637
@@ -0,0 +1,103 @@
1
+ import { isWrappable, ownEnumerableKeys } from "./store.js";
2
+
3
+ const DELETE = Symbol(0);
4
+
5
+ function isPrototypePollutionKey(t) {
6
+ return t === "__proto__" || t === "constructor" || t === "prototype";
7
+ }
8
+
9
+ function updatePath(t, e, o = 0) {
10
+ let r, n = t;
11
+ if (o < e.length - 1) {
12
+ r = e[o];
13
+ const i = typeof r;
14
+ const f = Array.isArray(t);
15
+ if (i === "string" && isPrototypePollutionKey(r)) return;
16
+ if (Array.isArray(r)) {
17
+ for (let n = 0; n < r.length; n++) {
18
+ e[o] = r[n];
19
+ updatePath(t, e, o);
20
+ }
21
+ e[o] = r;
22
+ return;
23
+ } else if (f && i === "function") {
24
+ for (let n = 0; n < t.length; n++) {
25
+ if (r(t[n], n)) {
26
+ e[o] = n;
27
+ updatePath(t, e, o);
28
+ }
29
+ }
30
+ e[o] = r;
31
+ return;
32
+ } else if (f && i === "object") {
33
+ const {from: n = 0, to: i = t.length - 1, by: f = 1} = r;
34
+ for (let r = n; r <= i; r += f) {
35
+ e[o] = r;
36
+ updatePath(t, e, o);
37
+ }
38
+ e[o] = r;
39
+ return;
40
+ } else if (o < e.length - 2) {
41
+ updatePath(t[r], e, o + 1);
42
+ return;
43
+ }
44
+ n = t[r];
45
+ }
46
+ let i = e[e.length - 1];
47
+ if (typeof i === "function") {
48
+ i = i(n);
49
+ if (i === n) return;
50
+ }
51
+ if (r === undefined && i == undefined) return;
52
+ if (i === DELETE) {
53
+ delete t[r];
54
+ } else if (r === undefined || isWrappable(n) && isWrappable(i) && !Array.isArray(i)) {
55
+ const e = r !== undefined ? t[r] : t;
56
+ const o = ownEnumerableKeys(i);
57
+ for (let t = 0; t < o.length; t++) {
58
+ const r = o[t];
59
+ if (typeof r === "string" && isPrototypePollutionKey(r)) continue;
60
+ const n = Object.getOwnPropertyDescriptor(i, r);
61
+ if (n.get || n.set) Object.defineProperty(e, r, n); else e[r] = n.value;
62
+ }
63
+ } else {
64
+ t[r] = i;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Path-based setter helper for `createStore`. Call `storePath(...path, value)`
70
+ * to produce a draft-mutating function suitable for passing to `setStore`.
71
+ *
72
+ * The canonical setter form in Solid 2.0 is the draft-mutating callback
73
+ * (`setStore(s => { s.user.name = "Ada"; })`). `storePath` is a backwards-
74
+ * compatibility helper for users porting from Solid 1.x's
75
+ * `setStore("user", "name", "Ada")` style — it's optional and you can mix the
76
+ * two styles freely.
77
+ *
78
+ * Path parts can be:
79
+ * - a single key — `"user"`, `0`
80
+ * - an array of keys — `[0, 1, 2]`
81
+ * - a range over an array — `{ from?, to?, by? }`
82
+ * - a filter `(item, index) => boolean` for arrays
83
+ *
84
+ * The final argument is the new value or an updater `(prev) => next`. Use
85
+ * `storePath.DELETE` to remove a property.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const [state, setState] = createStore({ user: { name: "Ada" }, todos: [] });
90
+ *
91
+ * setState(storePath("user", "name", "Grace"));
92
+ * setState(storePath("todos", t => !t.done, "done", true)); // mark all undone as done
93
+ * setState(storePath("user", "nickname", storePath.DELETE));
94
+ * ```
95
+ */ const storePath = /* @__PURE__ */ Object.assign(function storePath(...t) {
96
+ return e => {
97
+ updatePath(e, t);
98
+ };
99
+ }, {
100
+ DELETE: DELETE
101
+ });
102
+
103
+ export { storePath };
@@ -0,0 +1,316 @@
1
+ import { pendingCheckActive } from "../core/core.js";
2
+
3
+ import { SUPPORTS_PROXY } from "../core/constants.js";
4
+
5
+ import "../core/scheduler.js";
6
+
7
+ import "../core/invariants.js";
8
+
9
+ import "../core/verdict.js";
10
+
11
+ import "../core/effect.js";
12
+
13
+ import { createMemo } from "../signals.js";
14
+
15
+ import { ownEnumerableKeys, $PROXY, isWrappable, $TARGET, trackSelf, witnessAffectsMark, mergedOverlay, STORE_VALUE, storeLookup, STORE_LOOKUP, $DELETED, wrap, getStoreSymbols, getPropertyDescriptor, getStoreKeys, getKeys, $TRACK } from "./store.js";
16
+
17
+ function snapshotImpl(e, t, r, n) {
18
+ let o, s, i, c, f, u;
19
+ if (!isWrappable(e)) return e;
20
+ if (r && r.has(e)) return r.get(e);
21
+ if (!r) r = new Map;
22
+ if (o = e[$TARGET] || n?.get(e)?.[$TARGET]) {
23
+ if (t) {
24
+ trackSelf(o, $TRACK);
25
+ // A tracked walk reads THROUGH the record without touching the proxy
26
+ // traps — witness the record's affects() channel like a trap read would.
27
+ if (pendingCheckActive) witnessAffectsMark(o);
28
+ }
29
+ i = mergedOverlay(o);
30
+ s = Array.isArray(o[STORE_VALUE]);
31
+ r.set(e, i ? c = s ? [] : Object.create(Object.getPrototypeOf(o[STORE_VALUE])) : o[STORE_VALUE]);
32
+ e = o[STORE_VALUE];
33
+ n = o[STORE_LOOKUP] ?? storeLookup;
34
+ } else {
35
+ s = Array.isArray(e);
36
+ r.set(e, e);
37
+ }
38
+ if (s) {
39
+ const s = i?.length ?? e.length;
40
+ for (let p = 0; p < s; p++) {
41
+ u = i && p in i ? i[p] : e[p];
42
+ if (u === $DELETED) continue;
43
+ if (t && isWrappable(u)) wrap(u, o);
44
+ if ((f = snapshotImpl(u, t, r, n)) !== u || c) {
45
+ if (!c) r.set(e, c = [ ...e ]);
46
+ c[p] = f;
47
+ }
48
+ }
49
+ // Enumerate array symbols separately to avoid scanning indices twice.
50
+ // Spread copies omit symbols, so assign them after the numeric walk.
51
+ const p = n ? getStoreSymbols(e, i) : [];
52
+ for (let s = 0, l = p.length; s < l; s++) {
53
+ const l = p[s];
54
+ const a = getPropertyDescriptor(e, i, l);
55
+ if (!a || a.get) continue;
56
+ u = i && l in i ? i[l] : e[l];
57
+ if (t && isWrappable(u)) wrap(u, o);
58
+ f = snapshotImpl(u, t, r, n);
59
+ if (f !== u || c) {
60
+ if (!c) r.set(e, c = Object.assign([ ...e ], e));
61
+ c[l] = f;
62
+ }
63
+ }
64
+ // Deleted trailing slots are skipped above, so restore length to preserve
65
+ // holes instead of truncating the copy (#2846) — mirrors unwrapStoreValue.
66
+ if (c) c.length = s;
67
+ } else if (!i) {
68
+ // Specialized walk for the common no-overlay case (from #2756): the own
69
+ // descriptor gives the value directly, so each property is read once with
70
+ // no overlay membership checks.
71
+ // A lookup means this object belongs to an immutable store backing tree,
72
+ // even if that nested value has not needed its own proxy yet.
73
+ const s = n ? getStoreKeys(e, undefined) : getKeys(e, undefined);
74
+ for (let i = 0, p = s.length; i < p; i++) {
75
+ const p = s[i];
76
+ const l = Object.getOwnPropertyDescriptor(e, p);
77
+ if (l.get) continue;
78
+ u = l.value;
79
+ if (t && isWrappable(u)) wrap(u, o);
80
+ if ((f = snapshotImpl(u, t, r, n)) !== u || c) {
81
+ if (!c) {
82
+ c = Object.create(Object.getPrototypeOf(e));
83
+ Object.assign(c, e);
84
+ }
85
+ c[p] = f;
86
+ }
87
+ }
88
+ } else {
89
+ // An override only exists on a store record, and the target branch above
90
+ // always set `lookup` alongside it — so this branch is always store-keyed.
91
+ const s = getStoreKeys(e, i);
92
+ for (let p = 0, l = s.length; p < l; p++) {
93
+ let l = s[p];
94
+ const a = getPropertyDescriptor(e, i, l);
95
+ if (a.get) continue;
96
+ u = l in i ? i[l] : e[l];
97
+ if (t && isWrappable(u)) wrap(u, o);
98
+ if ((f = snapshotImpl(u, t, r, n)) !== e[l] || c) {
99
+ if (!c) {
100
+ c = Object.create(Object.getPrototypeOf(e));
101
+ Object.assign(c, e);
102
+ }
103
+ c[l] = f;
104
+ }
105
+ }
106
+ }
107
+ return c || e;
108
+ }
109
+
110
+ function snapshot(e, t, r) {
111
+ return snapshotImpl(e, false, t, r);
112
+ }
113
+
114
+ /**
115
+ * Returns a plain (non-proxy) deep copy **and** subscribes the current
116
+ * tracking scope to every nested change in the source store. Any write
117
+ * anywhere in the subtree invalidates the consumer.
118
+ *
119
+ * Use this when you need plain data inside a reactive scope and want to
120
+ * react to deep mutations (e.g. passing a snapshot to `reconcile()` or to a
121
+ * memo that should rerun on any nested change). For most read paths, prefer
122
+ * direct property access — Solid stores already track per-property reads
123
+ * with no `deep()` wrapper needed.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const [state] = createStore({ a: { b: { c: 1 } } });
128
+ *
129
+ * createEffect(
130
+ * () => deep(state), // reruns on any nested change
131
+ * plain => sendToWorker(plain) // worker gets a non-proxy copy
132
+ * );
133
+ * ```
134
+ */ function deep(e) {
135
+ return snapshotImpl(e, true);
136
+ }
137
+
138
+ function trueFn() {
139
+ return true;
140
+ }
141
+
142
+ const propTraps = {
143
+ get(e, t, r) {
144
+ if (t === $PROXY) return r;
145
+ return e.get(t);
146
+ },
147
+ has(e, t) {
148
+ if (t === $PROXY) return true;
149
+ return e.has(t);
150
+ },
151
+ set: trueFn,
152
+ deleteProperty: trueFn,
153
+ getOwnPropertyDescriptor(e, t) {
154
+ return {
155
+ configurable: true,
156
+ enumerable: true,
157
+ get() {
158
+ return e.get(t);
159
+ },
160
+ set: trueFn,
161
+ deleteProperty: trueFn
162
+ };
163
+ },
164
+ ownKeys(e) {
165
+ return e.keys();
166
+ }
167
+ };
168
+
169
+ function resolveSource(e) {
170
+ return !(e = typeof e === "function" ? e() : e) ? {} : e;
171
+ }
172
+
173
+ const $SOURCES = Symbol(0);
174
+
175
+ /**
176
+ * Merges multiple props-like objects into a single proxy that *preserves
177
+ * reactivity*. Reads are forwarded to the right-most source that defines the
178
+ * property, so later sources override earlier ones (like `Object.assign`).
179
+ *
180
+ * Function arguments are treated as memo-backed sources — useful for passing
181
+ * derived defaults whose computation should track reactively.
182
+ *
183
+ * Use this in component bodies to merge defaults / overrides without losing
184
+ * Solid's per-property tracking.
185
+ *
186
+ * @example
187
+ * ```tsx
188
+ * function Button(_props: { label: string; type?: string; disabled?: boolean }) {
189
+ * const props = merge({ type: "button", disabled: false }, _props);
190
+ *
191
+ * return <button type={props.type} disabled={props.disabled}>{props.label}</button>;
192
+ * }
193
+ * ```
194
+ */ function merge(...e) {
195
+ if (e.length === 1 && typeof e[0] !== "function") return e[0];
196
+ let t = false;
197
+ const r = [];
198
+ for (let n = 0; n < e.length; n++) {
199
+ const o = e[n];
200
+ t = t || !!o && $PROXY in o;
201
+ const s = !!o && o[$SOURCES];
202
+ if (s) {
203
+ for (let e = 0; e < s.length; e++) r.push(s[e]);
204
+ } else r.push(typeof o === "function" ? (t = true, createMemo(o)) : o);
205
+ }
206
+ if (SUPPORTS_PROXY && t) {
207
+ return new Proxy({
208
+ get(e) {
209
+ if (e === $SOURCES) return r;
210
+ for (let t = r.length - 1; t >= 0; t--) {
211
+ const n = resolveSource(r[t]);
212
+ if (e in n) return n[e];
213
+ }
214
+ },
215
+ has(e) {
216
+ for (let t = r.length - 1; t >= 0; t--) {
217
+ if (e in resolveSource(r[t])) return true;
218
+ }
219
+ return false;
220
+ },
221
+ keys() {
222
+ const e = new Set;
223
+ for (let t = 0; t < r.length; t++) {
224
+ const n = ownEnumerableKeys(resolveSource(r[t]));
225
+ for (let t = 0; t < n.length; t++) e.add(n[t]);
226
+ }
227
+ return [ ...e ];
228
+ }
229
+ }, propTraps);
230
+ }
231
+ const n = Object.create(null);
232
+ let o = false;
233
+ let s = r.length - 1;
234
+ for (let e = s; e >= 0; e--) {
235
+ const t = r[e];
236
+ if (!t) {
237
+ e === s && s--;
238
+ continue;
239
+ }
240
+ const i = Object.getOwnPropertyNames(t);
241
+ for (let r = i.length - 1; r >= 0; r--) {
242
+ const c = i[r];
243
+ if (c === "__proto__" || c === "constructor") continue;
244
+ if (!n[c]) {
245
+ o = o || e !== s;
246
+ const r = Object.getOwnPropertyDescriptor(t, c);
247
+ n[c] = r.get ? {
248
+ enumerable: true,
249
+ configurable: true,
250
+ get: r.get.bind(t)
251
+ } : r;
252
+ }
253
+ }
254
+ }
255
+ if (!o) return r[s];
256
+ const i = {};
257
+ const c = Object.keys(n);
258
+ for (let e = c.length - 1; e >= 0; e--) {
259
+ const t = c[e], r = n[t];
260
+ if (r.get) Object.defineProperty(i, t, r); else i[t] = r.value;
261
+ }
262
+ i[$SOURCES] = r;
263
+ return i;
264
+ }
265
+
266
+ /**
267
+ * Returns a reactive proxy of `props` with the listed keys hidden. Tracking
268
+ * on the remaining keys is preserved.
269
+ *
270
+ * Use it to forward "rest" props to a child element while pulling out the
271
+ * keys your component handles itself — the equivalent of `splitProps(p, ["a","b"])[1]`.
272
+ *
273
+ * @example
274
+ * ```tsx
275
+ * function Input(props: { label: string; value: string; onInput: (v: string) => void } & JSX.HTMLAttributes<HTMLInputElement>) {
276
+ * const rest = omit(props, "label", "value", "onInput");
277
+ *
278
+ * return (
279
+ * <label>
280
+ * {props.label}
281
+ * <input
282
+ * {...rest}
283
+ * value={props.value}
284
+ * onInput={e => props.onInput(e.currentTarget.value)}
285
+ * />
286
+ * </label>
287
+ * );
288
+ * }
289
+ * ```
290
+ */ function omit(e, ...t) {
291
+ if (SUPPORTS_PROXY && $PROXY in e) {
292
+ return new Proxy({
293
+ get(r) {
294
+ return t.includes(r) ? undefined : e[r];
295
+ },
296
+ has(r) {
297
+ return !t.includes(r) && r in e;
298
+ },
299
+ keys() {
300
+ return ownEnumerableKeys(e).filter(e => !t.includes(e));
301
+ }
302
+ }, propTraps);
303
+ }
304
+ const r = {};
305
+ const n = Object.getOwnPropertyNames(e);
306
+ const o = t.length > 4 && n.length > t.length ? new Set(t) : undefined;
307
+ for (const s of n) {
308
+ if (o ? !o.has(s) : !t.includes(s)) {
309
+ const t = Object.getOwnPropertyDescriptor(e, s);
310
+ !t.get && !t.set && t.enumerable && t.writable && t.configurable ? r[s] = t.value : Object.defineProperty(r, s, t);
311
+ }
312
+ }
313
+ return r;
314
+ }
315
+
316
+ export { deep, merge, omit, snapshot };
@@ -1,5 +1,5 @@
1
- import { type Store } from "./store/store.js";
2
1
  import type { Accessor } from "./signals.js";
2
+ import { type Store } from "./store/store.js";
3
3
  /**
4
4
  * Declares that in-flight work will change the targeted data: the named
5
5
  * slot(s) — and everything DERIVED from them — read as pending
@@ -20,7 +20,7 @@ export declare class RevealController {
20
20
  _evaluating: boolean;
21
21
  constructor(order: OrderAccessor, collapsed: BoolAccessor);
22
22
  _forEachOwnedSlot(fn: (slot: RevealSlot) => boolean | void): boolean;
23
- isReady(): boolean;
23
+ _isReady(): boolean;
24
24
  /**
25
25
  * "Minimally ready" = this group has something visible to show under its own policy.
26
26
  * Used by an enclosing `together` group to decide when it can release.
@@ -28,10 +28,10 @@ export declare class RevealController {
28
28
  * - `sequential`: the first owned slot is minimally ready (frontier can advance).
29
29
  * - `natural`: any owned slot is minimally ready.
30
30
  */
31
- isMinimallyReady(): boolean;
32
- register(slot: RevealSlot): void;
33
- unregister(slot: RevealSlot): void;
34
- evaluate(disabledOverride?: boolean, collapsedOverride?: boolean): void;
31
+ _isMinimallyReady(): boolean;
32
+ _register(slot: RevealSlot): void;
33
+ _unregister(slot: RevealSlot): void;
34
+ _evaluate(disabledOverride?: boolean, collapsedOverride?: boolean): void;
35
35
  }
36
36
  export declare class CollectionQueue extends Queue {
37
37
  _collectionType: number;
@@ -48,7 +48,7 @@ export declare class CollectionQueue extends Queue {
48
48
  constructor(type: number);
49
49
  run(type: number): void;
50
50
  notify(node: Effect<any>, type: number, flags: number, error?: any): boolean;
51
- checkSources(): void;
51
+ _checkSources(): void;
52
52
  }
53
53
  /**
54
54
  * Lower-level primitive that backs the `<Loading>` flow control. Catches
@@ -4,10 +4,22 @@
4
4
  * surrounding UI sees one atomic update per yielded step; nothing is committed
5
5
  * until the action either completes or the next `yield` resolves.
6
6
  *
7
- * Yield promises (or any awaitable) inside the generator the action waits
8
- * for each before continuing, but the writes you made beforehand are already
9
- * visible (or held by `<Loading>` if optimistic). Yield bare values for
10
- * synchronous batched steps.
7
+ * `yield` is the transaction-safe suspension point: the action waits for a
8
+ * yielded promise and re-enters the transaction before running the code after
9
+ * it. A plain `await` does NOT the runtime has no hook into an async
10
+ * generator's internal await continuations, so writes to fresh signals
11
+ * between an `await` and the next `yield` escape the transaction and commit
12
+ * immediately. `await` is still the ergonomic choice for typed results; just
13
+ * put a bare `yield` before any writes that follow it:
14
+ *
15
+ * ```ts
16
+ * const saved = await api.createTodo(text); // typed result
17
+ * yield; // re-enter the transaction before writing
18
+ * setTodos(t => { ... });
19
+ * ```
20
+ *
21
+ * (For the same reason, don't call `flush()` inside an action body — it
22
+ * drains the transaction mid-step.)
11
23
  *
12
24
  * Each call returns a `Promise` that resolves with the generator's return
13
25
  * value, or rejects if it throws. Pair with `createOptimistic` /
@@ -18,10 +30,11 @@
18
30
  * ```ts
19
31
  * const [todos, setTodos] = createOptimisticStore<Todo[]>([]);
20
32
  *
21
- * const addTodo = action(function* (text: string) {
33
+ * const addTodo = action(async function* (text: string) {
22
34
  * const tempId = crypto.randomUUID();
23
35
  * setTodos(t => { t.push({ id: tempId, text, pending: true }); }); // optimistic
24
- * const saved = yield api.createTodo(text); // network round-trip
36
+ * const saved = await api.createTodo(text); // network round-trip, typed
37
+ * yield; // re-enter the transaction
25
38
  * setTodos(t => {
26
39
  * const i = t.findIndex(x => x.id === tempId);
27
40
  * if (i >= 0) t[i] = saved;
@@ -1,44 +1,10 @@
1
1
  import { type OptimisticLane } from "./lanes.js";
2
- import type { Computed, Signal } from "./types.js";
2
+ import type { Computed, Link } from "./types.js";
3
+ export declare function addPendingSource(el: Computed<any>, source: Computed<any>): boolean;
4
+ export declare function setPendingError(el: Computed<any>, source?: Computed<any>, error?: any): void;
5
+ export declare function forEachDependent(el: Computed<any>, fn: (node: Computed<any>, link: Link) => void): void;
3
6
  export declare function settlePendingSource(el: Computed<any>, source?: Computed<any>, snap?: boolean): void;
4
7
  export declare function isThenable<T>(value: T | PromiseLike<T>): value is PromiseLike<T>;
5
8
  export declare function handleAsync<T>(el: Computed<T>, result: T | PromiseLike<T> | AsyncIterable<T>, setter?: (value: T) => void): T;
6
9
  export declare function clearStatus(el: Computed<any>, clearUninitialized?: boolean): void;
7
10
  export declare function notifyStatus(el: Computed<any>, status: number, error: any, blockStatus?: boolean, lane?: OptimisticLane): void;
8
- /**
9
- * The pending-source identity of a live `affects()` mark on `node` (lazy,
10
- * one per node, shared by overlapping registrations via the refcount).
11
- *
12
- * A mark rides the SAME status rails as real in-flight async — downstream
13
- * subscribers hold the sentinel in `_pendingSources` — but under its own
14
- * identity so the two channels can't clear each other:
15
- * - `_reask` is permanently `false`: a mark is by definition a declared
16
- * value change, so `quietPending` never silences a window it participates
17
- * in — even when the mark rides over an otherwise-quiet `refresh()`
18
- * re-ask of the same node (the whole point of declaring one).
19
- * - A landing on the marked node settles only the node's OWN source entry;
20
- * the sentinel entry survives until the mark's transaction releases it.
21
- * - The sentinel itself never carries `STATUS_PENDING`, so
22
- * `transitionComplete` never counts a mark as a blocker of its own
23
- * transaction (release happens AT settle — self-blocking would deadlock),
24
- * and reads of the marked node never throw (marks are value-transparent
25
- * at the source; pendingness is what propagates).
26
- */
27
- export declare function getAffectsSentinel(node: Signal<any> | Computed<any>): Computed<any>;
28
- /**
29
- * Push a live mark's pendingness downstream from the marked node through the
30
- * normal status rails. Runs on every registration (dedup in `notifyStatus`
31
- * stops re-descent at already-covered subscribers). Subscribers that
32
- * recompute mid-window shed this via `clearStatus` and re-acquire it through
33
- * the read path (`applyAffectsReads`) — the same shape as real async, where
34
- * the re-throw on read re-establishes the source.
35
- */
36
- export declare function propagateAffectsMark(node: Signal<any> | Computed<any>): void;
37
- /**
38
- * Re-establish mark pendingness on a computed that read marked sources
39
- * during its recompute (`clearStatus` at the top of the commit path wiped
40
- * any sentinel entries it held). Called by `recompute` after the commit —
41
- * not before, because setting `_error` earlier would make the commit path
42
- * treat the node as errored and skip the value write.
43
- */
44
- export declare function applyAffectsReads(el: Computed<any>, sources: (Signal<any> | Computed<any>)[]): void;
@@ -35,6 +35,18 @@ export declare const EFFECT_USER = 2;
35
35
  export declare const EFFECT_TRACKED = 3;
36
36
  export declare const NOT_PENDING: {};
37
37
  export declare const NO_SNAPSHOT: {};
38
+ /**
39
+ * Stand-in stored in `_overrideValue` for an optimistic write of literal
40
+ * `undefined` (#2898). The slot doubles as the optimistic-node brand
41
+ * (`undefined` = not optimistic, `NOT_PENDING` = at rest), so the raw value
42
+ * would erase the node's optimistic identity: the write turns invisible and
43
+ * follow-up writes route off the optimistic path and commit permanently.
44
+ * Same shape as NO_SNAPSHOT. Sites that surface the override VALUE unwrap
45
+ * via `visibleOverrideValue`; slot identity tests stay raw.
46
+ */
47
+ export declare const OVERRIDE_UNDEFINED: {};
48
+ /** Unwrap an active override's stored value for surfacing to readers (#2898). */
49
+ export declare function unwrapOverride<T = any>(v: unknown): T;
38
50
  export declare const STORE_SNAPSHOT_PROPS = "sp";
39
51
  export declare const SUPPORTS_PROXY: boolean;
40
52
  export declare const defaultContext: {};
@@ -5,6 +5,12 @@ export declare const PRIMITIVE_IN_FORBIDDEN_SCOPE_MESSAGE = "[PRIMITIVE_IN_FORBI
5
5
  export declare const REACTIVE_WRITE_IN_OWNED_SCOPE_SIGNAL_MESSAGE: string;
6
6
  export declare const REACTIVE_WRITE_IN_OWNED_SCOPE_REFRESH_MESSAGE: string;
7
7
  export declare let tracking: boolean;
8
+ /** @internal verdict-module glue */
9
+ export declare function setPendingCheckActive(v: boolean): void;
10
+ /** @internal verdict-module glue */
11
+ export declare function setLatestReadActive(v: boolean): void;
12
+ /** @internal verdict-module glue */
13
+ export declare function setContextInternal(v: Owner | null): void;
8
14
  export declare let stale: boolean;
9
15
  export declare let pendingCheckActive: boolean;
10
16
  export declare let latestReadActive: boolean;
@@ -60,6 +66,12 @@ export declare function setStrictRead(v: string | false): string | false;
60
66
  * ```
61
67
  */
62
68
  export declare function untrack<T>(fn: () => T, strictReadLabel?: string | false): T;
69
+ /**
70
+ * Bring a computed to a readable state: lazy/disposed nodes are (re)computed;
71
+ * an isPending() probe (`refresh`) additionally pulls the node fully up to
72
+ * date so its status flags reflect the current graph.
73
+ */
74
+ export declare function prepareComputed(comp: Computed<unknown>, refresh: boolean): void;
63
75
  export declare function read<T>(el: Signal<T> | Computed<T>): T;
64
76
  export declare function setSignal<T>(el: Signal<T> | Computed<T>, v: T | ((prev: T) => T)): T;
65
77
  /**
@@ -97,85 +109,7 @@ export declare function setMemo<T>(el: Computed<T>, v: T | ((prev: T) => T)): T;
97
109
  * ```
98
110
  */
99
111
  export declare function runWithOwner<T>(owner: Owner | null, fn: () => T): T;
100
- /**
101
- * Adds a node to the active isPending() probe without reading it. The store's
102
- * untracked-probe fallback (`witnessAffectsMark`) calls this with `affects()`
103
- * carrier nodes: an untracked read through a marked record may touch no real
104
- * signal node at all, so the probe collects the mark's carrier directly.
105
- *
106
- * @internal
107
- */
108
- export declare function witnessAffects(node: Signal<any> | Computed<any>): void;
109
- /**
110
- * Keep the lazily-created isPending()/latest() companion nodes in sync with a
111
- * new value. Every path that produces a value for `el` — direct set, async
112
- * resolution, transition-held sync recompute — must route through here so a
113
- * new write path can't silently skip the companions (#2831).
114
- */
115
- export declare function syncCompanions<T>(el: Signal<T> | Computed<T>, value: T): void;
116
- /**
117
- * Update _pendingSignal when pending state changes. When the override clears
118
- * (pending -> not pending), merge the sub-lane into the source's lane so
119
- * isPending effects are blocked until the full scope resolves.
120
- */
121
- export declare function updatePendingSignal(el: Signal<any> | Computed<any>): void;
122
- /**
123
- * A firewall's status change re-derives the verdicts of its probed leaves:
124
- * leaf companions consult the firewall (broad inheritance), so async
125
- * starting/settling on the firewall must poke them or they keep a stale
126
- * verdict forever (V4 stuck-companion class, #2838).
127
- */
128
- export declare function updateChildCompanions(el: Computed<any>): void;
129
- /**
130
- * Settlement checkpoint (#2838): re-derive a node's companions directly from
131
- * its committed state. Called when the transition machinery for the node is
132
- * done with it — a pending commit or an optimistic revert. Verdicts are
133
- * written committed (not through setSignal) because a transition-scoped
134
- * override window opened here would itself need a settlement, re-scheduling
135
- * forever while async is still in flight. This is what keeps companions
136
- * coherent past transition completion: a verdict is a property of the data
137
- * (A19), so it must survive the transition that happened to produce it.
138
- */
139
- export declare function snapCompanionsToState(owner: Signal<any> | Computed<any>): void;
140
112
  export declare function staleValues<T>(fn: () => T, set?: boolean): T;
141
- /**
142
- * Reads reactive expressions while bypassing any pending async overlay — i.e.
143
- * always returns the most-recently-committed value, even when newer reads
144
- * inside `fn` are still in flight.
145
- *
146
- * Useful inside a `<Loading>` boundary's children when you want to keep
147
- * showing the previous resolved data instead of the fallback while the next
148
- * value loads.
149
- *
150
- * @example
151
- * ```tsx
152
- * <Loading fallback={<Skeleton />}>
153
- * {/* During a transition, render the previous user instead of skeleton: *\/}
154
- * <UserCard user={latest(() => user())} />
155
- * </Loading>
156
- * ```
157
- */
158
- export declare function latest<T>(fn: () => T): T;
159
- /**
160
- * Returns `true` if any reactive read inside `fn` is showing a stale value
161
- * while newer async work is pending. Does not subscribe — pair with a tracked
162
- * memo if you want to react to pending status changes.
163
- *
164
- * Useful for showing inline transition indicators alongside the previous
165
- * value (rather than swapping to a `<Loading>` fallback).
166
- * Because `fn` is read normally, `isPending` participates in Loading/SSR
167
- * readiness the same way the read itself would.
168
- *
169
- * @example
170
- * ```tsx
171
- * const pending = createMemo(() => isPending(() => user()));
172
- *
173
- * <button disabled={pending()}>{pending() ? "Saving…" : "Save"}</button>
174
- *
175
- * <button disabled={isPending(() => user())}>Save</button>
176
- * ```
177
- */
178
- export declare function isPending(fn: () => any): boolean;
179
113
  /**
180
114
  * Invalidates one reactive source, forcing it to re-execute even if its inputs
181
115
  * haven't changed.