raf-effect-atom 0.2.2 → 0.2.4

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,31 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.4](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.3...raf-effect-atom%400.2.4) (2026-10-23)
4
+
5
+ ### Features
6
+
7
+ * **raf-effect-atom:** start a request registry with initialValues, readable atoms included ([67cf767](https://github.com/KATT/raf-effect-atom/commit/67cf767e121e76b385ff0808a087efc6662aed89))
8
+
9
+ ### Documentation
10
+
11
+ * correct the generated changelogs' cut-off breaking change and bogus issue links ([c21f64c](https://github.com/KATT/raf-effect-atom/commit/c21f64cad3b4638f2341968aa2e10af5a19904c1))
12
+
13
+ ## [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)
14
+
15
+ ### Features
16
+
17
+ - **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))
18
+
3
19
  ## [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)
4
20
 
5
21
  ### Features
6
22
 
7
- * **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)
23
+ - **raf-effect-atom:** stop a loader's wait when its request is aborted ([b1f2f16](https://github.com/KATT/raf-effect-atom/commit/b1f2f16fcce9cd5dd1ba18fa653b40c805eebd93))
8
24
 
9
25
  ### Bug Fixes
10
26
 
11
- * **raf-effect-atom:** refuse to hydrate a shared registry during server rendering ([2f793a6](https://github.com/KATT/raf-effect-atom/commit/2f793a63627482bdf2f0e2891829f5191077cfc7))
27
+ - **raf-effect-atom:** refuse to hydrate a shared registry during server rendering ([2f793a6](https://github.com/KATT/raf-effect-atom/commit/2f793a63627482bdf2f0e2891829f5191077cfc7))
12
28
 
13
29
  ### Documentation
14
30
 
15
- * **raf-effect-atom:** date clientAtomsMiddleware to 0.2.1, the release it shipped in ([c2a4288](https://github.com/KATT/raf-effect-atom/commit/c2a4288f4c5b63575e019712edc9fdc49a052bd2))
16
- * record that the packages' module state is keyed by what one request owns ([22b23f9](https://github.com/KATT/raf-effect-atom/commit/22b23f947544f8c4459ef172e8b21499403059c4))
31
+ - **raf-effect-atom:** date clientAtomsMiddleware to 0.2.1, the release it shipped in ([c2a4288](https://github.com/KATT/raf-effect-atom/commit/c2a4288f4c5b63575e019712edc9fdc49a052bd2))
32
+ - record that the packages' module state is keyed by what one request owns ([22b23f9](https://github.com/KATT/raf-effect-atom/commit/22b23f947544f8c4459ef172e8b21499403059c4))
17
33
 
18
34
  ## [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)
19
35
 
20
36
  ### Features
21
37
 
22
- - **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)
38
+ - **raf-effect-atom:** run React Router loaders in the browser after the first page ([bee00a9](https://github.com/KATT/raf-effect-atom/commit/bee00a981fd24fcdc9d746053dcd73dbc7ebc853))
23
39
 
24
40
  ## [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)
25
41
 
26
42
  ### ⚠ BREAKING CHANGES
27
43
 
28
- - **raf-effect-atom:** `dehydrate()` from `raf-effect-atom/next` returns a
44
+ - **raf-effect-atom:** `dehydrate()` from `raf-effect-atom/next` returns a promise: write `<HydrateAtoms state={await dehydrate()}>`.
29
45
 
30
46
  ### Features
31
47
 
@@ -117,7 +133,7 @@
117
133
 
118
134
  ### Bug Fixes
119
135
 
120
- - **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)
136
+ - **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))
121
137
  - **raf-effect-atom:** point waterfall reports at the URL before the escape hatches ([0d127e3](https://github.com/KATT/raf-effect-atom/commit/0d127e38a10cb3a44340c5540c75b53491faceee))
122
138
  - **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))
123
139
  - **raf-effect-atom:** wait for results streamed in a server component payload ([00cb981](https://github.com/KATT/raf-effect-atom/commit/00cb98125aad2cda70525fbfdedcd2b5a8f7fc79))
package/dist/Loader.d.mts CHANGED
@@ -165,21 +165,30 @@ 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>;
168
177
  }
169
178
  /**
170
179
  * Options for `make`.
171
180
  *
172
181
  * @stability unstable
173
182
  * @category models
174
- * @since 0.3.0
183
+ * @since 0.2.2
175
184
  */
176
185
  export interface MakeOptions {
177
186
  /** How long a lease lasts in the browser. See {@link LeaseTTL}. */
178
187
  readonly leaseTTL?: Duration.Input | undefined;
179
188
  /**
180
189
  * The request's or the navigation's abort signal. Aborting it interrupts
181
- * the `ensure` and `fetch` calls still waiting, which reject with its
182
- * reason. The atoms they started keep loading.
190
+ * the `ensure`, `fetch` and `run` calls still waiting, which reject with
191
+ * its reason. The atoms they started keep loading.
183
192
  */
184
193
  readonly signal?: AbortSignal | undefined;
185
194
  }
@@ -191,14 +200,14 @@ export interface MakeOptions {
191
200
  * Use in a framework integration, in loaders that are async functions, or
192
201
  * in an event handler that starts an atom before the state change that
193
202
  * renders its reader, for state that can't be in the URL, such as a
194
- * search-as-you-type query. In Effect code, use the verbs directly with the
195
- * `AtomRegistry` provided.
203
+ * search-as-you-type query. In Effect code, use the verbs directly, and run
204
+ * the Effect on the registry with `run`.
196
205
  *
197
206
  * **Details**
198
207
  *
199
- * With a `signal`, an aborted request or navigation stops waiting: `ensure`
200
- * and `fetch` reject with the signal's reason, and the atoms they started
201
- * keep loading. The React Router and Next.js integrations pass the
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
202
211
  * request's.
203
212
  *
204
213
  * **Example**
package/dist/Loader.mjs CHANGED
@@ -253,6 +253,7 @@ const result = (handle) => {
253
253
  const runPromise = (effect, signal) => Effect.runPromise(effect, { signal }).catch((error) => {
254
254
  throw signal?.aborted ? signal.reason : error;
255
255
  });
256
+ const either = (first, second) => first === void 0 || second === void 0 ? first ?? second : AbortSignal.any([first, second]);
256
257
  /**
257
258
  * Creates the Promise verbs over one registry.
258
259
  *
@@ -261,14 +262,14 @@ const runPromise = (effect, signal) => Effect.runPromise(effect, { signal }).cat
261
262
  * Use in a framework integration, in loaders that are async functions, or
262
263
  * in an event handler that starts an atom before the state change that
263
264
  * renders its reader, for state that can't be in the URL, such as a
264
- * search-as-you-type query. In Effect code, use the verbs directly with the
265
- * `AtomRegistry` provided.
265
+ * search-as-you-type query. In Effect code, use the verbs directly, and run
266
+ * the Effect on the registry with `run`.
266
267
  *
267
268
  * **Details**
268
269
  *
269
- * With a `signal`, an aborted request or navigation stops waiting: `ensure`
270
- * and `fetch` reject with the signal's reason, and the atoms they started
271
- * keep loading. The React Router and Next.js integrations pass the
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
272
273
  * request's.
273
274
  *
274
275
  * **Example**
@@ -301,7 +302,8 @@ const make = (registry, options) => {
301
302
  Effect.runSync(provide(start(atom)));
302
303
  });
303
304
  return make$1(args, atom);
304
- }
305
+ },
306
+ run: (effect, runOptions) => runPromise(provide(effect), either(signal, runOptions?.signal))
305
307
  };
306
308
  };
307
309
  export { LeaseTTL, Loader_exports, ensure, fetch, make, prefetch, result };
package/dist/Next.d.mts CHANGED
@@ -108,6 +108,26 @@ export declare const ensure: <S extends Source>(source: S, ...args: ArgsOf<S>) =
108
108
  * @since 0.1.0
109
109
  */
110
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"];
111
131
  /**
112
132
  * `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`.
113
133
  *
package/dist/Next.mjs CHANGED
@@ -90,6 +90,26 @@ const ensure = atoms.ensure;
90
90
  */
91
91
  const fetch = atoms.fetch;
92
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
+ /**
93
113
  * `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`.
94
114
  *
95
115
  * **Gotchas**
@@ -110,4 +130,4 @@ const fetch = atoms.fetch;
110
130
  * @since 0.1.0
111
131
  */
112
132
  const dehydrate = atoms.dehydrate;
113
- export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, makeRequestAtoms, prefetch };
133
+ export { HydrateAtoms, RegistryProvider, dehydrate, ensure, fetch, getRegistry, makeRequestAtoms, prefetch, run };
@@ -1,8 +1,9 @@
1
1
  import { Seed as Seed$1 } from "./internal/seeds.mjs";
2
+ import * as Atom from "effect/reactivity/Atom";
2
3
  import * as AtomRegistry from "effect/reactivity/AtomRegistry";
3
4
  import * as Layer from "effect/Layer";
4
5
  declare namespace RequestRegistry_d_exports {
5
- export { Options, Seed, isRequestRegistry, layer, make, settled };
6
+ export { InitialValues, Options, Seed, isRequestRegistry, layer, make, settled };
6
7
  }
7
8
  /**
8
9
  * Prepares a request registry before the loader verbs start atoms in it,
@@ -13,6 +14,16 @@ declare namespace RequestRegistry_d_exports {
13
14
  * @since 0.2.0
14
15
  */
15
16
  export type Seed = Seed$1;
17
+ /**
18
+ * Atoms paired with the values a registry starts with, as
19
+ * `AtomRegistry.make`'s `initialValues` takes them: build each pair with
20
+ * `Atom.initialValue`.
21
+ *
22
+ * @stability unstable
23
+ * @category models
24
+ * @since 0.2.4
25
+ */
26
+ export type InitialValues = Iterable<readonly [Atom.Atom<any>, any, Atom.InitialValueOptions?]>;
16
27
  /**
17
28
  * Options for `make`.
18
29
  *
@@ -21,6 +32,14 @@ export type Seed = Seed$1;
21
32
  * @since 0.2.0
22
33
  */
23
34
  export interface Options {
35
+ /**
36
+ * Values the registry starts with, as `AtomRegistry.make`'s
37
+ * `initialValues`. Unlike `registry.set` in a seed, they reach readable
38
+ * atoms, such as one that provides a per-request service. The registry
39
+ * keeps each of these atoms mounted until it's disposed, so the value
40
+ * outlives the atoms that read it.
41
+ */
42
+ readonly initialValues?: InitialValues | undefined;
24
43
  /**
25
44
  * Runs on the new registry before any verb starts an atom in it. When it
26
45
  * returns a promise, `ensure` and `fetch` wait for it, and `prefetch`
@@ -38,10 +57,15 @@ export interface Options {
38
57
  * in the background, and a loader's lease lasts until the registry is
39
58
  * disposed. In development, the loader verbs refuse an atom that isn't
40
59
  * serializable, because the browser would request it again.
60
+ * - `initialValues` are values the request's atoms start with, set on each
61
+ * atom, or on the end of its `initialValueTarget` chain, before anything
62
+ * reads it. They reach atoms `registry.set` can't, such as a readable atom
63
+ * that provides the request's `fetch`, and each stays mounted until the
64
+ * registry is disposed.
41
65
  * - `seed` sets what the request's atoms need before they run, such as the
42
66
  * page request's cookie for an RPC protocol that forwards it. It can be
43
67
  * async, for request data only a promise gives, such as Next.js's
44
- * `headers()`.
68
+ * `headers()`. It runs after `initialValues` are set.
45
69
  *
46
70
  * **Gotchas**
47
71
  *
@@ -52,9 +76,11 @@ export interface Options {
52
76
  * **Example**
53
77
  *
54
78
  * ```ts
79
+ * import { Atom } from "effect/reactivity"
55
80
  * import { RequestRegistry } from "raf-effect-atom"
56
81
  *
57
82
  * const registry = RequestRegistry.make({
83
+ * initialValues: [Atom.initialValue(requestFetch, request.fetch)],
58
84
  * seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
59
85
  * })
60
86
  * ```
@@ -33,10 +33,15 @@ const requestRegistries = /* @__PURE__ */ new WeakSet();
33
33
  * in the background, and a loader's lease lasts until the registry is
34
34
  * disposed. In development, the loader verbs refuse an atom that isn't
35
35
  * serializable, because the browser would request it again.
36
+ * - `initialValues` are values the request's atoms start with, set on each
37
+ * atom, or on the end of its `initialValueTarget` chain, before anything
38
+ * reads it. They reach atoms `registry.set` can't, such as a readable atom
39
+ * that provides the request's `fetch`, and each stays mounted until the
40
+ * registry is disposed.
36
41
  * - `seed` sets what the request's atoms need before they run, such as the
37
42
  * page request's cookie for an RPC protocol that forwards it. It can be
38
43
  * async, for request data only a promise gives, such as Next.js's
39
- * `headers()`.
44
+ * `headers()`. It runs after `initialValues` are set.
40
45
  *
41
46
  * **Gotchas**
42
47
  *
@@ -47,9 +52,11 @@ const requestRegistries = /* @__PURE__ */ new WeakSet();
47
52
  * **Example**
48
53
  *
49
54
  * ```ts
55
+ * import { Atom } from "effect/reactivity"
50
56
  * import { RequestRegistry } from "raf-effect-atom"
51
57
  *
52
58
  * const registry = RequestRegistry.make({
59
+ * initialValues: [Atom.initialValue(requestFetch, request.fetch)],
53
60
  * seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
54
61
  * })
55
62
  * ```
@@ -59,8 +66,10 @@ const requestRegistries = /* @__PURE__ */ new WeakSet();
59
66
  * @since 0.1.0
60
67
  */
61
68
  const make = (options) => {
62
- const registry = AtomRegistry.make();
69
+ const initialValues = options?.initialValues === void 0 ? void 0 : Array.from(options.initialValues);
70
+ const registry = AtomRegistry.make({ initialValues });
63
71
  requestRegistries.add(registry);
72
+ if (initialValues !== void 0) for (const [atom] of initialValues) registry.mount(atom);
64
73
  if (options?.seed !== void 0) run(registry, options.seed);
65
74
  return registry;
66
75
  };
@@ -1,4 +1,4 @@
1
- import { Seed } from "./RequestRegistry.mjs";
1
+ import { InitialValues, Seed } from "./RequestRegistry.mjs";
2
2
  import * as EffectHydration from "effect/reactivity/Hydration";
3
3
  import * as AtomRegistry from "effect/reactivity/AtomRegistry";
4
4
  import { AnyRouter } from "@tanstack/react-router";
@@ -39,6 +39,12 @@ export interface RouterAtomsOptions<TRouter extends AnyRouter> {
39
39
  * default registry does. An atom's own `Atom.setIdleTTL` or
40
40
  * `Atom.keepAlive` wins. The server's request registry lives for one
41
41
  * request, so it has none.
42
+ * - `initialValues` are values both registries start with, as
43
+ * `AtomRegistry.make`'s `initialValues`, such as the session the server
44
+ * verified or a readable atom that provides the request's `fetch`. The
45
+ * server's request registry keeps those atoms mounted until it's disposed.
46
+ * The browser's registry drops an atom no component reads, value included,
47
+ * so make one whose value must outlive its readers `Atom.keepAlive`.
42
48
  * - `seed` runs on the server's request registry before the loaders start
43
49
  * atoms in it, as `RequestRegistry.make`'s does: set what the request's
44
50
  * atoms need, such as its cookie. It can be async. The browser's registry
@@ -64,6 +70,7 @@ export interface RouterAtomsOptions<TRouter extends AnyRouter> {
64
70
  */
65
71
  export declare const makeRegistry: (options?: {
66
72
  readonly defaultIdleTTL?: number | undefined;
73
+ readonly initialValues?: InitialValues | undefined;
67
74
  readonly seed?: Seed | undefined;
68
75
  }) => AtomRegistry.AtomRegistry;
69
76
  /**
@@ -51,6 +51,12 @@ 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
+ * - `initialValues` are values both registries start with, as
55
+ * `AtomRegistry.make`'s `initialValues`, such as the session the server
56
+ * verified or a readable atom that provides the request's `fetch`. The
57
+ * server's request registry keeps those atoms mounted until it's disposed.
58
+ * The browser's registry drops an atom no component reads, value included,
59
+ * so make one whose value must outlive its readers `Atom.keepAlive`.
54
60
  * - `seed` runs on the server's request registry before the loaders start
55
61
  * atoms in it, as `RequestRegistry.make`'s does: set what the request's
56
62
  * atoms need, such as its cookie. It can be async. The browser's registry
@@ -22,7 +22,8 @@ const make = (options) => {
22
22
  ensure: (source, ...args) => getLoader().ensure(source, ...args),
23
23
  fetch: (source, ...args) => getLoader().fetch(source, ...args),
24
24
  getRegistry,
25
- prefetch: (source, ...args) => getLoader().prefetch(source, ...args)
25
+ prefetch: (source, ...args) => getLoader().prefetch(source, ...args),
26
+ run: (effect, runOptions) => getLoader().run(effect, runOptions)
26
27
  };
27
28
  };
28
29
  export { make };
@@ -19,8 +19,12 @@ const defaultIdleTTL = 400;
19
19
  * A request registry on the server, and in the browser a registry like
20
20
  * `@effect/atom-react`'s `RegistryProvider` makes.
21
21
  */
22
- const make = (options) => isServer() ? make$1({ seed: options?.seed }) : AtomRegistry.make({
22
+ const make = (options) => isServer() ? make$1({
23
+ initialValues: options?.initialValues,
24
+ seed: options?.seed
25
+ }) : AtomRegistry.make({
23
26
  defaultIdleTTL: options?.defaultIdleTTL ?? defaultIdleTTL,
27
+ initialValues: options?.initialValues,
24
28
  scheduleTask
25
29
  });
26
30
  let mounted;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raf-effect-atom",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",