@raf-query/react 0.11.1 → 0.12.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 (44) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +32 -59
  3. package/dist/bind.d.mts +19 -0
  4. package/dist/bind.mjs +21 -0
  5. package/dist/browser-render.mjs +46 -0
  6. package/dist/devtools.d.mts +24 -2
  7. package/dist/devtools.mjs +236 -1
  8. package/dist/hydration-boundary/hydrate.mjs +10 -13
  9. package/dist/hydration-boundary/hydration-boundary-react-server.d.mts +4 -0
  10. package/dist/hydration-boundary/hydration-boundary-react-server.mjs +18 -9
  11. package/dist/hydration-boundary/hydration-boundary.d.mts +9 -29
  12. package/dist/hydration-boundary/hydration-boundary.mjs +6 -27
  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 +4 -4
  20. package/dist/provider.mjs +5 -8
  21. package/dist/react-server-hooks.d.mts +17 -0
  22. package/dist/react-server-hooks.mjs +19 -0
  23. package/dist/tanstack-router-shared.d.mts +4 -7
  24. package/dist/tanstack-router-shared.mjs +12 -22
  25. package/dist/tanstack-router.d.mts +2 -2
  26. package/dist/tanstack-router.mjs +10 -14
  27. package/dist/tracked.d.mts +1 -0
  28. package/dist/tracked.mjs +65 -0
  29. package/dist/use-infinite-query.d.mts +27 -0
  30. package/dist/use-infinite-query.mjs +35 -0
  31. package/dist/use-is-fetching.d.mts +6 -16
  32. package/dist/use-is-fetching.mjs +11 -31
  33. package/dist/use-query.d.mts +43 -54
  34. package/dist/use-query.mjs +102 -47
  35. package/package.json +10 -9
  36. package/dist/devtools-panel.d.mts +0 -38
  37. package/dist/devtools-panel.mjs +0 -896
  38. package/dist/devtools-store.mjs +0 -104
  39. package/dist/hydration-boundary/sent.mjs +0 -16
  40. package/dist/query-provider/browser-requests.mjs +0 -38
  41. package/dist/query-provider/query-provider.d.mts +0 -21
  42. package/dist/query-provider/query-provider.mjs +0 -33
  43. package/dist/use-waterfall.d.mts +0 -18
  44. package/dist/use-waterfall.mjs +0 -41
