@raf-query/core 0.12.0 → 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 (38) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/dist/client.d.mts +38 -5
  3. package/dist/client.mjs +37 -6
  4. package/dist/entry.d.mts +7 -0
  5. package/dist/entry.mjs +16 -2
  6. package/dist/features/focus-refetch/focus-refetch.d.mts +2 -0
  7. package/dist/features/focus-refetch/focus-refetch.mjs +2 -0
  8. package/dist/features/polling/polling.d.mts +5 -1
  9. package/dist/features/polling/polling.mjs +5 -1
  10. package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +2 -0
  11. package/dist/features/reconnect-refetch/reconnect-refetch.mjs +2 -0
  12. package/dist/hydration/hydration.d.mts +38 -3
  13. package/dist/hydration/hydration.mjs +16 -2
  14. package/dist/index.d.mts +3 -2
  15. package/dist/index.mjs +2 -1
  16. package/dist/infinite/infinite.d.mts +27 -15
  17. package/dist/infinite/infinite.mjs +40 -8
  18. package/dist/infinite/paging.d.mts +71 -0
  19. package/dist/infinite/paging.mjs +62 -0
  20. package/dist/internal.d.mts +3 -3
  21. package/dist/internal.mjs +3 -3
  22. package/dist/keys/keys.d.mts +9 -1
  23. package/dist/keys/targets.d.mts +8 -2
  24. package/dist/links/retry/retry.d.mts +7 -0
  25. package/dist/links/retry/retry.mjs +2 -0
  26. package/dist/links/timeout/timeout.d.mts +10 -2
  27. package/dist/links/timeout/timeout.mjs +10 -2
  28. package/dist/load.d.mts +28 -3
  29. package/dist/load.mjs +17 -3
  30. package/dist/options.d.mts +77 -9
  31. package/dist/options.mjs +7 -3
  32. package/dist/presets.d.mts +7 -1
  33. package/dist/presets.mjs +2 -0
  34. package/dist/root.d.mts +5 -1
  35. package/dist/thenable.d.mts +2 -0
  36. package/dist/verbs.d.mts +56 -15
  37. package/dist/verbs.mjs +34 -6
  38. package/package.json +1 -1
@@ -1,13 +1,14 @@
1
+ import { hashKey } from "../keys/keys.mjs";
1
2
  //#region src/infinite/infinite.ts
2
3
  /**
3
4
  * Typed options for a paged query factory. Readers get `{ pages, pageParams }`
4
5
  * and, on their state, `fetchNextPage` and `fetchPreviousPage`:
5
6
  *
6
7
  * ```ts
7
- * export const issueList = (input: { status: Status }) =>
8
+ * export const commentsQuery = (input: { slug: string }) =>
8
9
  * infiniteQueryOptions({
9
- * queryKey: issues("list", input),
10
- * queryFn: ({ pageParam, signal }) => listIssues({ ...input, cursor: pageParam }, { signal }),
10
+ * queryKey: comments(input.slug),
11
+ * queryFn: ({ pageParam, signal }) => listComments({ ...input, cursor: pageParam }, { signal }),
11
12
  * initialPageParam: 0,
12
13
  * getNextPageParam: (lastPage) => lastPage.nextCursor,
13
14
  * });
@@ -15,6 +16,8 @@
15
16
  *
16
17
  * Its `meta` is any record. To check `meta` against a client's links and
17
18
  * features, use the builder {@link bindInfiniteQueryOptions} returns.
19
+ *
20
+ * @see https://raf-query.katt.dev/docs/infinite-queries
18
21
  */
