@raf-query/react 0.11.1 → 0.13.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/README.md +32 -59
  3. package/dist/bind.d.mts +21 -0
  4. package/dist/bind.mjs +23 -0
  5. package/dist/browser-render.mjs +46 -0
  6. package/dist/devtools.d.mts +42 -2
  7. package/dist/devtools.mjs +244 -1
  8. package/dist/hydration-boundary/hydrate.mjs +10 -13
  9. package/dist/hydration-boundary/hydration-boundary-react-server.d.mts +6 -0
  10. package/dist/hydration-boundary/hydration-boundary-react-server.mjs +20 -9
  11. package/dist/hydration-boundary/hydration-boundary.d.mts +15 -28
  12. package/dist/hydration-boundary/hydration-boundary.mjs +7 -26
  13. package/dist/index-react-server.d.mts +7 -6
  14. package/dist/index-react-server.mjs +4 -6
  15. package/dist/index-shared.d.mts +5 -6
  16. package/dist/index-shared.mjs +3 -6
  17. package/dist/index.d.mts +7 -6
  18. package/dist/index.mjs +5 -4
  19. package/dist/provider.d.mts +11 -5
  20. package/dist/provider.mjs +12 -9
  21. package/dist/react-server-hooks.d.mts +43 -0
  22. package/dist/react-server-hooks.mjs +45 -0
  23. package/dist/tanstack-router-browser.d.mts +2 -0
  24. package/dist/tanstack-router-browser.mjs +2 -0
  25. package/dist/tanstack-router-shared.d.mts +9 -7
  26. package/dist/tanstack-router-shared.mjs +12 -22
  27. package/dist/tanstack-router.d.mts +4 -2
  28. package/dist/tanstack-router.mjs +12 -14
  29. package/dist/tracked.d.mts +1 -0
  30. package/dist/tracked.mjs +65 -0
  31. package/dist/use-infinite-query.d.mts +23 -0
  32. package/dist/use-infinite-query.mjs +30 -0
  33. package/dist/use-is-fetching.d.mts +7 -15
  34. package/dist/use-is-fetching.mjs +12 -30
  35. package/dist/use-query.d.mts +52 -50
  36. package/dist/use-query.mjs +112 -47
  37. package/package.json +10 -9
  38. package/dist/devtools-panel.d.mts +0 -38
  39. package/dist/devtools-panel.mjs +0 -896
  40. package/dist/devtools-store.mjs +0 -104
  41. package/dist/hydration-boundary/sent.mjs +0 -16
  42. package/dist/query-provider/browser-requests.mjs +0 -38
  43. package/dist/query-provider/query-provider.d.mts +0 -21
  44. package/dist/query-provider/query-provider.mjs +0 -33
  45. package/dist/use-waterfall.d.mts +0 -18
  46. package/dist/use-waterfall.mjs +0 -41
@@ -1,27 +1,38 @@
1
1
  import { Hydrate } from "./hydrate.mjs";
2
- import { sentHashes } from "./sent.mjs";
3
2
  import { dehydrate } from "@raf-query/core";
4
3
  import { jsx } from "react/jsx-runtime";
5
4
  //#region src/hydration-boundary/hydration-boundary-react-server.tsx
5
+ const sentByClient = /* @__PURE__ */ new WeakMap();
6
+ /**
7
+ * Hashes of the request client's entries an earlier `HydrationBoundary`
8
+ * already sent, so each entry crosses once per request.
9
+ */
10
+ function sentHashes(client) {
11
+ const sent = sentByClient.get(client) ?? /* @__PURE__ */ new Set();
12
+ sentByClient.set(client, sent);
13
+ return sent;
14
+ }
6
15
  /**
7
16
  * `HydrationBoundary` in server components (the `react-server` export
8
17
  * condition). With `client`, sends the request client's entries to the
9
18
  * browser cache: pending entries cross as promises and stream in as they
10
19
  * settle, and entries an earlier boundary already sent in this request are
11
20
  * skipped. With `state`, writes that state, as the browser version does.
21
+ *
22
+ * It also marks the client components' server render: from there on, a read
23
+ * of a query nothing sent renders its Suspense fallback on the server and
24
+ * loads in the browser, instead of fetching where nothing would send it.
25
+ *
26
+ * @see https://raf-query.katt.dev/docs/react-server-components#setup
12
27
  */
