@robohall/react-query-factory 1.0.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/LICENSE +21 -0
- package/README.md +232 -0
- package/dist/index.d.mts +149 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +244 -0
- package/dist/index.mjs +217 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robert Hall
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# react-query-factory
|
|
2
|
+
|
|
3
|
+
TanStack Query is very good at caching. It is less good at deciding what your query keys should be, or at fetching every page of a cursor-paginated endpoint before your component has to think about it.
|
|
4
|
+
|
|
5
|
+
This library wraps query definitions in a small factory function that handles composable key namespacing, automatic multi-page crawling, and `useInfiniteQuery` generation — so the part of your codebase that knows *what* to fetch stays separate from the part that knows *how React renders it*.
|
|
6
|
+
|
|
7
|
+
It has zero runtime dependencies — all TanStack Query imports are type-only and erased at compile time.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install react-query-factory
|
|
15
|
+
# peer dependency: @tanstack/react-query >= 5.0.0
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
Define a factory once, call it in any component:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import {
|
|
26
|
+
EC2Client,
|
|
27
|
+
DescribeInstancesCommand,
|
|
28
|
+
type DescribeInstancesCommandInput,
|
|
29
|
+
} from '@aws-sdk/client-ec2';
|
|
30
|
+
import { queryFactory } from 'react-query-factory';
|
|
31
|
+
import { useQuery } from '@tanstack/react-query';
|
|
32
|
+
|
|
33
|
+
const ec2 = new EC2Client({ region: 'us-east-1' });
|
|
34
|
+
|
|
35
|
+
const describeInstances = queryFactory({
|
|
36
|
+
queryKey: ['ec2:DescribeInstances'],
|
|
37
|
+
queryFn: (params: DescribeInstancesCommandInput, ctx) =>
|
|
38
|
+
ec2.send(new DescribeInstancesCommand(params), { abortSignal: ctx.signal }),
|
|
39
|
+
staleTime: 30_000,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function InstanceList() {
|
|
43
|
+
const { data } = useQuery(
|
|
44
|
+
describeInstances({ Filters: [{ Name: 'instance-state-name', Values: ['running'] }] })
|
|
45
|
+
);
|
|
46
|
+
// query key: ['ec2:DescribeInstances', { Filters: [...] }]
|
|
47
|
+
// staleTime: 30 000 ms, no repetition required
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`describeInstances({ ... })` returns a plain object — `{ queryKey, queryFn, staleTime, … }` — that you spread or pass directly to `useQuery`. The factory does not touch your query client.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Crawling
|
|
56
|
+
|
|
57
|
+
`DescribeInstances` is paginated. If you have more than 1000 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
|
+
|
|
59
|
+
Add `getNextPageParam`, `initialPageParam`, and `reduce` to your factory config, and the generated `queryFn` will walk every page automatically, reducing them into a single flat array:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import type { Instance, DescribeInstancesCommandInput } from '@aws-sdk/client-ec2';
|
|
63
|
+
|
|
64
|
+
const describeInstances = queryFactory({
|
|
65
|
+
queryKey: ['ec2:DescribeInstances'],
|
|
66
|
+
queryFn: (params: DescribeInstancesCommandInput, ctx) =>
|
|
67
|
+
ec2.send(
|
|
68
|
+
new DescribeInstancesCommand({ ...params, NextToken: ctx.pageParam }),
|
|
69
|
+
{ abortSignal: ctx.signal },
|
|
70
|
+
),
|
|
71
|
+
getNextPageParam: response => response.NextToken,
|
|
72
|
+
initialPageParam: undefined as string | undefined,
|
|
73
|
+
reduce: (acc, page): Instance[] => [
|
|
74
|
+
...(acc ?? []),
|
|
75
|
+
...(page.Reservations?.flatMap(r => r.Instances ?? []) ?? []),
|
|
76
|
+
],
|
|
77
|
+
staleTime: 30_000,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function InstanceList() {
|
|
81
|
+
// one useQuery call; data is Instance[], not DescribeInstancesResponse[]
|
|
82
|
+
const { data } = useQuery(describeInstances({ MaxResults: 1000 }));
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**`shouldFetchNextPage`** lets a call site stop the crawl early based on what has been accumulated so far. The arguments are the current reduced value and a `crawlOptions` object passed at call time:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const describeInstances = queryFactory({
|
|
90
|
+
// ...
|
|
91
|
+
shouldFetchNextPage: (instances, opts: { minResults?: number }) =>
|
|
92
|
+
opts.minResults != null && (instances?.length ?? 0) < opts.minResults,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// stop after accumulating at least 50 instances
|
|
96
|
+
const { data } = useQuery(
|
|
97
|
+
describeInstances({ MaxResults: 1000 }, { minResults: 50 })
|
|
98
|
+
);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`crawlOptions` is appended to the query key, so `describeInstances({}, { minResults: 50 })` and `describeInstances({}, { minResults: 200 })` are separate cache entries — they crawl independently and never collide.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Factory composition
|
|
106
|
+
|
|
107
|
+
A factory can inherit from another factory. The child's query key is appended to the parent's, standard options are shallow-merged, and the `queryFn` and crawling config can be inherited or replaced.
|
|
108
|
+
|
|
109
|
+
**Inherit the queryFn, add a `select` transform:**
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const runningInstances = queryFactory(describeInstances, {
|
|
113
|
+
select: instances => instances.filter(i => i.State?.Name === 'running'),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// query key: ['ec2:DescribeInstances', { MaxResults: 1000 }] (same cache entry as parent)
|
|
117
|
+
// data: Instance[] filtered to State.Name === 'running'
|
|
118
|
+
const { data } = useQuery(runningInstances({ MaxResults: 1000 }));
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
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.
|
|
122
|
+
|
|
123
|
+
**Add a new queryFn under the parent's namespace:**
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const findInstance = queryFactory(describeInstances, {
|
|
127
|
+
queryKey: ['find'],
|
|
128
|
+
// queryFn, getNextPageParam, initialPageParam, and reduce are all inherited
|
|
129
|
+
shouldFetchNextPage: (instances, opts: { instanceId?: string }) =>
|
|
130
|
+
opts.instanceId != null &&
|
|
131
|
+
!instances?.some(i => i.InstanceId === opts.instanceId),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// query key: ['ec2:DescribeInstances', 'find', { MaxResults: 100 }, { instanceId: 'i-0abc123' }]
|
|
135
|
+
// crawls pages until the target instance appears, then stops
|
|
136
|
+
const { data } = useQuery(
|
|
137
|
+
findInstance({ MaxResults: 100 }, { instanceId: 'i-0abc123def456' })
|
|
138
|
+
);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Because `findInstance`'s key is nested under `['ec2:DescribeInstances']`, a single invalidation call busts the parent and all children:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// after a runInstances/terminateInstances mutation — invalidates everything in the namespace.
|
|
145
|
+
// Calling the factory with no args produces just the namespace key; TanStack prefix-matches it
|
|
146
|
+
// against all entries, so describeInstances, runningInstances, and findInstance are all busted.
|
|
147
|
+
await queryClient.invalidateQueries(describeInstances());
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Infinite queries
|
|
153
|
+
|
|
154
|
+
Every factory exposes a `.infinite()` method that returns `useInfiniteQuery`-compatible options. If the factory has `reduce` configured, each virtual page is itself a crawl — TanStack loads pages one at a time, but each "page load" makes multiple API calls and reduces them before handing the result back:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
|
|
158
|
+
// load 50 instances per UI page, each backed by up to 5 DescribeInstances calls
|
|
159
|
+
describeInstances.infinite({ MaxResults: 10 }, { minResults: 50 })
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// data.pages is Instance[][], one array per virtual page
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The `.infinite()` key includes an `'infinite'` segment to keep it separate from the regular `useQuery` cache entry:
|
|
166
|
+
- `describeInstances({ MaxResults: 10 })` → `['ec2:DescribeInstances', { MaxResults: 10 }]`
|
|
167
|
+
- `describeInstances.infinite({ MaxResults: 10 })` → `['ec2:DescribeInstances', 'infinite', { MaxResults: 10 }]`
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Public API
|
|
172
|
+
|
|
173
|
+
### `queryFactory(config)`
|
|
174
|
+
|
|
175
|
+
Creates a standalone factory.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
queryFactory<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>(
|
|
179
|
+
config: QueryFactoryConfig<...>
|
|
180
|
+
): QueryFactory<...>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### `queryFactory(parent, config)`
|
|
184
|
+
|
|
185
|
+
Creates a child factory. Two overloads:
|
|
186
|
+
- **With a new `queryFn`** — inherits key namespace and standard options; crawling config must be re-declared if needed.
|
|
187
|
+
- **Without a `queryFn`** — inherits everything; accepts only `queryKey`, `select`, and standard options. `select` is composed with the parent's.
|
|
188
|
+
|
|
189
|
+
### `QueryFactoryConfig`
|
|
190
|
+
|
|
191
|
+
All fields except `reduce` and `shouldFetchNextPage` are the standard TanStack Query API — the same types and semantics you'd pass to `useQuery` or `useInfiniteQuery`. The factory doesn't reinvent them; it just requires certain combinations to be present in order to activate crawling.
|
|
192
|
+
|
|
193
|
+
| Field | Type | Notes |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `queryKey` | `QueryKey` | Namespace segments. Params are appended at call time. |
|
|
196
|
+
| `queryFn` | `(params: TParams, ctx: QueryFunctionContext) => TData \| Promise<TData>` | Same as TanStack, with an extra leading `params` argument. |
|
|
197
|
+
| `select` | `(data: TData) => TSelected` | Exact TanStack API. Composed automatically on child factories. |
|
|
198
|
+
| `getNextPageParam` | `GetNextPageParamFunction<TPageParam, TData>` | Exact TanStack API. Providing this together with `initialPageParam` and `reduce` activates crawling. |
|
|
199
|
+
| `initialPageParam` | `TPageParam` | Exact TanStack API. Required alongside `getNextPageParam` to enable crawling. |
|
|
200
|
+
| `getPreviousPageParam` | `GetPreviousPageParamFunction<TPageParam, TData>` | Exact TanStack API. Passed through on `.infinite()`. |
|
|
201
|
+
| `reduce` | `(acc: TSelected \| undefined, page: TData) => TSelected` | Library addition. Folds crawled pages into a single value; required for crawling and `.infinite()` crawling. |
|
|
202
|
+
| `shouldFetchNextPage` | `(combined: TSelected \| undefined, crawlOptions: TCrawlOptions) => boolean` | Library addition. Return `false` to stop the crawl early based on accumulated results. |
|
|
203
|
+
| + all `StandardQueryOptions` fields | | `staleTime`, `gcTime`, `retry`, `enabled`, `refetchOnWindowFocus`, etc. |
|
|
204
|
+
|
|
205
|
+
### `QueryFactory<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>`
|
|
206
|
+
|
|
207
|
+
The callable factory returned by `queryFactory()`.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
factory(params: TParams, crawlOptions?: TCrawlOptions): ResolvedQueryOptions // → useQuery()
|
|
211
|
+
factory.infinite(params, crawlOptions?) : ResolvedInfiniteOptions // → useInfiniteQuery()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `ResolvedQueryOptions`
|
|
215
|
+
|
|
216
|
+
Return type of `factory(params)`. Pass directly to `useQuery()`. Contains an `initialPageParam?: never` field that prevents accidental use with `useInfiniteQuery`.
|
|
217
|
+
|
|
218
|
+
### `ResolvedInfiniteOptions`
|
|
219
|
+
|
|
220
|
+
Return type of `factory.infinite(params)`. Pass directly to `useInfiniteQuery()`. The `select` field is typed to `InfiniteData<TData, TPageParam>`, which prevents accidental use with `useQuery`.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Running the sandbox
|
|
225
|
+
|
|
226
|
+
The sandbox contains five interactive demos using a mock paginated API: basic single-page fetch, full crawl, factory composition, infinite query with per-page crawling, and a target-search that stops the crawl early.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
npm run sandbox
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
This starts a Vite dev server. Navigate to the URL it prints (typically `http://localhost:5173`).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { QueryKey, NotifyOnChangeProps, QueryMeta, QueryFunctionContext, InfiniteData, GetNextPageParamFunction, GetPreviousPageParamFunction } from '@tanstack/react-query';
|
|
2
|
+
|
|
3
|
+
/** Subset of TanStack Query options that apply to both regular and infinite queries. */
|
|
4
|
+
interface StandardQueryOptions<TError = Error> {
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
staleTime?: number | ((query: {
|
|
7
|
+
queryKey: QueryKey;
|
|
8
|
+
}) => number);
|
|
9
|
+
gcTime?: number;
|
|
10
|
+
retry?: boolean | number | ((failureCount: number, error: TError) => boolean);
|
|
11
|
+
retryDelay?: number | ((retryAttempt: number, error: TError) => number);
|
|
12
|
+
refetchOnWindowFocus?: boolean | 'always';
|
|
13
|
+
refetchOnReconnect?: boolean | 'always';
|
|
14
|
+
refetchOnMount?: boolean | 'always';
|
|
15
|
+
refetchInterval?: number | false | ((query: {
|
|
16
|
+
queryKey: QueryKey;
|
|
17
|
+
}) => number | false);
|
|
18
|
+
refetchIntervalInBackground?: boolean;
|
|
19
|
+
networkMode?: 'online' | 'always' | 'offlineFirst';
|
|
20
|
+
notifyOnChangeProps?: NotifyOnChangeProps;
|
|
21
|
+
throwOnError?: boolean | ((error: TError) => boolean);
|
|
22
|
+
meta?: QueryMeta;
|
|
23
|
+
structuralSharing?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Configuration passed to `queryFactory()`.
|
|
27
|
+
*
|
|
28
|
+
* - Set only `queryKey` + `queryFn` for a basic single-page query.
|
|
29
|
+
* - Add `getNextPageParam`, `initialPageParam`, and `reduce` to enable automatic
|
|
30
|
+
* pagination: the generated `queryFn` crawls all pages and reduces them to a
|
|
31
|
+
* single `TSelected` value.
|
|
32
|
+
* - Call `factory.infinite(params)` to get a `useInfiniteQuery`-compatible config
|
|
33
|
+
* where each virtual page is itself a crawl.
|
|
34
|
+
*/
|
|
35
|
+
interface QueryFactoryConfig<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>> extends StandardQueryOptions<TError> {
|
|
36
|
+
/** Namespace segments. Params are appended as the final element at call time,
|
|
37
|
+
* giving a full key of [...namespace, 'infinite'?, params, crawlOptions?]. */
|
|
38
|
+
queryKey: QueryKey;
|
|
39
|
+
queryFn?: (params: TParams, context: QueryFunctionContext<QueryKey, TPageParam>) => TData | Promise<TData>;
|
|
40
|
+
select?: (data: TData) => TSelected;
|
|
41
|
+
/** TanStack v5 generic order: GetNextPageParamFunction<TPageParam, TData> */
|
|
42
|
+
getNextPageParam?: GetNextPageParamFunction<TPageParam, TData>;
|
|
43
|
+
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
44
|
+
initialPageParam?: TPageParam;
|
|
45
|
+
/** Called after each page to decide whether to keep crawling.
|
|
46
|
+
* Receives the current combined result and the crawl options from the call site. */
|
|
47
|
+
shouldFetchNextPage?: (combined: TSelected | undefined, crawlOptions: TCrawlOptions) => boolean;
|
|
48
|
+
/** Reduces crawled pages incrementally into the final query result.
|
|
49
|
+
* Called once per page; accumulator is undefined on the first call.
|
|
50
|
+
* When set, enables crawling on both the regular and .infinite variants. */
|
|
51
|
+
reduce?: (accumulator: TSelected | undefined, page: TData) => TSelected;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What `factory(params)` returns — pass directly to `useQuery()`.
|
|
55
|
+
*
|
|
56
|
+
* The `initialPageParam?: never` field is a structural guard that makes this
|
|
57
|
+
* type incompatible with `useInfiniteQuery`, preventing accidental misuse.
|
|
58
|
+
*/
|
|
59
|
+
type ResolvedQueryOptions<TData = unknown, TError = Error, TSelected = TData> = StandardQueryOptions<TError> & {
|
|
60
|
+
queryKey: QueryKey;
|
|
61
|
+
queryFn?: (context: QueryFunctionContext) => TData | Promise<TData>;
|
|
62
|
+
select?: (data: TData) => TSelected;
|
|
63
|
+
/** Structural guard: makes this type incompatible with useInfiniteQuery, which requires initialPageParam. */
|
|
64
|
+
initialPageParam?: never;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* What `factory.infinite(params)` returns — pass directly to `useInfiniteQuery()`.
|
|
68
|
+
*
|
|
69
|
+
* The `select` field expects `InfiniteData<TData, TPageParam>`, which is a structural
|
|
70
|
+
* guard making this type incompatible with `useQuery`.
|
|
71
|
+
*/
|
|
72
|
+
type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unknown> = StandardQueryOptions<TError> & {
|
|
73
|
+
queryKey: QueryKey;
|
|
74
|
+
queryFn?: (context: QueryFunctionContext<QueryKey, TPageParam>) => TData | Promise<TData>;
|
|
75
|
+
/** Structural guard: the InfiniteData parameter type makes this incompatible with useQuery,
|
|
76
|
+
* whose select expects (data: TData) rather than (data: InfiniteData<TData, TPageParam>). */
|
|
77
|
+
select?: (data: InfiniteData<TData, TPageParam>) => any;
|
|
78
|
+
/** Required so this type satisfies useInfiniteQuery, which requires getNextPageParam. */
|
|
79
|
+
getNextPageParam: GetNextPageParamFunction<TPageParam, TData>;
|
|
80
|
+
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
81
|
+
/** Required so this type satisfies useInfiniteQuery, which requires initialPageParam. */
|
|
82
|
+
initialPageParam: TPageParam;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* A callable factory produced by `queryFactory()`.
|
|
86
|
+
*
|
|
87
|
+
* - `factory(params)` → `ResolvedQueryOptions` for `useQuery()`
|
|
88
|
+
* - `factory.infinite(params)` → `ResolvedInfiniteOptions` for `useInfiniteQuery()`
|
|
89
|
+
*
|
|
90
|
+
* Both signatures accept an optional `crawlOptions` object that is appended to
|
|
91
|
+
* the query key and passed to `shouldFetchNextPage` so different call sites can
|
|
92
|
+
* control crawl behavior independently.
|
|
93
|
+
*
|
|
94
|
+
* `params` is always optional. Calling with no arguments produces just the
|
|
95
|
+
* namespace key, which is useful for broad cache invalidation:
|
|
96
|
+
* `queryClient.invalidateQueries(factory())`
|
|
97
|
+
*/
|
|
98
|
+
interface QueryFactory<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>> {
|
|
99
|
+
(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedQueryOptions<TData, TError, TSelected>;
|
|
100
|
+
infinite(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedInfiniteOptions<TData, TError, TPageParam>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a standalone query factory from a config object.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* const usersFactory = queryFactory({
|
|
107
|
+
* queryKey: ['users'],
|
|
108
|
+
* queryFn: (params: { page: number }, ctx) => fetchUsers(params, ctx.signal),
|
|
109
|
+
* });
|
|
110
|
+
* // useQuery(usersFactory({ page: 1 }))
|
|
111
|
+
*/
|
|
112
|
+
declare function queryFactory<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>>(config: QueryFactoryConfig<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>): QueryFactory<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a child factory that inherits the query key and standard options from
|
|
115
|
+
* `parent` and introduces a new `queryFn`. The child's query key is appended to
|
|
116
|
+
* the parent's, and standard options are shallow-merged (child wins).
|
|
117
|
+
*
|
|
118
|
+
* Use this overload when the child fetches different data than the parent.
|
|
119
|
+
*/
|
|
120
|
+
declare function queryFactory<TChildParams extends TParentParams, TData = unknown, TError = Error, TChildSelected = TData, TParentParams = TChildParams, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>>(parent: QueryFactory<TParentParams, any, any, any, any, any>, config: Omit<QueryFactoryConfig<TChildParams, TData, TError, TChildSelected, TPageParam, TCrawlOptions>, 'queryKey' | 'getNextPageParam' | 'getPreviousPageParam' | 'initialPageParam' | 'shouldFetchNextPage' | 'reduce'> & {
|
|
121
|
+
queryKey?: QueryKey;
|
|
122
|
+
queryFn: NonNullable<QueryFactoryConfig<TChildParams, TData, TError, TChildSelected, TPageParam>['queryFn']>;
|
|
123
|
+
} & ({
|
|
124
|
+
getNextPageParam: GetNextPageParamFunction<TPageParam, TData>;
|
|
125
|
+
initialPageParam: TPageParam;
|
|
126
|
+
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
127
|
+
shouldFetchNextPage?: (combined: TChildSelected | undefined, crawlOptions: TCrawlOptions) => boolean;
|
|
128
|
+
reduce?: (accumulator: TChildSelected | undefined, page: TData) => TChildSelected;
|
|
129
|
+
} | {
|
|
130
|
+
getNextPageParam?: never;
|
|
131
|
+
initialPageParam?: never;
|
|
132
|
+
getPreviousPageParam?: never;
|
|
133
|
+
shouldFetchNextPage?: never;
|
|
134
|
+
reduce?: never;
|
|
135
|
+
})): QueryFactory<TChildParams, TData, TError, TChildSelected, TPageParam, TCrawlOptions>;
|
|
136
|
+
/**
|
|
137
|
+
* Creates a child factory that reuses the parent's `queryFn` and pagination
|
|
138
|
+
* config. Useful for adding a `select` transform, narrowing params, or
|
|
139
|
+
* overriding `shouldFetchNextPage` with a different crawl-options shape —
|
|
140
|
+
* without changing what data is fetched. Parent and child `select` functions
|
|
141
|
+
* are automatically composed: `child.select(parent.select(data))`.
|
|
142
|
+
*/
|
|
143
|
+
declare function queryFactory<TChildParams extends TParentParams, TData = unknown, TError = Error, TParentSelected = TData, TChildSelected = TParentSelected, TParentParams = TChildParams, TPageParam = unknown, TParentCrawlOptions extends Record<string, unknown> = Record<string, unknown>, TChildCrawlOptions extends Record<string, unknown> = TParentCrawlOptions>(parent: QueryFactory<TParentParams, TData, any, TParentSelected, TPageParam, TParentCrawlOptions>, config: Omit<QueryFactoryConfig<TChildParams, TData, TError, TChildSelected, TPageParam, TChildCrawlOptions>, 'queryKey' | 'queryFn' | 'select'> & {
|
|
144
|
+
queryKey?: QueryKey;
|
|
145
|
+
queryFn?: never;
|
|
146
|
+
select?: (data: TParentSelected) => TChildSelected;
|
|
147
|
+
}): QueryFactory<TChildParams, TData, TError, TChildSelected, TPageParam, TChildCrawlOptions>;
|
|
148
|
+
|
|
149
|
+
export { type QueryFactory, type QueryFactoryConfig, type ResolvedInfiniteOptions, type ResolvedQueryOptions, type StandardQueryOptions, queryFactory };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { QueryKey, NotifyOnChangeProps, QueryMeta, QueryFunctionContext, InfiniteData, GetNextPageParamFunction, GetPreviousPageParamFunction } from '@tanstack/react-query';
|
|
2
|
+
|
|
3
|
+
/** Subset of TanStack Query options that apply to both regular and infinite queries. */
|
|
4
|
+
interface StandardQueryOptions<TError = Error> {
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
staleTime?: number | ((query: {
|
|
7
|
+
queryKey: QueryKey;
|
|
8
|
+
}) => number);
|
|
9
|
+
gcTime?: number;
|
|
10
|
+
retry?: boolean | number | ((failureCount: number, error: TError) => boolean);
|
|
11
|
+
retryDelay?: number | ((retryAttempt: number, error: TError) => number);
|
|
12
|
+
refetchOnWindowFocus?: boolean | 'always';
|
|
13
|
+
refetchOnReconnect?: boolean | 'always';
|
|
14
|
+
refetchOnMount?: boolean | 'always';
|
|
15
|
+
refetchInterval?: number | false | ((query: {
|
|
16
|
+
queryKey: QueryKey;
|
|
17
|
+
}) => number | false);
|
|
18
|
+
refetchIntervalInBackground?: boolean;
|
|
19
|
+
networkMode?: 'online' | 'always' | 'offlineFirst';
|
|
20
|
+
notifyOnChangeProps?: NotifyOnChangeProps;
|
|
21
|
+
throwOnError?: boolean | ((error: TError) => boolean);
|
|
22
|
+
meta?: QueryMeta;
|
|
23
|
+
structuralSharing?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Configuration passed to `queryFactory()`.
|
|
27
|
+
*
|
|
28
|
+
* - Set only `queryKey` + `queryFn` for a basic single-page query.
|
|
29
|
+
* - Add `getNextPageParam`, `initialPageParam`, and `reduce` to enable automatic
|
|
30
|
+
* pagination: the generated `queryFn` crawls all pages and reduces them to a
|
|
31
|
+
* single `TSelected` value.
|
|
32
|
+
* - Call `factory.infinite(params)` to get a `useInfiniteQuery`-compatible config
|
|
33
|
+
* where each virtual page is itself a crawl.
|
|
34
|
+
*/
|
|
35
|
+
interface QueryFactoryConfig<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>> extends StandardQueryOptions<TError> {
|
|
36
|
+
/** Namespace segments. Params are appended as the final element at call time,
|
|
37
|
+
* giving a full key of [...namespace, 'infinite'?, params, crawlOptions?]. */
|
|
38
|
+
queryKey: QueryKey;
|
|
39
|
+
queryFn?: (params: TParams, context: QueryFunctionContext<QueryKey, TPageParam>) => TData | Promise<TData>;
|
|
40
|
+
select?: (data: TData) => TSelected;
|
|
41
|
+
/** TanStack v5 generic order: GetNextPageParamFunction<TPageParam, TData> */
|
|
42
|
+
getNextPageParam?: GetNextPageParamFunction<TPageParam, TData>;
|
|
43
|
+
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
44
|
+
initialPageParam?: TPageParam;
|
|
45
|
+
/** Called after each page to decide whether to keep crawling.
|
|
46
|
+
* Receives the current combined result and the crawl options from the call site. */
|
|
47
|
+
shouldFetchNextPage?: (combined: TSelected | undefined, crawlOptions: TCrawlOptions) => boolean;
|
|
48
|
+
/** Reduces crawled pages incrementally into the final query result.
|
|
49
|
+
* Called once per page; accumulator is undefined on the first call.
|
|
50
|
+
* When set, enables crawling on both the regular and .infinite variants. */
|
|
51
|
+
reduce?: (accumulator: TSelected | undefined, page: TData) => TSelected;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What `factory(params)` returns — pass directly to `useQuery()`.
|
|
55
|
+
*
|
|
56
|
+
* The `initialPageParam?: never` field is a structural guard that makes this
|
|
57
|
+
* type incompatible with `useInfiniteQuery`, preventing accidental misuse.
|
|
58
|
+
*/
|
|
59
|
+
type ResolvedQueryOptions<TData = unknown, TError = Error, TSelected = TData> = StandardQueryOptions<TError> & {
|
|
60
|
+
queryKey: QueryKey;
|
|
61
|
+
queryFn?: (context: QueryFunctionContext) => TData | Promise<TData>;
|
|
62
|
+
select?: (data: TData) => TSelected;
|
|
63
|
+
/** Structural guard: makes this type incompatible with useInfiniteQuery, which requires initialPageParam. */
|
|
64
|
+
initialPageParam?: never;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* What `factory.infinite(params)` returns — pass directly to `useInfiniteQuery()`.
|
|
68
|
+
*
|
|
69
|
+
* The `select` field expects `InfiniteData<TData, TPageParam>`, which is a structural
|
|
70
|
+
* guard making this type incompatible with `useQuery`.
|
|
71
|
+
*/
|
|
72
|
+
type ResolvedInfiniteOptions<TData = unknown, TError = Error, TPageParam = unknown> = StandardQueryOptions<TError> & {
|
|
73
|
+
queryKey: QueryKey;
|
|
74
|
+
queryFn?: (context: QueryFunctionContext<QueryKey, TPageParam>) => TData | Promise<TData>;
|
|
75
|
+
/** Structural guard: the InfiniteData parameter type makes this incompatible with useQuery,
|
|
76
|
+
* whose select expects (data: TData) rather than (data: InfiniteData<TData, TPageParam>). */
|
|
77
|
+
select?: (data: InfiniteData<TData, TPageParam>) => any;
|
|
78
|
+
/** Required so this type satisfies useInfiniteQuery, which requires getNextPageParam. */
|
|
79
|
+
getNextPageParam: GetNextPageParamFunction<TPageParam, TData>;
|
|
80
|
+
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
81
|
+
/** Required so this type satisfies useInfiniteQuery, which requires initialPageParam. */
|
|
82
|
+
initialPageParam: TPageParam;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* A callable factory produced by `queryFactory()`.
|
|
86
|
+
*
|
|
87
|
+
* - `factory(params)` → `ResolvedQueryOptions` for `useQuery()`
|
|
88
|
+
* - `factory.infinite(params)` → `ResolvedInfiniteOptions` for `useInfiniteQuery()`
|
|
89
|
+
*
|
|
90
|
+
* Both signatures accept an optional `crawlOptions` object that is appended to
|
|
91
|
+
* the query key and passed to `shouldFetchNextPage` so different call sites can
|
|
92
|
+
* control crawl behavior independently.
|
|
93
|
+
*
|
|
94
|
+
* `params` is always optional. Calling with no arguments produces just the
|
|
95
|
+
* namespace key, which is useful for broad cache invalidation:
|
|
96
|
+
* `queryClient.invalidateQueries(factory())`
|
|
97
|
+
*/
|
|
98
|
+
interface QueryFactory<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>> {
|
|
99
|
+
(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedQueryOptions<TData, TError, TSelected>;
|
|
100
|
+
infinite(params?: TParams, crawlOptions?: TCrawlOptions): ResolvedInfiniteOptions<TData, TError, TPageParam>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a standalone query factory from a config object.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* const usersFactory = queryFactory({
|
|
107
|
+
* queryKey: ['users'],
|
|
108
|
+
* queryFn: (params: { page: number }, ctx) => fetchUsers(params, ctx.signal),
|
|
109
|
+
* });
|
|
110
|
+
* // useQuery(usersFactory({ page: 1 }))
|
|
111
|
+
*/
|
|
112
|
+
declare function queryFactory<TParams = void, TData = unknown, TError = Error, TSelected = TData, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>>(config: QueryFactoryConfig<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>): QueryFactory<TParams, TData, TError, TSelected, TPageParam, TCrawlOptions>;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a child factory that inherits the query key and standard options from
|
|
115
|
+
* `parent` and introduces a new `queryFn`. The child's query key is appended to
|
|
116
|
+
* the parent's, and standard options are shallow-merged (child wins).
|
|
117
|
+
*
|
|
118
|
+
* Use this overload when the child fetches different data than the parent.
|
|
119
|
+
*/
|
|
120
|
+
declare function queryFactory<TChildParams extends TParentParams, TData = unknown, TError = Error, TChildSelected = TData, TParentParams = TChildParams, TPageParam = unknown, TCrawlOptions extends Record<string, unknown> = Record<string, unknown>>(parent: QueryFactory<TParentParams, any, any, any, any, any>, config: Omit<QueryFactoryConfig<TChildParams, TData, TError, TChildSelected, TPageParam, TCrawlOptions>, 'queryKey' | 'getNextPageParam' | 'getPreviousPageParam' | 'initialPageParam' | 'shouldFetchNextPage' | 'reduce'> & {
|
|
121
|
+
queryKey?: QueryKey;
|
|
122
|
+
queryFn: NonNullable<QueryFactoryConfig<TChildParams, TData, TError, TChildSelected, TPageParam>['queryFn']>;
|
|
123
|
+
} & ({
|
|
124
|
+
getNextPageParam: GetNextPageParamFunction<TPageParam, TData>;
|
|
125
|
+
initialPageParam: TPageParam;
|
|
126
|
+
getPreviousPageParam?: GetPreviousPageParamFunction<TPageParam, TData>;
|
|
127
|
+
shouldFetchNextPage?: (combined: TChildSelected | undefined, crawlOptions: TCrawlOptions) => boolean;
|
|
128
|
+
reduce?: (accumulator: TChildSelected | undefined, page: TData) => TChildSelected;
|
|
129
|
+
} | {
|
|
130
|
+
getNextPageParam?: never;
|
|
131
|
+
initialPageParam?: never;
|
|
132
|
+
getPreviousPageParam?: never;
|
|
133
|
+
shouldFetchNextPage?: never;
|
|
134
|
+
reduce?: never;
|
|
135
|
+
})): QueryFactory<TChildParams, TData, TError, TChildSelected, TPageParam, TCrawlOptions>;
|
|
136
|
+
/**
|
|
137
|
+
* Creates a child factory that reuses the parent's `queryFn` and pagination
|
|
138
|
+
* config. Useful for adding a `select` transform, narrowing params, or
|
|
139
|
+
* overriding `shouldFetchNextPage` with a different crawl-options shape —
|
|
140
|
+
* without changing what data is fetched. Parent and child `select` functions
|
|
141
|
+
* are automatically composed: `child.select(parent.select(data))`.
|
|
142
|
+
*/
|
|
143
|
+
declare function queryFactory<TChildParams extends TParentParams, TData = unknown, TError = Error, TParentSelected = TData, TChildSelected = TParentSelected, TParentParams = TChildParams, TPageParam = unknown, TParentCrawlOptions extends Record<string, unknown> = Record<string, unknown>, TChildCrawlOptions extends Record<string, unknown> = TParentCrawlOptions>(parent: QueryFactory<TParentParams, TData, any, TParentSelected, TPageParam, TParentCrawlOptions>, config: Omit<QueryFactoryConfig<TChildParams, TData, TError, TChildSelected, TPageParam, TChildCrawlOptions>, 'queryKey' | 'queryFn' | 'select'> & {
|
|
144
|
+
queryKey?: QueryKey;
|
|
145
|
+
queryFn?: never;
|
|
146
|
+
select?: (data: TParentSelected) => TChildSelected;
|
|
147
|
+
}): QueryFactory<TChildParams, TData, TError, TChildSelected, TPageParam, TChildCrawlOptions>;
|
|
148
|
+
|
|
149
|
+
export { type QueryFactory, type QueryFactoryConfig, type ResolvedInfiniteOptions, type ResolvedQueryOptions, type StandardQueryOptions, queryFactory };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
queryFactory: () => queryFactory
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/queryFactory.ts
|
|
28
|
+
var FACTORY_CONFIG = /* @__PURE__ */ Symbol("factoryConfig");
|
|
29
|
+
function resolveKey(namespace, params, crawlOptions) {
|
|
30
|
+
const base = Array.isArray(namespace) ? namespace : [namespace];
|
|
31
|
+
const withParams = params === void 0 ? base : [...base, params];
|
|
32
|
+
if (!crawlOptions) return withParams;
|
|
33
|
+
const defined = Object.fromEntries(Object.entries(crawlOptions).filter(([, v]) => v !== void 0));
|
|
34
|
+
return Object.keys(defined).length > 0 ? [...withParams, defined] : withParams;
|
|
35
|
+
}
|
|
36
|
+
function wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions, reduce) {
|
|
37
|
+
return (lastPage, allPages, lastPageParam, allPageParams) => {
|
|
38
|
+
const combined = reduce ? allPages.reduce((acc, page) => reduce(acc, page), void 0) : lastPage;
|
|
39
|
+
if (!shouldFetchNextPage(combined, crawlOptions)) return void 0;
|
|
40
|
+
return getNextPageParam(lastPage, allPages, lastPageParam, allPageParams);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function buildCrawlingQueryFn(queryFn, getNextPageParam, initialPageParam, shouldFetchNextPage, reduce, crawlOptions) {
|
|
44
|
+
return async (context) => {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
const pages = [];
|
|
47
|
+
const pageParams = [];
|
|
48
|
+
let currentParam = initialPageParam;
|
|
49
|
+
let acc = void 0;
|
|
50
|
+
while (true) {
|
|
51
|
+
if ((_a = context.signal) == null ? void 0 : _a.aborted) break;
|
|
52
|
+
const page = await queryFn({ ...context, pageParam: currentParam });
|
|
53
|
+
pages.push(page);
|
|
54
|
+
pageParams.push(currentParam);
|
|
55
|
+
if (reduce) acc = reduce(acc, page);
|
|
56
|
+
if ((_b = context.signal) == null ? void 0 : _b.aborted) break;
|
|
57
|
+
if (shouldFetchNextPage && !shouldFetchNextPage(acc, crawlOptions)) break;
|
|
58
|
+
const nextParam = getNextPageParam(page, pages, currentParam, pageParams);
|
|
59
|
+
if (nextParam == null) break;
|
|
60
|
+
currentParam = nextParam;
|
|
61
|
+
}
|
|
62
|
+
if (reduce) {
|
|
63
|
+
if (acc === void 0) throw new DOMException("Aborted", "AbortError");
|
|
64
|
+
return acc;
|
|
65
|
+
}
|
|
66
|
+
return pages;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function buildInfiniteCrawlingQueryFn(queryFn, getNextPageParam, shouldFetchNextPage, reduce, crawlOptions) {
|
|
70
|
+
return async (context) => {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
const pages = [];
|
|
73
|
+
const pageParams = [];
|
|
74
|
+
let currentParam = context.pageParam;
|
|
75
|
+
let acc = void 0;
|
|
76
|
+
let nextBatchParam = null;
|
|
77
|
+
while (true) {
|
|
78
|
+
if ((_a = context.signal) == null ? void 0 : _a.aborted) break;
|
|
79
|
+
const page = await queryFn({ ...context, pageParam: currentParam });
|
|
80
|
+
pages.push(page);
|
|
81
|
+
pageParams.push(currentParam);
|
|
82
|
+
acc = reduce(acc, page);
|
|
83
|
+
if ((_b = context.signal) == null ? void 0 : _b.aborted) break;
|
|
84
|
+
const nextParam = getNextPageParam(page, pages, currentParam, pageParams);
|
|
85
|
+
nextBatchParam = nextParam != null ? nextParam : null;
|
|
86
|
+
if (nextParam == null) break;
|
|
87
|
+
if (shouldFetchNextPage && !shouldFetchNextPage(acc, crawlOptions)) break;
|
|
88
|
+
currentParam = nextParam;
|
|
89
|
+
}
|
|
90
|
+
if (acc === void 0) throw new DOMException("Aborted", "AbortError");
|
|
91
|
+
return { data: acc, nextPageParam: nextBatchParam };
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function buildFactory(cfg) {
|
|
95
|
+
const {
|
|
96
|
+
queryKey: namespace,
|
|
97
|
+
queryFn: rawQueryFn,
|
|
98
|
+
select,
|
|
99
|
+
getNextPageParam,
|
|
100
|
+
getPreviousPageParam,
|
|
101
|
+
initialPageParam,
|
|
102
|
+
shouldFetchNextPage,
|
|
103
|
+
reduce,
|
|
104
|
+
standardOptions
|
|
105
|
+
} = cfg;
|
|
106
|
+
const hasCrawling = rawQueryFn !== void 0 && getNextPageParam !== void 0;
|
|
107
|
+
const hasInfiniteCrawling = hasCrawling && reduce !== void 0;
|
|
108
|
+
const factory = function(params, crawlOptions = {}) {
|
|
109
|
+
const queryKey = resolveKey(namespace, params, crawlOptions);
|
|
110
|
+
const boundQueryFn = rawQueryFn ? (ctx) => rawQueryFn(params, ctx) : void 0;
|
|
111
|
+
const resolvedQueryFn = hasCrawling ? buildCrawlingQueryFn(boundQueryFn, getNextPageParam, initialPageParam, shouldFetchNextPage, reduce, crawlOptions) : boundQueryFn;
|
|
112
|
+
return {
|
|
113
|
+
...standardOptions,
|
|
114
|
+
queryKey,
|
|
115
|
+
...resolvedQueryFn !== void 0 && { queryFn: resolvedQueryFn },
|
|
116
|
+
...select !== void 0 && { select },
|
|
117
|
+
[FACTORY_CONFIG]: cfg
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
factory.infinite = function(params, crawlOptions = {}) {
|
|
121
|
+
const queryKey = resolveKey([...resolveKey(namespace, void 0), "infinite"], params, crawlOptions);
|
|
122
|
+
const boundQueryFn = rawQueryFn ? (ctx) => rawQueryFn(params, ctx) : void 0;
|
|
123
|
+
if (hasInfiniteCrawling) {
|
|
124
|
+
const crawlingQueryFn = buildInfiniteCrawlingQueryFn(
|
|
125
|
+
boundQueryFn,
|
|
126
|
+
getNextPageParam,
|
|
127
|
+
shouldFetchNextPage,
|
|
128
|
+
reduce,
|
|
129
|
+
crawlOptions
|
|
130
|
+
);
|
|
131
|
+
const envelopeGetNextPageParam = (envelope) => envelope.nextPageParam;
|
|
132
|
+
const envelopeSelect = (data) => ({
|
|
133
|
+
...data,
|
|
134
|
+
pages: data.pages.map((e) => select ? select(e.data) : e.data)
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
...standardOptions,
|
|
138
|
+
queryKey,
|
|
139
|
+
queryFn: crawlingQueryFn,
|
|
140
|
+
getNextPageParam: envelopeGetNextPageParam,
|
|
141
|
+
initialPageParam,
|
|
142
|
+
select: envelopeSelect,
|
|
143
|
+
...getPreviousPageParam !== void 0 && { getPreviousPageParam },
|
|
144
|
+
[FACTORY_CONFIG]: cfg
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const infiniteGetNextPageParam = getNextPageParam && shouldFetchNextPage ? wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions, reduce) : getNextPageParam != null ? getNextPageParam : (() => void 0);
|
|
148
|
+
const infiniteSelect = select ? (data) => ({
|
|
149
|
+
...data,
|
|
150
|
+
pages: data.pages.map(select)
|
|
151
|
+
}) : void 0;
|
|
152
|
+
return {
|
|
153
|
+
...standardOptions,
|
|
154
|
+
queryKey,
|
|
155
|
+
...boundQueryFn !== void 0 && { queryFn: boundQueryFn },
|
|
156
|
+
...infiniteSelect !== void 0 && { select: infiniteSelect },
|
|
157
|
+
getNextPageParam: infiniteGetNextPageParam,
|
|
158
|
+
...getPreviousPageParam !== void 0 && { getPreviousPageParam },
|
|
159
|
+
...initialPageParam !== void 0 && { initialPageParam },
|
|
160
|
+
[FACTORY_CONFIG]: cfg
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
return factory;
|
|
164
|
+
}
|
|
165
|
+
function queryFactory(configOrParent, childConfig) {
|
|
166
|
+
var _a, _b, _c, _d, _e;
|
|
167
|
+
if (childConfig !== void 0 && typeof configOrParent === "function") {
|
|
168
|
+
const result = configOrParent();
|
|
169
|
+
const parentCfg = result == null ? void 0 : result[FACTORY_CONFIG];
|
|
170
|
+
if (!parentCfg) {
|
|
171
|
+
throw new Error("queryFactory: first argument must be a factory created by queryFactory()");
|
|
172
|
+
}
|
|
173
|
+
const hasNewQueryFn = childConfig.queryFn !== void 0;
|
|
174
|
+
const childNamespace = childConfig.queryKey ? Array.isArray(childConfig.queryKey) ? childConfig.queryKey : [childConfig.queryKey] : [];
|
|
175
|
+
const composedNamespace = [...parentCfg.queryKey, ...childNamespace];
|
|
176
|
+
let resolvedSelect;
|
|
177
|
+
if (hasNewQueryFn) {
|
|
178
|
+
resolvedSelect = childConfig.select;
|
|
179
|
+
} else if (childConfig.select && parentCfg.select) {
|
|
180
|
+
const p = parentCfg.select;
|
|
181
|
+
const c = childConfig.select;
|
|
182
|
+
resolvedSelect = (data) => c(p(data));
|
|
183
|
+
} else {
|
|
184
|
+
resolvedSelect = (_a = childConfig.select) != null ? _a : parentCfg.select;
|
|
185
|
+
}
|
|
186
|
+
const crawling = hasNewQueryFn ? {
|
|
187
|
+
getNextPageParam: childConfig.getNextPageParam,
|
|
188
|
+
getPreviousPageParam: childConfig.getPreviousPageParam,
|
|
189
|
+
initialPageParam: childConfig.initialPageParam,
|
|
190
|
+
shouldFetchNextPage: childConfig.shouldFetchNextPage,
|
|
191
|
+
reduce: childConfig.reduce
|
|
192
|
+
} : {
|
|
193
|
+
getNextPageParam: (_b = childConfig.getNextPageParam) != null ? _b : parentCfg.getNextPageParam,
|
|
194
|
+
getPreviousPageParam: (_c = childConfig.getPreviousPageParam) != null ? _c : parentCfg.getPreviousPageParam,
|
|
195
|
+
initialPageParam: childConfig.initialPageParam !== void 0 ? childConfig.initialPageParam : parentCfg.initialPageParam,
|
|
196
|
+
shouldFetchNextPage: (_d = childConfig.shouldFetchNextPage) != null ? _d : parentCfg.shouldFetchNextPage,
|
|
197
|
+
reduce: (_e = childConfig.reduce) != null ? _e : parentCfg.reduce
|
|
198
|
+
};
|
|
199
|
+
const {
|
|
200
|
+
queryKey: _k,
|
|
201
|
+
queryFn: _f,
|
|
202
|
+
select: _s,
|
|
203
|
+
getNextPageParam: _g,
|
|
204
|
+
getPreviousPageParam: _gp,
|
|
205
|
+
initialPageParam: _ip,
|
|
206
|
+
shouldFetchNextPage: _sfnp,
|
|
207
|
+
reduce: _c2,
|
|
208
|
+
...childStandardOptions
|
|
209
|
+
} = childConfig;
|
|
210
|
+
return buildFactory({
|
|
211
|
+
queryKey: composedNamespace,
|
|
212
|
+
queryFn: hasNewQueryFn ? childConfig.queryFn : parentCfg.queryFn,
|
|
213
|
+
select: resolvedSelect,
|
|
214
|
+
...crawling,
|
|
215
|
+
standardOptions: { ...parentCfg.standardOptions, ...childStandardOptions }
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
const {
|
|
219
|
+
queryKey,
|
|
220
|
+
queryFn,
|
|
221
|
+
select,
|
|
222
|
+
getNextPageParam,
|
|
223
|
+
getPreviousPageParam,
|
|
224
|
+
initialPageParam,
|
|
225
|
+
shouldFetchNextPage,
|
|
226
|
+
reduce,
|
|
227
|
+
...standardOptions
|
|
228
|
+
} = configOrParent;
|
|
229
|
+
return buildFactory({
|
|
230
|
+
queryKey,
|
|
231
|
+
queryFn,
|
|
232
|
+
select,
|
|
233
|
+
getNextPageParam,
|
|
234
|
+
getPreviousPageParam,
|
|
235
|
+
initialPageParam,
|
|
236
|
+
shouldFetchNextPage,
|
|
237
|
+
reduce,
|
|
238
|
+
standardOptions
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
242
|
+
0 && (module.exports = {
|
|
243
|
+
queryFactory
|
|
244
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// src/queryFactory.ts
|
|
2
|
+
var FACTORY_CONFIG = /* @__PURE__ */ Symbol("factoryConfig");
|
|
3
|
+
function resolveKey(namespace, params, crawlOptions) {
|
|
4
|
+
const base = Array.isArray(namespace) ? namespace : [namespace];
|
|
5
|
+
const withParams = params === void 0 ? base : [...base, params];
|
|
6
|
+
if (!crawlOptions) return withParams;
|
|
7
|
+
const defined = Object.fromEntries(Object.entries(crawlOptions).filter(([, v]) => v !== void 0));
|
|
8
|
+
return Object.keys(defined).length > 0 ? [...withParams, defined] : withParams;
|
|
9
|
+
}
|
|
10
|
+
function wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions, reduce) {
|
|
11
|
+
return (lastPage, allPages, lastPageParam, allPageParams) => {
|
|
12
|
+
const combined = reduce ? allPages.reduce((acc, page) => reduce(acc, page), void 0) : lastPage;
|
|
13
|
+
if (!shouldFetchNextPage(combined, crawlOptions)) return void 0;
|
|
14
|
+
return getNextPageParam(lastPage, allPages, lastPageParam, allPageParams);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function buildCrawlingQueryFn(queryFn, getNextPageParam, initialPageParam, shouldFetchNextPage, reduce, crawlOptions) {
|
|
18
|
+
return async (context) => {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
const pages = [];
|
|
21
|
+
const pageParams = [];
|
|
22
|
+
let currentParam = initialPageParam;
|
|
23
|
+
let acc = void 0;
|
|
24
|
+
while (true) {
|
|
25
|
+
if ((_a = context.signal) == null ? void 0 : _a.aborted) break;
|
|
26
|
+
const page = await queryFn({ ...context, pageParam: currentParam });
|
|
27
|
+
pages.push(page);
|
|
28
|
+
pageParams.push(currentParam);
|
|
29
|
+
if (reduce) acc = reduce(acc, page);
|
|
30
|
+
if ((_b = context.signal) == null ? void 0 : _b.aborted) break;
|
|
31
|
+
if (shouldFetchNextPage && !shouldFetchNextPage(acc, crawlOptions)) break;
|
|
32
|
+
const nextParam = getNextPageParam(page, pages, currentParam, pageParams);
|
|
33
|
+
if (nextParam == null) break;
|
|
34
|
+
currentParam = nextParam;
|
|
35
|
+
}
|
|
36
|
+
if (reduce) {
|
|
37
|
+
if (acc === void 0) throw new DOMException("Aborted", "AbortError");
|
|
38
|
+
return acc;
|
|
39
|
+
}
|
|
40
|
+
return pages;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function buildInfiniteCrawlingQueryFn(queryFn, getNextPageParam, shouldFetchNextPage, reduce, crawlOptions) {
|
|
44
|
+
return async (context) => {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
const pages = [];
|
|
47
|
+
const pageParams = [];
|
|
48
|
+
let currentParam = context.pageParam;
|
|
49
|
+
let acc = void 0;
|
|
50
|
+
let nextBatchParam = null;
|
|
51
|
+
while (true) {
|
|
52
|
+
if ((_a = context.signal) == null ? void 0 : _a.aborted) break;
|
|
53
|
+
const page = await queryFn({ ...context, pageParam: currentParam });
|
|
54
|
+
pages.push(page);
|
|
55
|
+
pageParams.push(currentParam);
|
|
56
|
+
acc = reduce(acc, page);
|
|
57
|
+
if ((_b = context.signal) == null ? void 0 : _b.aborted) break;
|
|
58
|
+
const nextParam = getNextPageParam(page, pages, currentParam, pageParams);
|
|
59
|
+
nextBatchParam = nextParam != null ? nextParam : null;
|
|
60
|
+
if (nextParam == null) break;
|
|
61
|
+
if (shouldFetchNextPage && !shouldFetchNextPage(acc, crawlOptions)) break;
|
|
62
|
+
currentParam = nextParam;
|
|
63
|
+
}
|
|
64
|
+
if (acc === void 0) throw new DOMException("Aborted", "AbortError");
|
|
65
|
+
return { data: acc, nextPageParam: nextBatchParam };
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function buildFactory(cfg) {
|
|
69
|
+
const {
|
|
70
|
+
queryKey: namespace,
|
|
71
|
+
queryFn: rawQueryFn,
|
|
72
|
+
select,
|
|
73
|
+
getNextPageParam,
|
|
74
|
+
getPreviousPageParam,
|
|
75
|
+
initialPageParam,
|
|
76
|
+
shouldFetchNextPage,
|
|
77
|
+
reduce,
|
|
78
|
+
standardOptions
|
|
79
|
+
} = cfg;
|
|
80
|
+
const hasCrawling = rawQueryFn !== void 0 && getNextPageParam !== void 0;
|
|
81
|
+
const hasInfiniteCrawling = hasCrawling && reduce !== void 0;
|
|
82
|
+
const factory = function(params, crawlOptions = {}) {
|
|
83
|
+
const queryKey = resolveKey(namespace, params, crawlOptions);
|
|
84
|
+
const boundQueryFn = rawQueryFn ? (ctx) => rawQueryFn(params, ctx) : void 0;
|
|
85
|
+
const resolvedQueryFn = hasCrawling ? buildCrawlingQueryFn(boundQueryFn, getNextPageParam, initialPageParam, shouldFetchNextPage, reduce, crawlOptions) : boundQueryFn;
|
|
86
|
+
return {
|
|
87
|
+
...standardOptions,
|
|
88
|
+
queryKey,
|
|
89
|
+
...resolvedQueryFn !== void 0 && { queryFn: resolvedQueryFn },
|
|
90
|
+
...select !== void 0 && { select },
|
|
91
|
+
[FACTORY_CONFIG]: cfg
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
factory.infinite = function(params, crawlOptions = {}) {
|
|
95
|
+
const queryKey = resolveKey([...resolveKey(namespace, void 0), "infinite"], params, crawlOptions);
|
|
96
|
+
const boundQueryFn = rawQueryFn ? (ctx) => rawQueryFn(params, ctx) : void 0;
|
|
97
|
+
if (hasInfiniteCrawling) {
|
|
98
|
+
const crawlingQueryFn = buildInfiniteCrawlingQueryFn(
|
|
99
|
+
boundQueryFn,
|
|
100
|
+
getNextPageParam,
|
|
101
|
+
shouldFetchNextPage,
|
|
102
|
+
reduce,
|
|
103
|
+
crawlOptions
|
|
104
|
+
);
|
|
105
|
+
const envelopeGetNextPageParam = (envelope) => envelope.nextPageParam;
|
|
106
|
+
const envelopeSelect = (data) => ({
|
|
107
|
+
...data,
|
|
108
|
+
pages: data.pages.map((e) => select ? select(e.data) : e.data)
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
...standardOptions,
|
|
112
|
+
queryKey,
|
|
113
|
+
queryFn: crawlingQueryFn,
|
|
114
|
+
getNextPageParam: envelopeGetNextPageParam,
|
|
115
|
+
initialPageParam,
|
|
116
|
+
select: envelopeSelect,
|
|
117
|
+
...getPreviousPageParam !== void 0 && { getPreviousPageParam },
|
|
118
|
+
[FACTORY_CONFIG]: cfg
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const infiniteGetNextPageParam = getNextPageParam && shouldFetchNextPage ? wrapGetNextPageParam(getNextPageParam, shouldFetchNextPage, crawlOptions, reduce) : getNextPageParam != null ? getNextPageParam : (() => void 0);
|
|
122
|
+
const infiniteSelect = select ? (data) => ({
|
|
123
|
+
...data,
|
|
124
|
+
pages: data.pages.map(select)
|
|
125
|
+
}) : void 0;
|
|
126
|
+
return {
|
|
127
|
+
...standardOptions,
|
|
128
|
+
queryKey,
|
|
129
|
+
...boundQueryFn !== void 0 && { queryFn: boundQueryFn },
|
|
130
|
+
...infiniteSelect !== void 0 && { select: infiniteSelect },
|
|
131
|
+
getNextPageParam: infiniteGetNextPageParam,
|
|
132
|
+
...getPreviousPageParam !== void 0 && { getPreviousPageParam },
|
|
133
|
+
...initialPageParam !== void 0 && { initialPageParam },
|
|
134
|
+
[FACTORY_CONFIG]: cfg
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
return factory;
|
|
138
|
+
}
|
|
139
|
+
function queryFactory(configOrParent, childConfig) {
|
|
140
|
+
var _a, _b, _c, _d, _e;
|
|
141
|
+
if (childConfig !== void 0 && typeof configOrParent === "function") {
|
|
142
|
+
const result = configOrParent();
|
|
143
|
+
const parentCfg = result == null ? void 0 : result[FACTORY_CONFIG];
|
|
144
|
+
if (!parentCfg) {
|
|
145
|
+
throw new Error("queryFactory: first argument must be a factory created by queryFactory()");
|
|
146
|
+
}
|
|
147
|
+
const hasNewQueryFn = childConfig.queryFn !== void 0;
|
|
148
|
+
const childNamespace = childConfig.queryKey ? Array.isArray(childConfig.queryKey) ? childConfig.queryKey : [childConfig.queryKey] : [];
|
|
149
|
+
const composedNamespace = [...parentCfg.queryKey, ...childNamespace];
|
|
150
|
+
let resolvedSelect;
|
|
151
|
+
if (hasNewQueryFn) {
|
|
152
|
+
resolvedSelect = childConfig.select;
|
|
153
|
+
} else if (childConfig.select && parentCfg.select) {
|
|
154
|
+
const p = parentCfg.select;
|
|
155
|
+
const c = childConfig.select;
|
|
156
|
+
resolvedSelect = (data) => c(p(data));
|
|
157
|
+
} else {
|
|
158
|
+
resolvedSelect = (_a = childConfig.select) != null ? _a : parentCfg.select;
|
|
159
|
+
}
|
|
160
|
+
const crawling = hasNewQueryFn ? {
|
|
161
|
+
getNextPageParam: childConfig.getNextPageParam,
|
|
162
|
+
getPreviousPageParam: childConfig.getPreviousPageParam,
|
|
163
|
+
initialPageParam: childConfig.initialPageParam,
|
|
164
|
+
shouldFetchNextPage: childConfig.shouldFetchNextPage,
|
|
165
|
+
reduce: childConfig.reduce
|
|
166
|
+
} : {
|
|
167
|
+
getNextPageParam: (_b = childConfig.getNextPageParam) != null ? _b : parentCfg.getNextPageParam,
|
|
168
|
+
getPreviousPageParam: (_c = childConfig.getPreviousPageParam) != null ? _c : parentCfg.getPreviousPageParam,
|
|
169
|
+
initialPageParam: childConfig.initialPageParam !== void 0 ? childConfig.initialPageParam : parentCfg.initialPageParam,
|
|
170
|
+
shouldFetchNextPage: (_d = childConfig.shouldFetchNextPage) != null ? _d : parentCfg.shouldFetchNextPage,
|
|
171
|
+
reduce: (_e = childConfig.reduce) != null ? _e : parentCfg.reduce
|
|
172
|
+
};
|
|
173
|
+
const {
|
|
174
|
+
queryKey: _k,
|
|
175
|
+
queryFn: _f,
|
|
176
|
+
select: _s,
|
|
177
|
+
getNextPageParam: _g,
|
|
178
|
+
getPreviousPageParam: _gp,
|
|
179
|
+
initialPageParam: _ip,
|
|
180
|
+
shouldFetchNextPage: _sfnp,
|
|
181
|
+
reduce: _c2,
|
|
182
|
+
...childStandardOptions
|
|
183
|
+
} = childConfig;
|
|
184
|
+
return buildFactory({
|
|
185
|
+
queryKey: composedNamespace,
|
|
186
|
+
queryFn: hasNewQueryFn ? childConfig.queryFn : parentCfg.queryFn,
|
|
187
|
+
select: resolvedSelect,
|
|
188
|
+
...crawling,
|
|
189
|
+
standardOptions: { ...parentCfg.standardOptions, ...childStandardOptions }
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const {
|
|
193
|
+
queryKey,
|
|
194
|
+
queryFn,
|
|
195
|
+
select,
|
|
196
|
+
getNextPageParam,
|
|
197
|
+
getPreviousPageParam,
|
|
198
|
+
initialPageParam,
|
|
199
|
+
shouldFetchNextPage,
|
|
200
|
+
reduce,
|
|
201
|
+
...standardOptions
|
|
202
|
+
} = configOrParent;
|
|
203
|
+
return buildFactory({
|
|
204
|
+
queryKey,
|
|
205
|
+
queryFn,
|
|
206
|
+
select,
|
|
207
|
+
getNextPageParam,
|
|
208
|
+
getPreviousPageParam,
|
|
209
|
+
initialPageParam,
|
|
210
|
+
shouldFetchNextPage,
|
|
211
|
+
reduce,
|
|
212
|
+
standardOptions
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
queryFactory
|
|
217
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@robohall/react-query-factory",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A factory abstraction for TanStack Query (React Query) with composable keys, crawling support, and automatic infinite query generation",
|
|
5
|
+
"author": "Robert Hall",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/roberth26/react-query-factory.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": "https://github.com/roberth26/react-query-factory/issues",
|
|
12
|
+
"homepage": "https://github.com/roberth26/react-query-factory#readme",
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"module": "dist/index.mjs",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
},
|
|
22
|
+
"require": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"prepublishOnly": "npm run build && npm test",
|
|
38
|
+
"sandbox": "cd sandbox && npm run dev"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@tanstack/react-query": ">=5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@tanstack/react-query": "^5.0.0",
|
|
45
|
+
"tsup": "^8.0.0",
|
|
46
|
+
"typescript": "^5.0.0",
|
|
47
|
+
"vitest": "^2.0.0"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"react-query",
|
|
51
|
+
"tanstack-query",
|
|
52
|
+
"query-factory",
|
|
53
|
+
"typescript"
|
|
54
|
+
]
|
|
55
|
+
}
|