raf-effect-atom 0.2.1 → 0.2.3
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/CHANGELOG.md +22 -1
- package/dist/Hooks.mjs +2 -5
- package/dist/Loader.d.mts +38 -7
- package/dist/Loader.mjs +19 -6
- package/dist/Next.d.mts +23 -1
- package/dist/Next.mjs +24 -2
- package/dist/ReactHydration.d.mts +6 -0
- package/dist/ReactHydration.mjs +8 -1
- package/dist/ReactRouter.d.mts +8 -1
- package/dist/ReactRouter.mjs +14 -16
- package/dist/internal/next.mjs +7 -3
- package/dist/internal/registry.mjs +12 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.3](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.2...raf-effect-atom%400.2.3) (2026-10-23)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **raf-effect-atom:** run Effect code on a loader's registry with Loader.Loader.run ([d26307d](https://github.com/KATT/raf-effect-atom/commit/d26307dc6279106ebd9d26078c88e4ad10a8cbc9))
|
|
8
|
+
|
|
9
|
+
## [0.2.2](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.1...raf-effect-atom%400.2.2) (2026-10-23)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- **raf-effect-atom:** stop a loader's wait when its request is aborted ([b1f2f16](https://github.com/KATT/raf-effect-atom/commit/b1f2f16fcce9cd5dd1ba18fa653b40c805eebd93)), references [#41](https://github.com/KATT/raf-effect-atom/issues/41) [#40](https://github.com/KATT/raf-effect-atom/issues/40) [#40](https://github.com/KATT/raf-effect-atom/issues/40) [#41](https://github.com/KATT/raf-effect-atom/issues/41)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- **raf-effect-atom:** refuse to hydrate a shared registry during server rendering ([2f793a6](https://github.com/KATT/raf-effect-atom/commit/2f793a63627482bdf2f0e2891829f5191077cfc7))
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
- **raf-effect-atom:** date clientAtomsMiddleware to 0.2.1, the release it shipped in ([c2a4288](https://github.com/KATT/raf-effect-atom/commit/c2a4288f4c5b63575e019712edc9fdc49a052bd2))
|
|
22
|
+
- record that the packages' module state is keyed by what one request owns ([22b23f9](https://github.com/KATT/raf-effect-atom/commit/22b23f947544f8c4459ef172e8b21499403059c4))
|
|
23
|
+
|
|
3
24
|
## [0.2.1](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.0...raf-effect-atom%400.2.1) (2026-10-23)
|
|
4
25
|
|
|
5
26
|
### Features
|
|
6
27
|
|
|
7
|
-
|
|
28
|
+
- **raf-effect-atom:** run React Router loaders in the browser after the first page ([bee00a9](https://github.com/KATT/raf-effect-atom/commit/bee00a981fd24fcdc9d746053dcd73dbc7ebc853)), references [#41](https://github.com/KATT/raf-effect-atom/issues/41) [#41](https://github.com/KATT/raf-effect-atom/issues/41)
|
|
8
29
|
|
|
9
30
|
## [0.2.0](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.1.0...raf-effect-atom%400.2.0) (2026-10-23)
|
|
10
31
|
|
package/dist/Hooks.mjs
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { stream } from "./internal/pending.mjs";
|
|
3
3
|
import { hydrated } from "./Hydration.mjs";
|
|
4
|
-
import { isDevelopment } from "./internal/development.mjs";
|
|
5
4
|
import { keyOf, release } from "./internal/leases.mjs";
|
|
6
5
|
import { atomOf } from "./internal/handles.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import { isServer } from "./internal/registry.mjs";
|
|
6
|
+
import { requireRequestRegistry } from "./internal/registry.mjs";
|
|
9
7
|
import { ModeContext, check } from "./internal/waterfalls.mjs";
|
|
10
8
|
import * as Atom from "effect/reactivity/Atom";
|
|
11
9
|
import * as Predicate from "effect/Predicate";
|
|
12
10
|
import * as Cause from "effect/Cause";
|
|
13
|
-
import * as Effect from "effect/Effect";
|
|
14
11
|
import * as Option from "effect/Option";
|
|
15
12
|
import * as AsyncResult from "effect/reactivity/AsyncResult";
|
|
16
13
|
import { RegistryContext, useAtomSuspense } from "@effect/atom-react";
|
|
@@ -111,7 +108,7 @@ const sourceOf = (atom) => {
|
|
|
111
108
|
const useAtomResult = (source, handle) => {
|
|
112
109
|
const registry = React.useContext(RegistryContext);
|
|
113
110
|
const atom = atomOf(source, handle.args);
|
|
114
|
-
|
|
111
|
+
requireRequestRegistry(registry, "useAtomResult");
|
|
115
112
|
const target = sourceOf(atom);
|
|
116
113
|
const started = hydrated(target);
|
|
117
114
|
const read = target === atom ? hydrated(atom) : atom;
|
package/dist/Loader.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import * as Effect from "effect/Effect";
|
|
|
5
5
|
import * as AsyncResult from "effect/reactivity/AsyncResult";
|
|
6
6
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
7
7
|
declare namespace Loader_d_exports {
|
|
8
|
-
export { LeaseTTL, Loader, SettledResult, ensure, fetch, make, prefetch, result };
|
|
8
|
+
export { LeaseTTL, Loader, MakeOptions, SettledResult, ensure, fetch, make, prefetch, result };
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* A settled `AsyncResult`: what `ensure` and `fetch` return.
|
|
@@ -165,6 +165,32 @@ export interface Loader {
|
|
|
165
165
|
* the seed resolves.
|
|
166
166
|
*/
|
|
167
167
|
readonly prefetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Handle<S>;
|
|
168
|
+
/**
|
|
169
|
+
* Runs an Effect that needs the registry, such as the Effect verbs in an
|
|
170
|
+
* `Effect.gen`, as a Promise that rejects with its squashed cause.
|
|
171
|
+
* Aborting `signal`, or the one the verbs were made with, interrupts it,
|
|
172
|
+
* and the Promise rejects with the abort reason.
|
|
173
|
+
*/
|
|
174
|
+
readonly run: <A, E>(effect: Effect.Effect<A, E, AtomRegistry.AtomRegistry>, options?: {
|
|
175
|
+
readonly signal?: AbortSignal | undefined;
|
|
176
|
+
}) => Promise<A>;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Options for `make`.
|
|
180
|
+
*
|
|
181
|
+
* @stability unstable
|
|
182
|
+
* @category models
|
|
183
|
+
* @since 0.2.2
|
|
184
|
+
*/
|
|
185
|
+
export interface MakeOptions {
|
|
186
|
+
/** How long a lease lasts in the browser. See {@link LeaseTTL}. */
|
|
187
|
+
readonly leaseTTL?: Duration.Input | undefined;
|
|
188
|
+
/**
|
|
189
|
+
* The request's or the navigation's abort signal. Aborting it interrupts
|
|
190
|
+
* the `ensure`, `fetch` and `run` calls still waiting, which reject with
|
|
191
|
+
* its reason. The atoms they started keep loading.
|
|
192
|
+
*/
|
|
193
|
+
readonly signal?: AbortSignal | undefined;
|
|
168
194
|
}
|
|
169
195
|
/**
|
|
170
196
|
* Creates the Promise verbs over one registry.
|
|
@@ -174,15 +200,22 @@ export interface Loader {
|
|
|
174
200
|
* Use in a framework integration, in loaders that are async functions, or
|
|
175
201
|
* in an event handler that starts an atom before the state change that
|
|
176
202
|
* renders its reader, for state that can't be in the URL, such as a
|
|
177
|
-
* search-as-you-type query. In Effect code, use
|
|
178
|
-
* the
|
|
203
|
+
* search-as-you-type query. In Effect code, use the verbs directly, and run
|
|
204
|
+
* the Effect on the registry with `run`.
|
|
205
|
+
*
|
|
206
|
+
* **Details**
|
|
207
|
+
*
|
|
208
|
+
* With a `signal`, an aborted request or navigation stops waiting: `ensure`,
|
|
209
|
+
* `fetch` and `run` reject with the signal's reason, and the atoms they
|
|
210
|
+
* started keep loading. The React Router and Next.js integrations pass the
|
|
211
|
+
* request's.
|
|
179
212
|
*
|
|
180
213
|
* **Example**
|
|
181
214
|
*
|
|
182
215
|
* ```ts
|
|
183
216
|
* import { Loader, RequestRegistry } from "raf-effect-atom"
|
|
184
217
|
*
|
|
185
|
-
* const loader = Loader.make(RequestRegistry.make())
|
|
218
|
+
* const loader = Loader.make(RequestRegistry.make(), { signal: request.signal })
|
|
186
219
|
* const comments = loader.prefetch(commentPage, 1, 0)
|
|
187
220
|
* const issueHandle = await loader.ensure(issue, 1)
|
|
188
221
|
* ```
|
|
@@ -191,7 +224,5 @@ export interface Loader {
|
|
|
191
224
|
* @category constructors
|
|
192
225
|
* @since 0.1.0
|
|
193
226
|
*/
|
|
194
|
-
export declare const make: (registry: AtomRegistry.AtomRegistry, options?:
|
|
195
|
-
readonly leaseTTL?: Duration.Input | undefined;
|
|
196
|
-
}) => Loader;
|
|
227
|
+
export declare const make: (registry: AtomRegistry.AtomRegistry, options?: MakeOptions) => Loader;
|
|
197
228
|
export { Loader_d_exports };
|
package/dist/Loader.mjs
CHANGED
|
@@ -250,6 +250,10 @@ const result = (handle) => {
|
|
|
250
250
|
if (settledResult === void 0) throw new Error("raf-effect-atom: Loader.result got a handle that no ensure or fetch returned here. Only the code that ran the verb has its result: a handle that crossed the network is a copy. Read it with useAtomResult instead.");
|
|
251
251
|
return settledResult;
|
|
252
252
|
};
|
|
253
|
+
const runPromise = (effect, signal) => Effect.runPromise(effect, { signal }).catch((error) => {
|
|
254
|
+
throw signal?.aborted ? signal.reason : error;
|
|
255
|
+
});
|
|
256
|
+
const either = (first, second) => first === void 0 || second === void 0 ? first ?? second : AbortSignal.any([first, second]);
|
|
253
257
|
/**
|
|
254
258
|
* Creates the Promise verbs over one registry.
|
|
255
259
|
*
|
|
@@ -258,15 +262,22 @@ const result = (handle) => {
|
|
|
258
262
|
* Use in a framework integration, in loaders that are async functions, or
|
|
259
263
|
* in an event handler that starts an atom before the state change that
|
|
260
264
|
* renders its reader, for state that can't be in the URL, such as a
|
|
261
|
-
* search-as-you-type query. In Effect code, use
|
|
262
|
-
* the
|
|
265
|
+
* search-as-you-type query. In Effect code, use the verbs directly, and run
|
|
266
|
+
* the Effect on the registry with `run`.
|
|
267
|
+
*
|
|
268
|
+
* **Details**
|
|
269
|
+
*
|
|
270
|
+
* With a `signal`, an aborted request or navigation stops waiting: `ensure`,
|
|
271
|
+
* `fetch` and `run` reject with the signal's reason, and the atoms they
|
|
272
|
+
* started keep loading. The React Router and Next.js integrations pass the
|
|
273
|
+
* request's.
|
|
263
274
|
*
|
|
264
275
|
* **Example**
|
|
265
276
|
*
|
|
266
277
|
* ```ts
|
|
267
278
|
* import { Loader, RequestRegistry } from "raf-effect-atom"
|
|
268
279
|
*
|
|
269
|
-
* const loader = Loader.make(RequestRegistry.make())
|
|
280
|
+
* const loader = Loader.make(RequestRegistry.make(), { signal: request.signal })
|
|
270
281
|
* const comments = loader.prefetch(commentPage, 1, 0)
|
|
271
282
|
* const issueHandle = await loader.ensure(issue, 1)
|
|
272
283
|
* ```
|
|
@@ -277,20 +288,22 @@ const result = (handle) => {
|
|
|
277
288
|
*/
|
|
278
289
|
const make = (registry, options) => {
|
|
279
290
|
const leaseTTL = options?.leaseTTL === void 0 ? void 0 : Duration.fromInputUnsafe(options.leaseTTL);
|
|
291
|
+
const signal = options?.signal;
|
|
280
292
|
const provide = (effect) => {
|
|
281
293
|
const provided = Effect.provideService(effect, AtomRegistry.AtomRegistry, registry);
|
|
282
294
|
return leaseTTL === void 0 ? provided : Effect.provideService(provided, LeaseTTL, leaseTTL);
|
|
283
295
|
};
|
|
284
296
|
return {
|
|
285
|
-
ensure: (source, ...args) =>
|
|
286
|
-
fetch: (source, ...args) =>
|
|
297
|
+
ensure: (source, ...args) => runPromise(provide(ensure(source, ...args)), signal),
|
|
298
|
+
fetch: (source, ...args) => runPromise(provide(fetch(source, ...args)), signal),
|
|
287
299
|
prefetch: (source, ...args) => {
|
|
288
300
|
const atom = atomOf(source, args);
|
|
289
301
|
afterSeed(registry, () => {
|
|
290
302
|
Effect.runSync(provide(start(atom)));
|
|
291
303
|
});
|
|
292
304
|
return make$1(args, atom);
|
|
293
|
-
}
|
|
305
|
+
},
|
|
306
|
+
run: (effect, runOptions) => runPromise(provide(effect), either(signal, runOptions?.signal))
|
|
294
307
|
};
|
|
295
308
|
};
|
|
296
309
|
export { LeaseTTL, Loader_exports, ensure, fetch, make, prefetch, result };
|
package/dist/Next.d.mts
CHANGED
|
@@ -67,7 +67,9 @@ export declare const makeRequestAtoms: (options?: Options) => RequestAtoms;
|
|
|
67
67
|
*
|
|
68
68
|
* Made once per request through React's `cache`, and disposed through
|
|
69
69
|
* `cacheSignal` when React finishes the render, after the last streamed
|
|
70
|
-
* result has been sent.
|
|
70
|
+
* result has been sent. The verbs take the same signal: when React aborts
|
|
71
|
+
* the render, such as for a navigation the browser left, the `ensure` and
|
|
72
|
+
* `fetch` calls still waiting reject with its reason.
|
|
71
73
|
*
|
|
72
74
|
* **Gotchas**
|
|
73
75
|
*
|
|
@@ -106,6 +108,26 @@ export declare const ensure: <S extends Source>(source: S, ...args: ArgsOf<S>) =
|
|
|
106
108
|
* @since 0.1.0
|
|
107
109
|
*/
|
|
108
110
|
export declare const fetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
|
|
111
|
+
/**
|
|
112
|
+
* `Loader.Loader`'s `run` on the request's registry: runs an Effect that
|
|
113
|
+
* needs the registry, such as the Effect verbs in an `Effect.gen`.
|
|
114
|
+
*
|
|
115
|
+
* **Example**
|
|
116
|
+
*
|
|
117
|
+
* ```ts
|
|
118
|
+
* const handles = await run(
|
|
119
|
+
* Effect.gen(function* () {
|
|
120
|
+
* const issueHandle = yield* Loader.ensure(issue, id)
|
|
121
|
+
* return { comments: yield* Loader.prefetch(commentPage, id, 0), issue: issueHandle }
|
|
122
|
+
* })
|
|
123
|
+
* )
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @stability unstable
|
|
127
|
+
* @category verbs
|
|
128
|
+
* @since 0.2.3
|
|
129
|
+
*/
|
|
130
|
+
export declare const run: Loader["run"];
|
|
109
131
|
/**
|
|
110
132
|
* `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`.
|
|
111
133
|
*
|
package/dist/Next.mjs
CHANGED
|
@@ -48,7 +48,9 @@ const atoms = make();
|
|
|
48
48
|
*
|
|
49
49
|
* Made once per request through React's `cache`, and disposed through
|
|
50
50
|
* `cacheSignal` when React finishes the render, after the last streamed
|
|
51
|
-
* result has been sent.
|
|
51
|
+
* result has been sent. The verbs take the same signal: when React aborts
|
|
52
|
+
* the render, such as for a navigation the browser left, the `ensure` and
|
|
53
|
+
* `fetch` calls still waiting reject with its reason.
|
|
52
54
|
*
|
|
53
55
|
* **Gotchas**
|
|
54
56
|
*
|
|
@@ -88,6 +90,26 @@ const ensure = atoms.ensure;
|
|
|
88
90
|
*/
|
|
89
91
|
const fetch = atoms.fetch;
|
|
90
92
|
/**
|
|
93
|
+
* `Loader.Loader`'s `run` on the request's registry: runs an Effect that
|
|
94
|
+
* needs the registry, such as the Effect verbs in an `Effect.gen`.
|
|
95
|
+
*
|
|
96
|
+
* **Example**
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* const handles = await run(
|
|
100
|
+
* Effect.gen(function* () {
|
|
101
|
+
* const issueHandle = yield* Loader.ensure(issue, id)
|
|
102
|
+
* return { comments: yield* Loader.prefetch(commentPage, id, 0), issue: issueHandle }
|
|
103
|
+
* })
|
|
104
|
+
* )
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @stability unstable
|
|
108
|
+
* @category verbs
|
|
109
|
+
* @since 0.2.3
|
|
110
|
+
*/
|
|
111
|
+
const run = atoms.run;
|
|
112
|
+
/**
|
|
91
113
|
* `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`.
|
|
92
114
|
*
|
|
93
115
|
* **Gotchas**
|
|
@@ -108,4 +130,4 @@ const fetch = atoms.fetch;
|
|
|
108
130
|
* @since 0.1.0
|
|
109
131
|
*/
|
|
110
132
|
const dehydrate = atoms.dehydrate;
|
|
111
|
-
export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, makeRequestAtoms, prefetch };
|
|
133
|
+
export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, makeRequestAtoms, prefetch, run };
|
|
@@ -35,6 +35,12 @@ export interface HydrationBoundaryProps {
|
|
|
35
35
|
* there, so an `effect-atom-boundary` boundary sees their failures before
|
|
36
36
|
* its children read them and renders its handler on the server.
|
|
37
37
|
*
|
|
38
|
+
* **Gotchas**
|
|
39
|
+
*
|
|
40
|
+
* In development, it throws during server rendering when the registry isn't
|
|
41
|
+
* a request registry, such as `@effect/atom-react`'s default one, which
|
|
42
|
+
* every request shares: one request's state would reach the others.
|
|
43
|
+
*
|
|
38
44
|
* **Example**
|
|
39
45
|
*
|
|
40
46
|
* ```tsx
|
package/dist/ReactHydration.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { split } from "./internal/pending.mjs";
|
|
3
3
|
import { sourceOf } from "./internal/sources.mjs";
|
|
4
|
-
import { isServer } from "./internal/registry.mjs";
|
|
4
|
+
import { isServer, requireRequestRegistry } from "./internal/registry.mjs";
|
|
5
5
|
import { HydrationBoundary, RegistryContext } from "@effect/atom-react";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -36,6 +36,12 @@ import { jsx } from "react/jsx-runtime";
|
|
|
36
36
|
* there, so an `effect-atom-boundary` boundary sees their failures before
|
|
37
37
|
* its children read them and renders its handler on the server.
|
|
38
38
|
*
|
|
39
|
+
* **Gotchas**
|
|
40
|
+
*
|
|
41
|
+
* In development, it throws during server rendering when the registry isn't
|
|
42
|
+
* a request registry, such as `@effect/atom-react`'s default one, which
|
|
43
|
+
* every request shares: one request's state would reach the others.
|
|
44
|
+
*
|
|
39
45
|
* **Example**
|
|
40
46
|
*
|
|
41
47
|
* ```tsx
|
|
@@ -55,6 +61,7 @@ const HydrationBoundary$1 = (props) => {
|
|
|
55
61
|
const state = props.state;
|
|
56
62
|
const source = isServer() ? sourceOf(state) : void 0;
|
|
57
63
|
const registry = source ?? context;
|
|
64
|
+
requireRequestRegistry(registry, "HydrationBoundary");
|
|
58
65
|
const settled = React.useMemo(() => state === void 0 || source !== void 0 ? void 0 : split(registry, state), [
|
|
59
66
|
registry,
|
|
60
67
|
source,
|
package/dist/ReactRouter.d.mts
CHANGED
|
@@ -70,6 +70,10 @@ export interface AtomsMiddlewareOptions {
|
|
|
70
70
|
* `seed` runs on the new registry before `next`, so every loader's atoms
|
|
71
71
|
* start with what it sets.
|
|
72
72
|
*
|
|
73
|
+
* The verbs take the request's `signal`: when the client goes before the
|
|
74
|
+
* response, such as a navigation that leaves while its loaders wait, the
|
|
75
|
+
* `ensure` and `fetch` calls still waiting reject with its reason.
|
|
76
|
+
*
|
|
73
77
|
* **Gotchas**
|
|
74
78
|
*
|
|
75
79
|
* It's server middleware: export it as the root route's `middleware`, not
|
|
@@ -110,6 +114,9 @@ export declare const atomsMiddleware: (context: AtomsContext, options?: AtomsMid
|
|
|
110
114
|
* The registry is the one the mounted `RegistryProvider` provides. Its
|
|
111
115
|
* `dehydrate` returns nothing, so a loader's data has the same shape in
|
|
112
116
|
* the browser and on the server, and `HydrationBoundary` has nothing to do.
|
|
117
|
+
* The verbs take the navigation's `signal`: when another navigation
|
|
118
|
+
* interrupts it, the `ensure` and `fetch` calls still waiting reject with
|
|
119
|
+
* its reason, and the atoms keep loading.
|
|
113
120
|
*
|
|
114
121
|
* **Gotchas**
|
|
115
122
|
*
|
|
@@ -131,7 +138,7 @@ export declare const atomsMiddleware: (context: AtomsContext, options?: AtomsMid
|
|
|
131
138
|
*
|
|
132
139
|
* @stability unstable
|
|
133
140
|
* @category integrations
|
|
134
|
-
* @since 0.2.
|
|
141
|
+
* @since 0.2.1
|
|
135
142
|
*/
|
|
136
143
|
export declare const clientAtomsMiddleware: (context: AtomsContext) => <Result>(args: MiddlewareArgs, next: () => Promise<Result>) => Promise<Result>;
|
|
137
144
|
export { RegistryProvider };
|
package/dist/ReactRouter.mjs
CHANGED
|
@@ -69,6 +69,10 @@ import * as Predicate from "effect/Predicate";
|
|
|
69
69
|
* `seed` runs on the new registry before `next`, so every loader's atoms
|
|
70
70
|
* start with what it sets.
|
|
71
71
|
*
|
|
72
|
+
* The verbs take the request's `signal`: when the client goes before the
|
|
73
|
+
* response, such as a navigation that leaves while its loaders wait, the
|
|
74
|
+
* `ensure` and `fetch` calls still waiting reject with its reason.
|
|
75
|
+
*
|
|
72
76
|
* **Gotchas**
|
|
73
77
|
*
|
|
74
78
|
* It's server middleware: export it as the root route's `middleware`, not
|
|
@@ -99,7 +103,7 @@ const atomsMiddleware = (context, options) => async (args, next) => {
|
|
|
99
103
|
registry.dispose();
|
|
100
104
|
throw error;
|
|
101
105
|
}
|
|
102
|
-
const loader = make$1(registry);
|
|
106
|
+
const loader = make$1(registry, { signal: args.request.signal });
|
|
103
107
|
args.context.set(context, {
|
|
104
108
|
...loader,
|
|
105
109
|
dehydrate: () => dehydrate(registry),
|
|
@@ -142,19 +146,6 @@ const disposeAfter = (body, registry) => {
|
|
|
142
146
|
}
|
|
143
147
|
});
|
|
144
148
|
};
|
|
145
|
-
const browserAtoms = /* @__PURE__ */ new WeakMap();
|
|
146
|
-
const browserAtomsOf = (registry) => {
|
|
147
|
-
let atoms = browserAtoms.get(registry);
|
|
148
|
-
if (atoms === void 0) {
|
|
149
|
-
atoms = {
|
|
150
|
-
...make$1(registry),
|
|
151
|
-
dehydrate: () => [],
|
|
152
|
-
registry
|
|
153
|
-
};
|
|
154
|
-
browserAtoms.set(registry, atoms);
|
|
155
|
-
}
|
|
156
|
-
return atoms;
|
|
157
|
-
};
|
|
158
149
|
/**
|
|
159
150
|
* Creates a root client middleware that gives client loaders the browser's
|
|
160
151
|
* registry, under the context `atomsMiddleware` uses on the server.
|
|
@@ -173,6 +164,9 @@ const browserAtomsOf = (registry) => {
|
|
|
173
164
|
* The registry is the one the mounted `RegistryProvider` provides. Its
|
|
174
165
|
* `dehydrate` returns nothing, so a loader's data has the same shape in
|
|
175
166
|
* the browser and on the server, and `HydrationBoundary` has nothing to do.
|
|
167
|
+
* The verbs take the navigation's `signal`: when another navigation
|
|
168
|
+
* interrupts it, the `ensure` and `fetch` calls still waiting reject with
|
|
169
|
+
* its reason, and the atoms keep loading.
|
|
176
170
|
*
|
|
177
171
|
* **Gotchas**
|
|
178
172
|
*
|
|
@@ -194,12 +188,16 @@ const browserAtomsOf = (registry) => {
|
|
|
194
188
|
*
|
|
195
189
|
* @stability unstable
|
|
196
190
|
* @category integrations
|
|
197
|
-
* @since 0.2.
|
|
191
|
+
* @since 0.2.1
|
|
198
192
|
*/
|
|
199
193
|
const clientAtomsMiddleware = (context) => (args, next) => {
|
|
200
194
|
const registry = current();
|
|
201
195
|
if (registry === void 0) throw new Error("clientAtomsMiddleware found no mounted RegistryProvider: render raf-effect-atom/react-router's RegistryProvider around the app in the root layout.");
|
|
202
|
-
args.context.set(context,
|
|
196
|
+
args.context.set(context, {
|
|
197
|
+
...make$1(registry, { signal: args.request.signal }),
|
|
198
|
+
dehydrate: () => [],
|
|
199
|
+
registry
|
|
200
|
+
});
|
|
203
201
|
return next();
|
|
204
202
|
};
|
|
205
203
|
export { RegistryProvider, atomsMiddleware, clientAtomsMiddleware };
|
package/dist/internal/next.mjs
CHANGED
|
@@ -3,13 +3,16 @@ import { make as make$1, settled } from "../RequestRegistry.mjs";
|
|
|
3
3
|
import { make as make$2 } from "../Loader.mjs";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
// src/internal/next.ts
|
|
6
|
+
const renderSignal = () => {
|
|
7
|
+
return React.cacheSignal();
|
|
8
|
+
};
|
|
6
9
|
const make = (options) => {
|
|
7
10
|
const getRegistry = React.cache(() => {
|
|
8
11
|
const registry = make$1(options);
|
|
9
|
-
|
|
12
|
+
renderSignal()?.addEventListener("abort", () => registry.dispose(), { once: true });
|
|
10
13
|
return registry;
|
|
11
14
|
});
|
|
12
|
-
const getLoader = React.cache(() => make$2(getRegistry()));
|
|
15
|
+
const getLoader = React.cache(() => make$2(getRegistry(), { signal: renderSignal() ?? void 0 }));
|
|
13
16
|
return {
|
|
14
17
|
dehydrate: async () => {
|
|
15
18
|
const registry = getRegistry();
|
|
@@ -19,7 +22,8 @@ const make = (options) => {
|
|
|
19
22
|
ensure: (source, ...args) => getLoader().ensure(source, ...args),
|
|
20
23
|
fetch: (source, ...args) => getLoader().fetch(source, ...args),
|
|
21
24
|
getRegistry,
|
|
22
|
-
prefetch: (source, ...args) => getLoader().prefetch(source, ...args)
|
|
25
|
+
prefetch: (source, ...args) => getLoader().prefetch(source, ...args),
|
|
26
|
+
run: (effect, runOptions) => getLoader().run(effect, runOptions)
|
|
23
27
|
};
|
|
24
28
|
};
|
|
25
29
|
export { make };
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDevelopment } from "./development.mjs";
|
|
2
|
+
import { isRequestRegistry, make as make$1 } from "../RequestRegistry.mjs";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
2
4
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
3
5
|
import { scheduleTask } from "@effect/atom-react";
|
|
4
6
|
// src/internal/registry.ts
|
|
5
7
|
const isServer = () => !("window" in globalThis);
|
|
8
|
+
/**
|
|
9
|
+
* In development, throws when `rendered` renders on the server without a
|
|
10
|
+
* request registry: in one every request shares, such as
|
|
11
|
+
* `@effect/atom-react`'s default `RegistryContext`.
|
|
12
|
+
*/
|
|
13
|
+
const requireRequestRegistry = (registry, rendered) => {
|
|
14
|
+
if (isServer() && !isRequestRegistry(registry) && Effect.runSync(isDevelopment)) throw new Error(`raf-effect-atom: ${rendered} rendered on the server without a request registry, so results would be shared between requests. Provide RequestRegistry.make() through RegistryContext, as the framework integrations do.`);
|
|
15
|
+
};
|
|
6
16
|
/** `@effect/atom-react`'s default registry keeps an unused atom this long. */
|
|
7
17
|
const defaultIdleTTL = 400;
|
|
8
18
|
/**
|
|
@@ -27,4 +37,4 @@ const mount = (registry) => {
|
|
|
27
37
|
};
|
|
28
38
|
/** The browser registry of the mounted `RegistryProvider`, if one is. */
|
|
29
39
|
const current = () => mounted;
|
|
30
|
-
export { current, isServer, make, mount };
|
|
40
|
+
export { current, isServer, make, mount, requireRequestRegistry };
|
package/package.json
CHANGED