13
28
  function HydrationBoundary(props) {
14
- if (props.client === void 0) return /* @__PURE__ */ jsx(Hydrate, {
15
- options: props.options,
16
- state: props.state,
17
- children: props.children
18
- });
19
29
  const client = props.client;
20
- const state = dehydrate(client, {
21
- sent: sentHashes(client),
22
- ...props.options
30
+ const state = client === void 0 ? props.state : dehydrate(client, {
31
+ ...props.options,
32
+ sent: sentHashes(client)
23
33
  });
24
34
  return /* @__PURE__ */ jsx(Hydrate, {
35
+ fromServerComponents: true,
25
36
  state,
26
37
  children: props.children
27
38
  });
@@ -1,6 +1,11 @@
1
- import { DehydrateOptions, DehydratedState, HydrateOptions, QueryClient } from "@raf-query/core";
1
+ import { DehydrateOptions, DehydratedState, QueryClient } from "@raf-query/core";
2
2
  import { ReactNode } from "react";
3
3
  //#region src/hydration-boundary/hydration-boundary.d.ts
4
+ /**
5
+ * Props of {@link HydrationBoundary}: a dehydrated `state`, or in server components the request's `client`.
6
+ *
7
+ * @see https://raf-query.katt.dev/docs/react-router#server-loaders
8
+ */
4
9
  export type HydrationBoundaryProps = {
5
10
  readonly children?: ReactNode;
6
11
  /**
@@ -13,8 +18,8 @@ export type HydrationBoundaryProps = {
13
18
  } | {
14
19
  readonly children?: ReactNode;
15
20
  readonly client?: never;
16
- readonly options?: HydrateOptions;
17
- /** A loader's `dehydrate(qc)`. */
21
+ readonly options?: never;
22
+ /** A loader's `dehydrate(client)`. */
18
23
  readonly state: DehydratedState;
19
24
  };
20
25
  /**
@@ -27,38 +32,20 @@ export type HydrationBoundaryProps = {
27
32
  * through the package's `react-server` export condition.
28
33
  *
29
34
  * ```tsx
30
- * const qc = getQueryClient();
31
- * const comments = qc.prefetch(commentsQuery, { slug });
35
+ * const client = getQueryClient();
36
+ * const [handles] = await load(client, { comments: prefetch(commentsQuery, { slug }) });
32
37
  * return (
33
- * <HydrationBoundary client={qc}>
34
- * <Comments handle={comments} />
38
+ * <HydrationBoundary client={client}>
39
+ * <Comments handle={handles.comments} />
35
40
  * </HydrationBoundary>
36
41
  * );
37
42
  * ```
38
43
  *
39
44
  * With loaders, such as React Router Framework Mode, pass the `state` the
40
- * loader dehydrated:
41
- *
42
- * ```tsx
43
- * export async function loader(args: Route.LoaderArgs) {
44
- * const qc = new QueryClient();
45
- * const post = await qc.ensure(postQuery, { slug: args.params.slug });
46
- * return { dehydratedState: dehydrate(qc), post };
47
- * }
48
- *
49
- * export default function Page(props: Route.ComponentProps) {
50
- * const { dehydratedState, post } = props.loaderData;
51
- * return (
52
- * <HydrationBoundary state={dehydratedState}>
53
- * <Post handle={post} />
54
- * </HydrationBoundary>
55
- * );
56
- * }
57
- * ```
45
+ * loader dehydrated. Pending entries cross as promises when the framework's
46
+ * serializer supports them (React Router's Single Fetch does).
58
47
  *
59
- * Pending entries cross as promises when the framework's serializer
60
- * supports them (React Router's Single Fetch does). Nested boundaries
61
- * are fine: each only writes what its `state` holds.
48
+ * @see https://raf-query.katt.dev/docs/react-router#server-loaders
62
49
  */
63
50
  export declare function HydrationBoundary(props: HydrationBoundaryProps): import("react").JSX.Element;
64
51
  //#endregion
@@ -12,43 +12,24 @@ import { jsx } from "react/jsx-runtime";
12
12
  * through the package's `react-server` export condition.
13
13
  *
14
14
  * ```tsx
15
- * const qc = getQueryClient();
16
- * const comments = qc.prefetch(commentsQuery, { slug });
15
+ * const client = getQueryClient();
16
+ * const [handles] = await load(client, { comments: prefetch(commentsQuery, { slug }) });
17
17
  * return (
18
- * <HydrationBoundary client={qc}>
19
- * <Comments handle={comments} />
18
+ * <HydrationBoundary client={client}>
19
+ * <Comments handle={handles.comments} />
20
20
  * </HydrationBoundary>
21
21
  * );
22
22
  * ```
23
23
  *
24
24
  * With loaders, such as React Router Framework Mode, pass the `state` the
25
- * loader dehydrated:
25
+ * loader dehydrated. Pending entries cross as promises when the framework's
26
+ * serializer supports them (React Router's Single Fetch does).
26
27
  *
27
- * ```tsx
28
- * export async function loader(args: Route.LoaderArgs) {
29
- * const qc = new QueryClient();
30
- * const post = await qc.ensure(postQuery, { slug: args.params.slug });
31
- * return { dehydratedState: dehydrate(qc), post };
32
- * }
33
- *
34
- * export default function Page(props: Route.ComponentProps) {
35
- * const { dehydratedState, post } = props.loaderData;
36
- * return (
37
- * <HydrationBoundary state={dehydratedState}>
38
- * <Post handle={post} />
39
- * </HydrationBoundary>
40
- * );
41
- * }
42
- * ```
43
- *
44
- * Pending entries cross as promises when the framework's serializer
45
- * supports them (React Router's Single Fetch does). Nested boundaries
46
- * are fine: each only writes what its `state` holds.
28
+ * @see https://raf-query.katt.dev/docs/react-router#server-loaders
47
29
  */
48
30
  function HydrationBoundary(props) {
49
31
  if (props.state === void 0) throw new Error("HydrationBoundary needs `state` here. `client` works only in server components.");
50
32
  return /* @__PURE__ */ jsx(Hydrate, {
51
- options: props.options,
52
33
  state: props.state,
53
34
  children: props.children
54
35
  });
@@ -1,9 +1,10 @@
1
- import { QueryClientProvider, useQueryClient } from "./provider.mjs";
2
- import { QueryProvider } from "./query-provider/query-provider.mjs";
3
- import { useIsFetching } from "./use-is-fetching.mjs";
4
- import { UseQueryResult, UseQueryResultWithData, useQuery } from "./use-query.mjs";
5
- import { useWaterfall } from "./use-waterfall.mjs";
1
+ import { QueryDevtools, QueryDevtoolsPanel, QueryDevtoolsPanelProps, QueryDevtoolsProps } from "./devtools.mjs";
2
+ import { QueryClientProvider } from "./provider.mjs";
3
+ import { QueryState, QueryValue, UseQueryOptions } from "./use-query.mjs";
4
+ import { InfiniteQueryState } from "./use-infinite-query.mjs";
6
5
  import "./index-shared.mjs";
6
+ import { HydrationBoundaryProps } from "./hydration-boundary/hydration-boundary.mjs";
7
7
  import { HydrationBoundary } from "./hydration-boundary/hydration-boundary-react-server.mjs";
8
+ import { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient } from "./react-server-hooks.mjs";
8
9
  export * from "@raf-query/core";
9
- export { HydrationBoundary, QueryClientProvider, QueryProvider, type UseQueryResult, type UseQueryResultWithData, useIsFetching, useQuery, useQueryClient, useWaterfall };
10
+ export { HydrationBoundary, type HydrationBoundaryProps, type InfiniteQueryState, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, type QueryDevtoolsPanelProps, type QueryDevtoolsProps, type QueryState, type QueryValue, type UseQueryOptions, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
@@ -1,8 +1,6 @@
1
- import { QueryClientProvider, useQueryClient } from "./provider.mjs";
2
- import { QueryProvider } from "./query-provider/query-provider.mjs";
3
- import { useIsFetching } from "./use-is-fetching.mjs";
4
- import { useQuery } from "./use-query.mjs";
5
- import { useWaterfall } from "./use-waterfall.mjs";
1
+ import { QueryClientProvider } from "./provider.mjs";
2
+ import { QueryDevtools, QueryDevtoolsPanel } from "./devtools.mjs";
6
3
  import { HydrationBoundary } from "./hydration-boundary/hydration-boundary-react-server.mjs";
4
+ import { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient } from "./react-server-hooks.mjs";
7
5
  export * from "@raf-query/core";
8
- export { HydrationBoundary, QueryClientProvider, QueryProvider, useIsFetching, useQuery, useQueryClient, useWaterfall };
6
+ export { HydrationBoundary, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
@@ -1,7 +1,6 @@
1
- import { QueryClientProvider, useQueryClient } from "./provider.mjs";
2
- import { QueryProvider } from "./query-provider/query-provider.mjs";
3
- import { useIsFetching } from "./use-is-fetching.mjs";
4
- import { UseQueryResult, UseQueryResultWithData, useQuery } from "./use-query.mjs";
5
- import { useWaterfall } from "./use-waterfall.mjs";
1
+ import { QueryDevtools, QueryDevtoolsPanel, QueryDevtoolsPanelProps, QueryDevtoolsProps } from "./devtools.mjs";
2
+ import { QueryClientProvider } from "./provider.mjs";
3
+ import { QueryState, QueryValue, UseQueryOptions } from "./use-query.mjs";
4
+ import { InfiniteQueryState } from "./use-infinite-query.mjs";
6
5
  export * from "@raf-query/core";
7
- export { QueryClientProvider, QueryProvider, type UseQueryResult, type UseQueryResultWithData, useIsFetching, useQuery, useQueryClient, useWaterfall };
6
+ export { type InfiniteQueryState, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, type QueryDevtoolsPanelProps, type QueryDevtoolsProps, type QueryState, type QueryValue, type UseQueryOptions };
@@ -1,7 +1,4 @@
1
- import { QueryClientProvider, useQueryClient } from "./provider.mjs";
2
- import { QueryProvider } from "./query-provider/query-provider.mjs";
3
- import { useIsFetching } from "./use-is-fetching.mjs";
4
- import { useQuery } from "./use-query.mjs";
5
- import { useWaterfall } from "./use-waterfall.mjs";
1
+ import { QueryClientProvider } from "./provider.mjs";
2
+ import { QueryDevtools, QueryDevtoolsPanel } from "./devtools.mjs";
6
3
  export * from "@raf-query/core";
7
- export { QueryClientProvider, QueryProvider, useIsFetching, useQuery, useQueryClient, useWaterfall };
4
+ export { QueryClientProvider, QueryDevtools, QueryDevtoolsPanel };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
+ import { QueryDevtools, QueryDevtoolsPanel, QueryDevtoolsPanelProps, QueryDevtoolsProps } from "./devtools.mjs";
1
2
  import { QueryClientProvider, useQueryClient } from "./provider.mjs";
2
- import { QueryProvider } from "./query-provider/query-provider.mjs";
3
- import { useIsFetching } from "./use-is-fetching.mjs";
4
- import { UseQueryResult, UseQueryResultWithData, useQuery } from "./use-query.mjs";
5
- import { useWaterfall } from "./use-waterfall.mjs";
3
+ import { QueryState, QueryValue, UseQueryOptions, useQuery } from "./use-query.mjs";
4
+ import { InfiniteQueryState, useInfiniteQuery } from "./use-infinite-query.mjs";
6
5
  import "./index-shared.mjs";
7
- import { HydrationBoundary } from "./hydration-boundary/hydration-boundary.mjs";
6
+ import { HydrationBoundary, HydrationBoundaryProps } from "./hydration-boundary/hydration-boundary.mjs";
7
+ import { useIsFetching } from "./use-is-fetching.mjs";
8
+ import { bindUseQueryClient } from "./bind.mjs";
8
9
  export * from "@raf-query/core";
9
- export { HydrationBoundary, QueryClientProvider, QueryProvider, type UseQueryResult, type UseQueryResultWithData, useIsFetching, useQuery, useQueryClient, useWaterfall };
10
+ export { HydrationBoundary, type HydrationBoundaryProps, type InfiniteQueryState, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, type QueryDevtoolsPanelProps, type QueryDevtoolsProps, type QueryState, type QueryValue, type UseQueryOptions, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
package/dist/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  import { QueryClientProvider, useQueryClient } from "./provider.mjs";
2
- import { QueryProvider } from "./query-provider/query-provider.mjs";
2
+ import { QueryDevtools, QueryDevtoolsPanel } from "./devtools.mjs";
3
+ import { bindUseQueryClient } from "./bind.mjs";
4
+ import { HydrationBoundary } from "./hydration-boundary/hydration-boundary.mjs";
3
5
  import { useIsFetching } from "./use-is-fetching.mjs";
4
6
  import { useQuery } from "./use-query.mjs";
5
- import { useWaterfall } from "./use-waterfall.mjs";
6
- import { HydrationBoundary } from "./hydration-boundary/hydration-boundary.mjs";
7
+ import { useInfiniteQuery } from "./use-infinite-query.mjs";
7
8
  export * from "@raf-query/core";
8
- export { HydrationBoundary, QueryClientProvider, QueryProvider, useIsFetching, useQuery, useQueryClient, useWaterfall };
9
+ export { HydrationBoundary, QueryClientProvider, QueryDevtools, QueryDevtoolsPanel, bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
@@ -2,13 +2,19 @@ import { QueryClient } from "@raf-query/core";
2
2
  import { ReactNode } from "react";
3
3
  //#region src/provider.d.ts
4
4
  /**
5
- * Provides a client to the tree, and refetches its read, stale queries on
6
- * window focus and reconnect while mounted.
5
+ * Provides a client to the tree, and runs its features (focus and
6
+ * reconnect refetching, polling, …) while mounted.
7
+ *
8
+ * @see https://raf-query.katt.dev/docs/ssr-and-hydration#the-provider
7
9
  */
8
10
  export declare function QueryClientProvider(props: {
9
11
  readonly children?: ReactNode;
10
- readonly client: QueryClient<unknown>;
12
+ readonly client: QueryClient;
11
13
  }): import("react").JSX.Element;
12
- /** The client from the nearest {@link QueryClientProvider}. */
13
- export declare function useQueryClient(): QueryClient<unknown>;
14
+ /**
15
+ * The client from the nearest {@link QueryClientProvider}.
16
+ *
17
+ * @see https://raf-query.katt.dev/docs/api-reference#providers
18
+ */
19
+ export declare function useQueryClient(): QueryClient;
14
20
  //#endregion
package/dist/provider.mjs CHANGED
@@ -1,19 +1,18 @@
1
1
  "use client";
2
+ import { mount } from "@raf-query/core/internal";
2
3
  import { createContext, use, useEffect } from "react";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  //#region src/provider.tsx
5
- /**
6
- * A hook can't know which services the provider's client has, so its verbs
7
- * take any factory here.
8
- */
9
6
  const QueryClientContext = createContext(null);
10
7
  /**
11
- * Provides a client to the tree, and refetches its read, stale queries on
12
- * window focus and reconnect while mounted.
8
+ * Provides a client to the tree, and runs its features (focus and
9
+ * reconnect refetching, polling, …) while mounted.
10
+ *
11
+ * @see https://raf-query.katt.dev/docs/ssr-and-hydration#the-provider
13
12
  */
14
13
  function QueryClientProvider(props) {
15
14
  const client = props.client;
16
- useEffect(() => client.mount(), [client]);
15
+ useEffect(() => mount(client), [client]);
17
16
  return /* @__PURE__ */ jsx(QueryClientContext, {
18
17
  value: client,
19
18
  children: props.children
@@ -23,9 +22,13 @@ function QueryClientProvider(props) {
23
22
  function useOptionalQueryClient() {
24
23
  return use(QueryClientContext);
25
24
  }
26
- /** The client from the nearest {@link QueryClientProvider}. */
25
+ /**
26
+ * The client from the nearest {@link QueryClientProvider}.
27
+ *
28
+ * @see https://raf-query.katt.dev/docs/api-reference#providers
29
+ */
27
30
  function useQueryClient() {
28
- const client = use(QueryClientContext);
31
+ const client = useOptionalQueryClient();
29
32
  if (!client) throw new Error("useQueryClient: no QueryClientProvider above this component");
30
33
  return client;
31
34
  }
@@ -0,0 +1,43 @@
1
+ import { useQueryClient as useQueryClient$1 } from "./provider.mjs";
2
+ import { useQuery as useQuery$1 } from "./use-query.mjs";
3
+ import { useInfiniteQuery as useInfiniteQuery$1 } from "./use-infinite-query.mjs";
4
+ import { useIsFetching as useIsFetching$1 } from "./use-is-fetching.mjs";
5
+ import { QueryClient } from "@raf-query/core";
6
+ import { RootOf } from "@raf-query/core/internal";
7
+ //#region src/react-server-hooks.d.ts
8
+ /**
9
+ * `useQuery` in a server component: throws, since hooks belong in client
10
+ * components.
11
+ *
12
+ * @see https://raf-query.katt.dev/docs/react-server-components
13
+ */
14
+ export declare const useQuery: typeof useQuery$1;
15
+ /**
16
+ * `useInfiniteQuery` in a server component: throws, since hooks belong in client
17
+ * components.
18
+ *
19
+ * @see https://raf-query.katt.dev/docs/react-server-components
20
+ */
21
+ export declare const useInfiniteQuery: typeof useInfiniteQuery$1;
22
+ /**
23
+ * `useIsFetching` in a server component: throws, since hooks belong in client
24
+ * components.
25
+ *
26
+ * @see https://raf-query.katt.dev/docs/react-server-components
27
+ */
28
+ export declare const useIsFetching: typeof useIsFetching$1;
29
+ /**
30
+ * `useQueryClient` in a server component: throws, since hooks belong in client
31
+ * components.
32
+ *
33
+ * @see https://raf-query.katt.dev/docs/react-server-components
34
+ */
35
+ export declare const useQueryClient: typeof useQueryClient$1;
36
+ /**
37
+ * `bindUseQueryClient` runs at module level in a file server modules may
38
+ * import too, so it works here and returns the throwing `useQueryClient`.
39
+ *
40
+ * @see https://raf-query.katt.dev/docs/links-and-features#typing-meta-by-its-root
41
+ */
42
+ export declare function bindUseQueryClient<T>(): () => QueryClient<RootOf<T>>;
43
+ //#endregion
@@ -0,0 +1,45 @@
1
+ //#region src/react-server-hooks.ts
2
+ function clientOnly(name) {
3
+ return () => {
4
+ throw new Error(`${name} reads in client components: move this into a file with "use client". Server components start queries with load() and send them with <HydrationBoundary>.`);
5
+ };
6
+ }
7
+ /**
8
+ * `useQuery` in a server component: throws, since hooks belong in client
9
+ * components.
10
+ *
11
+ * @see https://raf-query.katt.dev/docs/react-server-components
12
+ */
13
+ const useQuery = /* @__PURE__ */ clientOnly("useQuery");
14
+ /**
15
+ * `useInfiniteQuery` in a server component: throws, since hooks belong in client
16
+ * components.
17
+ *
18
+ * @see https://raf-query.katt.dev/docs/react-server-components
19
+ */
20
+ const useInfiniteQuery = /* @__PURE__ */ clientOnly("useInfiniteQuery");
21
+ /**
22
+ * `useIsFetching` in a server component: throws, since hooks belong in client
23
+ * components.
24
+ *
25
+ * @see https://raf-query.katt.dev/docs/react-server-components
26
+ */
27
+ const useIsFetching = /* @__PURE__ */ clientOnly("useIsFetching");
28
+ /**
29
+ * `useQueryClient` in a server component: throws, since hooks belong in client
30
+ * components.
31
+ *
32
+ * @see https://raf-query.katt.dev/docs/react-server-components
33
+ */
34
+ const useQueryClient = /* @__PURE__ */ clientOnly("useQueryClient");
35
+ /**
36
+ * `bindUseQueryClient` runs at module level in a file server modules may
37
+ * import too, so it works here and returns the throwing `useQueryClient`.
38
+ *
39
+ * @see https://raf-query.katt.dev/docs/links-and-features#typing-meta-by-its-root
40
+ */
41
+ function bindUseQueryClient() {
42
+ return useQueryClient;
43
+ }
44
+ //#endregion
45
+ export { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
@@ -5,6 +5,8 @@ import { AnyRouter } from "@tanstack/react-router";
5
5
  * The browser build of `@raf-query/react/tanstack-router` (the `browser`
6
6
  * export condition): the client half only, so the server's dehydration
7
7
  * stream stays out of the app bundle.
8
+ *
9
+ * @see https://raf-query.katt.dev/docs/tanstack-start#setup
8
10
  */
9
11
  export declare function setupRouterIntegration<TRouter extends AnyRouter>(options: RouterIntegrationOptions<TRouter>): void;
10
12
  //#endregion
@@ -4,6 +4,8 @@ import { handleRedirects, setupClient } from "./tanstack-router-shared.mjs";
4
4
  * The browser build of `@raf-query/react/tanstack-router` (the `browser`
5
5
  * export condition): the client half only, so the server's dehydration
6
6
  * stream stays out of the app bundle.
7
+ *
8
+ * @see https://raf-query.katt.dev/docs/tanstack-start#setup
7
9
  */
8
10
  function setupRouterIntegration(options) {
9
11
  setupClient(options);
@@ -1,23 +1,25 @@
1
- import { DehydrateOptions, DehydratedState, HydrateOptions, QueryClient, QueryHandle } from "@raf-query/core";
1
+ import { DehydrateOptions, DehydratedState, QueryClient, QueryHandle } from "@raf-query/core";
2
2
  import { AnyRouter } from "@tanstack/react-router";
3
3
  //#region src/tanstack-router-shared.d.ts
4
4
  declare module "@tanstack/react-router" {
5
5
  interface SerializableExtensions {
6
6
  /**
7
- * Handles are `{ key, input, data? }`, which the router's serializer
8
- * sends as is (`data` is dehydrated anyway, so it is serializable). Its
9
- * type check can't see that on its own: keys are
10
- * `ReadonlyArray<unknown>`, and the handle type names its factory.
11
- * So `beforeLoad` can put handles in route context.
7
+ * Handles are plain `{ queryKey, input }`, which the router's serializer
8
+ * sends as is. Its type check can't see that on its own: the handle
9
+ * type names its factory. So loaders can return handles.
12
10
  */
13
11
  rafQueryHandle: QueryHandle;
14
12
  }
15
13
  }
14
+ /**
15
+ * Options for `setupRouterIntegration`.
16
+ *
17
+ * @see https://raf-query.katt.dev/docs/tanstack-start#setup
18
+ */
16
19
  export interface RouterIntegrationOptions<TRouter extends AnyRouter> {
17
20
  readonly dehydrateOptions?: DehydrateOptions;
18
21
  /** Navigate when a `queryFn` throws `redirect()`. Default `true`. */
19
22
  readonly handleRedirects?: boolean;
20
- readonly hydrateOptions?: HydrateOptions;
21
23
  readonly queryClient: QueryClient;
22
24
  readonly router: TRouter;
23
25
  }
@@ -1,34 +1,22 @@
1
1
  import { hydrate } from "@raf-query/core";
2
- import { Predicate } from "effect";
3
2
  import { isRedirect } from "@tanstack/react-router";
4
3
  //#region src/tanstack-router-shared.ts
5
4
  function hasRafQueries(value) {
6
- return Predicate.hasProperty(value, "raf") && Predicate.isObject(value.raf);
5
+ return typeof value === "object" && value !== null && "raf" in value;
7
6
  }
8
- /**
9
- * Hydrate each state as the server's stream delivers it. Reads the stream
10
- * directly: `Stream.fromReadableStream` + `QueryCache.hydrateStream` would
11
- * put Effect's `Stream` and `Queue` in the browser bundle.
12
- */
13
- async function hydrateEach(client, stream, options) {
14
- const reader = stream.getReader();
15
- for (;;) {
16
- const next = await reader.read();
17
- if (next.done) return;
18
- hydrate(client, next.value, options);
19
- }
7
+ /** Hydrate each state as the server's stream delivers it. */
8
+ async function hydrateEach(client, stream) {
9
+ for await (const state of stream) hydrate(client, state);
20
10
  }
21
11
  function setupClient(options) {
22
12
  const router = options.router;
23
13
  const client = options.queryClient;
24
- const hydrateOptions = options.hydrateOptions;
25
14
  const originalHydrate = router.options.hydrate;
26
15
  router.options.hydrate = async (dehydrated) => {
27
16
  await originalHydrate?.(dehydrated);
28
17
  if (!hasRafQueries(dehydrated)) return;
29
- const raf = dehydrated.raf;
30
- hydrate(client, raf.initial, hydrateOptions);
31
- hydrateEach(client, raf.stream, hydrateOptions).catch((error) => {
18
+ hydrate(client, dehydrated.raf.initial);
19
+ hydrateEach(client, dehydrated.raf.stream).catch((error) => {
32
20
  console.error("raf-query: reading the query stream failed", error);
33
21
  });
34
22
  };
@@ -36,11 +24,13 @@ function setupClient(options) {
36
24
  function handleRedirects(options) {
37
25
  if (!(options.handleRedirects ?? true)) return;
38
26
  const router = options.router;
27
+ const handled = /* @__PURE__ */ new WeakSet();
39
28
  options.queryClient.subscribe((event) => {
40
- if (event.type === "failed" && isRedirect(event.error)) {
41
- const redirect = event.error;
42
- redirect.options._fromLocation = router.stores.location.get();
43
- router.navigate(router.resolveRedirect(redirect).options);
29
+ const error = event.entry.state.error;
30
+ if (event.type === "updated" && isRedirect(error) && !handled.has(error)) {
31
+ handled.add(error);
32
+ error.options._fromLocation = router.stores.location.get();
33
+ router.navigate(router.resolveRedirect(error).options);
44
34
  }
45
35
  });
46
36
  }
@@ -5,12 +5,14 @@ import { AnyRouter } from "@tanstack/react-router";
5
5
  * Connects a query client to a TanStack Router (and TanStack Start) router:
6
6
  *
7
7
  * - During SSR, sends the cache to the browser with the router's dehydrated
8
- * state, then streams entries as they start or settle. Pending entries
9
- * cross as promises, so the browser resumes them instead of refetching.
8
+ * state, then streams entries as they start. Pending entries cross as
9
+ * promises, so the browser resumes them instead of refetching.
10
10
  * - Clears the request's cache when the response finishes.
11
11
  * - Turns `redirect()` thrown from a `queryFn` into a navigation.
12
12
  *
13
13
  * Call it in `getRouter`, which runs once per request on the server.
14
+ *
15
+ * @see https://raf-query.katt.dev/docs/tanstack-start#setup
14
16
  */
15
17
  export declare function setupRouterIntegration<TRouter extends AnyRouter>(options: RouterIntegrationOptions<TRouter>): void;
16
18
  //#endregion
@@ -1,17 +1,17 @@
1
1
  import { handleRedirects, setupClient } from "./tanstack-router-shared.mjs";
2
- import { Effect, Exit, Scope, Stream } from "effect";
2
+ import { dehydrateStream, removeAll } from "@raf-query/core";
3
3
  //#region src/tanstack-router.ts
4
4
  const noop = () => {};
5
5
  function setupServer(options) {
6
6
  const router = options.router;
7
- const queryClient = options.queryClient;
7
+ const client = options.queryClient;
8
8
  const originalDehydrate = router.options.dehydrate;
9
9
  let finish = noop;
10
10
  let cleanedUp = false;
11
11
  const teardown = () => {
12
12
  cleanedUp = true;
13
13
  finish();
14
- queryClient.clear();
14
+ removeAll(client);
15
15
  };
16
16
  router.serverSsrLifecycle = {
17
17
  ...router.serverSsrLifecycle,
@@ -20,21 +20,17 @@ function setupServer(options) {
20
20
  router.options.dehydrate = async () => {
21
21
  const dehydrated = await originalDehydrate?.();
22
22
  if (cleanedUp) {
23
- queryClient.clear();
23
+ removeAll(client);
24
24
  return;
25
25
  }
26
- const scope = Scope.makeUnsafe();
27
- const stream = Effect.runSync(queryClient.effect.dehydrateStream(options.dehydrateOptions).pipe(Scope.provide(scope)));
28
- finish = () => {
29
- finish = noop;
30
- Effect.runSync(Scope.close(scope, Exit.void));
31
- };
32
- router.serverSsr?.onRenderFinished(() => finish());
26
+ const stream = dehydrateStream(client, options.dehydrateOptions);
27
+ finish = () => stream.close();
28
+ router.serverSsr?.onRenderFinished(() => stream.close());
33
29
  return {
34
30
  ...dehydrated,
35
31
  raf: {
36
32
  initial: stream.initial,
37
- stream: Stream.toReadableStream(stream.updates)
33
+ stream: stream.updates
38
34
  }
39
35
  };
40
36
  };
@@ -43,12 +39,14 @@ function setupServer(options) {
43
39
  * Connects a query client to a TanStack Router (and TanStack Start) router:
44
40
  *
45
41
  * - During SSR, sends the cache to the browser with the router's dehydrated
46
- * state, then streams entries as they start or settle. Pending entries
47
- * cross as promises, so the browser resumes them instead of refetching.
42
+ * state, then streams entries as they start. Pending entries cross as
43
+ * promises, so the browser resumes them instead of refetching.
48
44
  * - Clears the request's cache when the response finishes.
49
45
  * - Turns `redirect()` thrown from a `queryFn` into a navigation.
50
46
  *
51
47
  * Call it in `getRouter`, which runs once per request on the server.
48
+ *
49
+ * @see https://raf-query.katt.dev/docs/tanstack-start#setup
52
50
  */
53
51
  function setupRouterIntegration(options) {
54
52
  if (options.router.isServer) setupServer(options);
@@ -0,0 +1 @@
1
+ import { Thenable } from "@raf-query/core";