raf-effect-atom 0.0.0-stage → 0.1.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.
@@ -0,0 +1,134 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { split } from "./internal/pending.mjs";
3
+ import { remember } from "./internal/sources.mjs";
4
+ import * as Atom from "effect/reactivity/Atom";
5
+ import * as EffectHydration from "effect/reactivity/Hydration";
6
+ // src/Hydration.ts
7
+ /**
8
+ * Streams a server registry into the browser's, typed failures intact, and
9
+ * hydrates it without requesting anything again.
10
+ *
11
+ * `dehydrate` encodes the request registry's serializable atoms. Settled
12
+ * results cross as values; results still loading cross as promises that
13
+ * resolve when they settle, carried by the framework's own streaming.
14
+ * `hydrate` applies settled values at once and holds each streamed result
15
+ * until its promise resolves, so `useAtomResult` waits for it instead of
16
+ * starting the atom. `hydrated` keeps a hydrated value on the atom's first
17
+ * read instead of running its effect again.
18
+ *
19
+ * Typed failures survive because a serializable `AsyncResult` atom, such as
20
+ * an `AtomRpc` query with a `serializationKey`, encodes its `Cause` with the
21
+ * procedure's error Schemas: a server-side `IssueNotFound` decodes as an
22
+ * `IssueNotFound` in the browser.
23
+ *
24
+ * @stability unstable
25
+ * @since 0.1.0
26
+ */
27
+ var Hydration_exports = /* @__PURE__ */ __exportAll({
28
+ dehydrate: () => dehydrate,
29
+ hydrate: () => hydrate,
30
+ hydrated: () => hydrated
31
+ });
32
+ const copies = /* @__PURE__ */ new WeakMap();
33
+ /**
34
+ * Returns a serializable atom that keeps its hydrated value on its first
35
+ * read, instead of running its effect again.
36
+ *
37
+ * **When to use**
38
+ *
39
+ * The loader verbs and `useAtomResult` already read through it. Use it when
40
+ * reading a hydrated atom with `@effect/atom-react`'s own hooks.
41
+ *
42
+ * **Details**
43
+ *
44
+ * The copy shares the atom's serialization key, so both read the same
45
+ * registry node. It sets `skipInitialRun` on the atom's `Serializable`
46
+ * metadata, and is memoized per atom, so every call returns the same copy.
47
+ * An atom that isn't serializable, or already skips its initial run, is
48
+ * returned as it is.
49
+ *
50
+ * **Gotchas**
51
+ *
52
+ * The copy keeps the hydrated value only for the first read: a refresh, or a
53
+ * change to a dependency, runs the effect as usual. An `Initial` value never
54
+ * skips the effect, which is why a result the server was still loading
55
+ * crosses as a promise that `useAtomResult` waits for.
56
+ *
57
+ * **Example**
58
+ *
59
+ * ```ts
60
+ * import { useAtomValue } from "@effect/atom-react"
61
+ * import { Hydration } from "raf-effect-atom"
62
+ *
63
+ * const result = useAtomValue(Hydration.hydrated(issue(1)))
64
+ * ```
65
+ *
66
+ * @see {@link Atom.serializable} for the metadata it copies
67
+ *
68
+ * @stability unstable
69
+ * @category combinators
70
+ * @since 0.1.0
71
+ */
72
+ const hydrated = (atom) => {
73
+ if (!Atom.isSerializable(atom) || atom[Atom.SerializableTypeId].skipInitialRun === true) return atom;
74
+ const cached = copies.get(atom);
75
+ if (cached !== void 0) return cached;
76
+ const copy = Object.assign(Object.create(Object.getPrototypeOf(atom)), {
77
+ ...atom,
78
+ [Atom.SerializableTypeId]: {
79
+ ...atom[Atom.SerializableTypeId],
80
+ skipInitialRun: true
81
+ }
82
+ });
83
+ copies.set(atom, copy);
84
+ return copy;
85
+ };
86
+ /**
87
+ * Encodes a request registry's serializable atoms for the browser.
88
+ *
89
+ * **Details**
90
+ *
91
+ * `Hydration.dehydrate` with `encodeInitialAs: "promise"`: a settled result
92
+ * crosses as its encoded value, and a result still loading crosses as a
93
+ * promise of its encoded value, which resolves when it settles. The
94
+ * framework's serializer carries the promises (TanStack Router's dehydrated
95
+ * state, React's server component props, React Router's Single Fetch).
96
+ *
97
+ * **Gotchas**
98
+ *
99
+ * Only atoms in the registry when it's called are included, so call it once
100
+ * the loaders have started theirs.
101
+ *
102
+ * @see {@link hydrate} for the browser side
103
+ *
104
+ * @stability unstable
105
+ * @category dehydration
106
+ * @since 0.1.0
107
+ */
108
+ const dehydrate = (registry) => remember(EffectHydration.dehydrate(registry, { encodeInitialAs: "promise" }), registry);
109
+ /**
110
+ * Applies dehydrated state to the browser's registry.
111
+ *
112
+ * **When to use**
113
+ *
114
+ * Use outside React, such as in a router's `hydrate` hook, before the app
115
+ * renders. Inside React, use `HydrationBoundary`.
116
+ *
117
+ * **Details**
118
+ *
119
+ * Settled values go through `Hydration.hydrate`. A streamed result is held
120
+ * until its promise resolves, then hydrated; until then `useAtomResult`
121
+ * suspends on it instead of starting the atom, which would request it again.
122
+ * A streamed result that can't be decoded, or whose promise rejects, is
123
+ * dropped, and the atom runs in the browser.
124
+ *
125
+ * @see {@link dehydrate} for the server side
126
+ *
127
+ * @stability unstable
128
+ * @category hydration
129
+ * @since 0.1.0
130
+ */
131
+ const hydrate = (registry, state) => {
132
+ EffectHydration.hydrate(registry, split(registry, state));
133
+ };
134
+ export { Hydration_exports, dehydrate, hydrate, hydrated };
@@ -0,0 +1,192 @@
1
+ import { ArgsOf, ErrorOf, Handle, LoadedHandle, Source, SuccessOf } from "./Handle.mjs";
2
+ import * as Context from "effect/Context";
3
+ import * as Duration from "effect/Duration";
4
+ import * as Effect from "effect/Effect";
5
+ import * as AsyncResult from "effect/reactivity/AsyncResult";
6
+ import * as AtomRegistry from "effect/reactivity/AtomRegistry";
7
+ declare namespace Loader_d_exports {
8
+ export { LeaseTTL, Loader, SettledResult, ensure, fetch, make, prefetch, result };
9
+ }
10
+ /**
11
+ * A settled `AsyncResult`: what `ensure` and `fetch` return.
12
+ *
13
+ * @stability unstable
14
+ * @category models
15
+ * @since 0.1.0
16
+ */
17
+ export type SettledResult<A, E> = AsyncResult.Failure<A, E> | AsyncResult.Success<A, E>;
18
+ /**
19
+ * How long a loader's lease keeps an atom mounted when no component takes it
20
+ * over. Default 30 seconds.
21
+ *
22
+ * **Details**
23
+ *
24
+ * A lease ends when `useAtomResult` mounts the atom, or when this time has
25
+ * passed, after which the registry's idle TTL applies as usual. On a request
26
+ * registry, leases last until the registry is disposed.
27
+ *
28
+ * @stability unstable
29
+ * @category references
30
+ * @since 0.1.0
31
+ */
32
+ export declare const LeaseTTL: Context.Reference<Duration.Duration>;
33
+ /**
34
+ * Starts a source's atom without waiting for it, and returns its handle.
35
+ *
36
+ * **When to use**
37
+ *
38
+ * Use for data the route needs but its loader doesn't: it streams in while
39
+ * the route renders.
40
+ *
41
+ * **Details**
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.
46
+ *
47
+ * **Example**
48
+ *
49
+ * ```ts
50
+ * import { Effect } from "effect"
51
+ * import { Loader } from "raf-effect-atom"
52
+ *
53
+ * const program = Loader.prefetch(commentPage, issueId, 0)
54
+ * ```
55
+ *
56
+ * @stability unstable
57
+ * @category verbs
58
+ * @since 0.1.0
59
+ */
60
+ export declare const prefetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Effect.Effect<Handle<S>, never, AtomRegistry.AtomRegistry>;
61
+ /**
62
+ * Starts a source's atom, waits only while it has no result, and returns a
63
+ * handle whose reader renders without suspending.
64
+ *
65
+ * **When to use**
66
+ *
67
+ * Use when the loader needs the result, such as to redirect on a tag, or
68
+ * when the route shouldn't render without it.
69
+ *
70
+ * **Details**
71
+ *
72
+ * An atom with a result returns at once, without refetching. On a request
73
+ * registry, it also waits out a refresh in flight, so the server never
74
+ * renders a result that's about to change. A typed failure settles it like
75
+ * a success: `result` returns it. A defect or an interruption fails the
76
+ * Effect.
77
+ *
78
+ * **Example**
79
+ *
80
+ * ```ts
81
+ * import { Effect } from "effect"
82
+ * import { AsyncResult } from "effect/reactivity"
83
+ * import { Loader } from "raf-effect-atom"
84
+ *
85
+ * const program = Effect.gen(function* () {
86
+ * const handle = yield* Loader.ensure(issue, 1)
87
+ * const result = Loader.result(handle)
88
+ * return AsyncResult.isSuccess(result) ? result.value.title : "missing"
89
+ * })
90
+ * ```
91
+ *
92
+ * @stability unstable
93
+ * @category verbs
94
+ * @since 0.1.0
95
+ */
96
+ export declare const ensure: <S extends Source>(source: S, ...args: ArgsOf<S>) => Effect.Effect<LoadedHandle<S>, never, AtomRegistry.AtomRegistry>;
97
+ /**
98
+ * Starts a source's atom, or refreshes it if it has a result, waits for the
99
+ * new result to settle, and returns a handle whose reader renders without
100
+ * suspending.
101
+ *
102
+ * **When to use**
103
+ *
104
+ * Use when the loader must not see a result from before it ran, such as
105
+ * after a mutation.
106
+ *
107
+ * **Details**
108
+ *
109
+ * Waits until the result settles and isn't refreshing. A typed failure
110
+ * settles it like a success: `result` returns it. A defect or an
111
+ * interruption fails the Effect.
112
+ *
113
+ * @stability unstable
114
+ * @category verbs
115
+ * @since 0.1.0
116
+ */
117
+ export declare const fetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Effect.Effect<LoadedHandle<S>, never, AtomRegistry.AtomRegistry>;
118
+ /**
119
+ * Returns the settled result `ensure` or `fetch` waited for.
120
+ *
121
+ * **When to use**
122
+ *
123
+ * Use in the loader that ran the verb, to branch on the result: redirect on
124
+ * a tag, or build another atom's arguments from it. Components read the
125
+ * handle with `useAtomResult`, which follows the registry.
126
+ *
127
+ * **Gotchas**
128
+ *
129
+ * Only the handle object the verb returned has it: a handle that crossed
130
+ * the network, as loader data or a server component's prop, is a copy, and
131
+ * this throws for it.
132
+ *
133
+ * **Example**
134
+ *
135
+ * ```ts
136
+ * import { AsyncResult } from "effect/reactivity"
137
+ * import { Loader } from "raf-effect-atom"
138
+ *
139
+ * const handle = await atoms.ensure(issue, id)
140
+ * const error = AsyncResult.error(Loader.result(handle))
141
+ * ```
142
+ *
143
+ * @stability unstable
144
+ * @category getters
145
+ * @since 0.1.0
146
+ */
147
+ export declare const result: <S extends Source>(handle: LoadedHandle<S>) => SettledResult<SuccessOf<S>, ErrorOf<S>>;
148
+ /**
149
+ * The loader verbs as Promises over one registry, as the framework
150
+ * integrations put them on the router context.
151
+ *
152
+ * @stability unstable
153
+ * @category models
154
+ * @since 0.1.0
155
+ */
156
+ export interface Loader {
157
+ /** {@link ensure}, as a Promise that rejects only on a defect. */
158
+ readonly ensure: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
159
+ /** {@link fetch}, as a Promise that rejects only on a defect. */
160
+ readonly fetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
161
+ /** {@link prefetch}, run at once. */
162
+ readonly prefetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Handle<S>;
163
+ }
164
+ /**
165
+ * Creates the Promise verbs over one registry.
166
+ *
167
+ * **When to use**
168
+ *
169
+ * Use in a framework integration, in loaders that are async functions, or
170
+ * in an event handler that starts an atom before the state change that
171
+ * renders its reader, for state that can't be in the URL, such as a
172
+ * search-as-you-type query. In Effect code, use
173
+ * the verbs directly with the `AtomRegistry` provided.
174
+ *
175
+ * **Example**
176
+ *
177
+ * ```ts
178
+ * import { Loader, RequestRegistry } from "raf-effect-atom"
179
+ *
180
+ * const loader = Loader.make(RequestRegistry.make())
181
+ * const comments = loader.prefetch(commentPage, 1, 0)
182
+ * const issueHandle = await loader.ensure(issue, 1)
183
+ * ```
184
+ *
185
+ * @stability unstable
186
+ * @category constructors
187
+ * @since 0.1.0
188
+ */
189
+ export declare const make: (registry: AtomRegistry.AtomRegistry, options?: {
190
+ readonly leaseTTL?: Duration.Input | undefined;
191
+ }) => Loader;
192
+ export { Loader_d_exports };
@@ -0,0 +1,286 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { hydrated } from "./Hydration.mjs";
3
+ import { isDevelopment } from "./internal/development.mjs";
4
+ import { keyOf, take } from "./internal/leases.mjs";
5
+ import { atomOf, loaded as loaded$1, make as make$1 } from "./internal/handles.mjs";
6
+ import { isRequestRegistry } from "./RequestRegistry.mjs";
7
+ import * as Atom from "effect/reactivity/Atom";
8
+ import * as Predicate from "effect/Predicate";
9
+ import * as Cause from "effect/Cause";
10
+ import * as Context from "effect/Context";
11
+ import * as Duration from "effect/Duration";
12
+ import * as Effect from "effect/Effect";
13
+ import * as Option from "effect/Option";
14
+ import * as AsyncResult from "effect/reactivity/AsyncResult";
15
+ import * as AtomRegistry from "effect/reactivity/AtomRegistry";
16
+ // src/Loader.ts
17
+ /**
18
+ * Starts atoms from route loaders, before anything renders, so nothing
19
+ * waterfalls.
20
+ *
21
+ * Each verb takes a source, an atom family or an `AsyncResult` atom, with its
22
+ * arguments. It starts the atom in the registry, takes a lease on it, so the
23
+ * registry's idle TTL can't evict it before the route renders and
24
+ * `useAtomResult` takes it over, and returns its handle for the component
25
+ * that reads it:
26
+ *
27
+ * | Verb | Starts the atom | Waits | Has a value already |
28
+ * | ---------- | ------------------------------- | ----------------------------- | --------------------- |
29
+ * | `prefetch` | if it isn't in the registry | never | leaves it alone |
30
+ * | `ensure` | if it isn't in the registry | only while it's `Initial` | returns it at once |
31
+ * | `fetch` | if it isn't, or refreshes it | until the new result settles | refreshes, then waits |
32
+ *
33
+ * Typed failures don't reject: `ensure` and `fetch` settle on them, and
34
+ * `result` returns the settled `AsyncResult`, so a loader can redirect on a
35
+ * tag and otherwise let a boundary render the failure. Only defects and
36
+ * interruptions reject.
37
+ *
38
+ * The verbs are Effects that need the `AtomRegistry`; `make` gives the same
39
+ * verbs as Promises over one registry, for loaders that are async functions.
40
+ *
41
+ * @stability unstable
42
+ * @since 0.1.0
43
+ */
44
+ var Loader_exports = /* @__PURE__ */ __exportAll({
45
+ LeaseTTL: () => LeaseTTL,
46
+ ensure: () => ensure,
47
+ fetch: () => fetch,
48
+ make: () => make,
49
+ prefetch: () => prefetch,
50
+ result: () => result
51
+ });
52
+ /**
53
+ * How long a loader's lease keeps an atom mounted when no component takes it
54
+ * over. Default 30 seconds.
55
+ *
56
+ * **Details**
57
+ *
58
+ * A lease ends when `useAtomResult` mounts the atom, or when this time has
59
+ * passed, after which the registry's idle TTL applies as usual. On a request
60
+ * registry, leases last until the registry is disposed.
61
+ *
62
+ * @stability unstable
63
+ * @category references
64
+ * @since 0.1.0
65
+ */
66
+ const LeaseTTL = Context.Reference("raf-effect-atom/Loader/LeaseTTL", { defaultValue: () => Duration.seconds(30) });
67
+ const start = Effect.fnUntraced(function* (atom) {
68
+ const registry = yield* AtomRegistry.AtomRegistry;
69
+ const request = isRequestRegistry(registry);
70
+ 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
+ const ttl = request ? void 0 : Duration.toMillis(yield* LeaseTTL);
72
+ const target = hydrated(atom);
73
+ const key = keyOf(target);
74
+ const existed = registry.getNodes().has(key) || Predicate.isString(key) && registry.preloadedSerializable.has(key);
75
+ take(registry, target, ttl);
76
+ return {
77
+ existed,
78
+ registry,
79
+ request,
80
+ target
81
+ };
82
+ });
83
+ const isSettled = (result) => AsyncResult.isNotInitial(result) && !result.waiting;
84
+ const settled = (registry, atom, isDone) => Effect.callback((resume) => {
85
+ const current = registry.get(atom);
86
+ if (isDone(current)) {
87
+ resume(Effect.succeed(current));
88
+ return;
89
+ }
90
+ const cancel = registry.subscribe(atom, (value) => {
91
+ if (isDone(value)) {
92
+ cancel();
93
+ resume(Effect.succeed(value));
94
+ }
95
+ });
96
+ return Effect.sync(cancel);
97
+ }).pipe(Effect.flatMap(rejectDefects));
98
+ const rejectDefects = (result) => {
99
+ if (AsyncResult.isFailure(result) && Option.isNone(Cause.findErrorOption(result.cause))) {
100
+ const defects = result.cause;
101
+ return Effect.failCause(defects);
102
+ }
103
+ return Effect.succeed(result);
104
+ };
105
+ /**
106
+ * Starts a source's atom without waiting for it, and returns its handle.
107
+ *
108
+ * **When to use**
109
+ *
110
+ * Use for data the route needs but its loader doesn't: it streams in while
111
+ * the route renders.
112
+ *
113
+ * **Details**
114
+ *
115
+ * Starts the atom if it isn't in the registry, and leaves it alone if it is.
116
+ * Takes a lease either way. The component that reads the handle suspends
117
+ * until the result settles.
118
+ *
119
+ * **Example**
120
+ *
121
+ * ```ts
122
+ * import { Effect } from "effect"
123
+ * import { Loader } from "raf-effect-atom"
124
+ *
125
+ * const program = Loader.prefetch(commentPage, issueId, 0)
126
+ * ```
127
+ *
128
+ * @stability unstable
129
+ * @category verbs
130
+ * @since 0.1.0
131
+ */
132
+ const prefetch = (source, ...args) => Effect.gen(function* () {
133
+ const atom = atomOf(source, args);
134
+ yield* start(atom);
135
+ return make$1(args, atom);
136
+ });
137
+ const loaded = (args, atom, result) => {
138
+ const handle = loaded$1(args, atom);
139
+ results.set(handle, result);
140
+ return handle;
141
+ };
142
+ /**
143
+ * Starts a source's atom, waits only while it has no result, and returns a
144
+ * handle whose reader renders without suspending.
145
+ *
146
+ * **When to use**
147
+ *
148
+ * Use when the loader needs the result, such as to redirect on a tag, or
149
+ * when the route shouldn't render without it.
150
+ *
151
+ * **Details**
152
+ *
153
+ * An atom with a result returns at once, without refetching. On a request
154
+ * registry, it also waits out a refresh in flight, so the server never
155
+ * renders a result that's about to change. A typed failure settles it like
156
+ * a success: `result` returns it. A defect or an interruption fails the
157
+ * Effect.
158
+ *
159
+ * **Example**
160
+ *
161
+ * ```ts
162
+ * import { Effect } from "effect"
163
+ * import { AsyncResult } from "effect/reactivity"
164
+ * import { Loader } from "raf-effect-atom"
165
+ *
166
+ * const program = Effect.gen(function* () {
167
+ * const handle = yield* Loader.ensure(issue, 1)
168
+ * const result = Loader.result(handle)
169
+ * return AsyncResult.isSuccess(result) ? result.value.title : "missing"
170
+ * })
171
+ * ```
172
+ *
173
+ * @stability unstable
174
+ * @category verbs
175
+ * @since 0.1.0
176
+ */
177
+ const ensure = (source, ...args) => Effect.gen(function* () {
178
+ const atom = atomOf(source, args);
179
+ const started = yield* start(atom);
180
+ const result = yield* settled(started.registry, started.target, started.request ? isSettled : AsyncResult.isNotInitial);
181
+ return loaded(args, atom, result);
182
+ });
183
+ /**
184
+ * Starts a source's atom, or refreshes it if it has a result, waits for the
185
+ * new result to settle, and returns a handle whose reader renders without
186
+ * suspending.
187
+ *
188
+ * **When to use**
189
+ *
190
+ * Use when the loader must not see a result from before it ran, such as
191
+ * after a mutation.
192
+ *
193
+ * **Details**
194
+ *
195
+ * Waits until the result settles and isn't refreshing. A typed failure
196
+ * settles it like a success: `result` returns it. A defect or an
197
+ * interruption fails the Effect.
198
+ *
199
+ * @stability unstable
200
+ * @category verbs
201
+ * @since 0.1.0
202
+ */
203
+ const fetch = (source, ...args) => Effect.gen(function* () {
204
+ const atom = atomOf(source, args);
205
+ const started = yield* start(atom);
206
+ const { registry, target } = started;
207
+ if (started.existed && isSettled(registry.get(target))) {
208
+ const node = registry.getNodes().get(keyOf(target));
209
+ registry.refresh(node?.atom ?? target);
210
+ }
211
+ const result = yield* settled(registry, target, isSettled);
212
+ return loaded(args, atom, result);
213
+ });
214
+ const results = /* @__PURE__ */ new WeakMap();
215
+ /**
216
+ * Returns the settled result `ensure` or `fetch` waited for.
217
+ *
218
+ * **When to use**
219
+ *
220
+ * Use in the loader that ran the verb, to branch on the result: redirect on
221
+ * a tag, or build another atom's arguments from it. Components read the
222
+ * handle with `useAtomResult`, which follows the registry.
223
+ *
224
+ * **Gotchas**
225
+ *
226
+ * Only the handle object the verb returned has it: a handle that crossed
227
+ * the network, as loader data or a server component's prop, is a copy, and
228
+ * this throws for it.
229
+ *
230
+ * **Example**
231
+ *
232
+ * ```ts
233
+ * import { AsyncResult } from "effect/reactivity"
234
+ * import { Loader } from "raf-effect-atom"
235
+ *
236
+ * const handle = await atoms.ensure(issue, id)
237
+ * const error = AsyncResult.error(Loader.result(handle))
238
+ * ```
239
+ *
240
+ * @stability unstable
241
+ * @category getters
242
+ * @since 0.1.0
243
+ */
244
+ const result = (handle) => {
245
+ const settledResult = results.get(handle);
246
+ if (settledResult === void 0) throw new Error("raf-effect-atom: Loader.result got a handle that no ensure or fetch returned here. Only the code that ran the verb has its result: a handle that crossed the network is a copy. Read it with useAtomResult instead.");
247
+ return settledResult;
248
+ };
249
+ /**
250
+ * Creates the Promise verbs over one registry.
251
+ *
252
+ * **When to use**
253
+ *
254
+ * Use in a framework integration, in loaders that are async functions, or
255
+ * in an event handler that starts an atom before the state change that
256
+ * renders its reader, for state that can't be in the URL, such as a
257
+ * search-as-you-type query. In Effect code, use
258
+ * the verbs directly with the `AtomRegistry` provided.
259
+ *
260
+ * **Example**
261
+ *
262
+ * ```ts
263
+ * import { Loader, RequestRegistry } from "raf-effect-atom"
264
+ *
265
+ * const loader = Loader.make(RequestRegistry.make())
266
+ * const comments = loader.prefetch(commentPage, 1, 0)
267
+ * const issueHandle = await loader.ensure(issue, 1)
268
+ * ```
269
+ *
270
+ * @stability unstable
271
+ * @category constructors
272
+ * @since 0.1.0
273
+ */
274
+ const make = (registry, options) => {
275
+ const leaseTTL = options?.leaseTTL === void 0 ? void 0 : Duration.fromInputUnsafe(options.leaseTTL);
276
+ const provide = (effect) => {
277
+ const provided = Effect.provideService(effect, AtomRegistry.AtomRegistry, registry);
278
+ return leaseTTL === void 0 ? provided : Effect.provideService(provided, LeaseTTL, leaseTTL);
279
+ };
280
+ return {
281
+ ensure: (source, ...args) => Effect.runPromise(provide(ensure(source, ...args))),
282
+ fetch: (source, ...args) => Effect.runPromise(provide(fetch(source, ...args))),
283
+ prefetch: (source, ...args) => Effect.runSync(provide(prefetch(source, ...args)))
284
+ };
285
+ };
286
+ export { LeaseTTL, Loader_exports, ensure, fetch, make, prefetch, result };
@@ -0,0 +1,65 @@
1
+ import { ArgsOf, Handle, LoadedHandle, Source } from "./Handle.mjs";
2
+ import { HydrateAtoms } from "./NextClient.mjs";
3
+ import { RegistryProvider } from "./RegistryProvider.mjs";
4
+ import * as EffectHydration from "effect/reactivity/Hydration";
5
+ import * as AtomRegistry from "effect/reactivity/AtomRegistry";
6
+ /**
7
+ * Returns the request registry of the server render in progress.
8
+ *
9
+ * **Details**
10
+ *
11
+ * Made once per request through React's `cache`, and disposed through
12
+ * `cacheSignal` when React finishes the render, after the last streamed
13
+ * result has been sent.
14
+ *
15
+ * **Gotchas**
16
+ *
17
+ * Only server components share it: outside a server component render,
18
+ * `cache` doesn't cache, so each call makes a new registry.
19
+ *
20
+ * @stability unstable
21
+ * @category registry
22
+ * @since 0.1.0
23
+ */
24
+ export declare const getRegistry: () => AtomRegistry.AtomRegistry;
25
+ /**
26
+ * `Loader.prefetch` on the request's registry: starts a source's atom, to
27
+ * stream it, and returns its handle.
28
+ *
29
+ * @stability unstable
30
+ * @category verbs
31
+ * @since 0.1.0
32
+ */
33
+ export declare const prefetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Handle<S>;
34
+ /**
35
+ * `Loader.ensure` on the request's registry: starts a source's atom, waits
36
+ * for its result, and returns its handle.
37
+ *
38
+ * @stability unstable
39
+ * @category verbs
40
+ * @since 0.1.0
41
+ */
42
+ export declare const ensure: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
43
+ /**
44
+ * `Loader.fetch` on the request's registry: starts or refreshes a source's
45
+ * atom, waits for the new result, and returns its handle.
46
+ *
47
+ * @stability unstable
48
+ * @category verbs
49
+ * @since 0.1.0
50
+ */
51
+ export declare const fetch: <S extends Source>(source: S, ...args: ArgsOf<S>) => Promise<LoadedHandle<S>>;
52
+ /**
53
+ * `Hydration.dehydrate` on the request's registry, for `HydrateAtoms`.
54
+ *
55
+ * **Gotchas**
56
+ *
57
+ * Includes the atoms started so far, so call it after the server component's
58
+ * verbs. Each `HydrateAtoms` hydrates what its own `dehydrate()` saw.
59
+ *
60
+ * @stability unstable
61
+ * @category dehydration
62
+ * @since 0.1.0
63
+ */
64
+ export declare const dehydrate: () => Array<EffectHydration.DehydratedAtom>;
65
+ export { HydrateAtoms, RegistryProvider };