raf-effect-atom 0.1.0 → 0.2.0
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 +37 -26
- package/README.md +1 -1
- package/dist/Loader.d.mts +7 -2
- package/dist/Loader.mjs +12 -2
- package/dist/Next.d.mts +66 -1
- package/dist/Next.mjs +54 -7
- package/dist/NextClient.d.mts +2 -2
- package/dist/NextClient.mjs +2 -2
- package/dist/ReactRouter.d.mts +33 -2
- package/dist/ReactRouter.mjs +25 -3
- package/dist/RequestRegistry.d.mts +64 -8
- package/dist/RequestRegistry.mjs +43 -9
- package/dist/TanStackRouter.d.mts +13 -2
- package/dist/TanStackRouter.mjs +13 -3
- package/dist/index.d.mts +2 -2
- package/dist/internal/next.mjs +23 -14
- package/dist/internal/registry.mjs +1 -1
- package/dist/internal/seeds.d.mts +3 -0
- package/dist/internal/seeds.mjs +56 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [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)
|
|
4
|
+
|
|
5
|
+
### ⚠ BREAKING CHANGES
|
|
6
|
+
|
|
7
|
+
* **raf-effect-atom:** `dehydrate()` from `raf-effect-atom/next` returns a
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **raf-effect-atom:** seed each request registry, async seeds included ([62b6180](https://github.com/KATT/raf-effect-atom/commit/62b618098df87e711ddb4c1d633e191ba3353438))
|
|
12
|
+
|
|
3
13
|
## 0.1.0 (2026-10-23)
|
|
4
14
|
|
|
5
15
|
### ⚠ BREAKING CHANGES
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
- **raf-effect-atom:** `useAtomResult(atom, claims)` is gone. Read
|
|
8
18
|
`useAtomResult(boundary.wrap(atom))` instead. `effect-atom-boundary` is no
|
|
9
19
|
longer a peer dependency.
|
|
10
20
|
|
|
@@ -15,8 +25,9 @@
|
|
|
15
25
|
|
|
16
26
|
The request waterfalls docs sample, written against the claims overload,
|
|
17
27
|
reads through `useIssueBoundary().wrap(atom)` too.
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
|
|
29
|
+
- **raf-effect-atom:** follow initialValueTarget in useAtomResult and throw defects
|
|
30
|
+
- **raf-effect-atom:** the loader verbs take a source and its arguments and
|
|
20
31
|
return a handle, and `useAtomResult` reads a handle through a source.
|
|
21
32
|
|
|
22
33
|
- `prefetch(issue, id)` returns a `Handle<typeof issue>`; `ensure` and
|
|
@@ -71,41 +82,41 @@
|
|
|
71
82
|
|
|
72
83
|
### Features
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
- **raf-effect-atom:** add the next subpath ([92cacd8](https://github.com/KATT/raf-effect-atom/commit/92cacd86cb6d6e1784d9a07f71bd9fc3ec5ca0da))
|
|
86
|
+
- **raf-effect-atom:** add the react-router subpath ([09b65dc](https://github.com/KATT/raf-effect-atom/commit/09b65dcbc6cfde2ae1ba2bb89e580746e9512f8b))
|
|
87
|
+
- **raf-effect-atom:** add the tanstack-router subpath ([0189c83](https://github.com/KATT/raf-effect-atom/commit/0189c834cd46fc05ed866859e7527b3bd2ac47df))
|
|
88
|
+
- **raf-effect-atom:** drop the claims overload and the effect-atom-boundary peer ([48fe91e](https://github.com/KATT/raf-effect-atom/commit/48fe91eabf71a0eb36b49c907fabae7afa5abd90))
|
|
89
|
+
- **raf-effect-atom:** follow initialValueTarget in useAtomResult and throw defects ([7f577ea](https://github.com/KATT/raf-effect-atom/commit/7f577eabbdd567313b542db61d532bb36a2183ca))
|
|
90
|
+
- **raf-effect-atom:** loader verbs return handles that components read ([52a4323](https://github.com/KATT/raf-effect-atom/commit/52a4323bae1507e6b5f524262b7ed1609b1ed5d6))
|
|
91
|
+
- **raf-effect-atom:** loader verbs, streaming hydration and the read hook ([6870b1a](https://github.com/KATT/raf-effect-atom/commit/6870b1a9c7742d2fa15ce1391c8fdac66307d89e))
|
|
92
|
+
- **raf-effect-atom:** make the browser registry's idle TTL configurable ([1c55d0e](https://github.com/KATT/raf-effect-atom/commit/1c55d0e74f54bed7cef895434357355229cff512))
|
|
93
|
+
- **raf-effect-atom:** read the loader's registry under HydrationBoundary during server rendering ([6751792](https://github.com/KATT/raf-effect-atom/commit/675179254563b85c61f9a96e646fa9c72f47cc83))
|
|
94
|
+
- **raf-effect-atom:** report request waterfalls in development, with useWaterfall as the escape hatch ([4b6fe2a](https://github.com/KATT/raf-effect-atom/commit/4b6fe2aebb939ea0a7095a5121558aa0cbc5bac5))
|
|
95
|
+
- **raf-effect-atom:** type useAtomResult as a success when no typed failure is left ([aeb8dc6](https://github.com/KATT/raf-effect-atom/commit/aeb8dc63e0ad874a23c3a992afed540c27c9bb7d))
|
|
85
96
|
|
|
86
97
|
### Bug Fixes
|
|
87
98
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
- **raf-effect-atom:** call use() on a streamed result's promise on every render that reads it ([36d2aaa](https://github.com/KATT/raf-effect-atom/commit/36d2aaa8bacf18a60507e4997fd61476d7a908c0)), references [#41](https://github.com/KATT/raf-effect-atom/issues/41)
|
|
100
|
+
- **raf-effect-atom:** point waterfall reports at the URL before the escape hatches ([0d127e3](https://github.com/KATT/raf-effect-atom/commit/0d127e38a10cb3a44340c5540c75b53491faceee))
|
|
101
|
+
- **raf-effect-atom:** refresh the atom the registry runs for a key in Loader.fetch ([0b35c97](https://github.com/KATT/raf-effect-atom/commit/0b35c97b4de60de254c7fc921b89aed7be753ec8))
|
|
102
|
+
- **raf-effect-atom:** wait for results streamed in a server component payload ([00cb981](https://github.com/KATT/raf-effect-atom/commit/00cb98125aad2cda70525fbfdedcd2b5a8f7fc79))
|
|
92
103
|
|
|
93
104
|
### Code Refactoring
|
|
94
105
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
- **examples:** read boundary claims through wrap ([ae92f67](https://github.com/KATT/raf-effect-atom/commit/ae92f67722417542ee06a7299b327a77a0597814))
|
|
107
|
+
- **raf-effect-atom:** rename files to kebab-case ([285bf4b](https://github.com/KATT/raf-effect-atom/commit/285bf4b4e5e3184ac162129f51c04d9c96921650))
|
|
108
|
+
- **raf-effect-atom:** restore Effect's file names ([92dc1d8](https://github.com/KATT/raf-effect-atom/commit/92dc1d8e52212c28042153cbd7b1146870839831))
|
|
98
109
|
|
|
99
110
|
### Documentation
|
|
100
111
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
- catch RpcClientError in an app boundary, and read fully claimed atoms as successes ([fe9b485](https://github.com/KATT/raf-effect-atom/commit/fe9b485fd19643a34f1709ebb5b66c90036eea47))
|
|
113
|
+
- point coding agents at the rules from the READMEs ([e8d3047](https://github.com/KATT/raf-effect-atom/commit/e8d30477e840df83c0f47764bdf505bc61181f14))
|
|
114
|
+
- **raf-effect-atom:** add the package README ([69c589e](https://github.com/KATT/raf-effect-atom/commit/69c589e020527ca087d7a54fb99544460ebc7774))
|
|
104
115
|
|
|
105
116
|
### Tests
|
|
106
117
|
|
|
107
|
-
|
|
118
|
+
- keep each package's Vitest module cache to itself ([be11618](https://github.com/KATT/raf-effect-atom/commit/be11618b41f9cecaba6968a356677a7dd3bfe734))
|
|
108
119
|
|
|
109
120
|
### Continuous Integration
|
|
110
121
|
|
|
111
|
-
|
|
122
|
+
- release each changed package after CI passes on main, and pack-check the tarballs on every push ([8ab3b90](https://github.com/KATT/raf-effect-atom/commit/8ab3b90e4ac2e7d6c25c028aace7167279bdb2b3))
|
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ function IssueTitle(props: { readonly issue: Handle<typeof issue> }) {
|
|
|
52
52
|
Each framework has a subpath that gives every server request its own registry and sends it to the browser:
|
|
53
53
|
|
|
54
54
|
- `raf-effect-atom/tanstack-router`, for TanStack Router and TanStack Start: `setupRouterAtoms({ router, registry })` in `getRouter`, with `Loader.make(registry)` on the router context.
|
|
55
|
-
- `raf-effect-atom/next`, for the App Router: `prefetch`, `ensure` and `fetch` in server components, which pass the handles to client components as props, and `<HydrateAtoms state={dehydrate()}>` around them.
|
|
55
|
+
- `raf-effect-atom/next`, for the App Router: `prefetch`, `ensure` and `fetch` in server components, which pass the handles to client components as props, and `<HydrateAtoms state={await dehydrate()}>` around them. `makeRequestAtoms({ seed })` makes the same functions over a registry seeded from the request, such as with its cookie.
|
|
56
56
|
- `raf-effect-atom/react-router`, for Framework Mode: `atomsMiddleware(atomsContext)` as the root middleware, loaders that return `atoms.dehydrate()` with their handles, and `HydrationBoundary` in the route component.
|
|
57
57
|
|
|
58
58
|
In development, `useAtomResult` reports a read that starts an atom nothing started, a request waterfall. `<WaterfallDetection mode="throw">` at the root makes it throw to the nearest error boundary. `useWaterfall(source, ...args)` starts an atom whose input exists only in render, marks it as a known waterfall, and returns its handle.
|
package/dist/Loader.d.mts
CHANGED
|
@@ -42,7 +42,8 @@ export declare const LeaseTTL: Context.Reference<Duration.Duration>;
|
|
|
42
42
|
*
|
|
43
43
|
* Starts the atom if it isn't in the registry, and leaves it alone if it is.
|
|
44
44
|
* Takes a lease either way. The component that reads the handle suspends
|
|
45
|
-
* until the result settles.
|
|
45
|
+
* until the result settles. On a request registry whose async seed is still
|
|
46
|
+
* running, it waits for the seed first.
|
|
46
47
|
*
|
|
47
48
|
* **Example**
|
|
48
49
|
*
|
|
@@ -158,7 +159,11 @@ export interface Loader {
|
|
|
158
159
|
readonly ensure: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
|
|
159
160
|
/** {@link fetch}, as a Promise that rejects only on a defect. */
|
|
160
161
|
readonly fetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
|
|
161
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* {@link prefetch}, run at once. On a request registry whose async seed is
|
|
164
|
+
* still running, it returns the handle at once and starts the atom once
|
|
165
|
+
* the seed resolves.
|
|
166
|
+
*/
|
|
162
167
|
readonly prefetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Handle<S>;
|
|
163
168
|
}
|
|
164
169
|
/**
|
package/dist/Loader.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { hydrated } from "./Hydration.mjs";
|
|
|
3
3
|
import { isDevelopment } from "./internal/development.mjs";
|
|
4
4
|
import { keyOf, take } from "./internal/leases.mjs";
|
|
5
5
|
import { atomOf, loaded as loaded$1, make as make$1 } from "./internal/handles.mjs";
|
|
6
|
+
import { afterSeed, pending } from "./internal/seeds.mjs";
|
|
6
7
|
import { isRequestRegistry } from "./RequestRegistry.mjs";
|
|
7
8
|
import * as Atom from "effect/reactivity/Atom";
|
|
8
9
|
import * as Predicate from "effect/Predicate";
|
|
@@ -66,6 +67,8 @@ var Loader_exports = /* @__PURE__ */ __exportAll({
|
|
|
66
67
|
const LeaseTTL = Context.Reference("raf-effect-atom/Loader/LeaseTTL", { defaultValue: () => Duration.seconds(30) });
|
|
67
68
|
const start = Effect.fnUntraced(function* (atom) {
|
|
68
69
|
const registry = yield* AtomRegistry.AtomRegistry;
|
|
70
|
+
const seed = pending(registry);
|
|
71
|
+
if (seed !== void 0) yield* Effect.promise(() => seed);
|
|
69
72
|
const request = isRequestRegistry(registry);
|
|
70
73
|
if (request && !Atom.isSerializable(atom) && (yield* isDevelopment)) return yield* Effect.die(/* @__PURE__ */ new Error(`raf-effect-atom: a loader started ${String(atom)}, which isn't serializable, so the browser would request it again. Give it a serialization key, such as AtomRpc's serializationKey option.`));
|
|
71
74
|
const ttl = request ? void 0 : Duration.toMillis(yield* LeaseTTL);
|
|
@@ -114,7 +117,8 @@ const rejectDefects = (result) => {
|
|
|
114
117
|
*
|
|
115
118
|
* Starts the atom if it isn't in the registry, and leaves it alone if it is.
|
|
116
119
|
* Takes a lease either way. The component that reads the handle suspends
|
|
117
|
-
* until the result settles.
|
|
120
|
+
* until the result settles. On a request registry whose async seed is still
|
|
121
|
+
* running, it waits for the seed first.
|
|
118
122
|
*
|
|
119
123
|
* **Example**
|
|
120
124
|
*
|
|
@@ -280,7 +284,13 @@ const make = (registry, options) => {
|
|
|
280
284
|
return {
|
|
281
285
|
ensure: (source, ...args) => Effect.runPromise(provide(ensure(source, ...args))),
|
|
282
286
|
fetch: (source, ...args) => Effect.runPromise(provide(fetch(source, ...args))),
|
|
283
|
-
prefetch: (source, ...args) =>
|
|
287
|
+
prefetch: (source, ...args) => {
|
|
288
|
+
const atom = atomOf(source, args);
|
|
289
|
+
afterSeed(registry, () => {
|
|
290
|
+
Effect.runSync(provide(start(atom)));
|
|
291
|
+
});
|
|
292
|
+
return make$1(args, atom);
|
|
293
|
+
}
|
|
284
294
|
};
|
|
285
295
|
};
|
|
286
296
|
export { LeaseTTL, Loader_exports, ensure, fetch, make, prefetch, result };
|
package/dist/Next.d.mts
CHANGED
|
@@ -1,8 +1,65 @@
|
|
|
1
1
|
import { ArgsOf, Handle, LoadedHandle, Source } from "./Handle.mjs";
|
|
2
|
+
import { Loader } from "./Loader.mjs";
|
|
3
|
+
import { Options } from "./RequestRegistry.mjs";
|
|
2
4
|
import { HydrateAtoms } from "./NextClient.mjs";
|
|
3
5
|
import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
4
6
|
import * as EffectHydration from "effect/reactivity/Hydration";
|
|
5
7
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
8
|
+
/**
|
|
9
|
+
* The loader verbs over the request registry of the server render in
|
|
10
|
+
* progress, with its registry and `dehydrate`.
|
|
11
|
+
*
|
|
12
|
+
* @stability unstable
|
|
13
|
+
* @category models
|
|
14
|
+
* @since 0.2.0
|
|
15
|
+
*/
|
|
16
|
+
export interface RequestAtoms extends Loader {
|
|
17
|
+
/**
|
|
18
|
+
* `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`,
|
|
19
|
+
* once its seed has settled.
|
|
20
|
+
*/
|
|
21
|
+
readonly dehydrate: () => Promise<Array<EffectHydration.DehydratedAtom>>;
|
|
22
|
+
/** The request registry of the server render in progress. */
|
|
23
|
+
readonly getRegistry: () => AtomRegistry.AtomRegistry;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates the verbs, `getRegistry` and `dehydrate` over a request registry
|
|
27
|
+
* that `seed` prepares.
|
|
28
|
+
*
|
|
29
|
+
* **When to use**
|
|
30
|
+
*
|
|
31
|
+
* Use when the request's atoms need something from the request before they
|
|
32
|
+
* run, such as an RPC protocol that forwards the page request's cookie. Make
|
|
33
|
+
* them once, in a server-only module, and call the verbs on them instead of
|
|
34
|
+
* the ones `raf-effect-atom/next` exports.
|
|
35
|
+
*
|
|
36
|
+
* **Details**
|
|
37
|
+
*
|
|
38
|
+
* The seed runs on each request's registry when a server component first
|
|
39
|
+
* uses it. It can be async, as `headers()` is: `ensure` and `fetch` wait
|
|
40
|
+
* for it, `prefetch` returns its handle at once and starts the atom once the
|
|
41
|
+
* seed resolves, and `dehydrate` waits for both.
|
|
42
|
+
*
|
|
43
|
+
* **Example**
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* // src/atoms.server.ts
|
|
47
|
+
* import "server-only"
|
|
48
|
+
* import { headers } from "next/headers"
|
|
49
|
+
* import { makeRequestAtoms } from "raf-effect-atom/next"
|
|
50
|
+
*
|
|
51
|
+
* export const atoms = makeRequestAtoms({
|
|
52
|
+
* seed: async (registry) => {
|
|
53
|
+
* registry.set(requestCookie, (await headers()).get("cookie") ?? undefined)
|
|
54
|
+
* }
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @stability unstable
|
|
59
|
+
* @category constructors
|
|
60
|
+
* @since 0.2.0
|
|
61
|
+
*/
|
|
62
|
+
export declare const makeRequestAtoms: (options?: Options) => RequestAtoms;
|
|
6
63
|
/**
|
|
7
64
|
* Returns the request registry of the server render in progress.
|
|
8
65
|
*
|
|
@@ -57,9 +114,17 @@ export declare const fetch: <S extends Source>(source: S, ...args: ArgsOf<S>) =>
|
|
|
57
114
|
* Includes the atoms started so far, so call it after the server component's
|
|
58
115
|
* verbs. Each `HydrateAtoms` hydrates what its own `dehydrate()` saw.
|
|
59
116
|
*
|
|
117
|
+
* **Example**
|
|
118
|
+
*
|
|
119
|
+
* ```tsx
|
|
120
|
+
* <HydrateAtoms state={await dehydrate()}>
|
|
121
|
+
* <Issue issue={handle} />
|
|
122
|
+
* </HydrateAtoms>
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
60
125
|
* @stability unstable
|
|
61
126
|
* @category dehydration
|
|
62
127
|
* @since 0.1.0
|
|
63
128
|
*/
|
|
64
|
-
export declare const dehydrate: () => Array<EffectHydration.DehydratedAtom
|
|
129
|
+
export declare const dehydrate: () => Promise<Array<EffectHydration.DehydratedAtom>>;
|
|
65
130
|
export { HydrateAtoms, RegistryProvider };
|
package/dist/Next.mjs
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { make } from "./internal/next.mjs";
|
|
2
2
|
import { HydrateAtoms } from "./NextClient.mjs";
|
|
3
3
|
import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
4
4
|
// src/Next.ts
|
|
5
5
|
/**
|
|
6
|
+
* Creates the verbs, `getRegistry` and `dehydrate` over a request registry
|
|
7
|
+
* that `seed` prepares.
|
|
8
|
+
*
|
|
9
|
+
* **When to use**
|
|
10
|
+
*
|
|
11
|
+
* Use when the request's atoms need something from the request before they
|
|
12
|
+
* run, such as an RPC protocol that forwards the page request's cookie. Make
|
|
13
|
+
* them once, in a server-only module, and call the verbs on them instead of
|
|
14
|
+
* the ones `raf-effect-atom/next` exports.
|
|
15
|
+
*
|
|
16
|
+
* **Details**
|
|
17
|
+
*
|
|
18
|
+
* The seed runs on each request's registry when a server component first
|
|
19
|
+
* uses it. It can be async, as `headers()` is: `ensure` and `fetch` wait
|
|
20
|
+
* for it, `prefetch` returns its handle at once and starts the atom once the
|
|
21
|
+
* seed resolves, and `dehydrate` waits for both.
|
|
22
|
+
*
|
|
23
|
+
* **Example**
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* // src/atoms.server.ts
|
|
27
|
+
* import "server-only"
|
|
28
|
+
* import { headers } from "next/headers"
|
|
29
|
+
* import { makeRequestAtoms } from "raf-effect-atom/next"
|
|
30
|
+
*
|
|
31
|
+
* export const atoms = makeRequestAtoms({
|
|
32
|
+
* seed: async (registry) => {
|
|
33
|
+
* registry.set(requestCookie, (await headers()).get("cookie") ?? undefined)
|
|
34
|
+
* }
|
|
35
|
+
* })
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @stability unstable
|
|
39
|
+
* @category constructors
|
|
40
|
+
* @since 0.2.0
|
|
41
|
+
*/
|
|
42
|
+
const makeRequestAtoms = make;
|
|
43
|
+
const atoms = make();
|
|
44
|
+
/**
|
|
6
45
|
* Returns the request registry of the server render in progress.
|
|
7
46
|
*
|
|
8
47
|
* **Details**
|
|
@@ -20,7 +59,7 @@ import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
|
20
59
|
* @category registry
|
|
21
60
|
* @since 0.1.0
|
|
22
61
|
*/
|
|
23
|
-
const getRegistry = getRegistry
|
|
62
|
+
const getRegistry = atoms.getRegistry;
|
|
24
63
|
/**
|
|
25
64
|
* `Loader.prefetch` on the request's registry: starts a source's atom, to
|
|
26
65
|
* stream it, and returns its handle.
|
|
@@ -29,7 +68,7 @@ const getRegistry = getRegistry$1;
|
|
|
29
68
|
* @category verbs
|
|
30
69
|
* @since 0.1.0
|
|
31
70
|
*/
|
|
32
|
-
const prefetch = prefetch
|
|
71
|
+
const prefetch = atoms.prefetch;
|
|
33
72
|
/**
|
|
34
73
|
* `Loader.ensure` on the request's registry: starts a source's atom, waits
|
|
35
74
|
* for its result, and returns its handle.
|
|
@@ -38,7 +77,7 @@ const prefetch = prefetch$1;
|
|
|
38
77
|
* @category verbs
|
|
39
78
|
* @since 0.1.0
|
|
40
79
|
*/
|
|
41
|
-
const ensure = ensure
|
|
80
|
+
const ensure = atoms.ensure;
|
|
42
81
|
/**
|
|
43
82
|
* `Loader.fetch` on the request's registry: starts or refreshes a source's
|
|
44
83
|
* atom, waits for the new result, and returns its handle.
|
|
@@ -47,7 +86,7 @@ const ensure = ensure$1;
|
|
|
47
86
|
* @category verbs
|
|
48
87
|
* @since 0.1.0
|
|
49
88
|
*/
|
|
50
|
-
const fetch = fetch
|
|
89
|
+
const fetch = atoms.fetch;
|
|
51
90
|
/**
|
|
52
91
|
* `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`.
|
|
53
92
|
*
|
|
@@ -56,9 +95,17 @@ const fetch = fetch$1;
|
|
|
56
95
|
* Includes the atoms started so far, so call it after the server component's
|
|
57
96
|
* verbs. Each `HydrateAtoms` hydrates what its own `dehydrate()` saw.
|
|
58
97
|
*
|
|
98
|
+
* **Example**
|
|
99
|
+
*
|
|
100
|
+
* ```tsx
|
|
101
|
+
* <HydrateAtoms state={await dehydrate()}>
|
|
102
|
+
* <Issue issue={handle} />
|
|
103
|
+
* </HydrateAtoms>
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
59
106
|
* @stability unstable
|
|
60
107
|
* @category dehydration
|
|
61
108
|
* @since 0.1.0
|
|
62
109
|
*/
|
|
63
|
-
const dehydrate = dehydrate
|
|
64
|
-
export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, prefetch };
|
|
110
|
+
const dehydrate = atoms.dehydrate;
|
|
111
|
+
export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, makeRequestAtoms, prefetch };
|
package/dist/NextClient.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HydrationBoundaryProps } from "./ReactHydration.mjs";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
/**
|
|
4
|
-
* Hydrates the state a server component dehydrated with `dehydrate()`,
|
|
4
|
+
* Hydrates the state a server component dehydrated with `await dehydrate()`,
|
|
5
5
|
* streamed results included.
|
|
6
6
|
*
|
|
7
7
|
* **Details**
|
|
@@ -18,7 +18,7 @@ import * as React from "react";
|
|
|
18
18
|
* export default async function IssuePage(props: { params: Promise<{ id: string }> }) {
|
|
19
19
|
* const handle = await ensure(issue, Number((await props.params).id))
|
|
20
20
|
* return (
|
|
21
|
-
* <HydrateAtoms state={dehydrate()}>
|
|
21
|
+
* <HydrateAtoms state={await dehydrate()}>
|
|
22
22
|
* <Issue issue={handle} />
|
|
23
23
|
* </HydrateAtoms>
|
|
24
24
|
* )
|
package/dist/NextClient.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
9
9
|
* @since 0.1.0
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
|
-
* Hydrates the state a server component dehydrated with `dehydrate()`,
|
|
12
|
+
* Hydrates the state a server component dehydrated with `await dehydrate()`,
|
|
13
13
|
* streamed results included.
|
|
14
14
|
*
|
|
15
15
|
* **Details**
|
|
@@ -26,7 +26,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
26
26
|
* export default async function IssuePage(props: { params: Promise<{ id: string }> }) {
|
|
27
27
|
* const handle = await ensure(issue, Number((await props.params).id))
|
|
28
28
|
* return (
|
|
29
|
-
* <HydrateAtoms state={dehydrate()}>
|
|
29
|
+
* <HydrateAtoms state={await dehydrate()}>
|
|
30
30
|
* <Issue issue={handle} />
|
|
31
31
|
* </HydrateAtoms>
|
|
32
32
|
* )
|
package/dist/ReactRouter.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
2
1
|
import { Loader } from "./Loader.mjs";
|
|
2
|
+
import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
3
3
|
import * as EffectHydration from "effect/reactivity/Hydration";
|
|
4
4
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
5
5
|
/**
|
|
@@ -36,6 +36,22 @@ export interface MiddlewareArgs {
|
|
|
36
36
|
readonly context: {
|
|
37
37
|
readonly set: (context: AtomsContext, value: RequestAtoms) => void;
|
|
38
38
|
};
|
|
39
|
+
readonly request: Request;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Options for `atomsMiddleware`.
|
|
43
|
+
*
|
|
44
|
+
* @stability unstable
|
|
45
|
+
* @category models
|
|
46
|
+
* @since 0.2.0
|
|
47
|
+
*/
|
|
48
|
+
export interface AtomsMiddlewareOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Runs on each request's registry before the loaders, with the request,
|
|
51
|
+
* to set what its atoms need, such as its cookie. The middleware waits
|
|
52
|
+
* for one that returns a promise.
|
|
53
|
+
*/
|
|
54
|
+
readonly seed?: ((registry: AtomRegistry.AtomRegistry, request: Request) => void | Promise<void>) | undefined;
|
|
39
55
|
}
|
|
40
56
|
/**
|
|
41
57
|
* Creates a root middleware that gives each server request its own
|
|
@@ -49,15 +65,30 @@ export interface MiddlewareArgs {
|
|
|
49
65
|
* then. When `next` doesn't return a `Response`, as Data Mode's
|
|
50
66
|
* `query` can, the registry is left to be collected with the request.
|
|
51
67
|
*
|
|
68
|
+
* `seed` runs on the new registry before `next`, so every loader's atoms
|
|
69
|
+
* start with what it sets.
|
|
70
|
+
*
|
|
52
71
|
* **Gotchas**
|
|
53
72
|
*
|
|
54
73
|
* It's server middleware: export it as the root route's `middleware`, not
|
|
55
74
|
* `clientMiddleware`. In the browser, the root's `RegistryProvider` holds
|
|
56
75
|
* the registry.
|
|
57
76
|
*
|
|
77
|
+
* **Example**
|
|
78
|
+
*
|
|
79
|
+
* ```ts
|
|
80
|
+
* export const middleware = [
|
|
81
|
+
* atomsMiddleware(atomsContext, {
|
|
82
|
+
* // In-process RPC calls send the page request's cookie.
|
|
83
|
+
* seed: (registry, request) =>
|
|
84
|
+
* registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
85
|
+
* })
|
|
86
|
+
* ]
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
58
89
|
* @stability unstable
|
|
59
90
|
* @category integrations
|
|
60
91
|
* @since 0.1.0
|
|
61
92
|
*/
|
|
62
|
-
export declare const atomsMiddleware: (context: AtomsContext) => <Result>(args: MiddlewareArgs, next: () => Promise<Result>) => Promise<Result>;
|
|
93
|
+
export declare const atomsMiddleware: (context: AtomsContext, options?: AtomsMiddlewareOptions) => <Result>(args: MiddlewareArgs, next: () => Promise<Result>) => Promise<Result>;
|
|
63
94
|
export { RegistryProvider };
|
package/dist/ReactRouter.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dehydrate } from "./Hydration.mjs";
|
|
2
|
-
import { make } from "./RequestRegistry.mjs";
|
|
2
|
+
import { make, settled } from "./RequestRegistry.mjs";
|
|
3
3
|
import { make as make$1 } from "./Loader.mjs";
|
|
4
4
|
import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
5
5
|
import * as Predicate from "effect/Predicate";
|
|
@@ -58,18 +58,40 @@ import * as Predicate from "effect/Predicate";
|
|
|
58
58
|
* then. When `next` doesn't return a `Response`, as Data Mode's
|
|
59
59
|
* `query` can, the registry is left to be collected with the request.
|
|
60
60
|
*
|
|
61
|
+
* `seed` runs on the new registry before `next`, so every loader's atoms
|
|
62
|
+
* start with what it sets.
|
|
63
|
+
*
|
|
61
64
|
* **Gotchas**
|
|
62
65
|
*
|
|
63
66
|
* It's server middleware: export it as the root route's `middleware`, not
|
|
64
67
|
* `clientMiddleware`. In the browser, the root's `RegistryProvider` holds
|
|
65
68
|
* the registry.
|
|
66
69
|
*
|
|
70
|
+
* **Example**
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* export const middleware = [
|
|
74
|
+
* atomsMiddleware(atomsContext, {
|
|
75
|
+
* // In-process RPC calls send the page request's cookie.
|
|
76
|
+
* seed: (registry, request) =>
|
|
77
|
+
* registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
78
|
+
* })
|
|
79
|
+
* ]
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
67
82
|
* @stability unstable
|
|
68
83
|
* @category integrations
|
|
69
84
|
* @since 0.1.0
|
|
70
85
|
*/
|
|
71
|
-
const atomsMiddleware = (context) => async (args, next) => {
|
|
72
|
-
const
|
|
86
|
+
const atomsMiddleware = (context, options) => async (args, next) => {
|
|
87
|
+
const seed = options?.seed;
|
|
88
|
+
const registry = make({ seed: seed === void 0 ? void 0 : (created) => seed(created, args.request) });
|
|
89
|
+
try {
|
|
90
|
+
await settled(registry);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
registry.dispose();
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
73
95
|
const loader = make$1(registry);
|
|
74
96
|
args.context.set(context, {
|
|
75
97
|
...loader,
|
|
@@ -1,32 +1,88 @@
|
|
|
1
|
+
import { Seed as Seed$1 } from "./internal/seeds.mjs";
|
|
1
2
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
2
3
|
import * as Layer from "effect/Layer";
|
|
3
4
|
declare namespace RequestRegistry_d_exports {
|
|
4
|
-
export { isRequestRegistry, layer, make };
|
|
5
|
+
export { Options, Seed, isRequestRegistry, layer, make, settled };
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Prepares a request registry before the loader verbs start atoms in it,
|
|
9
|
+
* such as by setting an atom to the request's cookie.
|
|
10
|
+
*
|
|
11
|
+
* @stability unstable
|
|
12
|
+
* @category models
|
|
13
|
+
* @since 0.2.0
|
|
14
|
+
*/
|
|
15
|
+
export type Seed = Seed$1;
|
|
16
|
+
/**
|
|
17
|
+
* Options for `make`.
|
|
18
|
+
*
|
|
19
|
+
* @stability unstable
|
|
20
|
+
* @category models
|
|
21
|
+
* @since 0.2.0
|
|
22
|
+
*/
|
|
23
|
+
export interface Options {
|
|
24
|
+
/**
|
|
25
|
+
* Runs on the new registry before any verb starts an atom in it. When it
|
|
26
|
+
* returns a promise, `ensure` and `fetch` wait for it, and `prefetch`
|
|
27
|
+
* returns its handle at once and starts the atom once it resolves.
|
|
28
|
+
*/
|
|
29
|
+
readonly seed?: Seed | undefined;
|
|
5
30
|
}
|
|
6
31
|
/**
|
|
7
32
|
* Creates the registry for one server request.
|
|
8
33
|
*
|
|
9
34
|
* **Details**
|
|
10
35
|
*
|
|
11
|
-
* It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
12
|
-
*
|
|
13
|
-
* the background, and a loader's lease lasts until the registry is
|
|
14
|
-
* In development, the loader verbs refuse an atom that isn't
|
|
15
|
-
* because the browser would request it again.
|
|
36
|
+
* - It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
37
|
+
* request registry, `ensure` waits like `fetch` does, nothing revalidates
|
|
38
|
+
* in the background, and a loader's lease lasts until the registry is
|
|
39
|
+
* disposed. In development, the loader verbs refuse an atom that isn't
|
|
40
|
+
* serializable, because the browser would request it again.
|
|
41
|
+
* - `seed` sets what the request's atoms need before they run, such as the
|
|
42
|
+
* page request's cookie for an RPC protocol that forwards it. It can be
|
|
43
|
+
* async, for request data only a promise gives, such as Next.js's
|
|
44
|
+
* `headers()`.
|
|
45
|
+
*
|
|
46
|
+
* **Gotchas**
|
|
47
|
+
*
|
|
48
|
+
* With an async seed, a `prefetch` before it settles starts its atom later,
|
|
49
|
+
* so `Hydration.dehydrate` would miss it: wait for `settled` first, as the
|
|
50
|
+
* framework integrations do.
|
|
16
51
|
*
|
|
17
52
|
* **Example**
|
|
18
53
|
*
|
|
19
54
|
* ```ts
|
|
20
55
|
* import { RequestRegistry } from "raf-effect-atom"
|
|
21
56
|
*
|
|
22
|
-
* const registry = RequestRegistry.make(
|
|
57
|
+
* const registry = RequestRegistry.make({
|
|
58
|
+
* seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
59
|
+
* })
|
|
23
60
|
* ```
|
|
24
61
|
*
|
|
25
62
|
* @stability unstable
|
|
26
63
|
* @category constructors
|
|
27
64
|
* @since 0.1.0
|
|
28
65
|
*/
|
|
29
|
-
export declare const make: () => AtomRegistry.AtomRegistry;
|
|
66
|
+
export declare const make: (options?: Options) => AtomRegistry.AtomRegistry;
|
|
67
|
+
/**
|
|
68
|
+
* Waits until a request registry's seed has settled and every atom a verb
|
|
69
|
+
* started while it ran has started.
|
|
70
|
+
*
|
|
71
|
+
* **When to use**
|
|
72
|
+
*
|
|
73
|
+
* Use before `Hydration.dehydrate` on a registry with an async seed, outside
|
|
74
|
+
* the framework integrations, which call it for you.
|
|
75
|
+
*
|
|
76
|
+
* **Details**
|
|
77
|
+
*
|
|
78
|
+
* Resolves at once for a registry without a seed, or whose seed was
|
|
79
|
+
* synchronous. Rejects with the seed's error.
|
|
80
|
+
*
|
|
81
|
+
* @stability unstable
|
|
82
|
+
* @category getters
|
|
83
|
+
* @since 0.2.0
|
|
84
|
+
*/
|
|
85
|
+
export declare const settled: (registry: AtomRegistry.AtomRegistry) => Promise<void>;
|
|
30
86
|
/**
|
|
31
87
|
* A layer that provides a request registry as the `AtomRegistry`, disposed
|
|
32
88
|
* when the layer's scope closes.
|
package/dist/RequestRegistry.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { run, settled as settled$1 } from "./internal/seeds.mjs";
|
|
2
3
|
import * as Effect from "effect/Effect";
|
|
3
4
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
4
5
|
import * as Layer from "effect/Layer";
|
|
@@ -18,7 +19,8 @@ import * as Layer from "effect/Layer";
|
|
|
18
19
|
var RequestRegistry_exports = /* @__PURE__ */ __exportAll({
|
|
19
20
|
isRequestRegistry: () => isRequestRegistry,
|
|
20
21
|
layer: () => layer,
|
|
21
|
-
make: () => make
|
|
22
|
+
make: () => make,
|
|
23
|
+
settled: () => settled
|
|
22
24
|
});
|
|
23
25
|
const requestRegistries = /* @__PURE__ */ new WeakSet();
|
|
24
26
|
/**
|
|
@@ -26,30 +28,62 @@ const requestRegistries = /* @__PURE__ */ new WeakSet();
|
|
|
26
28
|
*
|
|
27
29
|
* **Details**
|
|
28
30
|
*
|
|
29
|
-
* It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
30
|
-
*
|
|
31
|
-
* the background, and a loader's lease lasts until the registry is
|
|
32
|
-
* In development, the loader verbs refuse an atom that isn't
|
|
33
|
-
* because the browser would request it again.
|
|
31
|
+
* - It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
32
|
+
* request registry, `ensure` waits like `fetch` does, nothing revalidates
|
|
33
|
+
* in the background, and a loader's lease lasts until the registry is
|
|
34
|
+
* disposed. In development, the loader verbs refuse an atom that isn't
|
|
35
|
+
* serializable, because the browser would request it again.
|
|
36
|
+
* - `seed` sets what the request's atoms need before they run, such as the
|
|
37
|
+
* page request's cookie for an RPC protocol that forwards it. It can be
|
|
38
|
+
* async, for request data only a promise gives, such as Next.js's
|
|
39
|
+
* `headers()`.
|
|
40
|
+
*
|
|
41
|
+
* **Gotchas**
|
|
42
|
+
*
|
|
43
|
+
* With an async seed, a `prefetch` before it settles starts its atom later,
|
|
44
|
+
* so `Hydration.dehydrate` would miss it: wait for `settled` first, as the
|
|
45
|
+
* framework integrations do.
|
|
34
46
|
*
|
|
35
47
|
* **Example**
|
|
36
48
|
*
|
|
37
49
|
* ```ts
|
|
38
50
|
* import { RequestRegistry } from "raf-effect-atom"
|
|
39
51
|
*
|
|
40
|
-
* const registry = RequestRegistry.make(
|
|
52
|
+
* const registry = RequestRegistry.make({
|
|
53
|
+
* seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
54
|
+
* })
|
|
41
55
|
* ```
|
|
42
56
|
*
|
|
43
57
|
* @stability unstable
|
|
44
58
|
* @category constructors
|
|
45
59
|
* @since 0.1.0
|
|
46
60
|
*/
|
|
47
|
-
const make = () => {
|
|
61
|
+
const make = (options) => {
|
|
48
62
|
const registry = AtomRegistry.make();
|
|
49
63
|
requestRegistries.add(registry);
|
|
64
|
+
if (options?.seed !== void 0) run(registry, options.seed);
|
|
50
65
|
return registry;
|
|
51
66
|
};
|
|
52
67
|
/**
|
|
68
|
+
* Waits until a request registry's seed has settled and every atom a verb
|
|
69
|
+
* started while it ran has started.
|
|
70
|
+
*
|
|
71
|
+
* **When to use**
|
|
72
|
+
*
|
|
73
|
+
* Use before `Hydration.dehydrate` on a registry with an async seed, outside
|
|
74
|
+
* the framework integrations, which call it for you.
|
|
75
|
+
*
|
|
76
|
+
* **Details**
|
|
77
|
+
*
|
|
78
|
+
* Resolves at once for a registry without a seed, or whose seed was
|
|
79
|
+
* synchronous. Rejects with the seed's error.
|
|
80
|
+
*
|
|
81
|
+
* @stability unstable
|
|
82
|
+
* @category getters
|
|
83
|
+
* @since 0.2.0
|
|
84
|
+
*/
|
|
85
|
+
const settled = (registry) => settled$1(registry);
|
|
86
|
+
/**
|
|
53
87
|
* A layer that provides a request registry as the `AtomRegistry`, disposed
|
|
54
88
|
* when the layer's scope closes.
|
|
55
89
|
*
|
|
@@ -73,4 +107,4 @@ const layer = Layer.effect(AtomRegistry.AtomRegistry, Effect.acquireRelease(Effe
|
|
|
73
107
|
* @since 0.1.0
|
|
74
108
|
*/
|
|
75
109
|
const isRequestRegistry = (registry) => requestRegistries.has(registry);
|
|
76
|
-
export { RequestRegistry_exports, isRequestRegistry, layer, make };
|
|
110
|
+
export { RequestRegistry_exports, isRequestRegistry, layer, make, settled };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Seed } from "./RequestRegistry.mjs";
|
|
1
2
|
import * as EffectHydration from "effect/reactivity/Hydration";
|
|
2
3
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
3
4
|
import { AnyRouter } from "@tanstack/react-router";
|
|
@@ -38,14 +39,23 @@ export interface RouterAtomsOptions<TRouter extends AnyRouter> {
|
|
|
38
39
|
* default registry does. An atom's own `Atom.setIdleTTL` or
|
|
39
40
|
* `Atom.keepAlive` wins. The server's request registry lives for one
|
|
40
41
|
* request, so it has none.
|
|
42
|
+
* - `seed` runs on the server's request registry before the loaders start
|
|
43
|
+
* atoms in it, as `RequestRegistry.make`'s does: set what the request's
|
|
44
|
+
* atoms need, such as its cookie. It can be async. The browser's registry
|
|
45
|
+
* isn't seeded.
|
|
41
46
|
*
|
|
42
47
|
* **Example**
|
|
43
48
|
*
|
|
44
49
|
* ```ts
|
|
50
|
+
* import { getRequestHeader } from "@tanstack/react-start/server"
|
|
45
51
|
* import { makeRegistry } from "raf-effect-atom/tanstack-router"
|
|
46
52
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
53
|
+
* const registry = makeRegistry({
|
|
54
|
+
* // Keeps an issue the user navigated away from for a minute.
|
|
55
|
+
* defaultIdleTTL: 60_000,
|
|
56
|
+
* // In-process RPC calls send the page request's cookie.
|
|
57
|
+
* seed: (registry) => registry.set(requestCookie, getRequestHeader("cookie"))
|
|
58
|
+
* })
|
|
49
59
|
* ```
|
|
50
60
|
*
|
|
51
61
|
* @stability unstable
|
|
@@ -54,6 +64,7 @@ export interface RouterAtomsOptions<TRouter extends AnyRouter> {
|
|
|
54
64
|
*/
|
|
55
65
|
export declare const makeRegistry: (options?: {
|
|
56
66
|
readonly defaultIdleTTL?: number | undefined;
|
|
67
|
+
readonly seed?: Seed | undefined;
|
|
57
68
|
}) => AtomRegistry.AtomRegistry;
|
|
58
69
|
/**
|
|
59
70
|
* Provides a registry to a TanStack router's app and streams it from the
|
package/dist/TanStackRouter.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dehydrate, hydrate } from "./Hydration.mjs";
|
|
2
|
-
import { isRequestRegistry } from "./RequestRegistry.mjs";
|
|
2
|
+
import { isRequestRegistry, settled } from "./RequestRegistry.mjs";
|
|
3
3
|
import { make } from "./internal/registry.mjs";
|
|
4
4
|
import * as Predicate from "effect/Predicate";
|
|
5
5
|
import { RegistryContext } from "@effect/atom-react";
|
|
@@ -51,14 +51,23 @@ import { jsx } from "react/jsx-runtime";
|
|
|
51
51
|
* default registry does. An atom's own `Atom.setIdleTTL` or
|
|
52
52
|
* `Atom.keepAlive` wins. The server's request registry lives for one
|
|
53
53
|
* request, so it has none.
|
|
54
|
+
* - `seed` runs on the server's request registry before the loaders start
|
|
55
|
+
* atoms in it, as `RequestRegistry.make`'s does: set what the request's
|
|
56
|
+
* atoms need, such as its cookie. It can be async. The browser's registry
|
|
57
|
+
* isn't seeded.
|
|
54
58
|
*
|
|
55
59
|
* **Example**
|
|
56
60
|
*
|
|
57
61
|
* ```ts
|
|
62
|
+
* import { getRequestHeader } from "@tanstack/react-start/server"
|
|
58
63
|
* import { makeRegistry } from "raf-effect-atom/tanstack-router"
|
|
59
64
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
65
|
+
* const registry = makeRegistry({
|
|
66
|
+
* // Keeps an issue the user navigated away from for a minute.
|
|
67
|
+
* defaultIdleTTL: 60_000,
|
|
68
|
+
* // In-process RPC calls send the page request's cookie.
|
|
69
|
+
* seed: (registry) => registry.set(requestCookie, getRequestHeader("cookie"))
|
|
70
|
+
* })
|
|
62
71
|
* ```
|
|
63
72
|
*
|
|
64
73
|
* @stability unstable
|
|
@@ -116,6 +125,7 @@ const setupRouterAtoms = (options) => {
|
|
|
116
125
|
const dehydrate$1 = router.options.dehydrate;
|
|
117
126
|
router.options.dehydrate = async () => {
|
|
118
127
|
const dehydrated = await dehydrate$1?.();
|
|
128
|
+
await settled(registry);
|
|
119
129
|
if (disposed) return dehydrated;
|
|
120
130
|
const atoms = { "raf-effect-atom": dehydrate(registry) };
|
|
121
131
|
return Predicate.isObject(dehydrated) ? {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ArgsOf, ErrorOf, Handle, HandleKey, LoadedHandle, Source, SuccessOf } from "./Handle.mjs";
|
|
2
|
-
import { HydrationBoundary, HydrationBoundaryProps } from "./ReactHydration.mjs";
|
|
3
2
|
import { Loader_d_exports } from "./Loader.mjs";
|
|
4
|
-
import { Hydration_d_exports } from "./Hydration.mjs";
|
|
5
3
|
import { RequestRegistry_d_exports } from "./RequestRegistry.mjs";
|
|
4
|
+
import { HydrationBoundary, HydrationBoundaryProps } from "./ReactHydration.mjs";
|
|
5
|
+
import { Hydration_d_exports } from "./Hydration.mjs";
|
|
6
6
|
import { useAtomResult } from "./Hooks.mjs";
|
|
7
7
|
import { WaterfallDetection, WaterfallMode, useWaterfall } from "./Waterfalls.mjs";
|
|
8
8
|
export { ArgsOf, ErrorOf, Handle, HandleKey, Hydration_d_exports as Hydration, HydrationBoundary, HydrationBoundaryProps, LoadedHandle, Loader_d_exports as Loader, RequestRegistry_d_exports as RequestRegistry, Source, SuccessOf, WaterfallDetection, WaterfallMode, useAtomResult, useWaterfall };
|
package/dist/internal/next.mjs
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import { dehydrate
|
|
2
|
-
import { make } from "../RequestRegistry.mjs";
|
|
3
|
-
import { make as make$
|
|
1
|
+
import { dehydrate } from "../Hydration.mjs";
|
|
2
|
+
import { make as make$1, settled } from "../RequestRegistry.mjs";
|
|
3
|
+
import { make as make$2 } from "../Loader.mjs";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
// src/internal/next.ts
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
6
|
+
const make = (options) => {
|
|
7
|
+
const getRegistry = React.cache(() => {
|
|
8
|
+
const registry = make$1(options);
|
|
9
|
+
React.cacheSignal()?.addEventListener("abort", () => registry.dispose(), { once: true });
|
|
10
|
+
return registry;
|
|
11
|
+
});
|
|
12
|
+
const getLoader = React.cache(() => make$2(getRegistry()));
|
|
13
|
+
return {
|
|
14
|
+
dehydrate: async () => {
|
|
15
|
+
const registry = getRegistry();
|
|
16
|
+
await settled(registry);
|
|
17
|
+
return dehydrate(registry);
|
|
18
|
+
},
|
|
19
|
+
ensure: (source, ...args) => getLoader().ensure(source, ...args),
|
|
20
|
+
fetch: (source, ...args) => getLoader().fetch(source, ...args),
|
|
21
|
+
getRegistry,
|
|
22
|
+
prefetch: (source, ...args) => getLoader().prefetch(source, ...args)
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export { make };
|
|
@@ -9,7 +9,7 @@ const defaultIdleTTL = 400;
|
|
|
9
9
|
* A request registry on the server, and in the browser a registry like
|
|
10
10
|
* `@effect/atom-react`'s `RegistryProvider` makes.
|
|
11
11
|
*/
|
|
12
|
-
const make = (options) => isServer() ? make$1() : AtomRegistry.make({
|
|
12
|
+
const make = (options) => isServer() ? make$1({ seed: options?.seed }) : AtomRegistry.make({
|
|
13
13
|
defaultIdleTTL: options?.defaultIdleTTL ?? defaultIdleTTL,
|
|
14
14
|
scheduleTask
|
|
15
15
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
// src/internal/seeds.ts
|
|
3
|
+
const seedings = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
const waiting = (registry) => {
|
|
5
|
+
const seeding = seedings.get(registry);
|
|
6
|
+
return seeding === void 0 || seeding.resolved ? void 0 : seeding;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Runs `seed` on `registry`. Until a seed's promise settles, the verbs wait
|
|
10
|
+
* for it.
|
|
11
|
+
*/
|
|
12
|
+
const run = (registry, seed) => {
|
|
13
|
+
const result = seed(registry);
|
|
14
|
+
if (!Predicate.isPromiseLike(result)) return;
|
|
15
|
+
const seeded = Promise.resolve(result);
|
|
16
|
+
const seeding = {
|
|
17
|
+
deferred: [],
|
|
18
|
+
resolved: false,
|
|
19
|
+
seeded
|
|
20
|
+
};
|
|
21
|
+
seeded.then(() => {
|
|
22
|
+
seeding.resolved = true;
|
|
23
|
+
}, () => void 0);
|
|
24
|
+
seedings.set(registry, seeding);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The seed the verbs wait for: `undefined` once it has resolved, and the
|
|
28
|
+
* rejected promise once it has failed.
|
|
29
|
+
*/
|
|
30
|
+
const pending = (registry) => waiting(registry)?.seeded;
|
|
31
|
+
/**
|
|
32
|
+
* Runs `start` once the registry's seed has resolved: at once, unless it's
|
|
33
|
+
* still running. A seed that fails never runs it.
|
|
34
|
+
*/
|
|
35
|
+
const afterSeed = (registry, start) => {
|
|
36
|
+
const seeding = waiting(registry);
|
|
37
|
+
if (seeding === void 0) {
|
|
38
|
+
start();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const started = seeding.seeded.then(start);
|
|
42
|
+
started.catch(() => void 0);
|
|
43
|
+
seeding.deferred.push(started);
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Waits for the registry's seed and the starts that waited for it, so
|
|
47
|
+
* `Hydration.dehydrate` sees every atom the verbs started. Rejects with the
|
|
48
|
+
* seed's error, or with a start's defect.
|
|
49
|
+
*/
|
|
50
|
+
const settled = async (registry) => {
|
|
51
|
+
const seeding = seedings.get(registry);
|
|
52
|
+
if (seeding === void 0) return;
|
|
53
|
+
await seeding.seeded;
|
|
54
|
+
await Promise.all(seeding.deferred);
|
|
55
|
+
};
|
|
56
|
+
export { afterSeed, pending, run, settled };
|
package/package.json
CHANGED