@raf-query/core 0.0.17 → 0.0.19
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 +13 -1
- package/README.md +9 -6
- package/dist/client.d.mts +8 -2
- package/dist/client.mjs +8 -7
- package/dist/index.d.mts +2 -1
- package/dist/observer.d.mts +2 -1
- package/dist/query.d.mts +3 -2
- package/dist/thenable.d.mts +24 -0
- package/dist/thenable.mjs +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.19](https://github.com/KATT/raf-query/compare/0.0.18...0.0.19) (2026-10-12)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **core:** type the cache's promises as Thenable ([#21](https://github.com/KATT/raf-query/issues/21)) ([c0541b0](https://github.com/KATT/raf-query/commit/c0541b0c14e687f25d47f25fe457e49b135d8c06))
|
|
8
|
+
|
|
9
|
+
## [0.0.18](https://github.com/KATT/raf-query/compare/0.0.17...0.0.18) (2026-10-12)
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
- **core:** allow undefined query data ([#22](https://github.com/KATT/raf-query/issues/22)) ([9b85e57](https://github.com/KATT/raf-query/commit/9b85e57bf2fed4413cdbc1b49f925cc60dad695f)), references [#20](https://github.com/KATT/raf-query/issues/20)
|
|
14
|
+
|
|
3
15
|
## [0.0.17](https://github.com/KATT/raf-query/compare/0.0.16...0.0.17) (2026-10-12)
|
|
4
16
|
|
|
5
17
|
### Features
|
|
6
18
|
|
|
7
|
-
|
|
19
|
+
- **core:** pause retries while the browser is offline ([#18](https://github.com/KATT/raf-query/issues/18)) ([a25c664](https://github.com/KATT/raf-query/commit/a25c664359fe7248cdc8d91a14d524e37b467c6c))
|
|
8
20
|
|
|
9
21
|
## [0.0.16](https://github.com/KATT/raf-query/compare/0.0.15...0.0.16) (2026-10-12)
|
|
10
22
|
|
package/README.md
CHANGED
|
@@ -363,6 +363,8 @@ function EditTitle(props: { post: Post }) {
|
|
|
363
363
|
| `qc.setData(factory, input, updater)` | Write data directly |
|
|
364
364
|
| `qc.getData(factory, input)` | Read cached data, or `undefined` |
|
|
365
365
|
|
|
366
|
+
Data can be `undefined` when the `queryFn`'s type allows it (`Promise<User | undefined>`); it's cached and reused like any other value, unlike in TanStack Query, where an `undefined` result is an error. So `getData` and an updater's `previous` return `undefined` both with no data and with `undefined` data; `qc.getQuery(factory, input).getState().hasData` tells them apart.
|
|
367
|
+
|
|
366
368
|
## SSR and hydration
|
|
367
369
|
|
|
368
370
|
The server's query client streams its cache to the browser's query client as entries settle. Pending queries cross as promises, so the browser resumes the server's request instead of starting a new one. Components never hydrate anything themselves; a handle only needs its key to find the entry.
|
|
@@ -572,16 +574,17 @@ In the browser, retries pause while `navigator.onLine` is `false`: after the ret
|
|
|
572
574
|
| `CancelledError` | Rejection of a request cancelled by `clear()` or a restart |
|
|
573
575
|
| `QueryHandle<TFactory>` | Handle type for props |
|
|
574
576
|
| `EnsuredQueryHandle<TFactory>` | Handle type for props fed by `ensure` |
|
|
577
|
+
| `Thenable<T>` | Promise with `status` / `value` / `reason`, read by `use()` |
|
|
575
578
|
| `DataOf<TFactory>`, `InputOf<TFactory>` | A factory's data and input types |
|
|
576
579
|
|
|
577
580
|
`@raf-query/react` (plus all of the above):
|
|
578
581
|
|
|
579
|
-
| Export | Use
|
|
580
|
-
| ------------------------------------- |
|
|
581
|
-
| `QueryClientProvider` | Provides a client to the tree
|
|
582
|
-
| `useQueryClient()` | Read the client
|
|
583
|
-
| `useQuery(factory, handle, options?)` | `[
|
|
584
|
-
| `useWaterfall(factory, input)` | Start a query during render and return its handle
|
|
582
|
+
| Export | Use |
|
|
583
|
+
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
584
|
+
| `QueryClientProvider` | Provides a client to the tree |
|
|
585
|
+
| `useQueryClient()` | Read the client |
|
|
586
|
+
| `useQuery(factory, handle, options?)` | `[Thenable<Data>, controls]`; for paged factories `Data` is `{ pages, pageParams }` and controls include paging |
|
|
587
|
+
| `useWaterfall(factory, input)` | Start a query during render and return its handle |
|
|
585
588
|
|
|
586
589
|
## Development
|
|
587
590
|
|
package/dist/client.d.mts
CHANGED
|
@@ -34,7 +34,10 @@ export interface QueryFilter {
|
|
|
34
34
|
}
|
|
35
35
|
/** `ensure` / `fetch` arguments: the input, then load options for paged factories. */
|
|
36
36
|
export type LoadArgs<TFactory extends QueryFactory> = InputArgs<TFactory> | [input: InputOf<TFactory>, options: LoadOptions<TFactory>];
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* A new value, or a function of the current one. `previous` is `undefined`
|
|
39
|
+
* both with no data and with `undefined` data.
|
|
40
|
+
*/
|
|
38
41
|
export type Updater<TData> = TData | ((previous: TData | undefined) => TData);
|
|
39
42
|
/** Effect-returning twins of the client's async methods. */
|
|
40
43
|
export interface QueryClientEffect {
|
|
@@ -100,7 +103,10 @@ export declare class QueryClient {
|
|
|
100
103
|
remove(filter: QueryFilter): void;
|
|
101
104
|
/** Write data for a factory call. Readers re-render without suspending. */
|
|
102
105
|
setData<TFactory extends QueryFactory>(factory: TFactory, input: InputOf<TFactory>, updater: Updater<DataOf<TFactory>>): void;
|
|
103
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Cached data for a factory call, or `undefined` if there is none. Data can
|
|
108
|
+
* itself be `undefined`; `getQuery(...).getState().hasData` tells them apart.
|
|
109
|
+
*/
|
|
104
110
|
getData<TFactory extends QueryFactory>(factory: TFactory, ...args: InputArgs<TFactory>): DataOf<TFactory> | undefined;
|
|
105
111
|
/** Every entry, for devtools and integrations. */
|
|
106
112
|
getAll(): ReadonlyArray<Query>;
|
package/dist/client.mjs
CHANGED
|
@@ -113,11 +113,8 @@ var QueryClient = class {
|
|
|
113
113
|
}
|
|
114
114
|
/** Like `ensure`, but resolves to the data. */
|
|
115
115
|
async fetch(factory, ...args) {
|
|
116
|
-
const entry = this.#build(factory, args[0]);
|
|
117
|
-
|
|
118
|
-
const data = typed(entry).getState().data;
|
|
119
|
-
if (data === void 0) throw new Error(`Query ${entry.hash} has no data`);
|
|
120
|
-
return data;
|
|
116
|
+
const entry = typed(this.#build(factory, args[0]));
|
|
117
|
+
return this.#load(entry, args[1]?.pages);
|
|
121
118
|
}
|
|
122
119
|
async invalidate(target, input) {
|
|
123
120
|
await Promise.all(this.#match(target, input).map((entry) => entry.invalidate()));
|
|
@@ -134,7 +131,10 @@ var QueryClient = class {
|
|
|
134
131
|
const next = isUpdateFunction(updater) ? updater(typed(entry).getState().data) : updater;
|
|
135
132
|
entry.setData(next);
|
|
136
133
|
}
|
|
137
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Cached data for a factory call, or `undefined` if there is none. Data can
|
|
136
|
+
* itself be `undefined`; `getQuery(...).getState().hasData` tells them apart.
|
|
137
|
+
*/
|
|
138
138
|
getData(factory, ...args) {
|
|
139
139
|
const options = optionsFor(factory, args[0]);
|
|
140
140
|
const entry = this.#cache.get(hashKey(options.queryKey));
|
|
@@ -198,11 +198,12 @@ var QueryClient = class {
|
|
|
198
198
|
entry.setOptions(options, input);
|
|
199
199
|
return entry;
|
|
200
200
|
}
|
|
201
|
+
/** Resolves to the entry's data: the cached thenable, or the first fetch. */
|
|
201
202
|
#load(entry, pages) {
|
|
202
203
|
const state = entry.getState();
|
|
203
204
|
if (!state.hasData) return entry.fetch({ pages });
|
|
204
205
|
this.#revalidate(entry);
|
|
205
|
-
return
|
|
206
|
+
return state.promise;
|
|
206
207
|
}
|
|
207
208
|
/**
|
|
208
209
|
* Background refetch of cached data a verb reuses. A server client renders
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Settled, allKeyed } from "./all-keyed.mjs";
|
|
2
2
|
import { QueryKey, hashKey, matchesKey } from "./key.mjs";
|
|
3
3
|
import { AnyQueryOptions, DataOf, EnsuredQueryHandle, InfiniteData, InfiniteQueryOptions, InputArgs, InputOf, IsPaged, LoadOptions, PageFunctionContext, PageOf, QueryBehavior, QueryFactory, QueryFnResult, QueryFunctionContext, QueryHandle, QueryOptions, RetryPolicy, infiniteQueryOptions, queryOptions } from "./options.mjs";
|
|
4
|
+
import { Thenable } from "./thenable.mjs";
|
|
4
5
|
import { CancelledError, FetchRequest, Query, QueryState, SubscribeOptions } from "./query.mjs";
|
|
5
6
|
import { CacheEvent, LoadArgs, QueryClient, QueryClientConfig, QueryClientEffect, QueryFilter, Updater } from "./client.mjs";
|
|
6
7
|
import { DehydrateOptions, DehydratedQuery, DehydratedState, HydrateOptions, canDehydrate, dehydrate, dehydrateQuery, hydrate } from "./hydration.mjs";
|
|
7
8
|
import { QueryObserver, QueryObserverOptions, QueryObserverResult } from "./observer.mjs";
|
|
8
|
-
export { type AnyQueryOptions, type CacheEvent, CancelledError, type DataOf, type DehydrateOptions, type DehydratedQuery, type DehydratedState, type EnsuredQueryHandle, type FetchRequest, type HydrateOptions, type InfiniteData, type InfiniteQueryOptions, type InputArgs, type InputOf, type IsPaged, type LoadArgs, type LoadOptions, type PageFunctionContext, type PageOf, type Query, type QueryBehavior, QueryClient, type QueryClientConfig, type QueryClientEffect, type QueryFactory, type QueryFilter, type QueryFnResult, type QueryFunctionContext, type QueryHandle, type QueryKey, QueryObserver, type QueryObserverOptions, type QueryObserverResult, type QueryOptions, type QueryState, type RetryPolicy, type Settled, type SubscribeOptions, type Updater, allKeyed, canDehydrate, dehydrate, dehydrateQuery, hashKey, hydrate, infiniteQueryOptions, matchesKey, queryOptions };
|
|
9
|
+
export { type AnyQueryOptions, type CacheEvent, CancelledError, type DataOf, type DehydrateOptions, type DehydratedQuery, type DehydratedState, type EnsuredQueryHandle, type FetchRequest, type HydrateOptions, type InfiniteData, type InfiniteQueryOptions, type InputArgs, type InputOf, type IsPaged, type LoadArgs, type LoadOptions, type PageFunctionContext, type PageOf, type Query, type QueryBehavior, QueryClient, type QueryClientConfig, type QueryClientEffect, type QueryFactory, type QueryFilter, type QueryFnResult, type QueryFunctionContext, type QueryHandle, type QueryKey, QueryObserver, type QueryObserverOptions, type QueryObserverResult, type QueryOptions, type QueryState, type RetryPolicy, type Settled, type SubscribeOptions, type Thenable, type Updater, allKeyed, canDehydrate, dehydrate, dehydrateQuery, hashKey, hydrate, infiniteQueryOptions, matchesKey, queryOptions };
|
package/dist/observer.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QueryKey } from "./key.mjs";
|
|
2
2
|
import { QueryFactory } from "./options.mjs";
|
|
3
|
+
import { Thenable } from "./thenable.mjs";
|
|
3
4
|
import { Query } from "./query.mjs";
|
|
4
5
|
//#region src/observer.d.ts
|
|
5
6
|
/** How one reader observes its query. */
|
|
@@ -42,7 +43,7 @@ export interface QueryObserverResult<TData = unknown> {
|
|
|
42
43
|
/** Key of the data in `promise`; differs from the query's while `isPrevious`. */
|
|
43
44
|
readonly key: QueryKey;
|
|
44
45
|
/** Settles with the data to show; see `QueryState.promise`. */
|
|
45
|
-
readonly promise:
|
|
46
|
+
readonly promise: Thenable<TData>;
|
|
46
47
|
refetch(): Promise<void>;
|
|
47
48
|
/** When the data in `promise` was fetched (ms since epoch); `0` before that. */
|
|
48
49
|
readonly updatedAt: number;
|
package/dist/query.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QueryKey } from "./key.mjs";
|
|
2
2
|
import { AnyQueryOptions, QueryFactory, QueryHandle } from "./options.mjs";
|
|
3
|
+
import { Thenable } from "./thenable.mjs";
|
|
3
4
|
//#region src/query.d.ts
|
|
4
5
|
/** A snapshot of one cache entry. Replaced, never mutated, on every change. */
|
|
5
6
|
export interface QueryState<TData> {
|
|
@@ -20,9 +21,9 @@ export interface QueryState<TData> {
|
|
|
20
21
|
readonly isStale: boolean;
|
|
21
22
|
/**
|
|
22
23
|
* Settles with `data`. A new promise is created per data version, already
|
|
23
|
-
* fulfilled once data exists, so
|
|
24
|
+
* fulfilled once data exists, so it can be read synchronously.
|
|
24
25
|
*/
|
|
25
|
-
readonly promise:
|
|
26
|
+
readonly promise: Thenable<TData>;
|
|
26
27
|
readonly status: "error" | "pending" | "success";
|
|
27
28
|
/** When `data` was fetched (ms since epoch); `0` without data. */
|
|
28
29
|
readonly updatedAt: number;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/thenable.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Promises that carry their own result, in the fields React's `use()` reads
|
|
4
|
+
* before suspending (`status`, `value`, `reason`). A settled one can be read
|
|
5
|
+
* synchronously, so React renders it without a fallback.
|
|
6
|
+
*/
|
|
7
|
+
interface PendingStatus {
|
|
8
|
+
readonly status: "pending";
|
|
9
|
+
}
|
|
10
|
+
interface FulfilledStatus<T> {
|
|
11
|
+
readonly status: "fulfilled";
|
|
12
|
+
readonly value: T;
|
|
13
|
+
}
|
|
14
|
+
interface RejectedStatus {
|
|
15
|
+
readonly reason: unknown;
|
|
16
|
+
readonly status: "rejected";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A promise that says whether it has settled, and with what. `status` moves
|
|
20
|
+
* from `"pending"` to `"fulfilled"` (with `value`) or `"rejected"` (with
|
|
21
|
+
* `reason`) once, when the promise settles.
|
|
22
|
+
*/
|
|
23
|
+
export type Thenable<T> = Promise<T> & (FulfilledStatus<T> | PendingStatus | RejectedStatus);
|
|
24
|
+
//#endregion
|
package/dist/thenable.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//#region src/thenable.ts
|
|
2
2
|
/**
|
|
3
|
-
* Promises that React's `use()`
|
|
4
|
-
* `status
|
|
5
|
-
*
|
|
3
|
+
* Promises that carry their own result, in the fields React's `use()` reads
|
|
4
|
+
* before suspending (`status`, `value`, `reason`). A settled one can be read
|
|
5
|
+
* synchronously, so React renders it without a fallback.
|
|
6
6
|
*/
|
|
7
7
|
const noop = () => {};
|
|
8
8
|
function fulfilled(value) {
|