@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.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.
- package/dist/dev-shared.js +6505 -0
- package/dist/dev.attribution.js +3484 -0
- package/dist/dev.js +10227 -9508
- package/dist/observe/affects-marks.js +86 -0
- package/dist/observe/affects.js +112 -0
- package/dist/observe/attribution.js +7 -0
- package/dist/observe/boundaries.js +696 -0
- package/dist/observe/core/action.js +182 -0
- package/dist/observe/core/async.js +824 -0
- package/dist/observe/core/attribution-costs.js +66 -0
- package/dist/observe/core/attribution-feedback.js +280 -0
- package/dist/observe/core/attribution-hooks.js +83 -0
- package/dist/observe/core/attribution-queries.js +43 -0
- package/dist/observe/core/attribution.js +3079 -0
- package/dist/observe/core/constants.js +269 -0
- package/dist/observe/core/context.js +63 -0
- package/dist/observe/core/copies.js +10 -0
- package/dist/observe/core/core.js +1707 -0
- package/dist/observe/core/dev.js +394 -0
- package/dist/observe/core/effect.js +193 -0
- package/dist/observe/core/error-hooks.js +71 -0
- package/dist/observe/core/error.js +82 -0
- package/dist/observe/core/external.js +97 -0
- package/dist/observe/core/graph.js +162 -0
- package/dist/observe/core/heap.js +124 -0
- package/dist/observe/core/identities.js +62 -0
- package/dist/observe/core/invariants.js +12 -0
- package/dist/observe/core/lanes.js +972 -0
- package/dist/observe/core/owner.js +371 -0
- package/dist/observe/core/scheduler.js +1157 -0
- package/dist/observe/core/truth.js +47 -0
- package/dist/observe/core/verdict.js +470 -0
- package/dist/observe/deferred.js +186 -0
- package/dist/observe/flatten.js +79 -0
- package/dist/observe/from.js +248 -0
- package/dist/observe/index.js +71 -0
- package/dist/observe/map.js +593 -0
- package/dist/observe/reveal.js +212 -0
- package/dist/observe/signals.js +717 -0
- package/dist/observe/store/affects.js +142 -0
- package/dist/observe/store/edges.js +39 -0
- package/dist/observe/store/hooks.js +16 -0
- package/dist/observe/store/index.js +47 -0
- package/dist/observe/store/optimistic.js +713 -0
- package/dist/observe/store/projection.js +345 -0
- package/dist/observe/store/reconcile.js +281 -0
- package/dist/observe/store/store.js +2127 -0
- package/dist/observe/store/storePath.js +103 -0
- package/dist/observe/store/target.js +46 -0
- package/dist/observe/store/types.js +83 -0
- package/dist/observe/store/utils.js +1063 -0
- package/dist/prod/affects-marks.js +86 -0
- package/dist/prod/affects.js +98 -112
- package/dist/prod/attribution.js +45 -0
- package/dist/prod/boundaries.js +578 -468
- package/dist/prod/core/action.js +92 -59
- package/dist/prod/core/async.js +522 -336
- package/dist/prod/core/attribution-hooks.js +3 -0
- package/dist/prod/core/constants.js +168 -44
- package/dist/prod/core/context.js +12 -16
- package/dist/prod/core/copies.js +10 -0
- package/dist/prod/core/core.js +1345 -627
- package/dist/prod/core/dev.js +123 -1
- package/dist/prod/core/effect.js +113 -80
- package/dist/prod/core/error-hooks.js +71 -0
- package/dist/prod/core/error.js +18 -4
- package/dist/prod/core/external.js +6 -7
- package/dist/prod/core/graph.js +119 -72
- package/dist/prod/core/heap.js +85 -109
- package/dist/prod/core/identities.js +62 -0
- package/dist/prod/core/invariants.js +1 -32
- package/dist/prod/core/lanes.js +931 -132
- package/dist/prod/core/owner.js +192 -129
- package/dist/prod/core/scheduler.js +948 -642
- package/dist/prod/core/truth.js +47 -0
- package/dist/prod/core/verdict.js +409 -420
- package/dist/prod/deferred.js +184 -0
- package/dist/prod/flatten.js +77 -0
- package/dist/prod/from.js +248 -0
- package/dist/prod/index.js +40 -16
- package/dist/prod/map.js +481 -211
- package/dist/prod/reveal.js +212 -0
- package/dist/prod/signals.js +401 -84
- package/dist/prod/store/affects.js +142 -0
- package/dist/prod/store/edges.js +39 -0
- package/dist/prod/store/hooks.js +16 -0
- package/dist/prod/store/index.js +27 -18
- package/dist/prod/store/optimistic.js +707 -0
- package/dist/prod/store/projection.js +331 -0
- package/dist/prod/store/reconcile.js +281 -0
- package/dist/prod/store/store.js +1952 -267
- package/dist/prod/store/storePath.js +1 -1
- package/dist/prod/store/target.js +43 -0
- package/dist/prod/store/types.js +83 -0
- package/dist/prod/store/utils.js +997 -138
- package/dist/types/affects-marks.d.ts +14 -0
- package/dist/types/affects.d.ts +12 -30
- package/dist/types/attribution.d.ts +19 -0
- package/dist/types/attribution.prod.d.ts +22 -0
- package/dist/types/boundaries.d.ts +99 -124
- package/dist/types/carved.d.ts +1 -0
- package/dist/types/core/action.d.ts +23 -9
- package/dist/types/core/async.d.ts +34 -6
- package/dist/types/core/attribution-costs.d.ts +35 -0
- package/dist/types/core/attribution-feedback.d.ts +133 -0
- package/dist/types/core/attribution-hooks.d.ts +307 -8
- package/dist/types/core/attribution-queries.d.ts +24 -0
- package/dist/types/core/attribution.d.ts +802 -72
- package/dist/types/core/constants.d.ts +195 -38
- package/dist/types/core/copies.d.ts +54 -0
- package/dist/types/core/core.d.ts +295 -65
- package/dist/types/core/dev.d.ts +509 -22
- package/dist/types/core/effect.d.ts +1 -1
- package/dist/types/core/error-hooks.d.ts +71 -0
- package/dist/types/core/error.d.ts +18 -1
- package/dist/types/core/graph.d.ts +40 -1
- package/dist/types/core/heap.d.ts +5 -5
- package/dist/types/core/identities.d.ts +17 -0
- package/dist/types/core/index.d.ts +11 -7
- package/dist/types/core/invariants.d.ts +1 -26
- package/dist/types/core/lanes.d.ts +74 -43
- package/dist/types/core/owner.d.ts +27 -9
- package/dist/types/core/scheduler.d.ts +353 -102
- package/dist/types/core/truth.d.ts +18 -0
- package/dist/types/core/types.d.ts +97 -71
- package/dist/types/core/verdict.d.ts +35 -0
- package/dist/types/deferred.d.ts +28 -0
- package/dist/types/flatten.d.ts +8 -0
- package/dist/types/from.d.ts +47 -0
- package/dist/types/index.d.ts +33 -7
- package/dist/types/map.d.ts +4 -2
- package/dist/types/reveal.d.ts +19 -0
- package/dist/types/signals.d.ts +196 -45
- package/dist/types/store/affects.d.ts +12 -0
- package/dist/types/store/edges.d.ts +9 -0
- package/dist/types/store/hooks.d.ts +62 -0
- package/dist/types/store/index.d.ts +42 -15
- package/dist/types/store/optimistic.d.ts +24 -45
- package/dist/types/store/projection.d.ts +17 -68
- package/dist/types/store/reconcile.d.ts +3 -46
- package/dist/types/store/store.d.ts +133 -136
- package/dist/types/store/storePath.d.ts +1 -1
- package/dist/types/store/target.d.ts +166 -0
- package/dist/types/store/types.d.ts +105 -0
- package/dist/types/store/utils.d.ts +188 -0
- package/package.json +31 -26
- package/dist/node.cjs +0 -8745
- package/dist/prod/core/optimistic.js +0 -265
- package/dist/prod/store/next/optimistic.js +0 -385
- package/dist/prod/store/next/projection.js +0 -172
- package/dist/prod/store/next/reconcile.js +0 -331
- package/dist/prod/store/next/store.js +0 -1499
- package/dist/prod/store/next/target.js +0 -20
- package/dist/types/core/optimistic.d.ts +0 -6
- package/dist/types/store/next/optimistic.d.ts +0 -20
- package/dist/types/store/next/projection.d.ts +0 -8
- package/dist/types/store/next/reconcile.d.ts +0 -3
- package/dist/types/store/next/store.d.ts +0 -77
- package/dist/types/store/next/target.d.ts +0 -117
- package/dist/types-cjs/affects.d.cts +0 -47
- package/dist/types-cjs/boundaries.d.cts +0 -175
- package/dist/types-cjs/core/action.d.cts +0 -64
- package/dist/types-cjs/core/async.d.cts +0 -21
- package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
- package/dist/types-cjs/core/attribution.d.cts +0 -186
- package/dist/types-cjs/core/constants.d.cts +0 -97
- package/dist/types-cjs/core/context.d.cts +0 -36
- package/dist/types-cjs/core/core.d.cts +0 -163
- package/dist/types-cjs/core/dev.d.cts +0 -81
- package/dist/types-cjs/core/effect.d.cts +0 -30
- package/dist/types-cjs/core/error.d.cts +0 -47
- package/dist/types-cjs/core/external.d.cts +0 -15
- package/dist/types-cjs/core/graph.d.cts +0 -6
- package/dist/types-cjs/core/heap.d.cts +0 -22
- package/dist/types-cjs/core/index.d.cts +0 -13
- package/dist/types-cjs/core/invariants.d.cts +0 -59
- package/dist/types-cjs/core/lanes.d.cts +0 -44
- package/dist/types-cjs/core/optimistic.d.cts +0 -6
- package/dist/types-cjs/core/owner.d.cts +0 -124
- package/dist/types-cjs/core/scheduler.d.cts +0 -180
- package/dist/types-cjs/core/types.d.cts +0 -192
- package/dist/types-cjs/core/verdict.d.cts +0 -2
- package/dist/types-cjs/index.d.cts +0 -10
- package/dist/types-cjs/map.d.cts +0 -69
- package/dist/types-cjs/package.json +0 -3
- package/dist/types-cjs/signals.d.cts +0 -624
- package/dist/types-cjs/store/index.d.cts +0 -19
- package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
- package/dist/types-cjs/store/next/projection.d.cts +0 -8
- package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
- package/dist/types-cjs/store/next/store.d.cts +0 -77
- package/dist/types-cjs/store/next/target.d.cts +0 -117
- package/dist/types-cjs/store/optimistic.d.cts +0 -45
- package/dist/types-cjs/store/projection.d.cts +0 -70
- package/dist/types-cjs/store/reconcile.d.cts +0 -46
- package/dist/types-cjs/store/store.d.cts +0 -138
- package/dist/types-cjs/store/storePath.d.cts +0 -58
- package/dist/types-cjs/store/utils.d.cts +0 -74
package/dist/prod/signals.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TimeoutError, NotReadyError, unwrapStatusError } from "./core/error.js";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import "./core/verdict.js";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { CONFIG_CHILDREN_FORBIDDEN, EFFECT_USER, $REFRESH, CONFIG_AUTO_DISPOSE, CONFIG_AUTHORITATIVE, CONFIG_REDERIVE, REACTIVE_JOINED, CONFIG_FRESH_READ, CONFIG_GUESS, STATUS_PENDING, STATUS_ERROR } from "./core/constants.js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { computed, signal, runWithOwner, setMemo, setSignal, markRefresh, read, untrack } from "./core/core.js";
|
|
8
8
|
|
|
9
|
-
import "./core/
|
|
9
|
+
import { schedule, globalQueue, flushTransaction, dirtyQueue, GlobalQueue, resolveTx, passTx, staleReader, joinFuture } from "./core/scheduler.js";
|
|
10
10
|
|
|
11
|
-
import "./core/
|
|
11
|
+
import { cleanup, createRoot, getOwner, dispose } from "./core/owner.js";
|
|
12
|
+
|
|
13
|
+
import { signalHook } from "./core/identities.js";
|
|
12
14
|
|
|
13
15
|
import { effect, trackedEffect } from "./core/effect.js";
|
|
14
16
|
|
|
15
|
-
import {
|
|
17
|
+
import { optimisticWrite } from "./core/lanes.js";
|
|
18
|
+
|
|
19
|
+
import { truthRead, truthConfirmed } from "./core/truth.js";
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* Low-level reactive-cleanup primitive. Registers a callback that runs when
|
|
@@ -41,6 +45,12 @@ import { installOptimisticEngine } from "./core/optimistic.js";
|
|
|
41
45
|
* Cannot be used inside `createTrackedEffect` or `onSettled` — return a
|
|
42
46
|
* cleanup function from the callback body instead.
|
|
43
47
|
*
|
|
48
|
+
* Cleanups run in unwind order: an owner's children are disposed before its
|
|
49
|
+
* own cleanups, and within one owner later registrations run before earlier
|
|
50
|
+
* ones. In production a component body shares its enclosing owner, so
|
|
51
|
+
* register cleanup before creating children when the order between them
|
|
52
|
+
* matters.
|
|
53
|
+
*
|
|
44
54
|
* @example
|
|
45
55
|
* ```ts
|
|
46
56
|
* // Library shape: thread a resource's disposal into a *captured* owner
|
|
@@ -64,22 +74,100 @@ function accessor(e) {
|
|
|
64
74
|
|
|
65
75
|
function createSignal(e, t) {
|
|
66
76
|
if (typeof e === "function") {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
return [ accessor(
|
|
77
|
+
const r = computed(e, t);
|
|
78
|
+
r.l &= ~CONFIG_AUTO_DISPOSE;
|
|
79
|
+
return [ accessor(r), setMemo.bind(null, r) ];
|
|
80
|
+
}
|
|
81
|
+
const r = signal(e, t);
|
|
82
|
+
if (signalHook !== null) signalHook(r);
|
|
83
|
+
return [ accessor(r), setSignal.bind(null, r) ];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createOptimistic(e, t) {
|
|
87
|
+
if (typeof e === "function") {
|
|
88
|
+
const r = computed(e, t);
|
|
89
|
+
r.l &= ~CONFIG_AUTO_DISPOSE;
|
|
90
|
+
return [ accessor(r), optimisticWrite.bind(null, r) ];
|
|
70
91
|
}
|
|
71
|
-
const
|
|
72
|
-
return [ accessor(
|
|
92
|
+
const r = signal(e, t);
|
|
93
|
+
return [ accessor(r), optimisticWrite.bind(null, r) ];
|
|
73
94
|
}
|
|
74
95
|
|
|
75
96
|
function createMemo(e, t) {
|
|
76
97
|
return accessor(computed(e, t));
|
|
77
98
|
}
|
|
78
99
|
|
|
79
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Creates a reactive effect with **separate compute and effect phases**.
|
|
102
|
+
*
|
|
103
|
+
* - `compute(prev)` runs reactively — *put all reactive reads here*. The
|
|
104
|
+
* returned value is passed to `effect` and is also the new "previous" value
|
|
105
|
+
* for the next run.
|
|
106
|
+
* - `effect(next, prev?)` runs imperatively (untracked) after the queue
|
|
107
|
+
* flushes. *Put DOM writes / fetch / logging / subscriptions here.* It may
|
|
108
|
+
* return a cleanup function which runs before the next effect or on
|
|
109
|
+
* disposal.
|
|
110
|
+
*
|
|
111
|
+
* Reactive reads inside `effect` will *not* re-trigger this effect — that's
|
|
112
|
+
* intentional. If you need a single-phase tracked effect, use
|
|
113
|
+
* `createTrackedEffect` (with the tradeoffs noted there).
|
|
114
|
+
*
|
|
115
|
+
* Pass an `EffectBundle` (`{ effect, error }`) instead of a plain function to
|
|
116
|
+
* intercept **compute-phase** errors — errors thrown by `compute` or arriving
|
|
117
|
+
* from upstream reactive sources (including async rejections), which your own
|
|
118
|
+
* code has no frame to `try/catch`. The `error` handler is the error arm of
|
|
119
|
+
* the effect phase: it runs on the same schedule and in the same imperative,
|
|
120
|
+
* writable scope as `effect` (setting error state via signals is fine), and
|
|
121
|
+
* only for *settled* errors — a transient error that recovers before the
|
|
122
|
+
* effect phase runs `effect` with the recovered value instead, and a held
|
|
123
|
+
* transition defers it exactly as it defers `effect`. Without an `error`
|
|
124
|
+
* handler a compute-phase error is logged and the effect simply skips that
|
|
125
|
+
* run — a non-render effect's reactivity failing does not crash the app.
|
|
126
|
+
* Rethrowing from `error` escalates it to the nearest error boundary
|
|
127
|
+
* (halting the system if none exists).
|
|
128
|
+
*
|
|
129
|
+
* The **effect phase is different**: it is your own imperative code, so handle
|
|
130
|
+
* failures with `try/catch` where they occur. An uncaught effect-phase throw
|
|
131
|
+
* is treated as an unhandled application error — caught by the nearest
|
|
132
|
+
* `createErrorBoundary`/`<Errored>`, and permanently halting the reactive
|
|
133
|
+
* system if there is none. It is *not* routed to the bundle's `error` handler.
|
|
134
|
+
*
|
|
135
|
+
* ```typescript
|
|
136
|
+
* createEffect<T>(compute, effectFn | { effect, error }, options?: EffectOptions);
|
|
137
|
+
* ```
|
|
138
|
+
* @param compute a function that receives its previous value and returns a new value used to react on a computation
|
|
139
|
+
* @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
|
|
140
|
+
* @param options `EffectOptions` -- name, defer, schedule, transparent
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* const [count, setCount] = createSignal(0);
|
|
145
|
+
*
|
|
146
|
+
* createEffect(
|
|
147
|
+
* () => count(), // compute: tracks `count`
|
|
148
|
+
* value => console.log(value) // effect: side effect
|
|
149
|
+
* );
|
|
150
|
+
*
|
|
151
|
+
* setCount(1); // logs 1 after the next flush
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* createEffect(
|
|
157
|
+
* () => userId(),
|
|
158
|
+
* id => {
|
|
159
|
+
* const ctrl = new AbortController();
|
|
160
|
+
* fetch(`/users/${id}`, { signal: ctrl.signal });
|
|
161
|
+
* return () => ctrl.abort(); // cleanup before next run / disposal
|
|
162
|
+
* }
|
|
163
|
+
* );
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* @description https://v2.solidjs.com/reference/solid-js/reactivity/create-effect
|
|
167
|
+
*/ function createEffect(e, t, r) {
|
|
80
168
|
effect(e, t.effect || t, t.error, {
|
|
81
169
|
user: true,
|
|
82
|
-
...
|
|
170
|
+
...r
|
|
83
171
|
});
|
|
84
172
|
}
|
|
85
173
|
|
|
@@ -110,15 +198,25 @@ function createEffect(e, t, n) {
|
|
|
110
198
|
* }
|
|
111
199
|
* ```
|
|
112
200
|
*
|
|
113
|
-
* @description https://
|
|
114
|
-
*/ function createRenderEffect(e, t,
|
|
115
|
-
effect(e, t, undefined,
|
|
201
|
+
* @description https://v2.solidjs.com/reference/solid-js/advanced/specialized-reactivity/create-render-effect
|
|
202
|
+
*/ function createRenderEffect(e, t, r) {
|
|
203
|
+
effect(e, t, undefined, r);
|
|
116
204
|
}
|
|
117
205
|
|
|
118
206
|
/**
|
|
119
207
|
* Creates a tracked reactive effect where dependency tracking and side effects happen
|
|
120
208
|
* in the same scope.
|
|
121
209
|
*
|
|
210
|
+
* @deprecated Do not use in new code. For a side effect that follows reactive
|
|
211
|
+
* state, use `createEffect(compute, effect)` — it separates tracking from the
|
|
212
|
+
* side effect, knows its dependencies before it runs, and participates in
|
|
213
|
+
* async and transitions. For one-time DOM work after render (measuring,
|
|
214
|
+
* attaching third-party widgets to a ref), use `onSettled`. Tracking from
|
|
215
|
+
* inside the effect phase — the only thing this primitive adds — is retained
|
|
216
|
+
* solely to ease 1.x migration: it runs beside user-effect callbacks after
|
|
217
|
+
* values commit, never holds a transition, and cannot observe a write staged
|
|
218
|
+
* earlier in the same flush by a signal it has not read yet.
|
|
219
|
+
*
|
|
122
220
|
* WARNING: Because tracking and effects happen in the same scope, this primitive
|
|
123
221
|
* may run multiple times for a single change or show tearing (reading inconsistent
|
|
124
222
|
* state). Use only when dynamic subscription patterns require same-scope tracking.
|
|
@@ -148,7 +246,7 @@ function createEffect(e, t, n) {
|
|
|
148
246
|
* });
|
|
149
247
|
* ```
|
|
150
248
|
*
|
|
151
|
-
* @description https://
|
|
249
|
+
* @description https://v2.solidjs.com/reference/solid-js/advanced/specialized-reactivity/create-tracked-effect
|
|
152
250
|
*/ function createTrackedEffect(e, t) {
|
|
153
251
|
trackedEffect(e, t);
|
|
154
252
|
}
|
|
@@ -177,35 +275,37 @@ function createEffect(e, t, n) {
|
|
|
177
275
|
* setCount(1); // logs once, reaction re-armed for next change
|
|
178
276
|
* ```
|
|
179
277
|
*
|
|
180
|
-
* @description https://
|
|
278
|
+
* @description https://v2.solidjs.com/reference/solid-js/advanced/specialized-reactivity/create-reaction
|
|
181
279
|
*/ function createReaction(e, t) {
|
|
182
|
-
let
|
|
183
|
-
cleanup(() =>
|
|
184
|
-
const
|
|
280
|
+
let r = undefined;
|
|
281
|
+
cleanup(() => r?.());
|
|
282
|
+
const n = getOwner();
|
|
185
283
|
// The currently armed effect node. `track()` replaces the previous
|
|
186
284
|
// subscription (1.x semantics): without disposing the superseded arm, its
|
|
187
285
|
// sources stayed live (firing the callback for replaced dependencies), each
|
|
188
286
|
// accumulated arm delivered its own fire, and un-fired arms leaked as live
|
|
189
287
|
// effect nodes until the owner disposed (#2861).
|
|
190
|
-
let
|
|
191
|
-
return
|
|
192
|
-
if (
|
|
193
|
-
dispose(
|
|
194
|
-
|
|
288
|
+
let o;
|
|
289
|
+
return c => {
|
|
290
|
+
if (o) {
|
|
291
|
+
dispose(o);
|
|
292
|
+
o = undefined;
|
|
195
293
|
}
|
|
196
|
-
runWithOwner(
|
|
197
|
-
effect(() =>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
294
|
+
runWithOwner(n, () => {
|
|
295
|
+
effect(() => {
|
|
296
|
+
const e = getOwner();
|
|
297
|
+
o = e;
|
|
298
|
+
c();
|
|
299
|
+
return e;
|
|
300
|
+
}, t => {
|
|
301
|
+
o = undefined;
|
|
302
|
+
r?.();
|
|
303
|
+
const n = (e.effect || e)?.();
|
|
304
|
+
if (false && n !== undefined && typeof n !== "function") ;
|
|
305
|
+
r = n;
|
|
203
306
|
dispose(t);
|
|
204
307
|
}, e.error, {
|
|
205
|
-
...
|
|
206
|
-
...t,
|
|
207
|
-
name: t?.name ?? "effect"
|
|
208
|
-
} : t,
|
|
308
|
+
...t,
|
|
209
309
|
user: true,
|
|
210
310
|
defer: true
|
|
211
311
|
});
|
|
@@ -213,10 +313,58 @@ function createEffect(e, t, n) {
|
|
|
213
313
|
};
|
|
214
314
|
}
|
|
215
315
|
|
|
216
|
-
/**
|
|
217
|
-
|
|
218
|
-
|
|
316
|
+
/** A promise over a reactive expression (`resolve`, `until`): a pass that
|
|
317
|
+
* runs the expression and settles on a microtask. Not an effect — an
|
|
318
|
+
* effect's run is the flush's effect phase, and a flush parked by the
|
|
319
|
+
* action that is `yield`ing on this promise stashes its runs with the
|
|
320
|
+
* transaction (#2930); the heap runs a pass parked or not. A pending read
|
|
321
|
+
* re-runs it when the source lands (the pass is pending itself); the
|
|
322
|
+
* expression throwing settles the rejection. */
|
|
323
|
+
/** The promise-delivery readers' pass (`resolve`, `until`, `refresh`'s
|
|
324
|
+
* waiter): delivers what it read on a microtask. By posture (#3482, #3490):
|
|
325
|
+
* - the reader of the transaction it was created in (`own`: an action's
|
|
326
|
+
* body, a `refresh()` called there — the waiter reads its slice's frame on
|
|
327
|
+
* a later microtask) delivers that frame — the staged landing an action
|
|
328
|
+
* holds (the action awaits the promise that would otherwise wait on its
|
|
329
|
+
* own settle);
|
|
330
|
+
* - a mainline reader whose pass read a frame another transaction holds
|
|
331
|
+
* joined it (L2, born held — A29) and delivers at that commit: the
|
|
332
|
+
* committed view, never a foreign hold's unrevealed frame. */ function watch(e, t, r, n = 0, o, c = flushTransaction) {
|
|
333
|
+
if (n & CONFIG_AUTHORITATIVE) {
|
|
334
|
+
GlobalQueue.rn = truthRead;
|
|
335
|
+
GlobalQueue.sn = truthConfirmed;
|
|
219
336
|
}
|
|
337
|
+
// `config` is on the node for its first pass too (the pass runs inside
|
|
338
|
+
// `computed()`): `until`'s predicate is authoritative from its first read,
|
|
339
|
+
// or an already-displayed guess would satisfy it in a later slice. (REDERIVE
|
|
340
|
+
// after: the first pass's own error is the promise's rejection.)
|
|
341
|
+
computed(() => {
|
|
342
|
+
let u, i = false;
|
|
343
|
+
try {
|
|
344
|
+
u = e();
|
|
345
|
+
} catch (e) {
|
|
346
|
+
if (e instanceof NotReadyError) throw e;
|
|
347
|
+
// An error is an answer (D-41): delivered by the same frame rule.
|
|
348
|
+
u = unwrapStatusError(e);
|
|
349
|
+
i = true;
|
|
350
|
+
}
|
|
351
|
+
const s = getOwner();
|
|
352
|
+
// A truth reader waits out a `createDeferred` flight its value derives
|
|
353
|
+
// from, which serves everyone else the stale answer (D-40).
|
|
354
|
+
const f = n & CONFIG_AUTHORITATIVE && GlobalQueue.er?.(s);
|
|
355
|
+
if (f) throw new NotReadyError(f);
|
|
356
|
+
// (Outside a flush a mainline pass joins through `passTx`.)
|
|
357
|
+
const a = flushTransaction ?? passTx;
|
|
358
|
+
if (s.R & REACTIVE_JOINED && a !== null && (c === null || resolveTx(c) !== resolveTx(a))) {
|
|
359
|
+
staleReader(s, resolveTx(a));
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (i) return void queueMicrotask(() => r(u));
|
|
363
|
+
o?.(u);
|
|
364
|
+
queueMicrotask(() => t(u));
|
|
365
|
+
}, {
|
|
366
|
+
xe: n
|
|
367
|
+
}).l |= CONFIG_REDERIVE;
|
|
220
368
|
}
|
|
221
369
|
|
|
222
370
|
/**
|
|
@@ -240,51 +388,170 @@ function createEffect(e, t, n) {
|
|
|
240
388
|
*
|
|
241
389
|
* @param fn a reactive expression to resolve
|
|
242
390
|
*/ function resolve(e) {
|
|
243
|
-
return new Promise((t,
|
|
244
|
-
createRoot(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
391
|
+
return new Promise((t, r) => {
|
|
392
|
+
createRoot(n => watch(e, e => (t(e), n()), e => (r(e), n())));
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Invalidates one reactive source, forcing it to re-execute even if its inputs
|
|
398
|
+
* haven't changed, and returns a promise for the target's NEXT QUIESCENT
|
|
399
|
+
* STATE — the re-ask (and anything that supersedes it) has settled.
|
|
400
|
+
*
|
|
401
|
+
* Pass either a Solid-created accessor or a projected store created from
|
|
402
|
+
* `createStore(fn, ...)` / `createProjection(...)`. `refresh()` is a
|
|
403
|
+
* write-like invalidation operation: it does not read the target's value, and
|
|
404
|
+
* refreshing a plain signal accessor is a no-op that resolves immediately.
|
|
405
|
+
*
|
|
406
|
+
* The returned promise is safe to ignore (fire-and-forget refresh is
|
|
407
|
+
* unchanged, and a failed refetch will not surface an unhandled rejection).
|
|
408
|
+
* Awaiting it gives imperative flows the settle point without a reactive
|
|
409
|
+
* read:
|
|
410
|
+
* - Accessor targets resolve with the settled value; store targets resolve
|
|
411
|
+
* with the store node passed (reads through it are fresh after the await).
|
|
412
|
+
* - A failed re-ask rejects with the error.
|
|
413
|
+
* - Semantics are quiescence, not flight identity: if another refresh (or
|
|
414
|
+
* any invalidation) supersedes this one mid-flight, the promise waits for
|
|
415
|
+
* — and delivers — whatever finally lands.
|
|
416
|
+
*
|
|
417
|
+
* @example
|
|
418
|
+
* ```ts
|
|
419
|
+
* const user = createMemo(async () => fetch(`/users/${id()}`).then(r => r.json()));
|
|
420
|
+
*
|
|
421
|
+
* // Fire-and-forget re-fetch
|
|
422
|
+
* <button onClick={() => refresh(user)}>Reload</button>;
|
|
423
|
+
*
|
|
424
|
+
* // Imperative settle point
|
|
425
|
+
* const fresh = await refresh(user);
|
|
426
|
+
* ```
|
|
427
|
+
*/ function refresh(e) {
|
|
428
|
+
const t = e?.[$REFRESH];
|
|
429
|
+
if (!t) {
|
|
430
|
+
return Promise.resolve(undefined);
|
|
431
|
+
}
|
|
432
|
+
// Mark now, watch on a microtask. The waiter is resolve()'s machinery with
|
|
433
|
+
// two extra reader bits, but it must NOT compute at call time (effects
|
|
434
|
+
// recompute eagerly on creation): same-tick refreshes coalesce into ONE
|
|
435
|
+
// re-ask only because every mark lands before anything pulls, and eager
|
|
436
|
+
// per-call pulls turned three refreshes into three fetches. Deferred, the
|
|
437
|
+
// waiter's first read sees the coalesced state: FRESH_READ pulls the node
|
|
438
|
+
// through recompute if it is still dirty (self-deduping — a clean node
|
|
439
|
+
// no-ops, so N waiters cost one pull; this also closes the race where a
|
|
440
|
+
// waiter reads the PRE-re-ask value as settled and delivers stale), after
|
|
441
|
+
// which the read either parks on the re-ask's pending window (async — the
|
|
442
|
+
// settle walk re-runs it on every landing, equal-value included, and a
|
|
443
|
+
// rejection arrives through the effect's error channel) or serves the sync
|
|
444
|
+
// answer. resolve()'s own eager compute is untouched: created after a
|
|
445
|
+
// refresh it still settles stale-while-revalidate (#2930) — its contract is
|
|
446
|
+
// "first settled value", not "next quiescent state".
|
|
447
|
+
markRefresh(t);
|
|
448
|
+
// The transaction `refresh()` is called in (an action's slice): the
|
|
449
|
+
// waiter, created on the microtask below, is its reader (`watch`).
|
|
450
|
+
const r = flushTransaction;
|
|
451
|
+
const n = new Promise((n, o) => {
|
|
452
|
+
queueMicrotask(() => {
|
|
453
|
+
// The root is load-bearing: an ownerless computed is auto-disposing,
|
|
454
|
+
// and the settle walk releases an unobserved one instead of re-running
|
|
455
|
+
// it, so the waiter would never see the refetch land (#3888). Settle
|
|
456
|
+
// disposes the waiter directly; the capture runs inside the watcher's
|
|
457
|
+
// own pass, where the ambient owner IS the watcher — exactly what
|
|
458
|
+
// dispose() takes.
|
|
459
|
+
let c = null;
|
|
460
|
+
const make = () => watch(() => {
|
|
461
|
+
if (c === null) c = getOwner();
|
|
462
|
+
// Authoritative: the truth beneath a guess (`until`'s view). A
|
|
463
|
+
// guessed node's own re-ask is not answered by it (#3895): park
|
|
464
|
+
// on that flight, or reject with its error.
|
|
465
|
+
const e = read(t);
|
|
466
|
+
if (t.l & CONFIG_GUESS && t.m & (STATUS_PENDING | STATUS_ERROR)) throw t.T?.U;
|
|
467
|
+
return e;
|
|
468
|
+
}, t => {
|
|
469
|
+
n(typeof e === "function" ? t : e);
|
|
470
|
+
dispose(c);
|
|
264
471
|
}, e => {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}, {
|
|
270
|
-
user: true
|
|
271
|
-
});
|
|
472
|
+
o(e);
|
|
473
|
+
dispose(c);
|
|
474
|
+
}, CONFIG_FRESH_READ | CONFIG_AUTHORITATIVE, undefined, r);
|
|
475
|
+
createRoot(make);
|
|
272
476
|
});
|
|
273
477
|
});
|
|
478
|
+
// Fire-and-forget refresh must not turn a failed refetch into an unhandled
|
|
479
|
+
// rejection; awaiting callers attach their own handlers to `promise`.
|
|
480
|
+
n.catch(() => {});
|
|
481
|
+
return n;
|
|
274
482
|
}
|
|
275
483
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
484
|
+
/**
|
|
485
|
+
* Awaits a reactive predicate and resolves the first time it settles *truthy*,
|
|
486
|
+
* with that (narrowed) value. Falsy results and pending async reads both mean
|
|
487
|
+
* "not yet": the subscription stays live and re-evaluates as sources change.
|
|
488
|
+
* If the predicate settles with an error — a throw, or an async source that
|
|
489
|
+
* rejects — the promise rejects with it, as do timeout and abort.
|
|
490
|
+
*
|
|
491
|
+
* Where {@link resolve} answers "what is this value" (first settled value,
|
|
492
|
+
* whatever it is), `until` answers "when does the world confirm this
|
|
493
|
+
* condition".
|
|
494
|
+
*
|
|
495
|
+
* This is the acknowledgment mechanism for mutations confirmed on a live data
|
|
496
|
+
* channel (sockets, subscriptions, live queries) rather than by the mutation's
|
|
497
|
+
* own response: correlate by a client-generated id or version in the predicate,
|
|
498
|
+
* and let truth arrive however it arrives — push, refetch, or another tab.
|
|
499
|
+
*
|
|
500
|
+
* Must be called *outside* a tracking scope.
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```ts
|
|
504
|
+
* const clientId = crypto.randomUUID();
|
|
505
|
+
* await socket.send({ clientId, text }); // fire-and-forget transport
|
|
506
|
+
* // Wait until the live source echoes the write:
|
|
507
|
+
* await until(() => messages.some(m => m.clientId === clientId), { timeout: 10_000 });
|
|
508
|
+
* ```
|
|
509
|
+
*
|
|
510
|
+
* @param fn a reactive predicate
|
|
511
|
+
* @param options optional `timeout` (ms) and abort `signal`
|
|
512
|
+
*/ function until(e, t) {
|
|
513
|
+
return new Promise((r, n) => {
|
|
514
|
+
const o = t?.signal;
|
|
515
|
+
if (o?.aborted) return n(o.reason);
|
|
516
|
+
// The awaiting action's transaction, if any (its body runs joined to
|
|
517
|
+
// it). `until()` is a declaration of relatedness (#3164 follow-up): the
|
|
518
|
+
// predicate names the condition that confirms the action, so the frame
|
|
519
|
+
// whose landing flips it truthy is the confirming event by the user's
|
|
520
|
+
// own definition — it reveals WITH the action's settle, not before
|
|
521
|
+
// (`saving=true` beside the confirmed `version=1` is a tear no timeline
|
|
522
|
+
// contains). Updates that do not flip the predicate reveal freely.
|
|
523
|
+
const c = flushTransaction;
|
|
524
|
+
createRoot(u => {
|
|
525
|
+
let i;
|
|
526
|
+
let s;
|
|
527
|
+
const settle = e => {
|
|
528
|
+
if (i !== undefined) clearTimeout(i);
|
|
529
|
+
if (s !== undefined) o.removeEventListener("abort", s);
|
|
530
|
+
e();
|
|
531
|
+
u();
|
|
532
|
+
};
|
|
533
|
+
// Authoritative: the predicate sees the truth, never the action's own
|
|
534
|
+
// optimistic guess — a guess cannot satisfy the wait for its own
|
|
535
|
+
// confirmation.
|
|
536
|
+
watch(e, e => {
|
|
537
|
+
// Falsy is "not yet": keep the subscription live and wait for the
|
|
538
|
+
// next evaluation. Only a truthy settled value resolves.
|
|
539
|
+
if (e) settle(() => r(e));
|
|
540
|
+
}, e => settle(() => n(e)), CONFIG_AUTHORITATIVE, e => {
|
|
541
|
+
// The flip, inside the pass of the flush carrying it: the flush
|
|
542
|
+
// joins the action's transaction, and what it staged is held and
|
|
543
|
+
// revealed with the action.
|
|
544
|
+
if (e && c !== null && globalQueue.Y && resolveTx(c).tt !== 0) joinFuture(c);
|
|
545
|
+
});
|
|
546
|
+
if (t?.timeout !== undefined) i = setTimeout(() => settle(() => n(new TimeoutError)), t.timeout);
|
|
547
|
+
if (o !== undefined) {
|
|
548
|
+
s = () => settle(() => n(o.reason));
|
|
549
|
+
o.addEventListener("abort", s, {
|
|
550
|
+
once: true
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
});
|
|
288
555
|
}
|
|
289
556
|
|
|
290
557
|
/**
|
|
@@ -382,13 +649,63 @@ function createOptimistic(e, t) {
|
|
|
382
649
|
* on owner disposal
|
|
383
650
|
*/ function onSettled(e) {
|
|
384
651
|
const t = getOwner();
|
|
385
|
-
t && !(t.
|
|
652
|
+
t && !(t.l & CONFIG_CHILDREN_FORBIDDEN) ? trackedEffect(() => untrack(e), undefined) : globalQueue.enqueue(EFFECT_USER, function fire() {
|
|
653
|
+
// Settled means derived. A settle that reverts optimism (or replays
|
|
654
|
+
// gated reads) only enqueues the affected subscribers; the pass after
|
|
655
|
+
// the commit re-derives them. Fired in the commit pass, the callback
|
|
656
|
+
// read the optimistic source already reverted beside a sync memo of it
|
|
657
|
+
// still holding the optimistic value — reads do not pull (#3411). Fall
|
|
658
|
+
// to the next pass while the heap has work; `run` swapped the queue,
|
|
659
|
+
// so this lands there, and `enqueue` keeps the drain alive.
|
|
660
|
+
if (dirtyQueue.Et >= dirtyQueue.Fe) return globalQueue.enqueue(EFFECT_USER, fire);
|
|
386
661
|
// Unowned, out-of-band fire (no owner, or a children-forbidden one this
|
|
387
662
|
// one-shot must not bind to): a returned cleanup has no lifecycle to
|
|
388
663
|
// attach to. Reject it in dev; in production the return is simply
|
|
389
664
|
// dropped — never bound to an unrelated owner or run eagerly.
|
|
390
|
-
|
|
665
|
+
e();
|
|
391
666
|
});
|
|
392
667
|
}
|
|
393
668
|
|
|
394
|
-
|
|
669
|
+
/**
|
|
670
|
+
* Schedules `callback` to run **once**, right after the pending flush
|
|
671
|
+
* finishes, in the same microtask checkpoint (before the browser paints).
|
|
672
|
+
* Unlike `onSettled`, it never waits on async work: if that flush is held by
|
|
673
|
+
* a pending transition, the callback still runs.
|
|
674
|
+
*
|
|
675
|
+
* Its main use is keeping a write out of another update's hold. Writes made
|
|
676
|
+
* in the same synchronous frame are applied together, so when one of them
|
|
677
|
+
* starts async work (a route change, a fetch), the others are held with it
|
|
678
|
+
* until it resolves. A write made in `onFlush` happens after that flush, so
|
|
679
|
+
* it is its own update: it commits on its own unless something it changes
|
|
680
|
+
* also derives from the held work (a memo or `createEffect` reading both),
|
|
681
|
+
* in which case it joins that hold as any later write would.
|
|
682
|
+
*
|
|
683
|
+
* Call it from event handlers and effect callbacks (including
|
|
684
|
+
* `createTrackedEffect` and `onSettled` bodies). In an owned scope such as a
|
|
685
|
+
* component body or a computation it warns in dev: use `onSettled` there.
|
|
686
|
+
*
|
|
687
|
+
* - A write in the callback lands after any write made inline in the same
|
|
688
|
+
* frame. Don't write the same signal both inside and outside it; use a
|
|
689
|
+
* functional setter if the order matters.
|
|
690
|
+
* - Several `onFlush` callbacks run in call order and batch into one flush.
|
|
691
|
+
* - `queueMicrotask` is not a substitute: called before the frame's first
|
|
692
|
+
* write, it runs before that flush and its write joins it.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```tsx
|
|
696
|
+
* // A menu closes now even when the consumer's onSelect starts async work.
|
|
697
|
+
* const select = (id: string) => {
|
|
698
|
+
* onFlush(() => setOpen(false));
|
|
699
|
+
* props.onSelect?.(id);
|
|
700
|
+
* };
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @param callback Function to run once after the pending flush
|
|
704
|
+
*/ function onFlush(e) {
|
|
705
|
+
// The frame's flush is queued (or already draining) ahead of this
|
|
706
|
+
// microtask, so the callback's writes start a flush of their own.
|
|
707
|
+
schedule();
|
|
708
|
+
queueMicrotask(e);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export { accessor, createEffect, createMemo, createOptimistic, createReaction, createRenderEffect, createSignal, createTrackedEffect, onCleanup, onFlush, onSettled, refresh, resolve, until };
|