@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.
- package/CHANGELOG.md +30 -2
- package/dist/client.d.mts +38 -5
- package/dist/client.mjs +37 -6
- package/dist/entry.d.mts +7 -0
- package/dist/entry.mjs +16 -2
- package/dist/features/focus-refetch/focus-refetch.d.mts +2 -0
- package/dist/features/focus-refetch/focus-refetch.mjs +2 -0
- package/dist/features/polling/polling.d.mts +5 -1
- package/dist/features/polling/polling.mjs +5 -1
- package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +2 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.mjs +2 -0
- package/dist/hydration/hydration.d.mts +38 -3
- package/dist/hydration/hydration.mjs +16 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +2 -1
- package/dist/infinite/infinite.d.mts +27 -15
- package/dist/infinite/infinite.mjs +40 -8
- package/dist/infinite/paging.d.mts +71 -0
- package/dist/infinite/paging.mjs +62 -0
- package/dist/internal.d.mts +3 -3
- package/dist/internal.mjs +3 -3
- package/dist/keys/keys.d.mts +9 -1
- package/dist/keys/targets.d.mts +8 -2
- package/dist/links/retry/retry.d.mts +7 -0
- package/dist/links/retry/retry.mjs +2 -0
- package/dist/links/timeout/timeout.d.mts +10 -2
- package/dist/links/timeout/timeout.mjs +10 -2
- package/dist/load.d.mts +28 -3
- package/dist/load.mjs +17 -3
- package/dist/options.d.mts +77 -9
- package/dist/options.mjs +7 -3
- package/dist/presets.d.mts +7 -1
- package/dist/presets.mjs +2 -0
- package/dist/root.d.mts +5 -1
- package/dist/thenable.d.mts +2 -0
- package/dist/verbs.d.mts +56 -15
- package/dist/verbs.mjs +34 -6
- 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
|
|
8
|
+
* export const commentsQuery = (input: { slug: string }) =>
|
|
8
9
|
* infiniteQueryOptions({
|
|
9
|
-
* queryKey:
|
|
10
|
-
* queryFn: ({ 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: [...
|
|
61
|
-
pages: [...
|
|
85
|
+
pageParams: [...current.pageParams, pageParam].slice(-max),
|
|
86
|
+
pages: [...current.pages, page].slice(-max)
|
|
62
87
|
} : {
|
|
63
|
-
pageParams: [pageParam, ...
|
|
64
|
-
pages: [page, ...
|
|
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 };
|
package/dist/internal.d.mts
CHANGED
|
@@ -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 {
|
|
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,
|
|
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 {
|
|
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,
|
|
7
|
+
export { entryOf, fetchEntry, fetchPage, fulfilled, hashKey, isBrowserOnly, isInvalidated, isStale, isWaterfallHandle, loadsOf, mapThenable, mount, pageState, pending, readData, rejected, remove, reset, startIfStale, subscribeEntry, systemClock };
|
package/dist/keys/keys.d.mts
CHANGED
|
@@ -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
|
-
/**
|
|
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]>;
|
package/dist/keys/targets.d.mts
CHANGED
|
@@ -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
|
-
/**
|
|
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, "
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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, {
|
|
100
|
-
*
|
|
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, {
|
|
97
|
-
*
|
|
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,
|