19
22
  const infiniteQueryOptions = (options) => ({
20
23
  ...options,
@@ -28,6 +31,8 @@ const infiniteQueryOptions = (options) => ({
28
31
  * // raf.ts
29
32
  * export const infiniteQueryOptions = bindInfiniteQueryOptions<typeof makeQueryClient>();
30
33
  * ```
34
+ *
35
+ * @see https://raf-query.katt.dev/docs/links-and-features#typing-meta-by-its-root
31
36
  */
32
37
  function bindInfiniteQueryOptions() {
33
38
  return infiniteQueryOptions;
@@ -41,8 +46,25 @@ function pageParamAfter(options, data, direction) {
41
46
  if (page === void 0 || !getParam) return;
42
47
  return getParam(page, data.pages, data.pageParams[index]) ?? void 0;
43
48
  }
49
+ /**
50
+ * The entry's data as it is now, which writes may have changed since `op`
51
+ * started, or `fallback` while it has none.
52
+ */
53
+ function latest(op, fallback) {
54
+ const state = op.client["~entries"].get(op.hash)?.state;
55
+ return state?.hasData ? state.data : fallback;
56
+ }
57
+ function sameParam(a, b) {
58
+ return hashKey([a]) === hashKey([b]);
59
+ }
60
+ /** The page `data` holds for `pageParam`, if any. */
61
+ function pageFor(data, pageParam) {
62
+ const index = data?.pageParams.findIndex((param) => sameParam(param, pageParam)) ?? -1;
63
+ return index === -1 ? void 0 : data?.pages[index];
64
+ }
44
65
  async function runPages(op, source) {
45
66
  const fetchPage = (pageParam, direction) => Promise.resolve(source.queryFn({
67
+ client: op.client,
46
68
  direction,
47
69
  meta: op.meta,
48
70
  pageParam,
@@ -55,20 +77,26 @@ async function runPages(op, source) {
55
77
  const pageParam = pageParamAfter(source, previous, direction);
56
78
  if (pageParam === void 0) return previous;
57
79
  const page = await fetchPage(pageParam, direction === "next" ? "forward" : "backward");
80
+ const current = latest(op, previous);
81
+ const edge = (data) => direction === "next" ? data.pageParams.at(-1) : data.pageParams[0];
82
+ if (!sameParam(edge(current), edge(previous))) return current;
58
83
  const max = source.maxPages ?? Infinity;
59
84
  return direction === "next" ? {
60
- pageParams: [...previous.pageParams, pageParam].slice(-max),
61
- pages: [...previous.pages, page].slice(-max)
85
+ pageParams: [...current.pageParams, pageParam].slice(-max),
86
+ pages: [...current.pages, page].slice(-max)
62
87
  } : {
63
- pageParams: [pageParam, ...previous.pageParams].slice(0, max),
64
- pages: [page, ...previous.pages].slice(0, max)
88
+ pageParams: [pageParam, ...current.pageParams].slice(0, max),
89
+ pages: [page, ...current.pages].slice(0, max)
65
90
  };
66
91
  }
67
92
  const count = previous?.pages.length ?? op.pages ?? 1;
68
93
  const pages = [];
69
94
  const pageParams = [];
95
+ /** The page the entry held for each param when that page's read started. */
96
+ const before = [];
70
97
  let pageParam = previous?.pageParams[0] ?? source.initialPageParam;
71
98
  while (pages.length < count) {
99
+ before.push(pageFor(latest(op, void 0), pageParam));
72
100
  const page = await fetchPage(pageParam, "forward");
73
101
  pages.push(page);
74
102
  pageParams.push(pageParam);
@@ -79,9 +107,13 @@ async function runPages(op, source) {
79
107
  if (next === void 0) break;
80
108
  pageParam = next;
81
109
  }
110
+ const now = latest(op, void 0);
82
111
  return {
83
112
  pageParams,
84
- pages
113
+ pages: pages.map((page, index) => {
114
+ const written = pageFor(now, pageParams[index]);
115
+ return written !== void 0 && written !== before[index] ? written : page;
116
+ })
85
117
  };
86
118
  }
87
119
  //#endregion
@@ -0,0 +1,71 @@
1
+ import { InfiniteQueryFactory, InputArgs } from "../options.mjs";
2
+ import { Entry, EntryState } from "../entry.mjs";
3
+ import { ClientFor } from "../verbs.mjs";
4
+ //#region src/infinite/paging.d.ts
5
+ /**
6
+ * Which way a page loads: after the last one, or before the first.
7
+ *
8
+ * @see https://raf-query.katt.dev/docs/infinite-queries#reading-pages
9
+ */
10
+ export type PageDirection = "next" | "previous";
11
+ /**
12
+ * Where a paged query can load more, and what it's loading now.
13
+ *
14
+ * @see https://raf-query.katt.dev/docs/infinite-queries#reading-pages
15
+ */
16
+ export interface PageState {
17
+ readonly hasNextPage: boolean;
18
+ readonly hasPreviousPage: boolean;
19
+ readonly isFetchingNextPage: boolean;
20
+ readonly isFetchingPreviousPage: boolean;
21
+ }
22
+ /**
23
+ * A paged query's {@link PageState} and the requests that load more:
24
+ * `useInfiniteQuery`'s paging controls.
25
+ *
26
+ * @see https://raf-query.katt.dev/docs/infinite-queries#reading-pages
27
+ */
28
+ export interface PageControls extends PageState {
29
+ /** Load the page after the last one. Resolves when it's there. */
30
+ fetchNextPage(): Promise<void>;
31
+ /** Load the page before the first one. Resolves when it's there. */
32
+ fetchPreviousPage(): Promise<void>;
33
+ }
34
+ /** A paged entry's {@link PageState}, from `state` (the entry's own by default). */
35
+ export declare function pageState(entry: Entry, state?: EntryState): PageState;
36
+ /**
37
+ * Load a paged entry's page in `direction`; resolves when the request
38
+ * settles, and does nothing without a page at that end. An error stays in
39
+ * the entry's state.
40
+ */
41
+ export declare function fetchPage(entry: Entry, direction: PageDirection): Promise<void>;
42
+ /**
43
+ * Load the page after the last one of a paged query, as `useInfiniteQuery`'s
44
+ * `fetchNextPage()` does. Resolves when the page is there, or the request
45
+ * settles; does nothing without a next page.
46
+ *
47
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
48
+ */
49
+ export declare function fetchNextPage<TFactory extends InfiniteQueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): Promise<void>;
50
+ /**
51
+ * Load the page before the first one of a paged query, as
52
+ * `useInfiniteQuery`'s `fetchPreviousPage()` does. See {@link fetchNextPage}.
53
+ *
54
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
55
+ */
56
+ export declare function fetchPreviousPage<TFactory extends InfiniteQueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): Promise<void>;
57
+ /**
58
+ * Whether a paged query has a page after its last one: `false` while it has
59
+ * no data, or once `getNextPageParam` returns `undefined`.
60
+ *
61
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
62
+ */
63
+ export declare function hasNextPage<TFactory extends InfiniteQueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): boolean;
64
+ /**
65
+ * Whether a paged query has a page before its first one. See
66
+ * {@link hasNextPage}.
67
+ *
68
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
69
+ */
70
+ export declare function hasPreviousPage<TFactory extends InfiniteQueryFactory>(client: ClientFor<TFactory>, factory: TFactory, ...input: InputArgs<TFactory>): boolean;
71
+ //#endregion
@@ -0,0 +1,62 @@
1
+ import { entryOf, fetchEntry } from "../entry.mjs";
2
+ import { pageParamAfter } from "./infinite.mjs";
3
+ //#region src/infinite/paging.ts
4
+ function hasPage(entry, state, direction) {
5
+ return state.hasData && pageParamAfter(entry.options, state.data, direction) !== void 0;
6
+ }
7
+ /** A paged entry's {@link PageState}, from `state` (the entry's own by default). */
8
+ function pageState(entry, state = entry.state) {
9
+ return {
10
+ hasNextPage: hasPage(entry, state, "next"),
11
+ hasPreviousPage: hasPage(entry, state, "previous"),
12
+ isFetchingNextPage: state.fetchingDirection === "next",
13
+ isFetchingPreviousPage: state.fetchingDirection === "previous"
14
+ };
15
+ }
16
+ /**
17
+ * Load a paged entry's page in `direction`; resolves when the request
18
+ * settles, and does nothing without a page at that end. An error stays in
19
+ * the entry's state.
20
+ */
21
+ function fetchPage(entry, direction) {
22
+ return fetchEntry(entry, { direction }).then(() => {});
23
+ }
24
+ /**
25
+ * Load the page after the last one of a paged query, as `useInfiniteQuery`'s
26
+ * `fetchNextPage()` does. Resolves when the page is there, or the request
27
+ * settles; does nothing without a next page.
28
+ *
29
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
30
+ */
31
+ function fetchNextPage(client, factory, ...input) {
32
+ return fetchPage(entryOf(client, factory, input[0]), "next");
33
+ }
34
+ /**
35
+ * Load the page before the first one of a paged query, as
36
+ * `useInfiniteQuery`'s `fetchPreviousPage()` does. See {@link fetchNextPage}.
37
+ *
38
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
39
+ */
40
+ function fetchPreviousPage(client, factory, ...input) {
41
+ return fetchPage(entryOf(client, factory, input[0]), "previous");
42
+ }
43
+ /**
44
+ * Whether a paged query has a page after its last one: `false` while it has
45
+ * no data, or once `getNextPageParam` returns `undefined`.
46
+ *
47
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
48
+ */
49
+ function hasNextPage(client, factory, ...input) {
50
+ return pageState(entryOf(client, factory, input[0])).hasNextPage;
51
+ }
52
+ /**
53
+ * Whether a paged query has a page before its first one. See
54
+ * {@link hasNextPage}.
55
+ *
56
+ * @see https://raf-query.katt.dev/docs/infinite-queries#outside-react
57
+ */
58
+ function hasPreviousPage(client, factory, ...input) {
59
+ return pageState(entryOf(client, factory, input[0])).hasPreviousPage;
60
+ }
61
+ //#endregion
62
+ export { fetchNextPage, fetchPage, fetchPreviousPage, hasNextPage, hasPreviousPage, pageState };
@@ -2,12 +2,12 @@ import { AnyFactory, hashKey } from "./keys/keys.mjs";
2
2
  import { Decl, DefaultTypes, MetaOf, RootOf, RootTypes } from "./root.mjs";
3
3
  import { AnyTarget } from "./keys/targets.mjs";
4
4
  import { fulfilled, mapThenable, pending, rejected } from "./thenable.mjs";
5
- import { Entry, EntryState, entryOf, fetchEntry, isInvalidated, isStale, remove, reset, subscribeEntry } from "./entry.mjs";
5
+ import { Entry, EntryState, entryOf, fetchEntry, isBrowserOnly, isInvalidated, isStale, remove, reset, subscribeEntry } from "./entry.mjs";
6
6
  import { AnyFeature, AnyLink, ClientEvent, mount, systemClock } from "./client.mjs";
7
7
  import { FocusRefetchMeta } from "./features/focus-refetch/focus-refetch.mjs";
8
8
  import { ReconnectRefetchMeta } from "./features/reconnect-refetch/reconnect-refetch.mjs";
9
9
  import { PollingMeta } from "./features/polling/polling.mjs";
10
- import { pageParamAfter } from "./infinite/infinite.mjs";
10
+ import { fetchPage, pageState } from "./infinite/paging.mjs";
11
11
  import { RetryMeta } from "./links/retry/retry.mjs";
12
12
  import { LoadRecord, isWaterfallHandle, loadsOf, readData, startIfStale } from "./load.mjs";
13
- export { type AnyFactory, type AnyFeature, type AnyLink, type AnyTarget, type ClientEvent, type Decl, type DefaultTypes, type Entry, type EntryState, type FocusRefetchMeta, type LoadRecord, type MetaOf, type PollingMeta, type ReconnectRefetchMeta, type RetryMeta, type RootOf, type RootTypes, entryOf, fetchEntry, fulfilled, hashKey, isInvalidated, isStale, isWaterfallHandle, loadsOf, mapThenable, mount, pageParamAfter, pending, readData, rejected, remove, reset, startIfStale, subscribeEntry, systemClock };
13
+ export { type AnyFactory, type AnyFeature, type AnyLink, type AnyTarget, type ClientEvent, type Decl, type DefaultTypes, type Entry, type EntryState, type FocusRefetchMeta, type LoadRecord, type MetaOf, type PollingMeta, type ReconnectRefetchMeta, type RetryMeta, type RootOf, type RootTypes, entryOf, fetchEntry, fetchPage, fulfilled, hashKey, isBrowserOnly, isInvalidated, isStale, isWaterfallHandle, loadsOf, mapThenable, mount, pageState, pending, readData, rejected, remove, reset, startIfStale, subscribeEntry, systemClock };
package/dist/internal.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mount, systemClock } from "./client.mjs";
2
2
  import { hashKey } from "./keys/keys.mjs";
3
3
  import { fulfilled, mapThenable, pending, rejected } from "./thenable.mjs";
4
- import { entryOf, fetchEntry, isInvalidated, isStale, remove, reset, subscribeEntry } from "./entry.mjs";
5
- import { pageParamAfter } from "./infinite/infinite.mjs";
4
+ import { entryOf, fetchEntry, isBrowserOnly, isInvalidated, isStale, remove, reset, subscribeEntry } from "./entry.mjs";
5
+ import { fetchPage, pageState } from "./infinite/paging.mjs";
6
6
  import { isWaterfallHandle, loadsOf, readData, startIfStale } from "./load.mjs";
7
- export { entryOf, fetchEntry, fulfilled, hashKey, isInvalidated, isStale, isWaterfallHandle, loadsOf, mapThenable, mount, pageParamAfter, pending, readData, rejected, remove, reset, startIfStale, subscribeEntry, systemClock };
7
+ export { entryOf, fetchEntry, fetchPage, fulfilled, hashKey, isBrowserOnly, isInvalidated, isStale, isWaterfallHandle, loadsOf, mapThenable, mount, pageState, pending, readData, rejected, remove, reset, startIfStale, subscribeEntry, systemClock };
@@ -8,13 +8,19 @@
8
8
  * that builds or matches a key lives in this folder, so the shape of
9
9
  * grouping can change without touching the cache.
10
10
  */
11
- /** A query's key: a plain, serializable array. */
11
+ /**
12
+ * A query's key: a plain, serializable array.
13
+ *
14
+ * @see https://raf-query.katt.dev/docs/query-keys
15
+ */
12
16
  export type QueryKey = ReadonlyArray<unknown>;
13
17
  declare const groupBrand: unique symbol;
14
18
  /**
15
19
  * A named set of queries: those whose key starts with its path. Call it
16
20
  * for a key under that path; a subgroup's path extends its parent's, so a
17
21
  * group covers its subgroups and their queries.
22
+ *
23
+ * @see https://raf-query.katt.dev/docs/query-keys#groups
18
24
  */
19
25
  export interface Group<TPath extends ReadonlyArray<string> = ReadonlyArray<string>> {
20
26
  <const TRest extends QueryKey>(...rest: TRest): readonly [...TPath, ...TRest];
@@ -34,6 +40,8 @@ export interface Group<TPath extends ReadonlyArray<string> = ReadonlyArray<strin
34
40
  * queryOptions({ queryKey: drafts("byId", input), queryFn }); // ["posts", "drafts", "byId", input]
35
41
  * await invalidateMany(client, posts); // drafts too
36
42
  * ```
43
+ *
44
+ * @see https://raf-query.katt.dev/docs/query-keys#groups
37
45
  */
38
46
  export declare function group<const TName extends string>(name: TName): Group<readonly [TName]>;
39
47
  export declare function group<const TPath extends ReadonlyArray<string>, const TName extends string>(parent: Group<TPath>, name: TName): Group<readonly [...TPath, TName]>;
@@ -1,7 +1,11 @@
1
1
  import { AnyFactory, Group, QueryKey } from "./keys.mjs";
2
2
  import { QueryMeta } from "../root.mjs";
3
3
  //#region src/keys/targets.d.ts
4
- /** What a target's `where` sees of a query. */
4
+ /**
5
+ * What a target's `where` sees of a query.
6
+ *
7
+ * @see https://raf-query.katt.dev/docs/query-invalidation#matching-with-where
8
+ */
5
9
  export interface QueryInfo<TMeta = QueryMeta> {
6
10
  /** The query's data, before `select`; `undefined` without data. */
7
11
  readonly data: unknown;
@@ -16,8 +20,10 @@ export interface QueryInfo<TMeta = QueryMeta> {
16
20
  *
17
21
  * ```ts
18
22
  * const ownedBy: Target<AppMeta, Team> = { where: (query, team) => query.meta.owner === team };
19
- * await invalidateMany(client, ownedBy, "posts-team");
23
+ * await invalidateMany(client, ownedBy, "editorial");
20
24
  * ```
25
+ *
26
+ * @see https://raf-query.katt.dev/docs/query-invalidation#targets
21
27
  */
22
28
  export interface Target<TMeta = QueryMeta, TParam = never> {
23
29
  readonly where: (query: QueryInfo<TMeta>, param: TParam) => boolean;
@@ -1,5 +1,10 @@
1
1
  import { Link } from "../../client.mjs";
2
2
  //#region src/links/retry/retry.d.ts
3
+ /**
4
+ * Options for {@link retry}.
5
+ *
6
+ * @see https://raf-query.katt.dev/docs/retries#options
7
+ */
3
8
  export interface RetryOptions {
4
9
  /** Retries after the first attempt. A query's `meta.retry` wins. Default 3. */
5
10
  readonly attempts?: number;
@@ -19,6 +24,8 @@ export interface RetryMeta {
19
24
  * server clients never retry, and a browser that is offline
20
25
  * waits until it's back online before retrying. A query sets its own
21
26
  * attempts with `meta: { retry }`.
27
+ *
28
+ * @see https://raf-query.katt.dev/docs/retries
22
29
  */
23
30
  export declare function retry(options?: RetryOptions): Link<{
24
31
  readonly meta: RetryMeta;
@@ -32,6 +32,8 @@ const notThrownResponse = (error) => !isResponse(error);
32
32
  * server clients never retry, and a browser that is offline
33
33
  * waits until it's back online before retrying. A query sets its own
34
34
  * attempts with `meta: { retry }`.
35
+ *
36
+ * @see https://raf-query.katt.dev/docs/retries
35
37
  */
36
38
  function retry(options = {}) {
37
39
  return link(async (op, next) => {
@@ -1,9 +1,17 @@
1
1
  import { Link } from "../../client.mjs";
2
2
  //#region src/links/timeout/timeout.d.ts
3
- /** What a request that took too long fails with. */
3
+ /**
4
+ * What a request that took too long fails with.
5
+ *
6
+ * @see https://raf-query.katt.dev/docs/retries#timeouts
7
+ */
4
8
  export declare class QueryTimeoutError extends Error {
5
9
  readonly name = "QueryTimeoutError";
6
10
  }
7
- /** Fail a request (and abort its `signal`) when it takes longer than `ms`. */
11
+ /**
12
+ * Fail a request (and abort its `signal`) when it takes longer than `ms`.
13
+ *
14
+ * @see https://raf-query.katt.dev/docs/retries#timeouts
15
+ */
8
16
  export declare function timeout(ms: number): Link;
9
17
  //#endregion
@@ -1,9 +1,17 @@
1
1
  //#region src/links/timeout/timeout.ts
2
- /** What a request that took too long fails with. */
2
+ /**
3
+ * What a request that took too long fails with.
4
+ *
5
+ * @see https://raf-query.katt.dev/docs/retries#timeouts
6
+ */
3
7
  var QueryTimeoutError = class extends Error {
4
8
  name = "QueryTimeoutError";
5
9
  };
6
- /** Fail a request (and abort its `signal`) when it takes longer than `ms`. */
10
+ /**
11
+ * Fail a request (and abort its `signal`) when it takes longer than `ms`.
12
+ *
13
+ * @see https://raf-query.katt.dev/docs/retries#timeouts
14
+ */
7
15
  function timeout(ms) {
8
16
  return (op, next) => {
9
17
  const controller = new AbortController();
package/dist/load.d.mts CHANGED
@@ -6,13 +6,19 @@ import { QueryClient } from "./client.mjs";
6
6
  * A small serializable reference to a query: its key and its factory's
7
7
  * input. It carries no data; readers get that from the cache. Handles of
8
8
  * different factories have different types.
9
+ *
10
+ * @see https://raf-query.katt.dev/docs/reading-queries#handles
9
11
  */
10
12
  export interface QueryHandle<TFactory extends QueryFactory = QueryFactory> {
11
13
  readonly input: InputOf<TFactory>;
12
14
  readonly queryKey: KeyOf<TFactory>;
13
15
  }
14
16
  type LoadKind = "prefetch" | "revalidate" | "swr";
15
- /** A query for `load` to start. Building one starts nothing. */
17
+ /**
18
+ * A query for `load` to start. Building one starts nothing.
19
+ *
20
+ * @see https://raf-query.katt.dev/docs/starting-queries
21
+ */
16
22
  export interface LoadEntry<TFactory extends QueryFactory = QueryFactory, TKind extends LoadKind = LoadKind> {
17
23
  readonly factory: TFactory;
18
24
  readonly input: InputOf<TFactory>;
@@ -20,6 +26,11 @@ export interface LoadEntry<TFactory extends QueryFactory = QueryFactory, TKind e
20
26
  /** For paged factories: how many pages a first load fetches. */
21
27
  readonly pages?: number;
22
28
  }
29
+ /**
30
+ * Options for a `load` entry.
31
+ *
32
+ * @see https://raf-query.katt.dev/docs/infinite-queries#loading-several-pages-up-front
33
+ */
23
34
  export interface LoadEntryOptions {
24
35
  /** For paged factories: how many pages a first load fetches. Default 1. */
25
36
  readonly pages?: number;
@@ -29,6 +40,8 @@ type LoadEntryArgs<TFactory extends QueryFactory> = [input: InputOf<TFactory>, o
29
40
  * Start the query if the cache has no fresh data, and don't wait for it.
30
41
  * It never makes the load reject: errors show up where a component reads
31
42
  * the handle.
43
+ *
44
+ * @see https://raf-query.katt.dev/docs/starting-queries#prefetch-or-swr
32
45
  */
33
46
  export declare function prefetch<TFactory extends QueryFactory>(factory: TFactory, ...args: LoadEntryArgs<TFactory>): LoadEntry<TFactory, "prefetch">;
34
47
  /**
@@ -37,6 +50,8 @@ export declare function prefetch<TFactory extends QueryFactory>(factory: TFactor
37
50
  * HTTP's
38
51
  * [`stale-while-revalidate`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#stale-while-revalidate)
39
52
  * does. The load rejects if the fetch it waits for fails.
53
+ *
54
+ * @see https://raf-query.katt.dev/docs/starting-queries#prefetch-or-swr
40
55
  */
41
56
  export declare function swr<TFactory extends QueryFactory>(factory: TFactory, ...args: LoadEntryArgs<TFactory>): LoadEntry<TFactory, "swr">;
42
57
  /**
@@ -46,6 +61,8 @@ export declare function swr<TFactory extends QueryFactory>(factory: TFactory, ..
46
61
  * is the max-age); with stale data or none it waits for the fetch. On a
47
62
  * server client, data counts as fresh until a write invalidates it. The
48
63
  * load rejects if the fetch fails.
64
+ *
65
+ * @see https://raf-query.katt.dev/docs/starting-queries#the-three-entries
49
66
  */
50
67
  export declare function revalidate<TFactory extends QueryFactory>(factory: TFactory, ...args: LoadEntryArgs<TFactory>): LoadEntry<TFactory, "revalidate">;
51
68
  /**
@@ -58,6 +75,8 @@ export declare function revalidate<TFactory extends QueryFactory>(factory: TFact
58
75
  * drafts: user.isAdmin ? prefetch(draftsQuery, { slug }) : skipToken,
59
76
  * });
60
77
  * ```
78
+ *
79
+ * @see https://raf-query.katt.dev/docs/starting-queries#conditional-queries
61
80
  */
62
81
  export declare const skipToken: unique symbol;
63
82
  /** What `load` takes per name: an entry, or `skipToken`. */
@@ -68,6 +87,8 @@ type LoadedData<TEntries extends LoadEntries> = { [TName in keyof TEntries as TE
68
87
  /**
69
88
  * What `load` resolves to: a handle per entry (`undefined` for `skipToken`),
70
89
  * and the data of `swr` and `revalidate` entries.
90
+ *
91
+ * @see https://raf-query.katt.dev/docs/starting-queries
71
92
  */
72
93
  export type Loaded<TEntries extends LoadEntries> = readonly [handles: { [TName in keyof TEntries]: HandleOf<TEntries[TName]>; }, data: LoadedData<TEntries>];
73
94
  /** A `load` call as devtools show it. */
@@ -96,10 +117,12 @@ export declare function startIfStale(entry: Entry): void;
96
117
  *
97
118
  * ```ts
98
119
  * const [handles, data] = await load(queryClient, {
99
- * comments: prefetch(commentsQuery, { issueId: id }),
100
- * issue: swr(issueQuery, { id }),
120
+ * comments: prefetch(commentsQuery, { slug }),
121
+ * post: swr(postQuery, { slug }),
101
122
  * });
102
123
  * ```
124
+ *
125
+ * @see https://raf-query.katt.dev/docs/starting-queries
103
126
  */
104
127
  export declare function load<const TEntries extends LoadEntries>(client: QueryClient, entries: TEntries): Promise<Loaded<TEntries>>;
105
128
  /** Whether a handle came from `waterfallHandle()`: reading it is expected to fetch. */
@@ -118,6 +141,8 @@ export declare function isWaterfallHandle(handle: Pick<QueryHandle, "queryKey">)
118
141
  * ```ts
119
142
  * const searchHandle = waterfallHandle(searchQuery, props.search, (input) => ({ ...input, q }));
120
143
  * ```
144
+ *
145
+ * @see https://raf-query.katt.dev/docs/request-waterfalls#render-time-inputs-waterfallhandle
121
146
  */
122
147
  export declare function waterfallHandle<TFactory extends QueryFactory>(factory: TFactory, ...input: InputArgs<TFactory>): QueryHandle<TFactory>;
123
148
  export declare function waterfallHandle<TFactory extends QueryFactory>(factory: TFactory, handle: QueryHandle<TFactory>, update: (input: InputOf<TFactory>) => InputOf<TFactory>): QueryHandle<TFactory>;
package/dist/load.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { hashKey } from "./keys/keys.mjs";
2
2
  import { optionsOf } from "./options.mjs";
3
- import { entryOf, fetchEntry, isInvalidated, isStale } from "./entry.mjs";
3
+ import { entryOf, fetchEntry, isBrowserOnly, isInvalidated, isStale } from "./entry.mjs";
4
4
  //#region src/load.ts
5
5
  function loadEntry(kind, factory, args) {
6
6
  return {
@@ -14,6 +14,8 @@ function loadEntry(kind, factory, args) {
14
14
  * Start the query if the cache has no fresh data, and don't wait for it.
15
15
  * It never makes the load reject: errors show up where a component reads
16
16
  * the handle.
17
+ *
18
+ * @see https://raf-query.katt.dev/docs/starting-queries#prefetch-or-swr
17
19
  */
18
20
  function prefetch(factory, ...args) {
19
21
  return loadEntry("prefetch", factory, args);
@@ -24,6 +26,8 @@ function prefetch(factory, ...args) {
24
26
  * HTTP's
25
27
  * [`stale-while-revalidate`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#stale-while-revalidate)
26
28
  * does. The load rejects if the fetch it waits for fails.
29
+ *
30
+ * @see https://raf-query.katt.dev/docs/starting-queries#prefetch-or-swr
27
31
  */
28
32
  function swr(factory, ...args) {
29
33
  return loadEntry("swr", factory, args);
@@ -35,6 +39,8 @@ function swr(factory, ...args) {
35
39
  * is the max-age); with stale data or none it waits for the fetch. On a
36
40
  * server client, data counts as fresh until a write invalidates it. The
37
41
  * load rejects if the fetch fails.
42
+ *
43
+ * @see https://raf-query.katt.dev/docs/starting-queries#the-three-entries
38
44
  */
39
45
  function revalidate(factory, ...args) {
40
46
  return loadEntry("revalidate", factory, args);
@@ -49,6 +55,8 @@ function revalidate(factory, ...args) {
49
55
  * drafts: user.isAdmin ? prefetch(draftsQuery, { slug }) : skipToken,
50
56
  * });
51
57
  * ```
58
+ *
59
+ * @see https://raf-query.katt.dev/docs/starting-queries#conditional-queries
52
60
  */
53
61
  const skipToken = Symbol("skipToken");
54
62
  const loadRecords = /* @__PURE__ */ new WeakMap();
@@ -93,10 +101,12 @@ function waitAny(entry, pages) {
93
101
  *
94
102
  * ```ts
95
103
  * const [handles, data] = await load(queryClient, {
96
- * comments: prefetch(commentsQuery, { issueId: id }),
97
- * issue: swr(issueQuery, { id }),
104
+ * comments: prefetch(commentsQuery, { slug }),
105
+ * post: swr(postQuery, { slug }),
98
106
  * });
99
107
  * ```
108
+ *
109
+ * @see https://raf-query.katt.dev/docs/starting-queries
100
110
  */
101
111
  async function load(client, entries) {
102
112
  const started = Object.entries(entries).flatMap(([name, item]) => item === skipToken ? [] : [{
@@ -107,6 +117,10 @@ async function load(client, entries) {
107
117
  const cached = entryOf(client, item.factory, item.input);
108
118
  let wait;
109
119
  if (item.kind === "prefetch") startIfStale(cached);
120
+ else if (isBrowserOnly(cached) && !cached.state.hasData) wait = Promise.resolve({
121
+ error: /* @__PURE__ */ new Error(`raf-query: ${cached.hash} has \`ssr: false\`, so the server can't wait for it. Load it with prefetch() instead of ${item.kind}().`),
122
+ ok: false
123
+ });
110
124
  else wait = (item.kind === "revalidate" ? waitFresh : waitAny)(cached, item.pages);
111
125
  return {
112
126
  entry: cached,