@robohall/react-query-factory 1.1.0 → 1.2.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/README.md +16 -16
- package/dist/index.d.mts +11 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.js +5 -3
- package/dist/index.mjs +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,9 +54,9 @@ function InstanceList() {
|
|
|
54
54
|
|
|
55
55
|
## Crawling
|
|
56
56
|
|
|
57
|
-
`DescribeInstances` is paginated. If you have more than
|
|
57
|
+
`DescribeInstances` is paginated. If you have more than 20 instances, one call won't get them all. The standard approach — chaining `fetchNextPage` calls, accumulating results, checking `NextToken` — is correct but tedious to repeat everywhere.
|
|
58
58
|
|
|
59
|
-
Add `getNextPageParam` and `shouldFetchNextPage` to activate crawling — those two are the only required pieces. `initialPageParam` types `ctx.pageParam` in your `queryFn` (without it, `ctx.pageParam` is `never`). `reduce` folds crawled pages into a single value; without it the result is
|
|
59
|
+
Add `getNextPageParam` and `shouldFetchNextPage` to activate crawling — those two are the only required pieces. `initialPageParam` types `ctx.pageParam` in your `queryFn` (without it and without a `getNextPageParam` that provides inference, `ctx.pageParam` is `never`). `reduce` folds crawled pages into a single value; without it the result is an array of all fetched raw pages (`TData[]`). **`shouldFetchNextPage`** is called after each page — return `true` to keep fetching, `false` to stop. Use `() => true` to walk every page:
|
|
60
60
|
|
|
61
61
|
```typescript
|
|
62
62
|
import type { Instance, DescribeInstancesCommandInput } from '@aws-sdk/client-ec2';
|
|
@@ -79,7 +79,7 @@ const describeInstances = queryFactory({
|
|
|
79
79
|
|
|
80
80
|
function InstanceList() {
|
|
81
81
|
// one useQuery call; data is Instance[], not DescribeInstancesResponse[]
|
|
82
|
-
const { data } = useQuery(describeInstances({ MaxResults:
|
|
82
|
+
const { data } = useQuery(describeInstances({ MaxResults: 20 }));
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
@@ -94,11 +94,11 @@ const describeInstances = queryFactory({
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
// fetch all pages
|
|
97
|
-
const { data: all } = useQuery(describeInstances({ MaxResults:
|
|
97
|
+
const { data: all } = useQuery(describeInstances({ MaxResults: 20 }));
|
|
98
98
|
|
|
99
|
-
// stop after accumulating at least 50 instances
|
|
99
|
+
// stop after accumulating at least 50 instances (≥ 3 API calls)
|
|
100
100
|
const { data: partial } = useQuery(
|
|
101
|
-
describeInstances({ MaxResults:
|
|
101
|
+
describeInstances({ MaxResults: 20 }, { minResults: 50 })
|
|
102
102
|
);
|
|
103
103
|
```
|
|
104
104
|
|
|
@@ -117,9 +117,9 @@ const runningInstances = queryFactory(describeInstances, {
|
|
|
117
117
|
select: instances => instances.filter(i => i.State?.Name === 'running'),
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
// query key: ['ec2:DescribeInstances', { MaxResults:
|
|
120
|
+
// query key: ['ec2:DescribeInstances', { MaxResults: 20 }] (same cache entry as parent)
|
|
121
121
|
// data: Instance[] filtered to State.Name === 'running'
|
|
122
|
-
const { data } = useQuery(runningInstances({ MaxResults:
|
|
122
|
+
const { data } = useQuery(runningInstances({ MaxResults: 20 }));
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
Parent and child `select` functions compose automatically — if the parent already has a `select`, the child's `select` receives the parent's output, not the raw API response.
|
|
@@ -135,10 +135,10 @@ const findInstance = queryFactory(describeInstances, {
|
|
|
135
135
|
!instances.some(i => i.InstanceId === opts.instanceId),
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
// query key: ['ec2:DescribeInstances', 'find', { MaxResults:
|
|
138
|
+
// query key: ['ec2:DescribeInstances', 'find', { MaxResults: 20 }, { instanceId: 'i-0abc123def456' }]
|
|
139
139
|
// crawls pages until the target instance appears, then stops
|
|
140
140
|
const { data } = useQuery(
|
|
141
|
-
findInstance({ MaxResults:
|
|
141
|
+
findInstance({ MaxResults: 20 }, { instanceId: 'i-0abc123def456' })
|
|
142
142
|
);
|
|
143
143
|
```
|
|
144
144
|
|
|
@@ -160,15 +160,15 @@ Every factory exposes a `.infinite()` method that returns `useInfiniteQuery`-com
|
|
|
160
160
|
```typescript
|
|
161
161
|
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
|
|
162
162
|
// load 50 instances per UI page, each backed by up to 5 DescribeInstances calls
|
|
163
|
-
describeInstances.infinite({ MaxResults:
|
|
163
|
+
describeInstances.infinite({ MaxResults: 20 }, { minResults: 50 })
|
|
164
164
|
);
|
|
165
165
|
|
|
166
166
|
// data.pages is Instance[][], one array per virtual page
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
The `.infinite()` key includes an `'infinite'` segment to keep it separate from the regular `useQuery` cache entry:
|
|
170
|
-
- `describeInstances({ MaxResults:
|
|
171
|
-
- `describeInstances.infinite({ MaxResults:
|
|
170
|
+
- `describeInstances({ MaxResults: 20 })` → `['ec2:DescribeInstances', { MaxResults: 20 }]`
|
|
171
|
+
- `describeInstances.infinite({ MaxResults: 20 })` → `['ec2:DescribeInstances', 'infinite', { MaxResults: 20 }]`
|
|
172
172
|
|
|
173
173
|
---
|
|
174
174
|
|
|
@@ -188,7 +188,7 @@ queryFactory<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>(
|
|
|
188
188
|
|
|
189
189
|
Creates a child factory. Two overloads:
|
|
190
190
|
- **With a new `queryFn`** — inherits key namespace and standard options; crawling config must be re-declared if needed.
|
|
191
|
-
- **Without a `queryFn`** — inherits everything; accepts
|
|
191
|
+
- **Without a `queryFn`** — inherits everything; accepts `queryKey`, `select`, standard options, and any crawling fields (`shouldFetchNextPage`, `reduce`, `getNextPageParam`, `getPreviousPageParam`, `initialPageParam`) to override the parent's. `select` is composed with the parent's.
|
|
192
192
|
|
|
193
193
|
### `QueryFactoryConfig`
|
|
194
194
|
|
|
@@ -200,10 +200,10 @@ All fields except `reduce` and `shouldFetchNextPage` are the standard TanStack Q
|
|
|
200
200
|
| `queryFn` | `(params: TParams, ctx: QueryFunctionContext) => TData \| Promise<TData>` | Same as TanStack, with an extra leading `params` argument. |
|
|
201
201
|
| `select` | `(data: TData) => TSelected` | Exact TanStack API. Composed automatically on child factories. |
|
|
202
202
|
| `getNextPageParam` | `GetNextPageParamFunction<TPageParam, TData>` | Exact TanStack API. Required (with `shouldFetchNextPage`) to activate crawling. Required (with `initialPageParam`) for `.infinite()`. |
|
|
203
|
-
| `initialPageParam` | `TPageParam` | Exact TanStack API. Drives `TPageParam` inference — without it `ctx.pageParam` is typed `never`. Required for `.infinite()` to work at runtime. |
|
|
203
|
+
| `initialPageParam` | `TPageParam` | Exact TanStack API. Drives `TPageParam` inference — without it and without a `getNextPageParam` that provides inference, `ctx.pageParam` is typed `never`. Required for `.infinite()` to work at runtime. |
|
|
204
204
|
| `getPreviousPageParam` | `GetPreviousPageParamFunction<TPageParam, TData>` | Exact TanStack API. Passed through on `.infinite()`. |
|
|
205
205
|
| `shouldFetchNextPage` | `(combined: TSelected \| undefined, crawlOptions: TCrawlOptions) => boolean` | Library addition. **Required (with `getNextPageParam`) to activate crawling.** Called after each page — return `true` to keep fetching, `false` to stop. |
|
|
206
|
-
| `reduce` | `(acc: TSelected \| undefined, page: TData) => TSelected` | Library addition. Optional. Folds crawled pages into a single `TSelected` value; when omitted the result is
|
|
206
|
+
| `reduce` | `(acc: TSelected \| undefined, page: TData) => TSelected` | Library addition. Optional. Folds crawled pages into a single `TSelected` value; when omitted the result is an array of all fetched raw pages (`TData[]`). |
|
|
207
207
|
| + all `StandardQueryOptions` fields | | All options accepted by TanStack's `useQuery` / `useInfiniteQuery` except `queryKey`, `queryFn`, and `select` (which the factory owns). Includes `staleTime`, `gcTime`, `retry`, `retryOnMount`, `enabled`, `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchOnMount`, `refetchInterval`, `refetchIntervalInBackground`, `networkMode`, `notifyOnChangeProps`, `throwOnError`, `structuralSharing`, `initialData`, `initialDataUpdatedAt`, `placeholderData`, `queryKeyHashFn`, `persister`, `meta`, `maxPages`, `experimental_prefetchInRender`. Function-form callbacks (e.g. `enabled: (query) => boolean`) are supported wherever TanStack accepts them. |
|
|
208
208
|
|
|
209
209
|
### `QueryFactory<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>`
|
package/dist/index.d.mts
CHANGED
|
@@ -62,15 +62,20 @@ type ResolvedQueryOptions<TData = unknown, TError = Error, TSelected = TData> =
|
|
|
62
62
|
/**
|
|
63
63
|
* What `factory.infinite(params)` returns — pass directly to `useInfiniteQuery()`.
|
|
64
64
|
*
|
|
65
|
-
* The `select` field
|
|
66
|
-
* guard making this type incompatible with `useQuery`.
|
|
65
|
+
* The `select` field input type (`InfiniteData<TData, TPageParam>`) is a structural
|
|
66
|
+
* guard making this type incompatible with `useQuery`, whose select expects `(data: TData)`.
|
|
67
|
+
*
|
|
68
|
+
* `TResult` is the concrete type that `useInfiniteQuery` will use as its `TData` — i.e.
|
|
69
|
+
* what `select` returns (or `InfiniteData<TData, TPageParam>` when no select is set).
|
|
70
|
+
* Carrying it explicitly avoids returning `any`, which would poison TResult inference
|
|
71
|
+
* in TypeScript 6 when callers spread this type and add their own `select`.
|
|
67
72
|
*/
|
|
68
|
-
type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unknown
|
|
73
|
+
type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unknown, TResult = InfiniteData<TData, TPageParam>> = Omit<StandardQueryOptions<TError, any>, 'persister'> & {
|
|
69
74
|
queryKey: QueryKey;
|
|
70
75
|
queryFn?: (context: QueryFunctionContext<QueryKey, TPageParam>) => TData | Promise<TData>;
|
|
71
|
-
/** Structural guard: the InfiniteData
|
|
76
|
+
/** Structural guard: the InfiniteData input type makes this incompatible with useQuery,
|
|
72
77
|
* whose select expects (data: TData) rather than (data: InfiniteData<TData, TPageParam>). */
|
|
73
|
-
select?: (data: InfiniteData<TData, TPageParam>) =>
|
|
78
|
+
select?: (data: InfiniteData<TData, TPageParam>) => TResult;
|
|
74
79
|
/** Required so this type satisfies useInfiniteQuery, which requires getNextPageParam. */
|
|
75
80
|
getNextPageParam: GetNextPageParamFunction<TPageParam, TData>;
|
|
76
81
|
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
@@ -93,7 +98,7 @@ type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unkno
|
|
|
93
98
|
*/
|
|
94
99
|
interface QueryFactory<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>, THasReduce extends boolean = boolean> {
|
|
95
100
|
(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedQueryOptions<TData, TError, TSelected>;
|
|
96
|
-
infinite(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedInfiniteOptions<TData, TError, TPageParam
|
|
101
|
+
infinite(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedInfiniteOptions<TData, TError, TPageParam, InfiniteData<TSelected, TPageParam>>;
|
|
97
102
|
}
|
|
98
103
|
/**
|
|
99
104
|
* Creates a standalone query factory with pagination and reduce. When `reduce` is
|
package/dist/index.d.ts
CHANGED
|
@@ -62,15 +62,20 @@ type ResolvedQueryOptions<TData = unknown, TError = Error, TSelected = TData> =
|
|
|
62
62
|
/**
|
|
63
63
|
* What `factory.infinite(params)` returns — pass directly to `useInfiniteQuery()`.
|
|
64
64
|
*
|
|
65
|
-
* The `select` field
|
|
66
|
-
* guard making this type incompatible with `useQuery`.
|
|
65
|
+
* The `select` field input type (`InfiniteData<TData, TPageParam>`) is a structural
|
|
66
|
+
* guard making this type incompatible with `useQuery`, whose select expects `(data: TData)`.
|
|
67
|
+
*
|
|
68
|
+
* `TResult` is the concrete type that `useInfiniteQuery` will use as its `TData` — i.e.
|
|
69
|
+
* what `select` returns (or `InfiniteData<TData, TPageParam>` when no select is set).
|
|
70
|
+
* Carrying it explicitly avoids returning `any`, which would poison TResult inference
|
|
71
|
+
* in TypeScript 6 when callers spread this type and add their own `select`.
|
|
67
72
|
*/
|
|
68
|
-
type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unknown
|
|
73
|
+
type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unknown, TResult = InfiniteData<TData, TPageParam>> = Omit<StandardQueryOptions<TError, any>, 'persister'> & {
|
|
69
74
|
queryKey: QueryKey;
|
|
70
75
|
queryFn?: (context: QueryFunctionContext<QueryKey, TPageParam>) => TData | Promise<TData>;
|
|
71
|
-
/** Structural guard: the InfiniteData
|
|
76
|
+
/** Structural guard: the InfiniteData input type makes this incompatible with useQuery,
|
|
72
77
|
* whose select expects (data: TData) rather than (data: InfiniteData<TData, TPageParam>). */
|
|
73
|
-
select?: (data: InfiniteData<TData, TPageParam>) =>
|
|
78
|
+
select?: (data: InfiniteData<TData, TPageParam>) => TResult;
|
|
74
79
|
/** Required so this type satisfies useInfiniteQuery, which requires getNextPageParam. */
|
|
75
80
|
getNextPageParam: GetNextPageParamFunction<TPageParam, TData>;
|
|
76
81
|
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
@@ -93,7 +98,7 @@ type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unkno
|
|
|
93
98
|
*/
|
|
94
99
|
interface QueryFactory<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>, THasReduce extends boolean = boolean> {
|
|
95
100
|
(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedQueryOptions<TData, TError, TSelected>;
|
|
96
|
-
infinite(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedInfiniteOptions<TData, TError, TPageParam
|
|
101
|
+
infinite(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedInfiniteOptions<TData, TError, TPageParam, InfiniteData<TSelected, TPageParam>>;
|
|
97
102
|
}
|
|
98
103
|
/**
|
|
99
104
|
* Creates a standalone query factory with pagination and reduce. When `reduce` is
|
package/dist/index.js
CHANGED
|
@@ -40,9 +40,10 @@ function resolveKey(namespace, params, crawlOptions) {
|
|
|
40
40
|
}
|
|
41
41
|
return defined ? [...withParams, defined] : withParams;
|
|
42
42
|
}
|
|
43
|
-
function wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions) {
|
|
43
|
+
function wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions, select) {
|
|
44
44
|
return (lastPage, allPages, lastPageParam, allPageParams) => {
|
|
45
|
-
|
|
45
|
+
const combined = select ? select(lastPage) : lastPage;
|
|
46
|
+
if (!shouldFetchNextPage(combined, crawlOptions))
|
|
46
47
|
return void 0;
|
|
47
48
|
return getNextPageParam(lastPage, allPages, lastPageParam, allPageParams);
|
|
48
49
|
};
|
|
@@ -167,7 +168,8 @@ function buildFactory(cfg) {
|
|
|
167
168
|
const infiniteGetNextPageParam = getNextPageParam && shouldFetchNextPage ? wrapGetNextPageParam(
|
|
168
169
|
getNextPageParam,
|
|
169
170
|
shouldFetchNextPage,
|
|
170
|
-
crawlOptions
|
|
171
|
+
crawlOptions,
|
|
172
|
+
select
|
|
171
173
|
) : getNextPageParam != null ? getNextPageParam : noNextPage;
|
|
172
174
|
return {
|
|
173
175
|
...standardOptions,
|
package/dist/index.mjs
CHANGED
|
@@ -14,9 +14,10 @@ function resolveKey(namespace, params, crawlOptions) {
|
|
|
14
14
|
}
|
|
15
15
|
return defined ? [...withParams, defined] : withParams;
|
|
16
16
|
}
|
|
17
|
-
function wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions) {
|
|
17
|
+
function wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions, select) {
|
|
18
18
|
return (lastPage, allPages, lastPageParam, allPageParams) => {
|
|
19
|
-
|
|
19
|
+
const combined = select ? select(lastPage) : lastPage;
|
|
20
|
+
if (!shouldFetchNextPage(combined, crawlOptions))
|
|
20
21
|
return void 0;
|
|
21
22
|
return getNextPageParam(lastPage, allPages, lastPageParam, allPageParams);
|
|
22
23
|
};
|
|
@@ -141,7 +142,8 @@ function buildFactory(cfg) {
|
|
|
141
142
|
const infiniteGetNextPageParam = getNextPageParam && shouldFetchNextPage ? wrapGetNextPageParam(
|
|
142
143
|
getNextPageParam,
|
|
143
144
|
shouldFetchNextPage,
|
|
144
|
-
crawlOptions
|
|
145
|
+
crawlOptions,
|
|
146
|
+
select
|
|
145
147
|
) : getNextPageParam != null ? getNextPageParam : noNextPage;
|
|
146
148
|
return {
|
|
147
149
|
...standardOptions,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robohall/react-query-factory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A factory abstraction for TanStack Query (React Query) with composable keys, crawling support, and automatic infinite query generation",
|
|
5
5
|
"author": "Robert Hall",
|
|
6
6
|
"license": "MIT",
|