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 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
- * **raf-effect-atom:** `useAtomResult(atom, claims)` is gone. Read
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
- * **raf-effect-atom:** follow initialValueTarget in useAtomResult and throw defects
19
- * **raf-effect-atom:** the loader verbs take a source and its arguments and
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
- * **raf-effect-atom:** add the next subpath ([92cacd8](https://github.com/KATT/raf-effect-atom/commit/92cacd86cb6d6e1784d9a07f71bd9fc3ec5ca0da))
75
- * **raf-effect-atom:** add the react-router subpath ([09b65dc](https://github.com/KATT/raf-effect-atom/commit/09b65dcbc6cfde2ae1ba2bb89e580746e9512f8b))
76
- * **raf-effect-atom:** add the tanstack-router subpath ([0189c83](https://github.com/KATT/raf-effect-atom/commit/0189c834cd46fc05ed866859e7527b3bd2ac47df))
77
- * **raf-effect-atom:** drop the claims overload and the effect-atom-boundary peer ([48fe91e](https://github.com/KATT/raf-effect-atom/commit/48fe91eabf71a0eb36b49c907fabae7afa5abd90))
78
- * **raf-effect-atom:** follow initialValueTarget in useAtomResult and throw defects ([7f577ea](https://github.com/KATT/raf-effect-atom/commit/7f577eabbdd567313b542db61d532bb36a2183ca))
79
- * **raf-effect-atom:** loader verbs return handles that components read ([52a4323](https://github.com/KATT/raf-effect-atom/commit/52a4323bae1507e6b5f524262b7ed1609b1ed5d6))
80
- * **raf-effect-atom:** loader verbs, streaming hydration and the read hook ([6870b1a](https://github.com/KATT/raf-effect-atom/commit/6870b1a9c7742d2fa15ce1391c8fdac66307d89e))
81
- * **raf-effect-atom:** make the browser registry's idle TTL configurable ([1c55d0e](https://github.com/KATT/raf-effect-atom/commit/1c55d0e74f54bed7cef895434357355229cff512))
82
- * **raf-effect-atom:** read the loader's registry under HydrationBoundary during server rendering ([6751792](https://github.com/KATT/raf-effect-atom/commit/675179254563b85c61f9a96e646fa9c72f47cc83))
83
- * **raf-effect-atom:** report request waterfalls in development, with useWaterfall as the escape hatch ([4b6fe2a](https://github.com/KATT/raf-effect-atom/commit/4b6fe2aebb939ea0a7095a5121558aa0cbc5bac5))
84
- * **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
+ - **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
- * **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)
89
- * **raf-effect-atom:** point waterfall reports at the URL before the escape hatches ([0d127e3](https://github.com/KATT/raf-effect-atom/commit/0d127e38a10cb3a44340c5540c75b53491faceee))
90
- * **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))
91
- * **raf-effect-atom:** wait for results streamed in a server component payload ([00cb981](https://github.com/KATT/raf-effect-atom/commit/00cb98125aad2cda70525fbfdedcd2b5a8f7fc79))
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
- * **examples:** read boundary claims through wrap ([ae92f67](https://github.com/KATT/raf-effect-atom/commit/ae92f67722417542ee06a7299b327a77a0597814))
96
- * **raf-effect-atom:** rename files to kebab-case ([285bf4b](https://github.com/KATT/raf-effect-atom/commit/285bf4b4e5e3184ac162129f51c04d9c96921650))
97
- * **raf-effect-atom:** restore Effect's file names ([92dc1d8](https://github.com/KATT/raf-effect-atom/commit/92dc1d8e52212c28042153cbd7b1146870839831))
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
- * catch RpcClientError in an app boundary, and read fully claimed atoms as successes ([fe9b485](https://github.com/KATT/raf-effect-atom/commit/fe9b485fd19643a34f1709ebb5b66c90036eea47))
102
- * point coding agents at the rules from the READMEs ([e8d3047](https://github.com/KATT/raf-effect-atom/commit/e8d30477e840df83c0f47764bdf505bc61181f14))
103
- * **raf-effect-atom:** add the package README ([69c589e](https://github.com/KATT/raf-effect-atom/commit/69c589e020527ca087d7a54fb99544460ebc7774))
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
- * keep each package's Vitest module cache to itself ([be11618](https://github.com/KATT/raf-effect-atom/commit/be11618b41f9cecaba6968a356677a7dd3bfe734))
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
- * 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))
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
- /** {@link prefetch}, run at once. */
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) => Effect.runSync(provide(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 { dehydrate as dehydrate$1, ensure as ensure$1, fetch as fetch$1, getRegistry as getRegistry$1, prefetch as prefetch$1 } from "./internal/next.mjs";
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$1;
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$1;
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$1;
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$1;
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$1;
64
- export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, prefetch };
110
+ const dehydrate = atoms.dehydrate;
111
+ export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, makeRequestAtoms, prefetch };
@@ -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
  * )
@@ -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
  * )
@@ -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 };
@@ -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 registry = make();
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
- * request registry, `ensure` waits like `fetch` does, nothing revalidates in
13
- * the background, and a loader's lease lasts until the registry is disposed.
14
- * In development, the loader verbs refuse an atom that isn't serializable,
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.
@@ -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
- * request registry, `ensure` waits like `fetch` does, nothing revalidates in
31
- * the background, and a loader's lease lasts until the registry is disposed.
32
- * In development, the loader verbs refuse an atom that isn't serializable,
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
- * // Keeps an issue the user navigated away from for a minute.
48
- * const registry = makeRegistry({ defaultIdleTTL: 60_000 })
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
@@ -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
- * // Keeps an issue the user navigated away from for a minute.
61
- * const registry = makeRegistry({ defaultIdleTTL: 60_000 })
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 };
@@ -1,16 +1,25 @@
1
- import { dehydrate as dehydrate$1 } from "../Hydration.mjs";
2
- import { make } from "../RequestRegistry.mjs";
3
- import { make as make$1 } from "../Loader.mjs";
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 getRegistry = React.cache(() => {
7
- const registry = make();
8
- React.cacheSignal()?.addEventListener("abort", () => registry.dispose(), { once: true });
9
- return registry;
10
- });
11
- const getLoader = React.cache(() => make$1(getRegistry()));
12
- const prefetch = (source, ...args) => getLoader().prefetch(source, ...args);
13
- const ensure = (source, ...args) => getLoader().ensure(source, ...args);
14
- const fetch = (source, ...args) => getLoader().fetch(source, ...args);
15
- const dehydrate = () => dehydrate$1(getRegistry());
16
- export { dehydrate, ensure, fetch, getRegistry, prefetch };
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,3 @@
1
+ import * as AtomRegistry from "effect/reactivity/AtomRegistry";
2
+ /** Prepares a request registry before any loader verb starts an atom in it. */
3
+ export type Seed = (registry: AtomRegistry.AtomRegistry) => void | Promise<void>;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raf-effect-atom",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Render-as-you-fetch for Effect Atom: start atoms from route loaders and stream them into the browser, typed failures intact",
5
5
  "keywords": [
6
6
  "effect",