raf-effect-atom 0.0.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 +122 -0
- package/README.md +17 -11
- package/dist/Handle.d.mts +90 -0
- package/dist/Hooks.d.mts +37 -16
- package/dist/Hooks.mjs +38 -22
- package/dist/Loader.d.mts +64 -24
- package/dist/Loader.mjs +96 -32
- package/dist/Next.d.mts +76 -12
- package/dist/Next.mjs +60 -12
- package/dist/NextClient.d.mts +4 -5
- package/dist/NextClient.mjs +4 -5
- package/dist/ReactHydration.d.mts +1 -1
- package/dist/ReactHydration.mjs +1 -1
- package/dist/ReactRouter.d.mts +32 -1
- package/dist/ReactRouter.mjs +28 -6
- package/dist/RequestRegistry.d.mts +64 -8
- package/dist/RequestRegistry.mjs +43 -9
- package/dist/TanStackRouter.d.mts +13 -2
- package/dist/TanStackRouter.mjs +13 -3
- package/dist/Waterfalls.d.mts +10 -9
- package/dist/Waterfalls.mjs +13 -9
- package/dist/index.d.mts +3 -2
- package/dist/internal/handles.mjs +22 -0
- package/dist/internal/next.mjs +23 -14
- package/dist/internal/registry.mjs +1 -1
- package/dist/internal/seeds.d.mts +3 -0
- package/dist/internal/seeds.mjs +56 -0
- package/dist/internal/waterfalls.mjs +1 -1
- package/package.json +2 -2
package/dist/NextClient.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HydrationBoundaryProps } from "./ReactHydration.mjs";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
/**
|
|
4
|
-
* Hydrates the state a server component dehydrated with `dehydrate()`,
|
|
4
|
+
* Hydrates the state a server component dehydrated with `await dehydrate()`,
|
|
5
5
|
* streamed results included.
|
|
6
6
|
*
|
|
7
7
|
* **Details**
|
|
@@ -16,11 +16,10 @@ import * as React from "react";
|
|
|
16
16
|
* import { dehydrate, ensure, HydrateAtoms } from "raf-effect-atom/next"
|
|
17
17
|
*
|
|
18
18
|
* export default async function IssuePage(props: { params: Promise<{ id: string }> }) {
|
|
19
|
-
* const
|
|
20
|
-
* await ensure(issue(id))
|
|
19
|
+
* const handle = await ensure(issue, Number((await props.params).id))
|
|
21
20
|
* return (
|
|
22
|
-
* <HydrateAtoms state={dehydrate()}>
|
|
23
|
-
* <Issue
|
|
21
|
+
* <HydrateAtoms state={await dehydrate()}>
|
|
22
|
+
* <Issue issue={handle} />
|
|
24
23
|
* </HydrateAtoms>
|
|
25
24
|
* )
|
|
26
25
|
* }
|
package/dist/NextClient.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
9
9
|
* @since 0.1.0
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
|
-
* Hydrates the state a server component dehydrated with `dehydrate()`,
|
|
12
|
+
* Hydrates the state a server component dehydrated with `await dehydrate()`,
|
|
13
13
|
* streamed results included.
|
|
14
14
|
*
|
|
15
15
|
* **Details**
|
|
@@ -24,11 +24,10 @@ import { jsx } from "react/jsx-runtime";
|
|
|
24
24
|
* import { dehydrate, ensure, HydrateAtoms } from "raf-effect-atom/next"
|
|
25
25
|
*
|
|
26
26
|
* export default async function IssuePage(props: { params: Promise<{ id: string }> }) {
|
|
27
|
-
* const
|
|
28
|
-
* await ensure(issue(id))
|
|
27
|
+
* const handle = await ensure(issue, Number((await props.params).id))
|
|
29
28
|
* return (
|
|
30
|
-
* <HydrateAtoms state={dehydrate()}>
|
|
31
|
-
* <Issue
|
|
29
|
+
* <HydrateAtoms state={await dehydrate()}>
|
|
30
|
+
* <Issue issue={handle} />
|
|
32
31
|
* </HydrateAtoms>
|
|
33
32
|
* )
|
|
34
33
|
* }
|
|
@@ -41,7 +41,7 @@ export interface HydrationBoundaryProps {
|
|
|
41
41
|
* import { HydrationBoundary } from "raf-effect-atom"
|
|
42
42
|
*
|
|
43
43
|
* <HydrationBoundary state={loaderData.atoms}>
|
|
44
|
-
* <IssuePage
|
|
44
|
+
* <IssuePage issue={loaderData.issue} />
|
|
45
45
|
* </HydrationBoundary>
|
|
46
46
|
* ```
|
|
47
47
|
*
|
package/dist/ReactHydration.mjs
CHANGED
|
@@ -42,7 +42,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
42
42
|
* import { HydrationBoundary } from "raf-effect-atom"
|
|
43
43
|
*
|
|
44
44
|
* <HydrationBoundary state={loaderData.atoms}>
|
|
45
|
-
* <IssuePage
|
|
45
|
+
* <IssuePage issue={loaderData.issue} />
|
|
46
46
|
* </HydrationBoundary>
|
|
47
47
|
* ```
|
|
48
48
|
*
|
package/dist/ReactRouter.d.mts
CHANGED
|
@@ -36,6 +36,22 @@ export interface MiddlewareArgs {
|
|
|
36
36
|
readonly context: {
|
|
37
37
|
readonly set: (context: AtomsContext, value: RequestAtoms) => void;
|
|
38
38
|
};
|
|
39
|
+
readonly request: Request;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Options for `atomsMiddleware`.
|
|
43
|
+
*
|
|
44
|
+
* @stability unstable
|
|
45
|
+
* @category models
|
|
46
|
+
* @since 0.2.0
|
|
47
|
+
*/
|
|
48
|
+
export interface AtomsMiddlewareOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Runs on each request's registry before the loaders, with the request,
|
|
51
|
+
* to set what its atoms need, such as its cookie. The middleware waits
|
|
52
|
+
* for one that returns a promise.
|
|
53
|
+
*/
|
|
54
|
+
readonly seed?: ((registry: AtomRegistry.AtomRegistry, request: Request) => void | Promise<void>) | undefined;
|
|
39
55
|
}
|
|
40
56
|
/**
|
|
41
57
|
* Creates a root middleware that gives each server request its own
|
|
@@ -49,15 +65,30 @@ export interface MiddlewareArgs {
|
|
|
49
65
|
* then. When `next` doesn't return a `Response`, as Data Mode's
|
|
50
66
|
* `query` can, the registry is left to be collected with the request.
|
|
51
67
|
*
|
|
68
|
+
* `seed` runs on the new registry before `next`, so every loader's atoms
|
|
69
|
+
* start with what it sets.
|
|
70
|
+
*
|
|
52
71
|
* **Gotchas**
|
|
53
72
|
*
|
|
54
73
|
* It's server middleware: export it as the root route's `middleware`, not
|
|
55
74
|
* `clientMiddleware`. In the browser, the root's `RegistryProvider` holds
|
|
56
75
|
* the registry.
|
|
57
76
|
*
|
|
77
|
+
* **Example**
|
|
78
|
+
*
|
|
79
|
+
* ```ts
|
|
80
|
+
* export const middleware = [
|
|
81
|
+
* atomsMiddleware(atomsContext, {
|
|
82
|
+
* // In-process RPC calls send the page request's cookie.
|
|
83
|
+
* seed: (registry, request) =>
|
|
84
|
+
* registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
85
|
+
* })
|
|
86
|
+
* ]
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
58
89
|
* @stability unstable
|
|
59
90
|
* @category integrations
|
|
60
91
|
* @since 0.1.0
|
|
61
92
|
*/
|
|
62
|
-
export declare const atomsMiddleware: (context: AtomsContext) => <Result>(args: MiddlewareArgs, next: () => Promise<Result>) => Promise<Result>;
|
|
93
|
+
export declare const atomsMiddleware: (context: AtomsContext, options?: AtomsMiddlewareOptions) => <Result>(args: MiddlewareArgs, next: () => Promise<Result>) => Promise<Result>;
|
|
63
94
|
export { RegistryProvider };
|
package/dist/ReactRouter.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dehydrate } from "./Hydration.mjs";
|
|
2
|
-
import { make } from "./RequestRegistry.mjs";
|
|
2
|
+
import { make, settled } from "./RequestRegistry.mjs";
|
|
3
3
|
import { make as make$1 } from "./Loader.mjs";
|
|
4
4
|
import { RegistryProvider } from "./RegistryProvider.mjs";
|
|
5
5
|
import * as Predicate from "effect/Predicate";
|
|
@@ -30,14 +30,14 @@ import * as Predicate from "effect/Predicate";
|
|
|
30
30
|
* // app/routes/issue.tsx
|
|
31
31
|
* export async function loader(args: Route.LoaderArgs) {
|
|
32
32
|
* const atoms = args.context.get(atomsContext)
|
|
33
|
-
* await atoms.ensure(issue
|
|
34
|
-
* return { atoms: atoms.dehydrate() }
|
|
33
|
+
* const handle = await atoms.ensure(issue, Number(args.params.id))
|
|
34
|
+
* return { atoms: atoms.dehydrate(), issue: handle }
|
|
35
35
|
* }
|
|
36
36
|
*
|
|
37
37
|
* export default function IssueRoute(props: Route.ComponentProps) {
|
|
38
38
|
* return (
|
|
39
39
|
* <HydrationBoundary state={props.loaderData.atoms}>
|
|
40
|
-
* <Issue
|
|
40
|
+
* <Issue issue={props.loaderData.issue} />
|
|
41
41
|
* </HydrationBoundary>
|
|
42
42
|
* )
|
|
43
43
|
* }
|
|
@@ -58,18 +58,40 @@ import * as Predicate from "effect/Predicate";
|
|
|
58
58
|
* then. When `next` doesn't return a `Response`, as Data Mode's
|
|
59
59
|
* `query` can, the registry is left to be collected with the request.
|
|
60
60
|
*
|
|
61
|
+
* `seed` runs on the new registry before `next`, so every loader's atoms
|
|
62
|
+
* start with what it sets.
|
|
63
|
+
*
|
|
61
64
|
* **Gotchas**
|
|
62
65
|
*
|
|
63
66
|
* It's server middleware: export it as the root route's `middleware`, not
|
|
64
67
|
* `clientMiddleware`. In the browser, the root's `RegistryProvider` holds
|
|
65
68
|
* the registry.
|
|
66
69
|
*
|
|
70
|
+
* **Example**
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* export const middleware = [
|
|
74
|
+
* atomsMiddleware(atomsContext, {
|
|
75
|
+
* // In-process RPC calls send the page request's cookie.
|
|
76
|
+
* seed: (registry, request) =>
|
|
77
|
+
* registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
78
|
+
* })
|
|
79
|
+
* ]
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
67
82
|
* @stability unstable
|
|
68
83
|
* @category integrations
|
|
69
84
|
* @since 0.1.0
|
|
70
85
|
*/
|
|
71
|
-
const atomsMiddleware = (context) => async (args, next) => {
|
|
72
|
-
const
|
|
86
|
+
const atomsMiddleware = (context, options) => async (args, next) => {
|
|
87
|
+
const seed = options?.seed;
|
|
88
|
+
const registry = make({ seed: seed === void 0 ? void 0 : (created) => seed(created, args.request) });
|
|
89
|
+
try {
|
|
90
|
+
await settled(registry);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
registry.dispose();
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
73
95
|
const loader = make$1(registry);
|
|
74
96
|
args.context.set(context, {
|
|
75
97
|
...loader,
|
|
@@ -1,32 +1,88 @@
|
|
|
1
|
+
import { Seed as Seed$1 } from "./internal/seeds.mjs";
|
|
1
2
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
2
3
|
import * as Layer from "effect/Layer";
|
|
3
4
|
declare namespace RequestRegistry_d_exports {
|
|
4
|
-
export { isRequestRegistry, layer, make };
|
|
5
|
+
export { Options, Seed, isRequestRegistry, layer, make, settled };
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Prepares a request registry before the loader verbs start atoms in it,
|
|
9
|
+
* such as by setting an atom to the request's cookie.
|
|
10
|
+
*
|
|
11
|
+
* @stability unstable
|
|
12
|
+
* @category models
|
|
13
|
+
* @since 0.2.0
|
|
14
|
+
*/
|
|
15
|
+
export type Seed = Seed$1;
|
|
16
|
+
/**
|
|
17
|
+
* Options for `make`.
|
|
18
|
+
*
|
|
19
|
+
* @stability unstable
|
|
20
|
+
* @category models
|
|
21
|
+
* @since 0.2.0
|
|
22
|
+
*/
|
|
23
|
+
export interface Options {
|
|
24
|
+
/**
|
|
25
|
+
* Runs on the new registry before any verb starts an atom in it. When it
|
|
26
|
+
* returns a promise, `ensure` and `fetch` wait for it, and `prefetch`
|
|
27
|
+
* returns its handle at once and starts the atom once it resolves.
|
|
28
|
+
*/
|
|
29
|
+
readonly seed?: Seed | undefined;
|
|
5
30
|
}
|
|
6
31
|
/**
|
|
7
32
|
* Creates the registry for one server request.
|
|
8
33
|
*
|
|
9
34
|
* **Details**
|
|
10
35
|
*
|
|
11
|
-
* It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
12
|
-
*
|
|
13
|
-
* the background, and a loader's lease lasts until the registry is
|
|
14
|
-
* In development, the loader verbs refuse an atom that isn't
|
|
15
|
-
* because the browser would request it again.
|
|
36
|
+
* - It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
37
|
+
* request registry, `ensure` waits like `fetch` does, nothing revalidates
|
|
38
|
+
* in the background, and a loader's lease lasts until the registry is
|
|
39
|
+
* disposed. In development, the loader verbs refuse an atom that isn't
|
|
40
|
+
* serializable, because the browser would request it again.
|
|
41
|
+
* - `seed` sets what the request's atoms need before they run, such as the
|
|
42
|
+
* page request's cookie for an RPC protocol that forwards it. It can be
|
|
43
|
+
* async, for request data only a promise gives, such as Next.js's
|
|
44
|
+
* `headers()`.
|
|
45
|
+
*
|
|
46
|
+
* **Gotchas**
|
|
47
|
+
*
|
|
48
|
+
* With an async seed, a `prefetch` before it settles starts its atom later,
|
|
49
|
+
* so `Hydration.dehydrate` would miss it: wait for `settled` first, as the
|
|
50
|
+
* framework integrations do.
|
|
16
51
|
*
|
|
17
52
|
* **Example**
|
|
18
53
|
*
|
|
19
54
|
* ```ts
|
|
20
55
|
* import { RequestRegistry } from "raf-effect-atom"
|
|
21
56
|
*
|
|
22
|
-
* const registry = RequestRegistry.make(
|
|
57
|
+
* const registry = RequestRegistry.make({
|
|
58
|
+
* seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
59
|
+
* })
|
|
23
60
|
* ```
|
|
24
61
|
*
|
|
25
62
|
* @stability unstable
|
|
26
63
|
* @category constructors
|
|
27
64
|
* @since 0.1.0
|
|
28
65
|
*/
|
|
29
|
-
export declare const make: () => AtomRegistry.AtomRegistry;
|
|
66
|
+
export declare const make: (options?: Options) => AtomRegistry.AtomRegistry;
|
|
67
|
+
/**
|
|
68
|
+
* Waits until a request registry's seed has settled and every atom a verb
|
|
69
|
+
* started while it ran has started.
|
|
70
|
+
*
|
|
71
|
+
* **When to use**
|
|
72
|
+
*
|
|
73
|
+
* Use before `Hydration.dehydrate` on a registry with an async seed, outside
|
|
74
|
+
* the framework integrations, which call it for you.
|
|
75
|
+
*
|
|
76
|
+
* **Details**
|
|
77
|
+
*
|
|
78
|
+
* Resolves at once for a registry without a seed, or whose seed was
|
|
79
|
+
* synchronous. Rejects with the seed's error.
|
|
80
|
+
*
|
|
81
|
+
* @stability unstable
|
|
82
|
+
* @category getters
|
|
83
|
+
* @since 0.2.0
|
|
84
|
+
*/
|
|
85
|
+
export declare const settled: (registry: AtomRegistry.AtomRegistry) => Promise<void>;
|
|
30
86
|
/**
|
|
31
87
|
* A layer that provides a request registry as the `AtomRegistry`, disposed
|
|
32
88
|
* when the layer's scope closes.
|
package/dist/RequestRegistry.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { run, settled as settled$1 } from "./internal/seeds.mjs";
|
|
2
3
|
import * as Effect from "effect/Effect";
|
|
3
4
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
4
5
|
import * as Layer from "effect/Layer";
|
|
@@ -18,7 +19,8 @@ import * as Layer from "effect/Layer";
|
|
|
18
19
|
var RequestRegistry_exports = /* @__PURE__ */ __exportAll({
|
|
19
20
|
isRequestRegistry: () => isRequestRegistry,
|
|
20
21
|
layer: () => layer,
|
|
21
|
-
make: () => make
|
|
22
|
+
make: () => make,
|
|
23
|
+
settled: () => settled
|
|
22
24
|
});
|
|
23
25
|
const requestRegistries = /* @__PURE__ */ new WeakSet();
|
|
24
26
|
/**
|
|
@@ -26,30 +28,62 @@ const requestRegistries = /* @__PURE__ */ new WeakSet();
|
|
|
26
28
|
*
|
|
27
29
|
* **Details**
|
|
28
30
|
*
|
|
29
|
-
* It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
30
|
-
*
|
|
31
|
-
* the background, and a loader's lease lasts until the registry is
|
|
32
|
-
* In development, the loader verbs refuse an atom that isn't
|
|
33
|
-
* because the browser would request it again.
|
|
31
|
+
* - It's an ordinary `AtomRegistry`, marked as a request registry. On a
|
|
32
|
+
* request registry, `ensure` waits like `fetch` does, nothing revalidates
|
|
33
|
+
* in the background, and a loader's lease lasts until the registry is
|
|
34
|
+
* disposed. In development, the loader verbs refuse an atom that isn't
|
|
35
|
+
* serializable, because the browser would request it again.
|
|
36
|
+
* - `seed` sets what the request's atoms need before they run, such as the
|
|
37
|
+
* page request's cookie for an RPC protocol that forwards it. It can be
|
|
38
|
+
* async, for request data only a promise gives, such as Next.js's
|
|
39
|
+
* `headers()`.
|
|
40
|
+
*
|
|
41
|
+
* **Gotchas**
|
|
42
|
+
*
|
|
43
|
+
* With an async seed, a `prefetch` before it settles starts its atom later,
|
|
44
|
+
* so `Hydration.dehydrate` would miss it: wait for `settled` first, as the
|
|
45
|
+
* framework integrations do.
|
|
34
46
|
*
|
|
35
47
|
* **Example**
|
|
36
48
|
*
|
|
37
49
|
* ```ts
|
|
38
50
|
* import { RequestRegistry } from "raf-effect-atom"
|
|
39
51
|
*
|
|
40
|
-
* const registry = RequestRegistry.make(
|
|
52
|
+
* const registry = RequestRegistry.make({
|
|
53
|
+
* seed: (registry) => registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
|
|
54
|
+
* })
|
|
41
55
|
* ```
|
|
42
56
|
*
|
|
43
57
|
* @stability unstable
|
|
44
58
|
* @category constructors
|
|
45
59
|
* @since 0.1.0
|
|
46
60
|
*/
|
|
47
|
-
const make = () => {
|
|
61
|
+
const make = (options) => {
|
|
48
62
|
const registry = AtomRegistry.make();
|
|
49
63
|
requestRegistries.add(registry);
|
|
64
|
+
if (options?.seed !== void 0) run(registry, options.seed);
|
|
50
65
|
return registry;
|
|
51
66
|
};
|
|
52
67
|
/**
|
|
68
|
+
* Waits until a request registry's seed has settled and every atom a verb
|
|
69
|
+
* started while it ran has started.
|
|
70
|
+
*
|
|
71
|
+
* **When to use**
|
|
72
|
+
*
|
|
73
|
+
* Use before `Hydration.dehydrate` on a registry with an async seed, outside
|
|
74
|
+
* the framework integrations, which call it for you.
|
|
75
|
+
*
|
|
76
|
+
* **Details**
|
|
77
|
+
*
|
|
78
|
+
* Resolves at once for a registry without a seed, or whose seed was
|
|
79
|
+
* synchronous. Rejects with the seed's error.
|
|
80
|
+
*
|
|
81
|
+
* @stability unstable
|
|
82
|
+
* @category getters
|
|
83
|
+
* @since 0.2.0
|
|
84
|
+
*/
|
|
85
|
+
const settled = (registry) => settled$1(registry);
|
|
86
|
+
/**
|
|
53
87
|
* A layer that provides a request registry as the `AtomRegistry`, disposed
|
|
54
88
|
* when the layer's scope closes.
|
|
55
89
|
*
|
|
@@ -73,4 +107,4 @@ const layer = Layer.effect(AtomRegistry.AtomRegistry, Effect.acquireRelease(Effe
|
|
|
73
107
|
* @since 0.1.0
|
|
74
108
|
*/
|
|
75
109
|
const isRequestRegistry = (registry) => requestRegistries.has(registry);
|
|
76
|
-
export { RequestRegistry_exports, isRequestRegistry, layer, make };
|
|
110
|
+
export { RequestRegistry_exports, isRequestRegistry, layer, make, settled };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Seed } from "./RequestRegistry.mjs";
|
|
1
2
|
import * as EffectHydration from "effect/reactivity/Hydration";
|
|
2
3
|
import * as AtomRegistry from "effect/reactivity/AtomRegistry";
|
|
3
4
|
import { AnyRouter } from "@tanstack/react-router";
|
|
@@ -38,14 +39,23 @@ export interface RouterAtomsOptions<TRouter extends AnyRouter> {
|
|
|
38
39
|
* default registry does. An atom's own `Atom.setIdleTTL` or
|
|
39
40
|
* `Atom.keepAlive` wins. The server's request registry lives for one
|
|
40
41
|
* request, so it has none.
|
|
42
|
+
* - `seed` runs on the server's request registry before the loaders start
|
|
43
|
+
* atoms in it, as `RequestRegistry.make`'s does: set what the request's
|
|
44
|
+
* atoms need, such as its cookie. It can be async. The browser's registry
|
|
45
|
+
* isn't seeded.
|
|
41
46
|
*
|
|
42
47
|
* **Example**
|
|
43
48
|
*
|
|
44
49
|
* ```ts
|
|
50
|
+
* import { getRequestHeader } from "@tanstack/react-start/server"
|
|
45
51
|
* import { makeRegistry } from "raf-effect-atom/tanstack-router"
|
|
46
52
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
53
|
+
* const registry = makeRegistry({
|
|
54
|
+
* // Keeps an issue the user navigated away from for a minute.
|
|
55
|
+
* defaultIdleTTL: 60_000,
|
|
56
|
+
* // In-process RPC calls send the page request's cookie.
|
|
57
|
+
* seed: (registry) => registry.set(requestCookie, getRequestHeader("cookie"))
|
|
58
|
+
* })
|
|
49
59
|
* ```
|
|
50
60
|
*
|
|
51
61
|
* @stability unstable
|
|
@@ -54,6 +64,7 @@ export interface RouterAtomsOptions<TRouter extends AnyRouter> {
|
|
|
54
64
|
*/
|
|
55
65
|
export declare const makeRegistry: (options?: {
|
|
56
66
|
readonly defaultIdleTTL?: number | undefined;
|
|
67
|
+
readonly seed?: Seed | undefined;
|
|
57
68
|
}) => AtomRegistry.AtomRegistry;
|
|
58
69
|
/**
|
|
59
70
|
* Provides a registry to a TanStack router's app and streams it from the
|
package/dist/TanStackRouter.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dehydrate, hydrate } from "./Hydration.mjs";
|
|
2
|
-
import { isRequestRegistry } from "./RequestRegistry.mjs";
|
|
2
|
+
import { isRequestRegistry, settled } from "./RequestRegistry.mjs";
|
|
3
3
|
import { make } from "./internal/registry.mjs";
|
|
4
4
|
import * as Predicate from "effect/Predicate";
|
|
5
5
|
import { RegistryContext } from "@effect/atom-react";
|
|
@@ -51,14 +51,23 @@ import { jsx } from "react/jsx-runtime";
|
|
|
51
51
|
* default registry does. An atom's own `Atom.setIdleTTL` or
|
|
52
52
|
* `Atom.keepAlive` wins. The server's request registry lives for one
|
|
53
53
|
* request, so it has none.
|
|
54
|
+
* - `seed` runs on the server's request registry before the loaders start
|
|
55
|
+
* atoms in it, as `RequestRegistry.make`'s does: set what the request's
|
|
56
|
+
* atoms need, such as its cookie. It can be async. The browser's registry
|
|
57
|
+
* isn't seeded.
|
|
54
58
|
*
|
|
55
59
|
* **Example**
|
|
56
60
|
*
|
|
57
61
|
* ```ts
|
|
62
|
+
* import { getRequestHeader } from "@tanstack/react-start/server"
|
|
58
63
|
* import { makeRegistry } from "raf-effect-atom/tanstack-router"
|
|
59
64
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
65
|
+
* const registry = makeRegistry({
|
|
66
|
+
* // Keeps an issue the user navigated away from for a minute.
|
|
67
|
+
* defaultIdleTTL: 60_000,
|
|
68
|
+
* // In-process RPC calls send the page request's cookie.
|
|
69
|
+
* seed: (registry) => registry.set(requestCookie, getRequestHeader("cookie"))
|
|
70
|
+
* })
|
|
62
71
|
* ```
|
|
63
72
|
*
|
|
64
73
|
* @stability unstable
|
|
@@ -116,6 +125,7 @@ const setupRouterAtoms = (options) => {
|
|
|
116
125
|
const dehydrate$1 = router.options.dehydrate;
|
|
117
126
|
router.options.dehydrate = async () => {
|
|
118
127
|
const dehydrated = await dehydrate$1?.();
|
|
128
|
+
await settled(registry);
|
|
119
129
|
if (disposed) return dehydrated;
|
|
120
130
|
const atoms = { "raf-effect-atom": dehydrate(registry) };
|
|
121
131
|
return Predicate.isObject(dehydrated) ? {
|
package/dist/Waterfalls.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { ArgsOf, Handle, Source } from "./Handle.mjs";
|
|
1
2
|
import { Mode } from "./internal/waterfalls.mjs";
|
|
2
|
-
import * as Atom from "effect/reactivity/Atom";
|
|
3
|
-
import * as AsyncResult from "effect/reactivity/AsyncResult";
|
|
4
3
|
import * as React from "react";
|
|
5
4
|
/**
|
|
6
5
|
* What `useAtomResult` does in development when a read starts an atom that
|
|
@@ -51,8 +50,8 @@ export declare const WaterfallDetection: (props: {
|
|
|
51
50
|
readonly mode: WaterfallMode;
|
|
52
51
|
}) => React.ReactNode;
|
|
53
52
|
/**
|
|
54
|
-
* Starts
|
|
55
|
-
* knows, such as a debounced search query.
|
|
53
|
+
* Starts a source's atom during render and returns its handle, for an input
|
|
54
|
+
* only a render knows, such as a debounced search query.
|
|
56
55
|
*
|
|
57
56
|
* **When to use**
|
|
58
57
|
*
|
|
@@ -71,12 +70,14 @@ export declare const WaterfallDetection: (props: {
|
|
|
71
70
|
* - A result the server is still streaming is awaited by `useAtomResult`,
|
|
72
71
|
* not started again.
|
|
73
72
|
* - `useAtomResult` doesn't report an atom started this way.
|
|
73
|
+
* - The handle is a prefetched one: its reader suspends until the result
|
|
74
|
+
* settles, as for a handle from a loader's `prefetch`.
|
|
74
75
|
*
|
|
75
76
|
* **Gotchas**
|
|
76
77
|
*
|
|
77
|
-
* Pass it the
|
|
78
|
-
* `
|
|
79
|
-
*
|
|
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.
|
|
80
81
|
*
|
|
81
82
|
* **Example**
|
|
82
83
|
*
|
|
@@ -84,7 +85,7 @@ export declare const WaterfallDetection: (props: {
|
|
|
84
85
|
* import { useAtomResult, useWaterfall } from "raf-effect-atom"
|
|
85
86
|
*
|
|
86
87
|
* function Author(props: { readonly userId: UserId }) {
|
|
87
|
-
* const result = useAtomResult(useWaterfall(user
|
|
88
|
+
* const result = useAtomResult(user, useWaterfall(user, props.userId))
|
|
88
89
|
* // ...
|
|
89
90
|
* }
|
|
90
91
|
* ```
|
|
@@ -95,4 +96,4 @@ export declare const WaterfallDetection: (props: {
|
|
|
95
96
|
* @category hooks
|
|
96
97
|
* @since 0.1.0
|
|
97
98
|
*/
|
|
98
|
-
export declare const useWaterfall: <
|
|
99
|
+
export declare const useWaterfall: <S extends Source>(source: S, ...args: ArgsOf<S>) => Handle<S>;
|
package/dist/Waterfalls.mjs
CHANGED
|
@@ -2,6 +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
6
|
import { prefetch } from "./Loader.mjs";
|
|
6
7
|
import { ModeContext, isStarted } from "./internal/waterfalls.mjs";
|
|
7
8
|
import * as Predicate from "effect/Predicate";
|
|
@@ -53,8 +54,8 @@ const WaterfallDetection = (props) => process.env.NODE_ENV === "production" ? pr
|
|
|
53
54
|
children: props.children
|
|
54
55
|
});
|
|
55
56
|
/**
|
|
56
|
-
* Starts
|
|
57
|
-
* knows, such as a debounced search query.
|
|
57
|
+
* Starts a source's atom during render and returns its handle, for an input
|
|
58
|
+
* only a render knows, such as a debounced search query.
|
|
58
59
|
*
|
|
59
60
|
* **When to use**
|
|
60
61
|
*
|
|
@@ -73,12 +74,14 @@ const WaterfallDetection = (props) => process.env.NODE_ENV === "production" ? pr
|
|
|
73
74
|
* - A result the server is still streaming is awaited by `useAtomResult`,
|
|
74
75
|
* not started again.
|
|
75
76
|
* - `useAtomResult` doesn't report an atom started this way.
|
|
77
|
+
* - The handle is a prefetched one: its reader suspends until the result
|
|
78
|
+
* settles, as for a handle from a loader's `prefetch`.
|
|
76
79
|
*
|
|
77
80
|
* **Gotchas**
|
|
78
81
|
*
|
|
79
|
-
* Pass it the
|
|
80
|
-
* `
|
|
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.
|
|
82
85
|
*
|
|
83
86
|
* **Example**
|
|
84
87
|
*
|
|
@@ -86,7 +89,7 @@ const WaterfallDetection = (props) => process.env.NODE_ENV === "production" ? pr
|
|
|
86
89
|
* import { useAtomResult, useWaterfall } from "raf-effect-atom"
|
|
87
90
|
*
|
|
88
91
|
* function Author(props: { readonly userId: UserId }) {
|
|
89
|
-
* const result = useAtomResult(useWaterfall(user
|
|
92
|
+
* const result = useAtomResult(user, useWaterfall(user, props.userId))
|
|
90
93
|
* // ...
|
|
91
94
|
* }
|
|
92
95
|
* ```
|
|
@@ -97,11 +100,12 @@ const WaterfallDetection = (props) => process.env.NODE_ENV === "production" ? pr
|
|
|
97
100
|
* @category hooks
|
|
98
101
|
* @since 0.1.0
|
|
99
102
|
*/
|
|
100
|
-
const useWaterfall = (
|
|
103
|
+
const useWaterfall = (source, ...args) => {
|
|
101
104
|
const registry = React.useContext(RegistryContext);
|
|
105
|
+
const atom = atomOf(source, args);
|
|
102
106
|
const target = hydrated(atom);
|
|
103
107
|
const key = keyOf(target);
|
|
104
|
-
if (!(Predicate.isString(key) && get(registry, key) !== void 0) && !isStarted(registry, target)) Effect.runSync(Effect.provideService(prefetch(
|
|
105
|
-
return atom;
|
|
108
|
+
if (!(Predicate.isString(key) && get(registry, key) !== void 0) && !isStarted(registry, target)) return Effect.runSync(Effect.provideService(prefetch(source, ...args), AtomRegistry.AtomRegistry, registry));
|
|
109
|
+
return make(args, atom);
|
|
106
110
|
};
|
|
107
111
|
export { WaterfallDetection, useWaterfall };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { ArgsOf, ErrorOf, Handle, HandleKey, LoadedHandle, Source, SuccessOf } from "./Handle.mjs";
|
|
1
2
|
import { Loader_d_exports } from "./Loader.mjs";
|
|
3
|
+
import { RequestRegistry_d_exports } from "./RequestRegistry.mjs";
|
|
2
4
|
import { HydrationBoundary, HydrationBoundaryProps } from "./ReactHydration.mjs";
|
|
3
5
|
import { Hydration_d_exports } from "./Hydration.mjs";
|
|
4
|
-
import { RequestRegistry_d_exports } from "./RequestRegistry.mjs";
|
|
5
6
|
import { useAtomResult } from "./Hooks.mjs";
|
|
6
7
|
import { WaterfallDetection, WaterfallMode, useWaterfall } from "./Waterfalls.mjs";
|
|
7
|
-
export { Hydration_d_exports as Hydration, HydrationBoundary, HydrationBoundaryProps, Loader_d_exports as Loader, RequestRegistry_d_exports as RequestRegistry, WaterfallDetection, WaterfallMode, useAtomResult, useWaterfall };
|
|
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 };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { keyOf as keyOf$1 } from "./leases.mjs";
|
|
2
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
3
|
+
import * as Predicate from "effect/Predicate";
|
|
4
|
+
// src/internal/handles.ts
|
|
5
|
+
/** The atom a source makes for its arguments. */
|
|
6
|
+
const atomOf = (source, args) => Atom.isAtom(source) ? source : source(...args);
|
|
7
|
+
/** The registry key a handle records: the atom's serialization key. */
|
|
8
|
+
const keyOf = (atom) => {
|
|
9
|
+
const key = keyOf$1(atom);
|
|
10
|
+
if (!Predicate.isString(key)) return;
|
|
11
|
+
return key;
|
|
12
|
+
};
|
|
13
|
+
const make = (args, atom) => ({
|
|
14
|
+
args,
|
|
15
|
+
key: keyOf(atom)
|
|
16
|
+
});
|
|
17
|
+
const loaded = (args, atom) => ({
|
|
18
|
+
args,
|
|
19
|
+
key: keyOf(atom),
|
|
20
|
+
loaded: true
|
|
21
|
+
});
|
|
22
|
+
export { atomOf, loaded, make };
|
package/dist/internal/next.mjs
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import { dehydrate
|
|
2
|
-
import { make } from "../RequestRegistry.mjs";
|
|
3
|
-
import { make as make$
|
|
1
|
+
import { dehydrate } from "../Hydration.mjs";
|
|
2
|
+
import { make as make$1, settled } from "../RequestRegistry.mjs";
|
|
3
|
+
import { make as make$2 } from "../Loader.mjs";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
// src/internal/next.ts
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
6
|
+
const make = (options) => {
|
|
7
|
+
const getRegistry = React.cache(() => {
|
|
8
|
+
const registry = make$1(options);
|
|
9
|
+
React.cacheSignal()?.addEventListener("abort", () => registry.dispose(), { once: true });
|
|
10
|
+
return registry;
|
|
11
|
+
});
|
|
12
|
+
const getLoader = React.cache(() => make$2(getRegistry()));
|
|
13
|
+
return {
|
|
14
|
+
dehydrate: async () => {
|
|
15
|
+
const registry = getRegistry();
|
|
16
|
+
await settled(registry);
|
|
17
|
+
return dehydrate(registry);
|
|
18
|
+
},
|
|
19
|
+
ensure: (source, ...args) => getLoader().ensure(source, ...args),
|
|
20
|
+
fetch: (source, ...args) => getLoader().fetch(source, ...args),
|
|
21
|
+
getRegistry,
|
|
22
|
+
prefetch: (source, ...args) => getLoader().prefetch(source, ...args)
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export { make };
|
|
@@ -9,7 +9,7 @@ const defaultIdleTTL = 400;
|
|
|
9
9
|
* A request registry on the server, and in the browser a registry like
|
|
10
10
|
* `@effect/atom-react`'s `RegistryProvider` makes.
|
|
11
11
|
*/
|
|
12
|
-
const make = (options) => isServer() ? make$1() : AtomRegistry.make({
|
|
12
|
+
const make = (options) => isServer() ? make$1({ seed: options?.seed }) : AtomRegistry.make({
|
|
13
13
|
defaultIdleTTL: options?.defaultIdleTTL ?? defaultIdleTTL,
|
|
14
14
|
scheduleTask
|
|
15
15
|
});
|