raf-effect-atom 0.2.3 → 0.2.5

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,16 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.5](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.4...raf-effect-atom%400.2.5) (2026-10-23)
4
+
5
+ ### Features
6
+
7
+ * **raf-effect-atom:** start a derived atom's source from the loader verbs, following initialValueTarget ([00a9760](https://github.com/KATT/raf-effect-atom/commit/00a97602068cabf085fa9cfe41e88bf853b6af32))
8
+
9
+ ## [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)
10
+
11
+ ### Features
12
+
13
+ - **raf-effect-atom:** start a request registry with initialValues, readable atoms included ([67cf767](https://github.com/KATT/raf-effect-atom/commit/67cf767e121e76b385ff0808a087efc6662aed89))
14
+
15
+ ### Documentation
16
+
17
+ - correct the generated changelogs' cut-off breaking change and bogus issue links ([c21f64c](https://github.com/KATT/raf-effect-atom/commit/c21f64cad3b4638f2341968aa2e10af5a19904c1))
18
+
3
19
  ## [0.2.3](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.2...raf-effect-atom%400.2.3) (2026-10-23)
4
20
 
5
21
  ### Features
6
22
 
7
- * **raf-effect-atom:** run Effect code on a loader's registry with Loader.Loader.run ([d26307d](https://github.com/KATT/raf-effect-atom/commit/d26307dc6279106ebd9d26078c88e4ad10a8cbc9))
23
+ - **raf-effect-atom:** run Effect code on a loader's registry with Loader.Loader.run ([d26307d](https://github.com/KATT/raf-effect-atom/commit/d26307dc6279106ebd9d26078c88e4ad10a8cbc9))
8
24
 
9
25
  ## [0.2.2](https://github.com/KATT/raf-effect-atom/compare/raf-effect-atom%400.2.1...raf-effect-atom%400.2.2) (2026-10-23)
10
26
 
11
27
  ### Features
12
28
 
13
- - **raf-effect-atom:** stop a loader's wait when its request is aborted ([b1f2f16](https://github.com/KATT/raf-effect-atom/commit/b1f2f16fcce9cd5dd1ba18fa653b40c805eebd93)), references [#41](https://github.com/KATT/raf-effect-atom/issues/41) [#40](https://github.com/KATT/raf-effect-atom/issues/40) [#40](https://github.com/KATT/raf-effect-atom/issues/40) [#41](https://github.com/KATT/raf-effect-atom/issues/41)
29
+ - **raf-effect-atom:** stop a loader's wait when its request is aborted ([b1f2f16](https://github.com/KATT/raf-effect-atom/commit/b1f2f16fcce9cd5dd1ba18fa653b40c805eebd93))
14
30
 
15
31
  ### Bug Fixes
16
32
 
@@ -25,13 +41,13 @@
25
41
 
26
42
  ### Features
27
43
 
28
- - **raf-effect-atom:** run React Router loaders in the browser after the first page ([bee00a9](https://github.com/KATT/raf-effect-atom/commit/bee00a981fd24fcdc9d746053dcd73dbc7ebc853)), references [#41](https://github.com/KATT/raf-effect-atom/issues/41) [#41](https://github.com/KATT/raf-effect-atom/issues/41)
44
+ - **raf-effect-atom:** run React Router loaders in the browser after the first page ([bee00a9](https://github.com/KATT/raf-effect-atom/commit/bee00a981fd24fcdc9d746053dcd73dbc7ebc853))
29
45
 
30
46
  ## [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)
31
47
 
32
48
  ### ⚠ BREAKING CHANGES
33
49
 
34
- - **raf-effect-atom:** `dehydrate()` from `raf-effect-atom/next` returns a
50
+ - **raf-effect-atom:** `dehydrate()` from `raf-effect-atom/next` returns a promise: write `<HydrateAtoms state={await dehydrate()}>`.
35
51
 
36
52
  ### Features
37
53
 
@@ -123,7 +139,7 @@
123
139
 
124
140
  ### Bug Fixes
125
141
 
126
- - **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)
142
+ - **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))
127
143
  - **raf-effect-atom:** point waterfall reports at the URL before the escape hatches ([0d127e3](https://github.com/KATT/raf-effect-atom/commit/0d127e38a10cb3a44340c5540c75b53491faceee))
128
144
  - **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))
129
145
  - **raf-effect-atom:** wait for results streamed in a server component payload ([00cb981](https://github.com/KATT/raf-effect-atom/commit/00cb98125aad2cda70525fbfdedcd2b5a8f7fc79))
package/dist/Hooks.mjs CHANGED
@@ -2,10 +2,9 @@
2
2
  import { stream } from "./internal/pending.mjs";
3
3
  import { hydrated } from "./Hydration.mjs";
4
4
  import { keyOf, release } from "./internal/leases.mjs";
5
- import { atomOf } from "./internal/handles.mjs";
5
+ import { atomOf, sourceOf } from "./internal/handles.mjs";
6
6
  import { requireRequestRegistry } from "./internal/registry.mjs";
7
7
  import { ModeContext, check } from "./internal/waterfalls.mjs";
8
- import * as Atom from "effect/reactivity/Atom";
9
8
  import * as Predicate from "effect/Predicate";
10
9
  import * as Cause from "effect/Cause";
11
10
  import * as Option from "effect/Option";
@@ -20,15 +19,6 @@ import * as React from "react";
20
19
  * @since 0.1.0
21
20
  */
22
21
  /**
23
- * The atom the loader started and the server sent: the first serializable
24
- * atom in `atom`'s `initialValueTarget` chain, starting with `atom`. An
25
- * AtomRpc query is serializable itself, though it points at an inner atom.
26
- */
27
- const sourceOf = (atom) => {
28
- for (let target = atom; target !== void 0; target = target.initialValueTarget) if (Atom.isSerializable(target)) return target;
29
- return atom;
30
- };
31
- /**
32
22
  * Reads a handle through React Suspense, through the source that made it: a
33
23
  * result streamed from the server, a hydrated one, or one a loader started.
34
24
  *
package/dist/Loader.d.mts CHANGED
@@ -40,10 +40,24 @@ export declare const LeaseTTL: Context.Reference<Duration.Duration>;
40
40
  *
41
41
  * **Details**
42
42
  *
43
- * Starts the atom if it isn't in the registry, and leaves it alone if it is.
44
- * Takes a lease either way. The component that reads the handle suspends
45
- * until the result settles. On a request registry whose async seed is still
46
- * running, it waits for the seed first.
43
+ * - Starts the atom if it isn't in the registry, and leaves it alone if it
44
+ * is. Takes a lease either way. The component that reads the handle
45
+ * suspends until the result settles. On a request registry whose async
46
+ * seed is still running, it waits for the seed first.
47
+ * - A derived atom that isn't serializable, such as an error boundary's
48
+ * `wrap(source)`, starts the first serializable atom its
49
+ * `initialValueTarget` chain points at, the one `useAtomResult` reads it
50
+ * through: the lease, the handle's key and what the server sends are
51
+ * that atom's. `ensure` and `fetch` wait for the derived atom's own
52
+ * result.
53
+ *
54
+ * **Gotchas**
55
+ *
56
+ * The server sends a result still loading as a promise only when the atom
57
+ * it sends holds an `AsyncResult`. A derived atom whose serializable atom
58
+ * holds another value, such as an `Option` snapshot, crosses with the value
59
+ * it has when the registry is dehydrated: point it at an `AsyncResult`
60
+ * atom, or `ensure` it.
47
61
  *
48
62
  * **Example**
49
63
  *
package/dist/Loader.mjs CHANGED
@@ -2,7 +2,7 @@ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
2
  import { hydrated } from "./Hydration.mjs";
3
3
  import { isDevelopment } from "./internal/development.mjs";
4
4
  import { keyOf, take } from "./internal/leases.mjs";
5
- import { atomOf, loaded as loaded$1, make as make$1 } from "./internal/handles.mjs";
5
+ import { atomOf, loaded as loaded$1, make as make$1, sourceOf } from "./internal/handles.mjs";
6
6
  import { afterSeed, pending } from "./internal/seeds.mjs";
7
7
  import { isRequestRegistry } from "./RequestRegistry.mjs";
8
8
  import * as Atom from "effect/reactivity/Atom";
@@ -70,14 +70,16 @@ const start = Effect.fnUntraced(function* (atom) {
70
70
  const seed = pending(registry);
71
71
  if (seed !== void 0) yield* Effect.promise(() => seed);
72
72
  const request = isRequestRegistry(registry);
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.`));
73
+ const source = sourceOf(atom);
74
+ if (request && !Atom.isSerializable(source) && (yield* isDevelopment)) return yield* Effect.die(/* @__PURE__ */ new Error(`raf-effect-atom: a loader started ${String(atom)}, which isn't serializable and doesn't point at a serializable atom with initialValueTarget, so the browser would request it again. Give it a serialization key, such as AtomRpc's serializationKey option.`));
74
75
  const ttl = request ? void 0 : Duration.toMillis(yield* LeaseTTL);
75
- const target = hydrated(atom);
76
+ const target = hydrated(source);
76
77
  const key = keyOf(target);
77
78
  const existed = registry.getNodes().has(key) || Predicate.isString(key) && registry.preloadedSerializable.has(key);
78
79
  take(registry, target, ttl);
79
80
  return {
80
81
  existed,
82
+ read: source === atom ? hydrated(atom) : atom,
81
83
  registry,
82
84
  request,
83
85
  target
@@ -115,10 +117,24 @@ const rejectDefects = (result) => {
115
117
  *
116
118
  * **Details**
117
119
  *
118
- * Starts the atom if it isn't in the registry, and leaves it alone if it is.
119
- * Takes a lease either way. The component that reads the handle suspends
120
- * until the result settles. On a request registry whose async seed is still
121
- * running, it waits for the seed first.
120
+ * - Starts the atom if it isn't in the registry, and leaves it alone if it
121
+ * is. Takes a lease either way. The component that reads the handle
122
+ * suspends until the result settles. On a request registry whose async
123
+ * seed is still running, it waits for the seed first.
124
+ * - A derived atom that isn't serializable, such as an error boundary's
125
+ * `wrap(source)`, starts the first serializable atom its
126
+ * `initialValueTarget` chain points at, the one `useAtomResult` reads it
127
+ * through: the lease, the handle's key and what the server sends are
128
+ * that atom's. `ensure` and `fetch` wait for the derived atom's own
129
+ * result.
130
+ *
131
+ * **Gotchas**
132
+ *
133
+ * The server sends a result still loading as a promise only when the atom
134
+ * it sends holds an `AsyncResult`. A derived atom whose serializable atom
135
+ * holds another value, such as an `Option` snapshot, crosses with the value
136
+ * it has when the registry is dehydrated: point it at an `AsyncResult`
137
+ * atom, or `ensure` it.
122
138
  *
123
139
  * **Example**
124
140
  *
@@ -181,7 +197,7 @@ const loaded = (args, atom, result) => {
181
197
  const ensure = (source, ...args) => Effect.gen(function* () {
182
198
  const atom = atomOf(source, args);
183
199
  const started = yield* start(atom);
184
- const result = yield* settled(started.registry, started.target, started.request ? isSettled : AsyncResult.isNotInitial);
200
+ const result = yield* settled(started.registry, started.read, started.request ? isSettled : AsyncResult.isNotInitial);
185
201
  return loaded(args, atom, result);
186
202
  });
187
203
  /**
@@ -207,12 +223,12 @@ const ensure = (source, ...args) => Effect.gen(function* () {
207
223
  const fetch = (source, ...args) => Effect.gen(function* () {
208
224
  const atom = atomOf(source, args);
209
225
  const started = yield* start(atom);
210
- const { registry, target } = started;
211
- if (started.existed && isSettled(registry.get(target))) {
226
+ const { read, registry, target } = started;
227
+ if (started.existed && isSettled(registry.get(read))) {
212
228
  const node = registry.getNodes().get(keyOf(target));
213
- registry.refresh(node?.atom ?? target);
229
+ registry.refresh(read === target ? node?.atom ?? target : read);
214
230
  }
215
- const result = yield* settled(registry, target, isSettled);
231
+ const result = yield* settled(registry, read, isSettled);
216
232
  return loaded(args, atom, result);
217
233
  });
218
234
  const results = /* @__PURE__ */ new WeakMap();
@@ -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
@@ -72,12 +72,9 @@ export declare const WaterfallDetection: (props: {
72
72
  * - `useAtomResult` doesn't report an atom started this way.
73
73
  * - The handle is a prefetched one: its reader suspends until the result
74
74
  * settles, as for a handle from a loader's `prefetch`.
75
- *
76
- * **Gotchas**
77
- *
78
- * Pass it the source a loader would start, not an error boundary's
79
- * `wrap(source)`: the wrapper's atoms aren't serializable, so the server
80
- * couldn't send them to the browser. Read the handle through the wrapper.
75
+ * - A derived source, such as an error boundary's `wrap(source)`, starts
76
+ * the serializable atom its `initialValueTarget` chain points at, as
77
+ * `Loader.prefetch` does.
81
78
  *
82
79
  * **Example**
83
80
  *
@@ -2,7 +2,7 @@
2
2
  import { get } from "./internal/pending.mjs";
3
3
  import { hydrated } from "./Hydration.mjs";
4
4
  import { keyOf } from "./internal/leases.mjs";
5
- import { atomOf, make } from "./internal/handles.mjs";
5
+ import { atomOf, make, sourceOf } from "./internal/handles.mjs";
6
6
  import { prefetch } from "./Loader.mjs";
7
7
  import { ModeContext, isStarted } from "./internal/waterfalls.mjs";
8
8
  import * as Predicate from "effect/Predicate";
@@ -76,12 +76,9 @@ const WaterfallDetection = (props) => process.env.NODE_ENV === "production" ? pr
76
76
  * - `useAtomResult` doesn't report an atom started this way.
77
77
  * - The handle is a prefetched one: its reader suspends until the result
78
78
  * settles, as for a handle from a loader's `prefetch`.
79
- *
80
- * **Gotchas**
81
- *
82
- * Pass it the source a loader would start, not an error boundary's
83
- * `wrap(source)`: the wrapper's atoms aren't serializable, so the server
84
- * couldn't send them to the browser. Read the handle through the wrapper.
79
+ * - A derived source, such as an error boundary's `wrap(source)`, starts
80
+ * the serializable atom its `initialValueTarget` chain points at, as
81
+ * `Loader.prefetch` does.
85
82
  *
86
83
  * **Example**
87
84
  *
@@ -103,7 +100,7 @@ const WaterfallDetection = (props) => process.env.NODE_ENV === "production" ? pr
103
100
  const useWaterfall = (source, ...args) => {
104
101
  const registry = React.useContext(RegistryContext);
105
102
  const atom = atomOf(source, args);
106
- const target = hydrated(atom);
103
+ const target = hydrated(sourceOf(atom));
107
104
  const key = keyOf(target);
108
105
  if (!(Predicate.isString(key) && get(registry, key) !== void 0) && !isStarted(registry, target)) return Effect.runSync(Effect.provideService(prefetch(source, ...args), AtomRegistry.AtomRegistry, registry));
109
106
  return make(args, atom);
@@ -4,9 +4,18 @@ import * as Predicate from "effect/Predicate";
4
4
  // src/internal/handles.ts
5
5
  /** The atom a source makes for its arguments. */
6
6
  const atomOf = (source, args) => Atom.isAtom(source) ? source : source(...args);
7
- /** The registry key a handle records: the atom's serialization key. */
7
+ /**
8
+ * The atom the loader starts and the server sends for `atom`: the first
9
+ * serializable atom in its `initialValueTarget` chain, starting with `atom`.
10
+ * An AtomRpc query is serializable itself, though it points at an inner atom.
11
+ */
12
+ const sourceOf = (atom) => {
13
+ for (let target = atom; target !== void 0; target = target.initialValueTarget) if (Atom.isSerializable(target)) return target;
14
+ return atom;
15
+ };
16
+ /** The registry key a handle records: its source atom's serialization key. */
8
17
  const keyOf = (atom) => {
9
- const key = keyOf$1(atom);
18
+ const key = keyOf$1(sourceOf(atom));
10
19
  if (!Predicate.isString(key)) return;
11
20
  return key;
12
21
  };
@@ -19,4 +28,4 @@ const loaded = (args, atom) => ({
19
28
  key: keyOf(atom),
20
29
  loaded: true
21
30
  });
22
- export { atomOf, loaded, make };
31
+ export { atomOf, loaded, make, sourceOf };
@@ -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.3",
3
+ "version": "0.2.5",
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",