@solidjs/signals 2.0.0-beta.32 → 2.0.0-beta.34
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 +235 -87
- package/dist/node.cjs +758 -635
- package/dist/prod/core/async.js +108 -54
- package/dist/prod/core/core.js +241 -204
- package/dist/prod/core/effect.js +27 -27
- package/dist/prod/core/external.js +4 -4
- package/dist/prod/core/graph.js +32 -32
- package/dist/prod/core/heap.js +36 -36
- package/dist/prod/core/lanes.js +14 -14
- package/dist/prod/core/optimistic.js +39 -39
- package/dist/prod/core/owner.js +53 -44
- package/dist/prod/core/scheduler.js +76 -72
- package/dist/prod/core/verdict.js +59 -48
- package/dist/prod/map.js +6 -6
- package/dist/prod/signals.js +2 -34
- package/dist/prod/store/optimistic.js +37 -32
- package/dist/prod/store/projection.js +41 -6
- package/dist/prod/store/store.js +7 -7
- package/dist/types/core/async.d.ts +9 -0
- package/dist/types/core/types.d.ts +24 -0
- package/dist/types/signals.d.ts +60 -4
- package/dist/types/store/store.d.ts +14 -0
- package/dist/types-cjs/core/async.d.cts +9 -0
- package/dist/types-cjs/core/types.d.cts +24 -0
- package/dist/types-cjs/signals.d.cts +60 -4
- package/dist/types-cjs/store/store.d.cts +14 -0
- package/package.json +1 -1
package/dist/prod/map.js
CHANGED
|
@@ -37,7 +37,7 @@ function mapArray(t, s, i) {
|
|
|
37
37
|
const o = computed(updateKeyedMap.bind(h));
|
|
38
38
|
// Untracked reads inside the internal owner resolve via _parentComputed; routing
|
|
39
39
|
// them through node lets store-proxy lookups see pending writes (not stale _value).
|
|
40
|
-
h.wt.
|
|
40
|
+
h.wt.dt = o;
|
|
41
41
|
o.T &= ~CONFIG_AUTO_DISPOSE;
|
|
42
42
|
return accessor(o);
|
|
43
43
|
}
|
|
@@ -235,18 +235,18 @@ function updateKeyedMap() {
|
|
|
235
235
|
wt: createOwner(),
|
|
236
236
|
jt: 0,
|
|
237
237
|
Jt: 0,
|
|
238
|
-
|
|
238
|
+
Vt: t,
|
|
239
239
|
Kt: e,
|
|
240
240
|
Ut: [],
|
|
241
241
|
xt: [],
|
|
242
|
-
|
|
242
|
+
Xt: i?.from,
|
|
243
243
|
Ht: i?.fallback
|
|
244
244
|
};
|
|
245
245
|
const n = computed(updateRepeat.bind(r));
|
|
246
246
|
// Same as mapArray: untracked reads inside the internal owner resolve via
|
|
247
247
|
// _parentComputed, so async reads in row callbacks register with the node
|
|
248
248
|
// (pending tracking + post-settle retry) instead of vanishing.
|
|
249
|
-
r.wt.
|
|
249
|
+
r.wt.dt = n;
|
|
250
250
|
n.T &= ~CONFIG_AUTO_DISPOSE;
|
|
251
251
|
return accessor(n);
|
|
252
252
|
}
|
|
@@ -259,8 +259,8 @@ function updateKeyedMap() {
|
|
|
259
259
|
// for the post-settle retry. The overlap math also subsumes the previous
|
|
260
260
|
// disjoint-window/front-clear/end-clear/shift special cases.
|
|
261
261
|
function updateRepeat() {
|
|
262
|
-
const t = this.
|
|
263
|
-
const s = this.
|
|
262
|
+
const t = this.Vt();
|
|
263
|
+
const s = this.Xt?.() || 0;
|
|
264
264
|
runWithOwner(this.wt, () => {
|
|
265
265
|
if (t === 0) {
|
|
266
266
|
if (this.jt !== 0) {
|
package/dist/prod/signals.js
CHANGED
|
@@ -72,38 +72,6 @@ function createSignal(e, t) {
|
|
|
72
72
|
return [ accessor(n), setSignal.bind(null, n) ];
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
/**
|
|
76
|
-
* Creates a readonly derived reactive memoized signal.
|
|
77
|
-
*
|
|
78
|
-
* ```typescript
|
|
79
|
-
* const value = createMemo<T>(compute, options?: MemoOptions<T>);
|
|
80
|
-
* ```
|
|
81
|
-
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
82
|
-
* @param options `MemoOptions` -- id, name, equals, unobserved, lazy
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```ts
|
|
86
|
-
* const [first, setFirst] = createSignal("Ada");
|
|
87
|
-
* const [last, setLast] = createSignal("Lovelace");
|
|
88
|
-
*
|
|
89
|
-
* const fullName = createMemo(() => `${first()} ${last()}`);
|
|
90
|
-
*
|
|
91
|
-
* fullName(); // "Ada Lovelace"
|
|
92
|
-
* ```
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```ts
|
|
96
|
-
* // Async memo — reads surface as pending inside <Loading>
|
|
97
|
-
* const user = createMemo(async () => {
|
|
98
|
-
* const res = await fetch(`/users/${id()}`);
|
|
99
|
-
* return res.json();
|
|
100
|
-
* });
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* @description https://docs.solidjs.com/reference/basic-reactivity/create-memo
|
|
104
|
-
*/
|
|
105
|
-
// NoInfer keeps the previous-value parameter from influencing T inference, so
|
|
106
|
-
// the memo/effect result type is still driven by the compute return type.
|
|
107
75
|
function createMemo(e, t) {
|
|
108
76
|
return accessor(computed(e, t));
|
|
109
77
|
}
|
|
@@ -129,7 +97,7 @@ function createEffect(e, t, n) {
|
|
|
129
97
|
* ```
|
|
130
98
|
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
131
99
|
* @param effectFn a function that receives the new value and is used to perform side effects
|
|
132
|
-
* @param options `EffectOptions` -- name, defer, schedule
|
|
100
|
+
* @param options `EffectOptions` -- name, defer, schedule, transparent
|
|
133
101
|
*
|
|
134
102
|
* @example
|
|
135
103
|
* ```ts
|
|
@@ -277,7 +245,7 @@ function createEffect(e, t, n) {
|
|
|
277
245
|
// route through the inherited queue.
|
|
278
246
|
const r = getOwner();
|
|
279
247
|
const o = new MicrotaskQueue;
|
|
280
|
-
o.
|
|
248
|
+
o.ke = r.C;
|
|
281
249
|
// notify() forwards up the normal chain
|
|
282
250
|
r.C = o;
|
|
283
251
|
// A user effect rather than a bare computed: computeds are pull-based and
|
|
@@ -16,7 +16,7 @@ import { runProjectionComputed } from "./projection.js";
|
|
|
16
16
|
|
|
17
17
|
import { $TARGET, STORE_FIREWALL, storeSetter, STORE_OPTIMISTIC_OVERRIDE, STORE_NODE, STORE_OPTIMISTIC_OWNERS, getOverlayLayer, STORE_VALUE, $DELETED, isWrappable, wrap, visibleNodeValue, $TRACK, notifySelf, STORE_WRAP, createStoreProxy, storeTraps, STORE_LOOKUP, STORE_SHALLOW, markRawIngest, STORE_OPTIMISTIC } from "./store.js";
|
|
18
18
|
|
|
19
|
-
function createOptimisticStore(e, t,
|
|
19
|
+
function createOptimisticStore(e, t, i) {
|
|
20
20
|
// Register clear function with scheduler; store nodes marked
|
|
21
21
|
// STORE_OPTIMISTIC take the engine's write path, so install it before any
|
|
22
22
|
// node can be created.
|
|
@@ -41,13 +41,13 @@ function createOptimisticStore(e, t, r) {
|
|
|
41
41
|
return e(t);
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
const
|
|
44
|
+
const r = typeof e === "function";
|
|
45
45
|
// Plain form: the second slot carries options.
|
|
46
|
-
if (!
|
|
47
|
-
const o =
|
|
48
|
-
const n =
|
|
46
|
+
if (!r && i === undefined) i = t;
|
|
47
|
+
const o = r ? t : e;
|
|
48
|
+
const n = r ? e : undefined;
|
|
49
49
|
// Create optimistic projection store
|
|
50
|
-
const {store: c} = createOptimisticProjectionInternal(n, o,
|
|
50
|
+
const {store: c} = createOptimisticProjectionInternal(n, o, i);
|
|
51
51
|
return [ c, e => storeSetter(c, e) ];
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -56,8 +56,8 @@ function createOptimisticStore(e, t, r) {
|
|
|
56
56
|
// scheduler's flush tail carries only a size-guarded hook call. The
|
|
57
57
|
// completing transition scopes each clear to its own layer keys (#2899).
|
|
58
58
|
function clearOptimisticStores(e, t) {
|
|
59
|
-
for (const
|
|
60
|
-
const e =
|
|
59
|
+
for (const i of e) {
|
|
60
|
+
const e = i[$TARGET];
|
|
61
61
|
if (e?.[STORE_OPTIMISTIC_OVERRIDE]) clearOptimisticOverride(e, t);
|
|
62
62
|
}
|
|
63
63
|
e.clear();
|
|
@@ -75,9 +75,9 @@ function clearOptimisticStores(e, t) {
|
|
|
75
75
|
* fresh authoritative data) consumes everything — the correction supersedes
|
|
76
76
|
* every tentative layer.
|
|
77
77
|
*/ function clearOptimisticOverride(e, t) {
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
80
|
-
const
|
|
78
|
+
const i = e[STORE_OPTIMISTIC_OVERRIDE];
|
|
79
|
+
if (!i) return;
|
|
80
|
+
const r = e[STORE_NODE];
|
|
81
81
|
const o = e[STORE_OPTIMISTIC_OWNERS];
|
|
82
82
|
const n = t !== undefined;
|
|
83
83
|
let c = false;
|
|
@@ -87,7 +87,7 @@ function clearOptimisticStores(e, t) {
|
|
|
87
87
|
const O = projectionWriteActive;
|
|
88
88
|
setProjectionWriteActive(true);
|
|
89
89
|
try {
|
|
90
|
-
for (const O of Reflect.ownKeys(
|
|
90
|
+
for (const O of Reflect.ownKeys(i)) {
|
|
91
91
|
if (n) {
|
|
92
92
|
let e = o?.[O] ?? null;
|
|
93
93
|
// Resolve merge chains (entangled actions settle as one); path-compress
|
|
@@ -104,25 +104,25 @@ function clearOptimisticStores(e, t) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
delete
|
|
107
|
+
delete i[O];
|
|
108
108
|
if (o) delete o[O];
|
|
109
109
|
c = true;
|
|
110
|
-
const T =
|
|
110
|
+
const T = r?.[O];
|
|
111
111
|
if (T) {
|
|
112
112
|
// Clear lane association so effects go to regular queue
|
|
113
|
-
T.
|
|
113
|
+
T.Ke = undefined;
|
|
114
114
|
// Re-read from base — this key left the optimistic layer above, so the
|
|
115
115
|
// overlay resolves to STORE_OVERRIDE or STORE_VALUE.
|
|
116
116
|
const t = getOverlayLayer(e, O);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const o = isWrappable(
|
|
117
|
+
const i = t ? t[O] : e[STORE_VALUE][O];
|
|
118
|
+
const r = i === $DELETED ? undefined : i;
|
|
119
|
+
const o = isWrappable(r) ? wrap(r, e) : r;
|
|
120
120
|
const n = visibleNodeValue(T);
|
|
121
|
-
T.
|
|
121
|
+
T._e = NOT_PENDING;
|
|
122
122
|
T.sn = null;
|
|
123
123
|
T.De = NOT_PENDING;
|
|
124
|
-
T.
|
|
125
|
-
if (!T.
|
|
124
|
+
T.Ue = o;
|
|
125
|
+
if (!T.be || !T.be(n, o)) {
|
|
126
126
|
insertSubs(T, true);
|
|
127
127
|
schedule();
|
|
128
128
|
}
|
|
@@ -135,8 +135,8 @@ function clearOptimisticStores(e, t) {
|
|
|
135
135
|
e[STORE_OPTIMISTIC_OWNERS] = undefined;
|
|
136
136
|
}
|
|
137
137
|
// Notify $TRACK
|
|
138
|
-
if (c &&
|
|
139
|
-
|
|
138
|
+
if (c && r?.[$TRACK]) {
|
|
139
|
+
r[$TRACK].Ke = undefined;
|
|
140
140
|
notifySelf(e);
|
|
141
141
|
}
|
|
142
142
|
} finally {
|
|
@@ -144,10 +144,10 @@ function clearOptimisticStores(e, t) {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
function createOptimisticProjectionInternal(e, t,
|
|
148
|
-
let
|
|
147
|
+
function createOptimisticProjectionInternal(e, t, i) {
|
|
148
|
+
let r;
|
|
149
149
|
const o = new WeakMap;
|
|
150
|
-
const n = !!
|
|
150
|
+
const n = !!i?.shallow;
|
|
151
151
|
const wrapper = e => {
|
|
152
152
|
e[STORE_WRAP] = wrapProjection;
|
|
153
153
|
e[STORE_LOOKUP] = o;
|
|
@@ -159,7 +159,7 @@ function createOptimisticProjectionInternal(e, t, r) {
|
|
|
159
159
|
// Mark as optimistic store
|
|
160
160
|
Object.defineProperty(e, STORE_FIREWALL, {
|
|
161
161
|
get() {
|
|
162
|
-
return
|
|
162
|
+
return r;
|
|
163
163
|
},
|
|
164
164
|
configurable: true
|
|
165
165
|
});
|
|
@@ -193,19 +193,24 @@ function createOptimisticProjectionInternal(e, t, r) {
|
|
|
193
193
|
setProjectionWriteActive(t);
|
|
194
194
|
}
|
|
195
195
|
};
|
|
196
|
-
|
|
196
|
+
// seedLoadingValue: born-committed firewall, same as createProjection.
|
|
197
|
+
let t;
|
|
198
|
+
if (i?.seedLoadingValue) t = {
|
|
199
|
+
loadingValue: undefined
|
|
200
|
+
};
|
|
201
|
+
r = computed(() => {
|
|
197
202
|
setProjectionWriteActive(true);
|
|
198
203
|
try {
|
|
199
|
-
runProjectionComputed(c, e,
|
|
204
|
+
runProjectionComputed(c, e, i?.key === undefined ? "id" : i.key, wrapCommit, clearProjectionOverride);
|
|
200
205
|
} finally {
|
|
201
206
|
setProjectionWriteActive(false);
|
|
202
207
|
}
|
|
203
|
-
},
|
|
204
|
-
|
|
208
|
+
}, t);
|
|
209
|
+
r.T &= ~CONFIG_AUTO_DISPOSE;
|
|
205
210
|
}
|
|
206
211
|
return {
|
|
207
212
|
store: c,
|
|
208
|
-
node:
|
|
213
|
+
node: r
|
|
209
214
|
};
|
|
210
215
|
}
|
|
211
216
|
|
|
@@ -14,7 +14,7 @@ import { CONFIG_AUTO_DISPOSE } from "../core/constants.js";
|
|
|
14
14
|
|
|
15
15
|
import { reconcileState } from "./reconcile.js";
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { $TARGET, STORE_VALUE, storeSetter, STORE_WRAP, createStoreProxy, storeTraps, setWriteOverride, STORE_LOOKUP, STORE_SHALLOW, markRawIngest, STORE_FIREWALL } from "./store.js";
|
|
18
18
|
|
|
19
19
|
function createProjectionInternal(e, t, r) {
|
|
20
20
|
let o;
|
|
@@ -44,10 +44,18 @@ function createProjectionInternal(e, t, r) {
|
|
|
44
44
|
return t;
|
|
45
45
|
};
|
|
46
46
|
const c = wrapProjection(t);
|
|
47
|
+
// seedLoadingValue: the firewall is born committed (the seed is commit #0);
|
|
48
|
+
// the internal handleAsync serves it during the derive's first flight. The
|
|
49
|
+
// node's own value channel is void, so the loading value itself is
|
|
50
|
+
// `undefined` — presence of the key is what flips the mode.
|
|
51
|
+
let s;
|
|
52
|
+
if (r?.seedLoadingValue) s = {
|
|
53
|
+
loadingValue: undefined
|
|
54
|
+
};
|
|
47
55
|
o = computed(() => {
|
|
48
56
|
if (!o) o = getOwner();
|
|
49
57
|
runProjectionComputed(c, e, r?.key === undefined ? "id" : r.key);
|
|
50
|
-
},
|
|
58
|
+
}, s);
|
|
51
59
|
o.T &= ~CONFIG_AUTO_DISPOSE;
|
|
52
60
|
return {
|
|
53
61
|
store: c,
|
|
@@ -122,16 +130,43 @@ function createProjectionInternal(e, t, r) {
|
|
|
122
130
|
const i = getOwner();
|
|
123
131
|
let c = false;
|
|
124
132
|
let s;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
// Open loading window (seedLoadingValue): the observable store IS commit #0
|
|
134
|
+
// for the whole first flight, so the derive works a detached shadow of the
|
|
135
|
+
// seed — draft writes (pre-await, or between yields) land on the shadow and
|
|
136
|
+
// cannot tear through to readers (#2988; store reads resolve from the live
|
|
137
|
+
// backing, and the born-committed firewall removed the status gate that hid
|
|
138
|
+
// windowless drafts). Every commit point — sync return, each yield, the
|
|
139
|
+
// async landing — reconciles the shadow through the normal commit path, so
|
|
140
|
+
// a fully-sync derive still lands immediately (commit #0 superseded before
|
|
141
|
+
// any observer runs, same as a sync answer superseding loadingValue). The
|
|
142
|
+
// JSON round-trip matches the server's frozen-seed copy (seedLock): a
|
|
143
|
+
// loading-window seed is renderable data by contract. Optimistic note:
|
|
144
|
+
// onDraftWrite (override clearing) shifts from write-time to commit-time
|
|
145
|
+
// for the shadow run — an invisible draft write must not clobber a visible
|
|
146
|
+
// optimistic override mid-window.
|
|
147
|
+
const u = i.Ee ? JSON.parse(JSON.stringify(e[$TARGET][STORE_VALUE])) : null;
|
|
148
|
+
const a = new Proxy(e, createWriteTraps(() => !c || i.Te === s, n));
|
|
149
|
+
storeSetter(a, n => {
|
|
150
|
+
s = t(u ?? n);
|
|
128
151
|
c = true;
|
|
129
152
|
const commit = t => {
|
|
153
|
+
// Shadow run: a void/self return is the mutation form — the shadow
|
|
154
|
+
// carries the writes and is what commits. Commit a detached snapshot,
|
|
155
|
+
// never the shadow itself: reconcile adopts a new root value by
|
|
156
|
+
// identity, and handing it the live shadow would fuse the draft to the
|
|
157
|
+
// observable store — later shadow writes would mutate the backing
|
|
158
|
+
// silently and the next yield would diff the shadow against itself.
|
|
159
|
+
if (u && (t === undefined || t === u)) t = JSON.parse(JSON.stringify(u));
|
|
130
160
|
if (t === n || t === undefined) return;
|
|
131
161
|
const write = () => storeSetter(e, e => reconcileState(t, e, r, true));
|
|
132
162
|
o ? o(write) : write();
|
|
133
163
|
};
|
|
134
|
-
|
|
164
|
+
const a = handleAsync(i, s, commit);
|
|
165
|
+
// A still-open window after handleAsync means the return was the
|
|
166
|
+
// commit-#0 fall-through, not a landing — real landings arrive through
|
|
167
|
+
// the setter. A closed one is a genuine sync landing (windowless nodes
|
|
168
|
+
// were never open); commit it.
|
|
169
|
+
if (!i.Ee) commit(a);
|
|
135
170
|
});
|
|
136
171
|
return i;
|
|
137
172
|
}
|
package/dist/prod/store/store.js
CHANGED
|
@@ -291,7 +291,7 @@ function ownEnumerableKeysPlain(e) {
|
|
|
291
291
|
* The value a store leaf's backing signal currently shows to readers: active
|
|
292
292
|
* override, else held pending value, else committed value.
|
|
293
293
|
*/ function visibleNodeValue(e) {
|
|
294
|
-
return e.
|
|
294
|
+
return e._e !== undefined && e._e !== NOT_PENDING ? unwrapOverride(e._e) : e.De !== NOT_PENDING ? e.De : e.Ue;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
function hasOwnStoreProperty(e, t) {
|
|
@@ -342,11 +342,11 @@ function getNode(e, t, r, n, o = isEqual, i) {
|
|
|
342
342
|
}
|
|
343
343
|
}, e[STORE_FIREWALL]);
|
|
344
344
|
if (e[STORE_OPTIMISTIC]) {
|
|
345
|
-
O.
|
|
345
|
+
O._e = NOT_PENDING;
|
|
346
346
|
}
|
|
347
347
|
if (i && r in i) {
|
|
348
348
|
const e = i[r];
|
|
349
|
-
O.
|
|
349
|
+
O.Le = e === undefined ? NO_SNAPSHOT : e;
|
|
350
350
|
snapshotSources?.add(O);
|
|
351
351
|
}
|
|
352
352
|
if (typeof r === "symbol" && r !== $TRACK && r !== $AFFECTS) symbolKeyedRecords.add(t);
|
|
@@ -464,11 +464,11 @@ o) {
|
|
|
464
464
|
// Callers guard on `pendingCheckActive`, which only flips inside
|
|
465
465
|
// isPending() — the verdict layer is loaded and its hook installed.
|
|
466
466
|
const r = e[STORE_NODE]?.[$AFFECTS];
|
|
467
|
-
if (r?.t) GlobalQueue.
|
|
467
|
+
if (r?.t) GlobalQueue.Lt(r);
|
|
468
468
|
if (affectsScopes.size) {
|
|
469
469
|
const n = e[STORE_VALUE];
|
|
470
470
|
for (const [e, o] of affectsScopes) {
|
|
471
|
-
if (e !== r && e.t && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.
|
|
471
|
+
if (e !== r && e.t && o.scope.has(n) && (o.key === undefined || o.key === t)) GlobalQueue.Lt(e);
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
474
|
}
|
|
@@ -610,8 +610,8 @@ function getPropertyDescriptor(e, t, r) {
|
|
|
610
610
|
function prepareStoreWrite(e, t, r) {
|
|
611
611
|
if (e[STORE_OPTIMISTIC]) {
|
|
612
612
|
const t = e[STORE_FIREWALL];
|
|
613
|
-
if (t?.
|
|
614
|
-
globalQueue.initTransition(t.
|
|
613
|
+
if (t?.Ne) {
|
|
614
|
+
globalQueue.initTransition(t.Ne);
|
|
615
615
|
}
|
|
616
616
|
}
|
|
617
617
|
const n = e[STORE_VALUE];
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { NotReadyError } from "./error.js";
|
|
1
2
|
import { type OptimisticLane } from "./lanes.js";
|
|
2
3
|
import type { Computed, Link } from "./types.js";
|
|
3
4
|
export declare function addPendingSource(el: Computed<any>, source: Computed<any>): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* A loading-window node hit an unready source (sync throw in recompute, or a
|
|
7
|
+
* NotReadyError-rejected flight): register for the source's settle — the
|
|
8
|
+
* settlePendingSource walk runs off `_pendingSources` + `_blocked` alone —
|
|
9
|
+
* with NO read-visible pending status, no downstream propagation, no
|
|
10
|
+
* transition, no lane registration. Commit #0 keeps serving.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parkLoadingWindow(el: Computed<any>, e: NotReadyError): void;
|
|
4
13
|
export declare function setPendingError(el: Computed<any>, source?: Computed<any>, error?: any): void;
|
|
5
14
|
export declare function forEachDependent(el: Computed<any>, fn: (node: Computed<any>, link: Link) => void): void;
|
|
6
15
|
export declare function releaseSettledDependents(el: Computed<any>): void;
|
|
@@ -30,6 +30,16 @@ export interface NodeOptions<T> {
|
|
|
30
30
|
unobserved?: () => void;
|
|
31
31
|
lazy?: boolean;
|
|
32
32
|
sync?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Commit #0. When present (checked with `in`, so an explicit `undefined`
|
|
35
|
+
* counts), the node is born committed with this value instead of
|
|
36
|
+
* STATUS_UNINITIALIZED: reads serve it everywhere, nothing suspends to
|
|
37
|
+
* Loading boundaries, transitions are never held, and the window is
|
|
38
|
+
* verdict-quiet (`isPending` stays false — commit #0 answers the question
|
|
39
|
+
* by declaration; first-load affordances live in the value itself). After
|
|
40
|
+
* the first real answer lands, normal refetch/pending semantics apply.
|
|
41
|
+
*/
|
|
42
|
+
loadingValue?: T;
|
|
33
43
|
}
|
|
34
44
|
export interface RawSignal<T> {
|
|
35
45
|
_subs: Link | null;
|
|
@@ -116,6 +126,20 @@ export interface Computed<T> extends RawSignal<T>, Owner {
|
|
|
116
126
|
* (`clearStatus`). Meaningless while not STATUS_PENDING.
|
|
117
127
|
*/
|
|
118
128
|
_reask: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* True while a `loadingValue` node's first real answer hasn't landed: the
|
|
131
|
+
* node was born committed (commit #0 = the loading value) and `handleAsync`
|
|
132
|
+
* serves that committed value instead of throwing NotReadyError, so first
|
|
133
|
+
* flights never suspend readers, trip boundaries, or hold transitions.
|
|
134
|
+
* The window is verdict-quiet: `isPending` stays false, because commit #0
|
|
135
|
+
* answers the question by declaration (first-load affordances belong to
|
|
136
|
+
* the value channel). Cleared by the first value landing on any path (sync
|
|
137
|
+
* return, sync-resolved promise, first iterator yield, async settle); a
|
|
138
|
+
* real error leaves it set — errors answer reads but don't enter the value
|
|
139
|
+
* lineage, so a retry serves the loading value again. Once cleared, normal
|
|
140
|
+
* pending/refetch semantics apply forever.
|
|
141
|
+
*/
|
|
142
|
+
_loading: boolean;
|
|
119
143
|
}
|
|
120
144
|
export interface Root extends Owner {
|
|
121
145
|
_root: true;
|
package/dist/types/signals.d.ts
CHANGED
|
@@ -111,6 +111,28 @@ export interface EffectOptions extends BaseEffectOptions {
|
|
|
111
111
|
* stored as-is and never awaited.
|
|
112
112
|
*/
|
|
113
113
|
sync?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Advanced (integration tier). When true, the effect is invisible to the
|
|
116
|
+
* hydration id scheme: it inherits its parent's id instead of consuming a
|
|
117
|
+
* child slot, and during hydration its compute runs live instead of
|
|
118
|
+
* adopting the serialized server value (its first run is not frozen to
|
|
119
|
+
* the server's decision).
|
|
120
|
+
*
|
|
121
|
+
* For **client-only effects created while hydrating** — effects with no
|
|
122
|
+
* server-rendered counterpart (a router wiring link state, scroll
|
|
123
|
+
* restoration, etc.). An id-consuming node the server never created would
|
|
124
|
+
* shift every later sibling's hydration id, making serialized lookups and
|
|
125
|
+
* template claims after it miss. `transparent` is also the supported
|
|
126
|
+
* alternative to branching on hydration state
|
|
127
|
+
* (`if (hydrating) createEffect(...)`), which freezes whatever the first
|
|
128
|
+
* run decided: create the effect unconditionally and let it observe live
|
|
129
|
+
* state instead.
|
|
130
|
+
*
|
|
131
|
+
* SSR ignores this option (a server-side effect always allocates its id
|
|
132
|
+
* slot), so only mark effects the server does not create. Outside
|
|
133
|
+
* hydration it is a no-op.
|
|
134
|
+
*/
|
|
135
|
+
transparent?: boolean;
|
|
114
136
|
}
|
|
115
137
|
/** Options for plain signals created with `createSignal(value)` or `createOptimistic(value)`. */
|
|
116
138
|
export interface SignalOptions<T> {
|
|
@@ -138,7 +160,14 @@ export interface MemoOptions<T> {
|
|
|
138
160
|
id?: string;
|
|
139
161
|
/** Debug name (dev mode only) */
|
|
140
162
|
name?: string;
|
|
141
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* Advanced (integration tier). When true, the memo is invisible to the
|
|
165
|
+
* hydration id scheme: it inherits its parent's id instead of consuming a
|
|
166
|
+
* child slot, and during hydration it computes live instead of adopting
|
|
167
|
+
* the serialized server value. For client-only memos with no
|
|
168
|
+
* server-rendered counterpart — see {@link EffectOptions.transparent} for
|
|
169
|
+
* the full semantics. No-op outside hydration.
|
|
170
|
+
*/
|
|
142
171
|
transparent?: boolean;
|
|
143
172
|
/**
|
|
144
173
|
* Custom equality function, or `false` to always notify subscribers.
|
|
@@ -168,6 +197,30 @@ export interface MemoOptions<T> {
|
|
|
168
197
|
* stored as-is and never awaited.
|
|
169
198
|
*/
|
|
170
199
|
sync?: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Commit #0: a committed value the memo is born with, shown until the
|
|
202
|
+
* compute's first real answer lands. While that first answer is in flight
|
|
203
|
+
* the memo reads as a settled value everywhere — nothing suspends to a
|
|
204
|
+
* `<Loading>` boundary, no transition is held (first-flight work is
|
|
205
|
+
* loading-class, like a boundary fallback), and `isPending(memo)` stays
|
|
206
|
+
* **false**: commit #0 answers the question by declaration, so first-load
|
|
207
|
+
* affordances are driven from the value itself (a `null` placeholder, a
|
|
208
|
+
* `skeleton: true` field, etc.). Once the first answer lands, the loading
|
|
209
|
+
* value leaves the lineage forever: refetches use normal pending semantics
|
|
210
|
+
* (stale value shown, `isPending` true, boundaries/transitions coordinate)
|
|
211
|
+
* — the canonical guard is `data.skeleton || isPending(data)`, whose two
|
|
212
|
+
* terms cover the two disjoint states.
|
|
213
|
+
*
|
|
214
|
+
* Typed strictly as `T`: to use `null`/`undefined` as the placeholder,
|
|
215
|
+
* declare it in the memo's type (e.g. `createMemo<User | null>(...)`), so
|
|
216
|
+
* every consumer sees the nullable window honestly. If the placeholder is
|
|
217
|
+
* shaped data standing in for real data, encode its provenance in the data
|
|
218
|
+
* (e.g. a `skeleton: true` field) rather than letting it impersonate truth.
|
|
219
|
+
*
|
|
220
|
+
* The loading value is also the compute's first `prev`, so `prev`-based
|
|
221
|
+
* memos fold from it.
|
|
222
|
+
*/
|
|
223
|
+
loadingValue?: T;
|
|
171
224
|
}
|
|
172
225
|
export type NoInfer<T extends any> = [T][T extends any ? 0 : never];
|
|
173
226
|
/**
|
|
@@ -216,7 +269,7 @@ export declare function createSignal<T>(fn: ComputeFunction<T>, options?: Signal
|
|
|
216
269
|
* const value = createMemo<T>(compute, options?: MemoOptions<T>);
|
|
217
270
|
* ```
|
|
218
271
|
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
219
|
-
* @param options `MemoOptions` -- id, name, equals, unobserved, lazy
|
|
272
|
+
* @param options `MemoOptions` -- id, name, equals, unobserved, lazy, transparent
|
|
220
273
|
*
|
|
221
274
|
* @example
|
|
222
275
|
* ```ts
|
|
@@ -239,6 +292,9 @@ export declare function createSignal<T>(fn: ComputeFunction<T>, options?: Signal
|
|
|
239
292
|
*
|
|
240
293
|
* @description https://docs.solidjs.com/reference/basic-reactivity/create-memo
|
|
241
294
|
*/
|
|
295
|
+
export declare function createMemo<T>(compute: ComputeFunction<NoInfer<T>, T>, options: MemoOptions<T> & {
|
|
296
|
+
loadingValue: T;
|
|
297
|
+
}): SourceAccessor<T>;
|
|
242
298
|
export declare function createMemo<T>(compute: ComputeFunction<undefined | NoInfer<T>, T>, options?: MemoOptions<T>): SourceAccessor<T>;
|
|
243
299
|
/**
|
|
244
300
|
* Creates a reactive effect with **separate compute and effect phases**.
|
|
@@ -280,7 +336,7 @@ export declare function createMemo<T>(compute: ComputeFunction<undefined | NoInf
|
|
|
280
336
|
* ```
|
|
281
337
|
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
282
338
|
* @param effectFn a function that receives the new value and is used to perform side effects (return a cleanup function), or an `EffectBundle` with `effect` and `error` handlers
|
|
283
|
-
* @param options `EffectOptions` -- name, defer, schedule
|
|
339
|
+
* @param options `EffectOptions` -- name, defer, schedule, transparent
|
|
284
340
|
*
|
|
285
341
|
* @example
|
|
286
342
|
* ```ts
|
|
@@ -334,7 +390,7 @@ export declare function createEffect<T>(compute: ComputeFunction<undefined | NoI
|
|
|
334
390
|
* ```
|
|
335
391
|
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
336
392
|
* @param effectFn a function that receives the new value and is used to perform side effects
|
|
337
|
-
* @param options `EffectOptions` -- name, defer, schedule
|
|
393
|
+
* @param options `EffectOptions` -- name, defer, schedule, transparent
|
|
338
394
|
*
|
|
339
395
|
* @example
|
|
340
396
|
* ```ts
|
|
@@ -33,6 +33,20 @@ export interface ProjectionOptions extends StoreOptions {
|
|
|
33
33
|
key?: string | ((item: NonNullable<any>) => any) | null;
|
|
34
34
|
/** Single-layer store: root keys reactive, values raw records replaced by reference */
|
|
35
35
|
shallow?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Treat the seed as commit #0: the store is born committed with the seed's
|
|
38
|
+
* contents, shown until the derive's first real answer lands. While that
|
|
39
|
+
* first answer is in flight, reads serve the seed everywhere — nothing
|
|
40
|
+
* suspends to a `<Loading>` boundary, no transition is held, and
|
|
41
|
+
* `isPending` stays false (the seed answers by declaration; first-load
|
|
42
|
+
* affordances belong to the data, e.g. a `skeleton: true` field in the
|
|
43
|
+
* seed). Once the first answer lands (reconciled into the seed), refetches
|
|
44
|
+
* use normal pending semantics with `isPending` true.
|
|
45
|
+
*
|
|
46
|
+
* The store equivalent of `MemoOptions.loadingValue`; the seed already
|
|
47
|
+
* carries the placeholder shape, so this is just the opt-in.
|
|
48
|
+
*/
|
|
49
|
+
seedLoadingValue?: boolean;
|
|
36
50
|
}
|
|
37
51
|
export type NoFn<T> = T extends Function ? never : T;
|
|
38
52
|
type DataNode = Signal<any>;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { NotReadyError } from "./error.cjs";
|
|
1
2
|
import { type OptimisticLane } from "./lanes.cjs";
|
|
2
3
|
import type { Computed, Link } from "./types.cjs";
|
|
3
4
|
export declare function addPendingSource(el: Computed<any>, source: Computed<any>): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* A loading-window node hit an unready source (sync throw in recompute, or a
|
|
7
|
+
* NotReadyError-rejected flight): register for the source's settle — the
|
|
8
|
+
* settlePendingSource walk runs off `_pendingSources` + `_blocked` alone —
|
|
9
|
+
* with NO read-visible pending status, no downstream propagation, no
|
|
10
|
+
* transition, no lane registration. Commit #0 keeps serving.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parkLoadingWindow(el: Computed<any>, e: NotReadyError): void;
|
|
4
13
|
export declare function setPendingError(el: Computed<any>, source?: Computed<any>, error?: any): void;
|
|
5
14
|
export declare function forEachDependent(el: Computed<any>, fn: (node: Computed<any>, link: Link) => void): void;
|
|
6
15
|
export declare function releaseSettledDependents(el: Computed<any>): void;
|
|
@@ -30,6 +30,16 @@ export interface NodeOptions<T> {
|
|
|
30
30
|
unobserved?: () => void;
|
|
31
31
|
lazy?: boolean;
|
|
32
32
|
sync?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Commit #0. When present (checked with `in`, so an explicit `undefined`
|
|
35
|
+
* counts), the node is born committed with this value instead of
|
|
36
|
+
* STATUS_UNINITIALIZED: reads serve it everywhere, nothing suspends to
|
|
37
|
+
* Loading boundaries, transitions are never held, and the window is
|
|
38
|
+
* verdict-quiet (`isPending` stays false — commit #0 answers the question
|
|
39
|
+
* by declaration; first-load affordances live in the value itself). After
|
|
40
|
+
* the first real answer lands, normal refetch/pending semantics apply.
|
|
41
|
+
*/
|
|
42
|
+
loadingValue?: T;
|
|
33
43
|
}
|
|
34
44
|
export interface RawSignal<T> {
|
|
35
45
|
_subs: Link | null;
|
|
@@ -116,6 +126,20 @@ export interface Computed<T> extends RawSignal<T>, Owner {
|
|
|
116
126
|
* (`clearStatus`). Meaningless while not STATUS_PENDING.
|
|
117
127
|
*/
|
|
118
128
|
_reask: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* True while a `loadingValue` node's first real answer hasn't landed: the
|
|
131
|
+
* node was born committed (commit #0 = the loading value) and `handleAsync`
|
|
132
|
+
* serves that committed value instead of throwing NotReadyError, so first
|
|
133
|
+
* flights never suspend readers, trip boundaries, or hold transitions.
|
|
134
|
+
* The window is verdict-quiet: `isPending` stays false, because commit #0
|
|
135
|
+
* answers the question by declaration (first-load affordances belong to
|
|
136
|
+
* the value channel). Cleared by the first value landing on any path (sync
|
|
137
|
+
* return, sync-resolved promise, first iterator yield, async settle); a
|
|
138
|
+
* real error leaves it set — errors answer reads but don't enter the value
|
|
139
|
+
* lineage, so a retry serves the loading value again. Once cleared, normal
|
|
140
|
+
* pending/refetch semantics apply forever.
|
|
141
|
+
*/
|
|
142
|
+
_loading: boolean;
|
|
119
143
|
}
|
|
120
144
|
export interface Root extends Owner {
|
|
121
145
|
_root: true;
|