@@ -1,4 +1,4 @@
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
4
  export type HydrationBoundaryProps = {
@@ -13,8 +13,8 @@ export type HydrationBoundaryProps = {
13
13
  } | {
14
14
  readonly children?: ReactNode;
15
15
  readonly client?: never;
16
- readonly options?: HydrateOptions;
17
- /** A loader's `dehydrate(qc)`. */
16
+ readonly options?: never;
17
+ /** A loader's `dehydrate(client)`. */
18
18
  readonly state: DehydratedState;
19
19
  };
20
20
  /**
@@ -27,38 +27,18 @@ export type HydrationBoundaryProps = {
27
27
  * through the package's `react-server` export condition.
28
28
  *
29
29
  * ```tsx
30
- * const qc = getQueryClient();
31
- * const comments = qc.prefetch(commentsQuery, { slug });
30
+ * const client = getQueryClient();
31
+ * const [handles] = await load(client, { comments: prefetch(commentsQuery, { slug }) });
32
32
  * return (
33
- * <HydrationBoundary client={qc}>
34
- * <Comments handle={comments} />
33
+ * <HydrationBoundary client={client}>
34
+ * <Comments handle={handles.comments} />
35
35
  * </HydrationBoundary>
36
36
  * );
37
37
  * ```
38
38
  *
39
39
  * 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
- * ```
58
- *
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.
40
+ * loader dehydrated. Pending entries cross as promises when the framework's
41
+ * serializer supports them (React Router's Single Fetch does).
62
42
  */
63
43
  export declare function HydrationBoundary(props: HydrationBoundaryProps): import("react").JSX.Element;
64
44
  //#endregion
@@ -12,43 +12,22 @@ 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:
26
- *
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.
25
+ * loader dehydrated. Pending entries cross as promises when the framework's
26
+ * serializer supports them (React Router's Single Fetch does).
47
27
  */
48
28
  function HydrationBoundary(props) {
49
29
  if (props.state === void 0) throw new Error("HydrationBoundary needs `state` here. `client` works only in server components.");
50
30
  return /* @__PURE__ */ jsx(Hydrate, {
51
- options: props.options,
52
31
  state: props.state,
53
32
  children: props.children
54
33
  });
@@ -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, PageControls } 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, type PageControls, 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, PageControls } 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, type PageControls, 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, PageControls, 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, type PageControls, 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,13 @@ 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
7
  */
8
8
  export declare function QueryClientProvider(props: {
9
9
  readonly children?: ReactNode;
10
- readonly client: QueryClient<unknown>;
10
+ readonly client: QueryClient;
11
11
  }): import("react").JSX.Element;
12
12
  /** The client from the nearest {@link QueryClientProvider}. */
13
- export declare function useQueryClient(): QueryClient<unknown>;
13
+ export declare function useQueryClient(): QueryClient;
14
14
  //#endregion
package/dist/provider.mjs CHANGED
@@ -1,19 +1,16 @@
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.
13
10
  */
14
11
  function QueryClientProvider(props) {
15
12
  const client = props.client;
16
- useEffect(() => client.mount(), [client]);
13
+ useEffect(() => mount(client), [client]);
17
14
  return /* @__PURE__ */ jsx(QueryClientContext, {
18
15
  value: client,
19
16
  children: props.children
@@ -25,7 +22,7 @@ function useOptionalQueryClient() {
25
22
  }
26
23
  /** The client from the nearest {@link QueryClientProvider}. */
27
24
  function useQueryClient() {
28
- const client = use(QueryClientContext);
25
+ const client = useOptionalQueryClient();
29
26
  if (!client) throw new Error("useQueryClient: no QueryClientProvider above this component");
30
27
  return client;
31
28
  }
@@ -0,0 +1,17 @@
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
+ export declare const useQuery: typeof useQuery$1;
9
+ export declare const useInfiniteQuery: typeof useInfiniteQuery$1;
10
+ export declare const useIsFetching: typeof useIsFetching$1;
11
+ export declare const useQueryClient: typeof useQueryClient$1;
12
+ /**
13
+ * `bindUseQueryClient` runs at module level in a file server modules may
14
+ * import too, so it works here and returns the throwing `useQueryClient`.
15
+ */
16
+ export declare function bindUseQueryClient<T>(): () => QueryClient<RootOf<T>>;
17
+ //#endregion
@@ -0,0 +1,19 @@
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
+ const useQuery = /* @__PURE__ */ clientOnly("useQuery");
8
+ const useInfiniteQuery = /* @__PURE__ */ clientOnly("useInfiniteQuery");
9
+ const useIsFetching = /* @__PURE__ */ clientOnly("useIsFetching");
10
+ const useQueryClient = /* @__PURE__ */ clientOnly("useQueryClient");
11
+ /**
12
+ * `bindUseQueryClient` runs at module level in a file server modules may
13
+ * import too, so it works here and returns the throwing `useQueryClient`.
14
+ */
15
+ function bindUseQueryClient() {
16
+ return useQueryClient;
17
+ }
18
+ //#endregion
19
+ export { bindUseQueryClient, useInfiniteQuery, useIsFetching, useQuery, useQueryClient };
@@ -1,14 +1,12 @@
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
  }
@@ -17,7 +15,6 @@ export interface RouterIntegrationOptions<TRouter extends AnyRouter> {
17
15
  readonly dehydrateOptions?: DehydrateOptions;
18
16
  /** Navigate when a `queryFn` throws `redirect()`. Default `true`. */
19
17
  readonly handleRedirects?: boolean;
20
- readonly hydrateOptions?: HydrateOptions;
21
18
  readonly queryClient: QueryClient;
22
19
  readonly router: TRouter;
23
20
  }
@@ -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,8 +5,8 @@ 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
  *
@@ -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,8 +39,8 @@ 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
  *
@@ -0,0 +1 @@
1
+ import { Thenable } from "@raf-query/core";
@@ -0,0 +1,65 @@
1
+ //#region src/tracked.ts
2
+ function createTracker() {
3
+ return {
4
+ actions: /* @__PURE__ */ new Map(),
5
+ proxy: void 0,
6
+ rendered: void 0,
7
+ tracked: /* @__PURE__ */ new Set()
8
+ };
9
+ }
10
+ function renderedOf(tracker) {
11
+ return tracker.rendered;
12
+ }
13
+ /** A function that calls the latest render's, so it keeps its identity across renders. */
14
+ function stableAction(tracker, name) {
15
+ let action = tracker.actions.get(name);
16
+ if (!action) {
17
+ action = (...args) => {
18
+ const latest = renderedOf(tracker).actions[name];
19
+ return latest(...args);
20
+ };
21
+ tracker.actions.set(name, action);
22
+ }
23
+ return action;
24
+ }
25
+ function proxyOf(tracker, read) {
26
+ return new Proxy({
27
+ ...read.fields,
28
+ ...read.actions
29
+ }, { get(_, key) {
30
+ const name = String(key);
31
+ if (read.actions[name]) return stableAction(tracker, name);
32
+ tracker.tracked.add(name);
33
+ return renderedOf(tracker).fields[name];
34
+ } });
35
+ }
36
+ /** Functions are stable, so only data fields count as changes. */
37
+ function trackedChanged(tracker, next) {
38
+ const previous = renderedOf(tracker).fields;
39
+ return [...tracker.tracked].some((name) => !Object.is(previous[name], next.fields[name]));
40
+ }
41
+ /**
42
+ * The state a render returns: the same object as the last render's unless
43
+ * a field the component read changed. Reads go through to this render's
44
+ * fields, so a field first read now is current, and is tracked from now on.
45
+ */
46
+ function trackedState(tracker, read, refresh) {
47
+ if (tracker.proxy === void 0 || trackedChanged(tracker, read)) tracker.proxy = proxyOf(tracker, read);
48
+ tracker.rendered = {
49
+ ...read,
50
+ refresh,
51
+ status: read.source.status
52
+ };
53
+ return tracker.proxy;
54
+ }
55
+ /**
56
+ * Whether a write changed what the component rendered: the thenable or its
57
+ * outcome, or a field it read. Fields it never read don't re-render it.
58
+ */
59
+ function renderedChanged(tracker) {
60
+ const rendered = renderedOf(tracker);
61
+ const next = rendered.refresh();
62
+ return next.source !== rendered.source || next.source.status !== rendered.status || trackedChanged(tracker, next);
63
+ }
64
+ //#endregion
65
+ export { createTracker, renderedChanged, trackedState };
@@ -0,0 +1,27 @@
1
+ import { QueryState, QueryValue, UseQueryOptions } from "./use-query.mjs";
2
+ import { InfiniteQueryFactory, QueryHandle } from "@raf-query/core";
3
+ //#region src/use-infinite-query.d.ts
4
+ /** The paging half of what `useInfiniteQuery` returns. */
5
+ export interface PageControls {
6
+ /** Load the page after the last one. Resolves when it's there. */
7
+ fetchNextPage(): Promise<void>;
8
+ /** Load the page before the first one. Resolves when it's there. */
9
+ fetchPreviousPage(): Promise<void>;
10
+ readonly hasNextPage: boolean;
11
+ readonly hasPreviousPage: boolean;
12
+ readonly isFetchingNextPage: boolean;
13
+ readonly isFetchingPreviousPage: boolean;
14
+ }
15
+ /** What `useInfiniteQuery` returns next to the data. */
16
+ export interface InfiniteQueryState<TFactory extends InfiniteQueryFactory = InfiniteQueryFactory> extends PageControls, QueryState<TFactory> {}
17
+ /**
18
+ * Read a paged query (`infiniteQueryOptions`) by its handle. The data is
19
+ * `{ pages, pageParams }`, and its state loads more pages.
20
+ *
21
+ * ```tsx
22
+ * const [pagesThenable, pagesState] = useInfiniteQuery(commentsQuery, handles.comments);
23
+ * const pages = use(pagesThenable);
24
+ * ```
25
+ */
26
+ export declare function useInfiniteQuery<TFactory extends InfiniteQueryFactory>(factory: TFactory, handle: QueryHandle<TFactory>, options?: UseQueryOptions): readonly [QueryValue<TFactory>, InfiniteQueryState<TFactory>];
27
+ //#endregion
@@ -0,0 +1,35 @@
1
+ import { settled, useQueryRead } from "./use-query.mjs";
2
+ import { fetchEntry, pageParamAfter } from "@raf-query/core/internal";
3
+ //#region src/use-infinite-query.ts
4
+ function hasPage(entry, state, direction) {
5
+ return state.hasData && pageParamAfter(entry.options, state.data, direction) !== void 0;
6
+ }
7
+ function pageFields(entry, state) {
8
+ return {
9
+ actions: {
10
+ fetchNextPage: () => settled(fetchEntry(entry, { direction: "next" })),
11
+ fetchPreviousPage: () => settled(fetchEntry(entry, { direction: "previous" }))
12
+ },
13
+ fields: {
14
+ hasNextPage: hasPage(entry, state, "next"),
15
+ hasPreviousPage: hasPage(entry, state, "previous"),
16
+ isFetchingNextPage: state.fetchingDirection === "next",
17
+ isFetchingPreviousPage: state.fetchingDirection === "previous"
18
+ }
19
+ };
20
+ }
21
+ /**
22
+ * Read a paged query (`infiniteQueryOptions`) by its handle. The data is
23
+ * `{ pages, pageParams }`, and its state loads more pages.
24
+ *
25
+ * ```tsx
26
+ * const [pagesThenable, pagesState] = useInfiniteQuery(commentsQuery, handles.comments);
27
+ * const pages = use(pagesThenable);
28
+ * ```
29
+ */
30
+ function useInfiniteQuery(factory, handle, options) {
31
+ const read = useQueryRead(factory, handle, options, pageFields);
32
+ return [read.thenable, read.controls];
33
+ }
34
+ //#endregion
35
+ export { useInfiniteQuery };
@@ -1,20 +1,10 @@
1
- import { QueryCache } from "@raf-query/core";
1
+ import { Group } from "@raf-query/core";
2
+ import { AnyFactory, AnyTarget } from "@raf-query/core/internal";
2
3
  //#region src/use-is-fetching.d.ts
3
4
  /**
4
- * How many queries have a request in flight: all of them, or those matching
5
- * `filter`. Re-renders when the count changes. It's `0` on the server and
6
- * while hydrating, so the server's HTML and the browser's first render agree.
7
- *
8
- * You probably don't need this. Prefer the router's pending UI for
9
- * navigations (`useRouterState` / `pendingComponent` in TanStack Router,
10
- * `useLinkStatus` in Next.js) and per-query `controls.isFetching` for
11
- * background refetches. Kept for TanStack Query compatibility when you want
12
- * one global count of every in-flight request:
13
- *
14
- * ```tsx
15
- * const fetching = useIsFetching();
16
- * return fetching > 0 ? <ProgressBar /> : null;
17
- * ```
5
+ * How many queries have a request in flight: all of them, or a factory's,
6
+ * a group's or a target's. For a global loading indicator. Always `0` on
7
+ * the server and while hydrating, so the HTML matches the first browser render.
18
8
  */
19
- export declare function useIsFetching(filter?: QueryCache.Filter): number;
9
+ export declare function useIsFetching(target?: AnyFactory | AnyTarget | Group): number;
20
10
  //#endregion