@spoosh/react 0.5.1 → 0.7.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 +2 -3
- package/dist/index.d.mts +70 -49
- package/dist/index.d.ts +70 -49
- package/dist/index.js +98 -20
- package/dist/index.mjs +98 -20
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Fetch data with automatic caching and refetching.
|
|
|
32
32
|
|
|
33
33
|
```typescript
|
|
34
34
|
function UserList() {
|
|
35
|
-
const { data, loading, error,
|
|
35
|
+
const { data, loading, error, trigger } = useRead(
|
|
36
36
|
(api) => api("users").GET()
|
|
37
37
|
);
|
|
38
38
|
|
|
@@ -177,7 +177,7 @@ function PostList() {
|
|
|
177
177
|
| `error` | `TError \| undefined` | Error if request failed |
|
|
178
178
|
| `loading` | `boolean` | True during initial load |
|
|
179
179
|
| `fetching` | `boolean` | True during any fetch |
|
|
180
|
-
| `
|
|
180
|
+
| `trigger` | `() => Promise` | Manually trigger fetch |
|
|
181
181
|
| `abort` | `() => void` | Abort current request |
|
|
182
182
|
|
|
183
183
|
### useWrite(writeFn)
|
|
@@ -190,7 +190,6 @@ function PostList() {
|
|
|
190
190
|
| `data` | `TData \| undefined` | Response data |
|
|
191
191
|
| `error` | `TError \| undefined` | Error if request failed |
|
|
192
192
|
| `loading` | `boolean` | True while mutation is in progress |
|
|
193
|
-
| `reset` | `() => void` | Reset state |
|
|
194
193
|
| `abort` | `() => void` | Abort current request |
|
|
195
194
|
|
|
196
195
|
### useInfiniteRead(readFn, options)
|
package/dist/index.d.mts
CHANGED
|
@@ -27,8 +27,11 @@ type BaseReadOptions = {
|
|
|
27
27
|
* @template TData - The response data type
|
|
28
28
|
* @template TError - The error type
|
|
29
29
|
* @template TMeta - Plugin-provided metadata fields
|
|
30
|
+
* @template TTriggerOptions - Options that can be passed to trigger()
|
|
30
31
|
*/
|
|
31
|
-
type BaseReadResult<TData, TError, TMeta = Record<string, unknown
|
|
32
|
+
type BaseReadResult<TData, TError, TMeta = Record<string, unknown>, TTriggerOptions = {
|
|
33
|
+
force?: boolean;
|
|
34
|
+
}> = {
|
|
32
35
|
/** True during the initial load (no data yet) */
|
|
33
36
|
loading: boolean;
|
|
34
37
|
/** True during any fetch operation */
|
|
@@ -41,8 +44,12 @@ type BaseReadResult<TData, TError, TMeta = Record<string, unknown>> = {
|
|
|
41
44
|
meta: TMeta;
|
|
42
45
|
/** Abort the current fetch operation */
|
|
43
46
|
abort: () => void;
|
|
44
|
-
/**
|
|
45
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Manually trigger a fetch.
|
|
49
|
+
*
|
|
50
|
+
* @param options - Optional override options (query, body, params) to use for this specific request
|
|
51
|
+
*/
|
|
52
|
+
trigger: (options?: TTriggerOptions) => Promise<SpooshResponse<TData, TError>>;
|
|
46
53
|
};
|
|
47
54
|
/**
|
|
48
55
|
* Result returned by `useWrite` hook.
|
|
@@ -63,8 +70,6 @@ type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> =
|
|
|
63
70
|
error: TError | undefined;
|
|
64
71
|
/** Plugin-provided metadata */
|
|
65
72
|
meta: TMeta;
|
|
66
|
-
/** Reset the state to initial values */
|
|
67
|
-
reset: () => void;
|
|
68
73
|
/** Abort the current mutation */
|
|
69
74
|
abort: () => void;
|
|
70
75
|
};
|
|
@@ -129,6 +134,33 @@ type ExtractResponseParamNames<T> = SuccessReturnType<T> extends {
|
|
|
129
134
|
params: Record<infer K, unknown>;
|
|
130
135
|
};
|
|
131
136
|
} ? K extends string ? K : never : never;
|
|
137
|
+
type TriggerAwaitedReturn<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
|
|
138
|
+
type ExtractInputFromResponse<T> = T extends {
|
|
139
|
+
input: infer I;
|
|
140
|
+
} ? I : never;
|
|
141
|
+
type ExtractTriggerQuery<I> = I extends {
|
|
142
|
+
query: infer Q;
|
|
143
|
+
} ? {
|
|
144
|
+
query?: Q;
|
|
145
|
+
} : unknown;
|
|
146
|
+
type ExtractTriggerBody<I> = I extends {
|
|
147
|
+
body: infer B;
|
|
148
|
+
} ? {
|
|
149
|
+
body?: B;
|
|
150
|
+
} : unknown;
|
|
151
|
+
type ExtractTriggerParams<I> = I extends {
|
|
152
|
+
params: infer P;
|
|
153
|
+
} ? {
|
|
154
|
+
params?: P;
|
|
155
|
+
} : unknown;
|
|
156
|
+
type TriggerOptions<T> = ExtractInputFromResponse<TriggerAwaitedReturn<T>> extends infer I ? [I] extends [never] ? {
|
|
157
|
+
force?: boolean;
|
|
158
|
+
} : ExtractTriggerQuery<I> & ExtractTriggerBody<I> & ExtractTriggerParams<I> & {
|
|
159
|
+
/** Force refetch even if data is cached */
|
|
160
|
+
force?: boolean;
|
|
161
|
+
} : {
|
|
162
|
+
force?: boolean;
|
|
163
|
+
};
|
|
132
164
|
type QueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
|
|
133
165
|
query?: Exclude<TQuery, undefined>;
|
|
134
166
|
} : {
|
|
@@ -237,8 +269,8 @@ type BaseInfiniteReadResult<TData, TError, TItem, TPluginResult = Record<string,
|
|
|
237
269
|
fetchNext: () => Promise<void>;
|
|
238
270
|
/** Fetch the previous page */
|
|
239
271
|
fetchPrev: () => Promise<void>;
|
|
240
|
-
/**
|
|
241
|
-
|
|
272
|
+
/** Trigger refetch of all pages from the beginning */
|
|
273
|
+
trigger: () => Promise<void>;
|
|
242
274
|
/** Abort the current fetch operation */
|
|
243
275
|
abort: () => void;
|
|
244
276
|
/** Error from the last failed request */
|
|
@@ -251,14 +283,8 @@ type InferError<T, TDefaultError> = [T] extends [unknown] ? TDefaultError : T;
|
|
|
251
283
|
type WriteResolverContext<TSchema, TMethod, TDefaultError> = ResolverContext<TSchema, ExtractMethodData<TMethod>, InferError<ExtractMethodError<TMethod>, TDefaultError>, ExtractMethodQuery<TMethod>, ExtractMethodBody<TMethod>, ExtractResponseParamNames<TMethod> extends never ? never : Record<ExtractResponseParamNames<TMethod>, string | number>>;
|
|
252
284
|
type ResolvedWriteOptions<TSchema, TPlugins extends PluginArray, TMethod, TDefaultError> = ResolveTypes<MergePluginOptions<TPlugins>["write"], WriteResolverContext<TSchema, TMethod, TDefaultError>>;
|
|
253
285
|
type UseReadFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
254
|
-
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<
|
|
255
|
-
|
|
256
|
-
error?: unknown;
|
|
257
|
-
}>, TReadOpts>(readFn: TReadFn, readOptions: TReadOpts & BaseReadOptions & ResolveTypes<MergePluginOptions<TPlugins>["read"], ResolverContext<TSchema, ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
258
|
-
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<{
|
|
259
|
-
data?: unknown;
|
|
260
|
-
error?: unknown;
|
|
261
|
-
}>>(readFn: TReadFn): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, MergePluginResults<TPlugins>["read"]> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
286
|
+
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<SpooshResponse<unknown, unknown>>, TReadOpts>(readFn: TReadFn, readOptions: TReadOpts & BaseReadOptions & ResolveTypes<MergePluginOptions<TPlugins>["read"], ResolverContext<TSchema, ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>, TriggerOptions<TReadFn>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
287
|
+
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<SpooshResponse<unknown, unknown>>>(readFn: TReadFn): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, MergePluginResults<TPlugins>["read"], TriggerOptions<TReadFn>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
262
288
|
};
|
|
263
289
|
type UseWriteFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
264
290
|
<TMethod extends (...args: never) => Promise<SpooshResponse<unknown, unknown>>>(writeFn: (api: WriteApiClient<TSchema, TDefaultError>) => TMethod): BaseWriteResult<ExtractMethodData<TMethod>, InferError<ExtractMethodError<TMethod>, TDefaultError>, Parameters<TMethod>[0] & ResolvedWriteOptions<TSchema, TPlugins, TMethod, TDefaultError>, MergePluginResults<TPlugins>["write"]> & WriteResponseInputFields<ExtractMethodQuery<TMethod>, ExtractMethodBody<TMethod>, ExtractResponseParamNames<TMethod>>;
|
|
@@ -285,7 +311,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
|
285
311
|
*
|
|
286
312
|
* @param readFn - Function that selects the API endpoint to call (e.g., `(api) => api("posts").GET()`)
|
|
287
313
|
* @param readOptions - Optional configuration including `enabled`, `tags`, and plugin-specific options
|
|
288
|
-
* @returns Object containing `data`, `error`, `loading`, `fetching`, `
|
|
314
|
+
* @returns Object containing `data`, `error`, `loading`, `fetching`, `trigger`, and `abort`
|
|
289
315
|
*
|
|
290
316
|
* @example
|
|
291
317
|
* ```tsx
|
|
@@ -302,7 +328,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
|
302
328
|
* React hook for mutations (POST, PUT, PATCH, DELETE) with manual triggering.
|
|
303
329
|
*
|
|
304
330
|
* @param writeFn - Function that selects the API endpoint (e.g., `(api) => api("posts").POST`)
|
|
305
|
-
* @returns Object containing `trigger`, `data`, `error`, `loading`,
|
|
331
|
+
* @returns Object containing `trigger`, `data`, `error`, `loading`, and `abort`
|
|
306
332
|
*
|
|
307
333
|
* @example
|
|
308
334
|
* ```tsx
|
|
@@ -378,38 +404,33 @@ type SpooshInstanceShape<TApi, TSchema, TDefaultError, TPlugins> = {
|
|
|
378
404
|
*/
|
|
379
405
|
declare function createReactSpoosh<TSchema, TDefaultError, TPlugins extends PluginArray, TApi>(instance: SpooshInstanceShape<TApi, TSchema, TDefaultError, TPlugins>): SpooshReactHooks<TDefaultError, TSchema, TPlugins>;
|
|
380
406
|
|
|
381
|
-
declare function createUseRead<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
error: infer E;
|
|
409
|
-
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
410
|
-
error: infer E;
|
|
411
|
-
}> ? E : unknown, MergePluginResults<TPlugins>["read"]> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
412
|
-
};
|
|
407
|
+
declare function createUseRead<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): <TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<SpooshResponse<unknown, unknown>>, TReadOpts extends BaseReadOptions & ResolveTypes<((TPlugins[number] extends infer T ? T extends TPlugins[number] ? T extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
408
|
+
readOptions: infer R;
|
|
409
|
+
} ? R : object : object : never : never) extends infer T_1 ? T_1 extends (TPlugins[number] extends infer T_2 ? T_2 extends TPlugins[number] ? T_2 extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
410
|
+
readOptions: infer R;
|
|
411
|
+
} ? R : object : object : never : never) ? T_1 extends unknown ? (x: T_1) => void : never : never : never) extends (x: infer I) => void ? I : never, ResolverContext<TSchema, TReadFn extends (...args: unknown[]) => Promise<{
|
|
412
|
+
data: infer D;
|
|
413
|
+
}> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
|
|
414
|
+
error: infer E;
|
|
415
|
+
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
416
|
+
error: infer E;
|
|
417
|
+
}> ? E : unknown, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>> = BaseReadOptions & ResolveTypes<((TPlugins[number] extends infer T_3 ? T_3 extends TPlugins[number] ? T_3 extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
418
|
+
readOptions: infer R;
|
|
419
|
+
} ? R : object : object : never : never) extends infer T_4 ? T_4 extends (TPlugins[number] extends infer T_5 ? T_5 extends TPlugins[number] ? T_5 extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
420
|
+
readOptions: infer R;
|
|
421
|
+
} ? R : object : object : never : never) ? T_4 extends unknown ? (x: T_4) => void : never : never : never) extends (x: infer I) => void ? I : never, ResolverContext<TSchema, TReadFn extends (...args: unknown[]) => Promise<{
|
|
422
|
+
data: infer D;
|
|
423
|
+
}> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
|
|
424
|
+
error: infer E;
|
|
425
|
+
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
426
|
+
error: infer E;
|
|
427
|
+
}> ? E : unknown, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>>(readFn: TReadFn, readOptions?: TReadOpts) => BaseReadResult<TReadFn extends (...args: unknown[]) => Promise<{
|
|
428
|
+
data: infer D;
|
|
429
|
+
}> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
|
|
430
|
+
error: infer E;
|
|
431
|
+
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
432
|
+
error: infer E;
|
|
433
|
+
}> ? E : unknown, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>, TriggerOptions<TReadFn>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
413
434
|
|
|
414
435
|
declare function createUseWrite<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): <TMethod extends (...args: never[]) => Promise<SpooshResponse<unknown, unknown>>>(writeFn: (api: WriteApiClient<TSchema, TDefaultError>) => TMethod) => BaseWriteResult<ExtractMethodData<TMethod>, [ExtractMethodError<TMethod>] extends [unknown] ? TDefaultError : ExtractMethodError<TMethod>, ExtractMethodOptions<TMethod> & ResolveTypes<((TPlugins[number] extends infer T ? T extends TPlugins[number] ? T extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
415
436
|
writeOptions: infer W;
|
package/dist/index.d.ts
CHANGED
|
@@ -27,8 +27,11 @@ type BaseReadOptions = {
|
|
|
27
27
|
* @template TData - The response data type
|
|
28
28
|
* @template TError - The error type
|
|
29
29
|
* @template TMeta - Plugin-provided metadata fields
|
|
30
|
+
* @template TTriggerOptions - Options that can be passed to trigger()
|
|
30
31
|
*/
|
|
31
|
-
type BaseReadResult<TData, TError, TMeta = Record<string, unknown
|
|
32
|
+
type BaseReadResult<TData, TError, TMeta = Record<string, unknown>, TTriggerOptions = {
|
|
33
|
+
force?: boolean;
|
|
34
|
+
}> = {
|
|
32
35
|
/** True during the initial load (no data yet) */
|
|
33
36
|
loading: boolean;
|
|
34
37
|
/** True during any fetch operation */
|
|
@@ -41,8 +44,12 @@ type BaseReadResult<TData, TError, TMeta = Record<string, unknown>> = {
|
|
|
41
44
|
meta: TMeta;
|
|
42
45
|
/** Abort the current fetch operation */
|
|
43
46
|
abort: () => void;
|
|
44
|
-
/**
|
|
45
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Manually trigger a fetch.
|
|
49
|
+
*
|
|
50
|
+
* @param options - Optional override options (query, body, params) to use for this specific request
|
|
51
|
+
*/
|
|
52
|
+
trigger: (options?: TTriggerOptions) => Promise<SpooshResponse<TData, TError>>;
|
|
46
53
|
};
|
|
47
54
|
/**
|
|
48
55
|
* Result returned by `useWrite` hook.
|
|
@@ -63,8 +70,6 @@ type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> =
|
|
|
63
70
|
error: TError | undefined;
|
|
64
71
|
/** Plugin-provided metadata */
|
|
65
72
|
meta: TMeta;
|
|
66
|
-
/** Reset the state to initial values */
|
|
67
|
-
reset: () => void;
|
|
68
73
|
/** Abort the current mutation */
|
|
69
74
|
abort: () => void;
|
|
70
75
|
};
|
|
@@ -129,6 +134,33 @@ type ExtractResponseParamNames<T> = SuccessReturnType<T> extends {
|
|
|
129
134
|
params: Record<infer K, unknown>;
|
|
130
135
|
};
|
|
131
136
|
} ? K extends string ? K : never : never;
|
|
137
|
+
type TriggerAwaitedReturn<T> = T extends (...args: never[]) => infer R ? Awaited<R> : never;
|
|
138
|
+
type ExtractInputFromResponse<T> = T extends {
|
|
139
|
+
input: infer I;
|
|
140
|
+
} ? I : never;
|
|
141
|
+
type ExtractTriggerQuery<I> = I extends {
|
|
142
|
+
query: infer Q;
|
|
143
|
+
} ? {
|
|
144
|
+
query?: Q;
|
|
145
|
+
} : unknown;
|
|
146
|
+
type ExtractTriggerBody<I> = I extends {
|
|
147
|
+
body: infer B;
|
|
148
|
+
} ? {
|
|
149
|
+
body?: B;
|
|
150
|
+
} : unknown;
|
|
151
|
+
type ExtractTriggerParams<I> = I extends {
|
|
152
|
+
params: infer P;
|
|
153
|
+
} ? {
|
|
154
|
+
params?: P;
|
|
155
|
+
} : unknown;
|
|
156
|
+
type TriggerOptions<T> = ExtractInputFromResponse<TriggerAwaitedReturn<T>> extends infer I ? [I] extends [never] ? {
|
|
157
|
+
force?: boolean;
|
|
158
|
+
} : ExtractTriggerQuery<I> & ExtractTriggerBody<I> & ExtractTriggerParams<I> & {
|
|
159
|
+
/** Force refetch even if data is cached */
|
|
160
|
+
force?: boolean;
|
|
161
|
+
} : {
|
|
162
|
+
force?: boolean;
|
|
163
|
+
};
|
|
132
164
|
type QueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
|
|
133
165
|
query?: Exclude<TQuery, undefined>;
|
|
134
166
|
} : {
|
|
@@ -237,8 +269,8 @@ type BaseInfiniteReadResult<TData, TError, TItem, TPluginResult = Record<string,
|
|
|
237
269
|
fetchNext: () => Promise<void>;
|
|
238
270
|
/** Fetch the previous page */
|
|
239
271
|
fetchPrev: () => Promise<void>;
|
|
240
|
-
/**
|
|
241
|
-
|
|
272
|
+
/** Trigger refetch of all pages from the beginning */
|
|
273
|
+
trigger: () => Promise<void>;
|
|
242
274
|
/** Abort the current fetch operation */
|
|
243
275
|
abort: () => void;
|
|
244
276
|
/** Error from the last failed request */
|
|
@@ -251,14 +283,8 @@ type InferError<T, TDefaultError> = [T] extends [unknown] ? TDefaultError : T;
|
|
|
251
283
|
type WriteResolverContext<TSchema, TMethod, TDefaultError> = ResolverContext<TSchema, ExtractMethodData<TMethod>, InferError<ExtractMethodError<TMethod>, TDefaultError>, ExtractMethodQuery<TMethod>, ExtractMethodBody<TMethod>, ExtractResponseParamNames<TMethod> extends never ? never : Record<ExtractResponseParamNames<TMethod>, string | number>>;
|
|
252
284
|
type ResolvedWriteOptions<TSchema, TPlugins extends PluginArray, TMethod, TDefaultError> = ResolveTypes<MergePluginOptions<TPlugins>["write"], WriteResolverContext<TSchema, TMethod, TDefaultError>>;
|
|
253
285
|
type UseReadFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
254
|
-
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<
|
|
255
|
-
|
|
256
|
-
error?: unknown;
|
|
257
|
-
}>, TReadOpts>(readFn: TReadFn, readOptions: TReadOpts & BaseReadOptions & ResolveTypes<MergePluginOptions<TPlugins>["read"], ResolverContext<TSchema, ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
258
|
-
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<{
|
|
259
|
-
data?: unknown;
|
|
260
|
-
error?: unknown;
|
|
261
|
-
}>>(readFn: TReadFn): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, MergePluginResults<TPlugins>["read"]> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
286
|
+
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<SpooshResponse<unknown, unknown>>, TReadOpts>(readFn: TReadFn, readOptions: TReadOpts & BaseReadOptions & ResolveTypes<MergePluginOptions<TPlugins>["read"], ResolverContext<TSchema, ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>, TriggerOptions<TReadFn>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
287
|
+
<TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<SpooshResponse<unknown, unknown>>>(readFn: TReadFn): BaseReadResult<ExtractMethodData<TReadFn>, InferError<ExtractMethodError<TReadFn>, TDefaultError>, MergePluginResults<TPlugins>["read"], TriggerOptions<TReadFn>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
262
288
|
};
|
|
263
289
|
type UseWriteFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
264
290
|
<TMethod extends (...args: never) => Promise<SpooshResponse<unknown, unknown>>>(writeFn: (api: WriteApiClient<TSchema, TDefaultError>) => TMethod): BaseWriteResult<ExtractMethodData<TMethod>, InferError<ExtractMethodError<TMethod>, TDefaultError>, Parameters<TMethod>[0] & ResolvedWriteOptions<TSchema, TPlugins, TMethod, TDefaultError>, MergePluginResults<TPlugins>["write"]> & WriteResponseInputFields<ExtractMethodQuery<TMethod>, ExtractMethodBody<TMethod>, ExtractResponseParamNames<TMethod>>;
|
|
@@ -285,7 +311,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
|
285
311
|
*
|
|
286
312
|
* @param readFn - Function that selects the API endpoint to call (e.g., `(api) => api("posts").GET()`)
|
|
287
313
|
* @param readOptions - Optional configuration including `enabled`, `tags`, and plugin-specific options
|
|
288
|
-
* @returns Object containing `data`, `error`, `loading`, `fetching`, `
|
|
314
|
+
* @returns Object containing `data`, `error`, `loading`, `fetching`, `trigger`, and `abort`
|
|
289
315
|
*
|
|
290
316
|
* @example
|
|
291
317
|
* ```tsx
|
|
@@ -302,7 +328,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
|
|
|
302
328
|
* React hook for mutations (POST, PUT, PATCH, DELETE) with manual triggering.
|
|
303
329
|
*
|
|
304
330
|
* @param writeFn - Function that selects the API endpoint (e.g., `(api) => api("posts").POST`)
|
|
305
|
-
* @returns Object containing `trigger`, `data`, `error`, `loading`,
|
|
331
|
+
* @returns Object containing `trigger`, `data`, `error`, `loading`, and `abort`
|
|
306
332
|
*
|
|
307
333
|
* @example
|
|
308
334
|
* ```tsx
|
|
@@ -378,38 +404,33 @@ type SpooshInstanceShape<TApi, TSchema, TDefaultError, TPlugins> = {
|
|
|
378
404
|
*/
|
|
379
405
|
declare function createReactSpoosh<TSchema, TDefaultError, TPlugins extends PluginArray, TApi>(instance: SpooshInstanceShape<TApi, TSchema, TDefaultError, TPlugins>): SpooshReactHooks<TDefaultError, TSchema, TPlugins>;
|
|
380
406
|
|
|
381
|
-
declare function createUseRead<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
error: infer E;
|
|
409
|
-
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
410
|
-
error: infer E;
|
|
411
|
-
}> ? E : unknown, MergePluginResults<TPlugins>["read"]> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
412
|
-
};
|
|
407
|
+
declare function createUseRead<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): <TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<SpooshResponse<unknown, unknown>>, TReadOpts extends BaseReadOptions & ResolveTypes<((TPlugins[number] extends infer T ? T extends TPlugins[number] ? T extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
408
|
+
readOptions: infer R;
|
|
409
|
+
} ? R : object : object : never : never) extends infer T_1 ? T_1 extends (TPlugins[number] extends infer T_2 ? T_2 extends TPlugins[number] ? T_2 extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
410
|
+
readOptions: infer R;
|
|
411
|
+
} ? R : object : object : never : never) ? T_1 extends unknown ? (x: T_1) => void : never : never : never) extends (x: infer I) => void ? I : never, ResolverContext<TSchema, TReadFn extends (...args: unknown[]) => Promise<{
|
|
412
|
+
data: infer D;
|
|
413
|
+
}> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
|
|
414
|
+
error: infer E;
|
|
415
|
+
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
416
|
+
error: infer E;
|
|
417
|
+
}> ? E : unknown, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>> = BaseReadOptions & ResolveTypes<((TPlugins[number] extends infer T_3 ? T_3 extends TPlugins[number] ? T_3 extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
418
|
+
readOptions: infer R;
|
|
419
|
+
} ? R : object : object : never : never) extends infer T_4 ? T_4 extends (TPlugins[number] extends infer T_5 ? T_5 extends TPlugins[number] ? T_5 extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
420
|
+
readOptions: infer R;
|
|
421
|
+
} ? R : object : object : never : never) ? T_4 extends unknown ? (x: T_4) => void : never : never : never) extends (x: infer I) => void ? I : never, ResolverContext<TSchema, TReadFn extends (...args: unknown[]) => Promise<{
|
|
422
|
+
data: infer D;
|
|
423
|
+
}> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
|
|
424
|
+
error: infer E;
|
|
425
|
+
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
426
|
+
error: infer E;
|
|
427
|
+
}> ? E : unknown, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>>(readFn: TReadFn, readOptions?: TReadOpts) => BaseReadResult<TReadFn extends (...args: unknown[]) => Promise<{
|
|
428
|
+
data: infer D;
|
|
429
|
+
}> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
|
|
430
|
+
error: infer E;
|
|
431
|
+
}> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
|
|
432
|
+
error: infer E;
|
|
433
|
+
}> ? E : unknown, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>, TriggerOptions<TReadFn>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
|
|
413
434
|
|
|
414
435
|
declare function createUseWrite<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): <TMethod extends (...args: never[]) => Promise<SpooshResponse<unknown, unknown>>>(writeFn: (api: WriteApiClient<TSchema, TDefaultError>) => TMethod) => BaseWriteResult<ExtractMethodData<TMethod>, [ExtractMethodError<TMethod>] extends [unknown] ? TDefaultError : ExtractMethodError<TMethod>, ExtractMethodOptions<TMethod> & ResolveTypes<((TPlugins[number] extends infer T ? T extends TPlugins[number] ? T extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
|
|
415
436
|
writeOptions: infer W;
|
package/dist/index.js
CHANGED
|
@@ -69,10 +69,11 @@ function createUseRead(options) {
|
|
|
69
69
|
initialized: false,
|
|
70
70
|
prevContext: null
|
|
71
71
|
});
|
|
72
|
-
|
|
72
|
+
const baseQueryKeyChanged = controllerRef.current && controllerRef.current.baseQueryKey !== queryKey;
|
|
73
|
+
if (baseQueryKeyChanged) {
|
|
73
74
|
lifecycleRef.current.prevContext = controllerRef.current.controller.getContext();
|
|
74
75
|
}
|
|
75
|
-
if (!controllerRef.current ||
|
|
76
|
+
if (!controllerRef.current || baseQueryKeyChanged) {
|
|
76
77
|
const controller2 = (0, import_core.createOperationController)({
|
|
77
78
|
operationType: "read",
|
|
78
79
|
path: pathSegments,
|
|
@@ -89,29 +90,36 @@ function createUseRead(options) {
|
|
|
89
90
|
return method(fetchOpts);
|
|
90
91
|
}
|
|
91
92
|
});
|
|
92
|
-
controllerRef.current = { controller: controller2, queryKey };
|
|
93
|
+
controllerRef.current = { controller: controller2, queryKey, baseQueryKey: queryKey };
|
|
93
94
|
}
|
|
94
95
|
const controller = controllerRef.current.controller;
|
|
95
96
|
controller.setPluginOptions(pluginOpts);
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const subscribe = (0, import_react.useCallback)(
|
|
98
|
+
(callback) => {
|
|
99
|
+
return controller.subscribe(callback);
|
|
100
|
+
},
|
|
101
|
+
[controller]
|
|
100
102
|
);
|
|
103
|
+
const getSnapshot = (0, import_react.useCallback)(() => {
|
|
104
|
+
return controller.getState();
|
|
105
|
+
}, [controller]);
|
|
106
|
+
const state = (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
101
107
|
const [requestState, setRequestState] = (0, import_react.useState)(() => {
|
|
102
108
|
const cachedEntry = stateManager.getCache(queryKey);
|
|
103
109
|
const hasCachedData = cachedEntry?.state?.data !== void 0;
|
|
104
110
|
return { isPending: enabled && !hasCachedData, error: void 0 };
|
|
105
111
|
});
|
|
112
|
+
const [, forceUpdate] = (0, import_react.useState)(0);
|
|
106
113
|
const abortRef = (0, import_react.useRef)(controller.abort);
|
|
107
114
|
abortRef.current = controller.abort;
|
|
108
115
|
const pluginOptsKey = JSON.stringify(pluginOpts);
|
|
109
116
|
const tagsKey = JSON.stringify(tags);
|
|
110
117
|
const executeWithTracking = (0, import_react.useCallback)(
|
|
111
|
-
async (force = false) => {
|
|
118
|
+
async (force = false, overrideOptions) => {
|
|
112
119
|
setRequestState((prev) => ({ ...prev, isPending: true }));
|
|
113
120
|
try {
|
|
114
|
-
const
|
|
121
|
+
const execOptions = overrideOptions ? { ...capturedCall.options ?? {}, ...overrideOptions } : void 0;
|
|
122
|
+
const response = await controller.execute(execOptions, { force });
|
|
115
123
|
if (response.error) {
|
|
116
124
|
setRequestState({ isPending: false, error: response.error });
|
|
117
125
|
} else {
|
|
@@ -123,7 +131,7 @@ function createUseRead(options) {
|
|
|
123
131
|
throw err;
|
|
124
132
|
}
|
|
125
133
|
},
|
|
126
|
-
[controller]
|
|
134
|
+
[controller, capturedCall.options]
|
|
127
135
|
);
|
|
128
136
|
(0, import_react.useEffect)(() => {
|
|
129
137
|
return () => {
|
|
@@ -171,9 +179,84 @@ function createUseRead(options) {
|
|
|
171
179
|
const abort = (0, import_react.useCallback)(() => {
|
|
172
180
|
abortRef.current();
|
|
173
181
|
}, []);
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
const trigger = (0, import_react.useCallback)(
|
|
183
|
+
async (triggerOptions) => {
|
|
184
|
+
const { force = false, ...overrideOptions } = triggerOptions ?? {};
|
|
185
|
+
const hasOverrides = Object.keys(overrideOptions).length > 0;
|
|
186
|
+
if (!hasOverrides) {
|
|
187
|
+
return executeWithTracking(force, void 0);
|
|
188
|
+
}
|
|
189
|
+
const mergedOptions = {
|
|
190
|
+
...capturedCall.options ?? {},
|
|
191
|
+
...overrideOptions
|
|
192
|
+
};
|
|
193
|
+
const newQueryKey = stateManager.createQueryKey({
|
|
194
|
+
path: pathSegments,
|
|
195
|
+
method: capturedCall.method,
|
|
196
|
+
options: mergedOptions
|
|
197
|
+
});
|
|
198
|
+
if (newQueryKey === controllerRef.current?.queryKey) {
|
|
199
|
+
return executeWithTracking(force, overrideOptions);
|
|
200
|
+
}
|
|
201
|
+
const params = mergedOptions?.params;
|
|
202
|
+
const newResolvedPath = (0, import_core.resolvePath)(pathSegments, params);
|
|
203
|
+
const newResolvedTags = (0, import_core.resolveTags)({ tags }, newResolvedPath);
|
|
204
|
+
const newController = (0, import_core.createOperationController)({
|
|
205
|
+
operationType: "read",
|
|
206
|
+
path: pathSegments,
|
|
207
|
+
method: capturedCall.method,
|
|
208
|
+
tags: newResolvedTags,
|
|
209
|
+
requestOptions: mergedOptions,
|
|
210
|
+
stateManager,
|
|
211
|
+
eventEmitter,
|
|
212
|
+
pluginExecutor,
|
|
213
|
+
hookId,
|
|
214
|
+
fetchFn: async (fetchOpts) => {
|
|
215
|
+
const pathMethods = api(capturedCall.path);
|
|
216
|
+
const method = pathMethods[capturedCall.method];
|
|
217
|
+
return method(fetchOpts);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
newController.setPluginOptions(pluginOpts);
|
|
221
|
+
const currentBaseQueryKey = controllerRef.current?.baseQueryKey ?? queryKey;
|
|
222
|
+
controllerRef.current = {
|
|
223
|
+
controller: newController,
|
|
224
|
+
queryKey: newQueryKey,
|
|
225
|
+
baseQueryKey: currentBaseQueryKey
|
|
226
|
+
};
|
|
227
|
+
forceUpdate((n) => n + 1);
|
|
228
|
+
newController.mount();
|
|
229
|
+
setRequestState((prev) => ({ ...prev, isPending: true }));
|
|
230
|
+
try {
|
|
231
|
+
const response = await newController.execute(mergedOptions, {
|
|
232
|
+
force
|
|
233
|
+
});
|
|
234
|
+
if (response.error) {
|
|
235
|
+
setRequestState({ isPending: false, error: response.error });
|
|
236
|
+
} else {
|
|
237
|
+
setRequestState({ isPending: false, error: void 0 });
|
|
238
|
+
}
|
|
239
|
+
return response;
|
|
240
|
+
} catch (err) {
|
|
241
|
+
setRequestState({ isPending: false, error: err });
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
[
|
|
246
|
+
executeWithTracking,
|
|
247
|
+
capturedCall.options,
|
|
248
|
+
capturedCall.method,
|
|
249
|
+
capturedCall.path,
|
|
250
|
+
pathSegments,
|
|
251
|
+
tags,
|
|
252
|
+
stateManager,
|
|
253
|
+
eventEmitter,
|
|
254
|
+
pluginExecutor,
|
|
255
|
+
hookId,
|
|
256
|
+
pluginOpts,
|
|
257
|
+
api
|
|
258
|
+
]
|
|
259
|
+
);
|
|
177
260
|
const entry = stateManager.getCache(queryKey);
|
|
178
261
|
const pluginResultData = entry?.meta ? Object.fromEntries(entry.meta) : {};
|
|
179
262
|
const opts = capturedCall.options;
|
|
@@ -200,7 +283,7 @@ function createUseRead(options) {
|
|
|
200
283
|
loading,
|
|
201
284
|
fetching,
|
|
202
285
|
abort,
|
|
203
|
-
|
|
286
|
+
trigger
|
|
204
287
|
};
|
|
205
288
|
}
|
|
206
289
|
return useRead;
|
|
@@ -262,10 +345,6 @@ function createUseWrite(options) {
|
|
|
262
345
|
);
|
|
263
346
|
const [lastTriggerOptions, setLastTriggerOptions] = (0, import_react2.useState)(void 0);
|
|
264
347
|
const [requestState, setRequestState] = (0, import_react2.useState)({ isPending: false, error: void 0 });
|
|
265
|
-
const reset = (0, import_react2.useCallback)(() => {
|
|
266
|
-
stateManager.deleteCache(queryKey);
|
|
267
|
-
setRequestState({ isPending: false, error: void 0 });
|
|
268
|
-
}, [queryKey]);
|
|
269
348
|
const abort = (0, import_react2.useCallback)(() => {
|
|
270
349
|
controller.abort();
|
|
271
350
|
}, []);
|
|
@@ -317,7 +396,6 @@ function createUseWrite(options) {
|
|
|
317
396
|
data: state.data,
|
|
318
397
|
error: requestState.error ?? state.error,
|
|
319
398
|
loading,
|
|
320
|
-
reset,
|
|
321
399
|
abort
|
|
322
400
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
323
401
|
};
|
|
@@ -496,7 +574,7 @@ function createUseInfiniteRead(options) {
|
|
|
496
574
|
canFetchPrev: state.canFetchPrev,
|
|
497
575
|
fetchNext: controller.fetchNext,
|
|
498
576
|
fetchPrev: controller.fetchPrev,
|
|
499
|
-
|
|
577
|
+
trigger: controller.refetch,
|
|
500
578
|
abort: controller.abort,
|
|
501
579
|
error: state.error
|
|
502
580
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -53,10 +53,11 @@ function createUseRead(options) {
|
|
|
53
53
|
initialized: false,
|
|
54
54
|
prevContext: null
|
|
55
55
|
});
|
|
56
|
-
|
|
56
|
+
const baseQueryKeyChanged = controllerRef.current && controllerRef.current.baseQueryKey !== queryKey;
|
|
57
|
+
if (baseQueryKeyChanged) {
|
|
57
58
|
lifecycleRef.current.prevContext = controllerRef.current.controller.getContext();
|
|
58
59
|
}
|
|
59
|
-
if (!controllerRef.current ||
|
|
60
|
+
if (!controllerRef.current || baseQueryKeyChanged) {
|
|
60
61
|
const controller2 = createOperationController({
|
|
61
62
|
operationType: "read",
|
|
62
63
|
path: pathSegments,
|
|
@@ -73,29 +74,36 @@ function createUseRead(options) {
|
|
|
73
74
|
return method(fetchOpts);
|
|
74
75
|
}
|
|
75
76
|
});
|
|
76
|
-
controllerRef.current = { controller: controller2, queryKey };
|
|
77
|
+
controllerRef.current = { controller: controller2, queryKey, baseQueryKey: queryKey };
|
|
77
78
|
}
|
|
78
79
|
const controller = controllerRef.current.controller;
|
|
79
80
|
controller.setPluginOptions(pluginOpts);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
const subscribe = useCallback(
|
|
82
|
+
(callback) => {
|
|
83
|
+
return controller.subscribe(callback);
|
|
84
|
+
},
|
|
85
|
+
[controller]
|
|
84
86
|
);
|
|
87
|
+
const getSnapshot = useCallback(() => {
|
|
88
|
+
return controller.getState();
|
|
89
|
+
}, [controller]);
|
|
90
|
+
const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
85
91
|
const [requestState, setRequestState] = useState(() => {
|
|
86
92
|
const cachedEntry = stateManager.getCache(queryKey);
|
|
87
93
|
const hasCachedData = cachedEntry?.state?.data !== void 0;
|
|
88
94
|
return { isPending: enabled && !hasCachedData, error: void 0 };
|
|
89
95
|
});
|
|
96
|
+
const [, forceUpdate] = useState(0);
|
|
90
97
|
const abortRef = useRef(controller.abort);
|
|
91
98
|
abortRef.current = controller.abort;
|
|
92
99
|
const pluginOptsKey = JSON.stringify(pluginOpts);
|
|
93
100
|
const tagsKey = JSON.stringify(tags);
|
|
94
101
|
const executeWithTracking = useCallback(
|
|
95
|
-
async (force = false) => {
|
|
102
|
+
async (force = false, overrideOptions) => {
|
|
96
103
|
setRequestState((prev) => ({ ...prev, isPending: true }));
|
|
97
104
|
try {
|
|
98
|
-
const
|
|
105
|
+
const execOptions = overrideOptions ? { ...capturedCall.options ?? {}, ...overrideOptions } : void 0;
|
|
106
|
+
const response = await controller.execute(execOptions, { force });
|
|
99
107
|
if (response.error) {
|
|
100
108
|
setRequestState({ isPending: false, error: response.error });
|
|
101
109
|
} else {
|
|
@@ -107,7 +115,7 @@ function createUseRead(options) {
|
|
|
107
115
|
throw err;
|
|
108
116
|
}
|
|
109
117
|
},
|
|
110
|
-
[controller]
|
|
118
|
+
[controller, capturedCall.options]
|
|
111
119
|
);
|
|
112
120
|
useEffect(() => {
|
|
113
121
|
return () => {
|
|
@@ -155,9 +163,84 @@ function createUseRead(options) {
|
|
|
155
163
|
const abort = useCallback(() => {
|
|
156
164
|
abortRef.current();
|
|
157
165
|
}, []);
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
const trigger = useCallback(
|
|
167
|
+
async (triggerOptions) => {
|
|
168
|
+
const { force = false, ...overrideOptions } = triggerOptions ?? {};
|
|
169
|
+
const hasOverrides = Object.keys(overrideOptions).length > 0;
|
|
170
|
+
if (!hasOverrides) {
|
|
171
|
+
return executeWithTracking(force, void 0);
|
|
172
|
+
}
|
|
173
|
+
const mergedOptions = {
|
|
174
|
+
...capturedCall.options ?? {},
|
|
175
|
+
...overrideOptions
|
|
176
|
+
};
|
|
177
|
+
const newQueryKey = stateManager.createQueryKey({
|
|
178
|
+
path: pathSegments,
|
|
179
|
+
method: capturedCall.method,
|
|
180
|
+
options: mergedOptions
|
|
181
|
+
});
|
|
182
|
+
if (newQueryKey === controllerRef.current?.queryKey) {
|
|
183
|
+
return executeWithTracking(force, overrideOptions);
|
|
184
|
+
}
|
|
185
|
+
const params = mergedOptions?.params;
|
|
186
|
+
const newResolvedPath = resolvePath(pathSegments, params);
|
|
187
|
+
const newResolvedTags = resolveTags({ tags }, newResolvedPath);
|
|
188
|
+
const newController = createOperationController({
|
|
189
|
+
operationType: "read",
|
|
190
|
+
path: pathSegments,
|
|
191
|
+
method: capturedCall.method,
|
|
192
|
+
tags: newResolvedTags,
|
|
193
|
+
requestOptions: mergedOptions,
|
|
194
|
+
stateManager,
|
|
195
|
+
eventEmitter,
|
|
196
|
+
pluginExecutor,
|
|
197
|
+
hookId,
|
|
198
|
+
fetchFn: async (fetchOpts) => {
|
|
199
|
+
const pathMethods = api(capturedCall.path);
|
|
200
|
+
const method = pathMethods[capturedCall.method];
|
|
201
|
+
return method(fetchOpts);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
newController.setPluginOptions(pluginOpts);
|
|
205
|
+
const currentBaseQueryKey = controllerRef.current?.baseQueryKey ?? queryKey;
|
|
206
|
+
controllerRef.current = {
|
|
207
|
+
controller: newController,
|
|
208
|
+
queryKey: newQueryKey,
|
|
209
|
+
baseQueryKey: currentBaseQueryKey
|
|
210
|
+
};
|
|
211
|
+
forceUpdate((n) => n + 1);
|
|
212
|
+
newController.mount();
|
|
213
|
+
setRequestState((prev) => ({ ...prev, isPending: true }));
|
|
214
|
+
try {
|
|
215
|
+
const response = await newController.execute(mergedOptions, {
|
|
216
|
+
force
|
|
217
|
+
});
|
|
218
|
+
if (response.error) {
|
|
219
|
+
setRequestState({ isPending: false, error: response.error });
|
|
220
|
+
} else {
|
|
221
|
+
setRequestState({ isPending: false, error: void 0 });
|
|
222
|
+
}
|
|
223
|
+
return response;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
setRequestState({ isPending: false, error: err });
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
[
|
|
230
|
+
executeWithTracking,
|
|
231
|
+
capturedCall.options,
|
|
232
|
+
capturedCall.method,
|
|
233
|
+
capturedCall.path,
|
|
234
|
+
pathSegments,
|
|
235
|
+
tags,
|
|
236
|
+
stateManager,
|
|
237
|
+
eventEmitter,
|
|
238
|
+
pluginExecutor,
|
|
239
|
+
hookId,
|
|
240
|
+
pluginOpts,
|
|
241
|
+
api
|
|
242
|
+
]
|
|
243
|
+
);
|
|
161
244
|
const entry = stateManager.getCache(queryKey);
|
|
162
245
|
const pluginResultData = entry?.meta ? Object.fromEntries(entry.meta) : {};
|
|
163
246
|
const opts = capturedCall.options;
|
|
@@ -184,7 +267,7 @@ function createUseRead(options) {
|
|
|
184
267
|
loading,
|
|
185
268
|
fetching,
|
|
186
269
|
abort,
|
|
187
|
-
|
|
270
|
+
trigger
|
|
188
271
|
};
|
|
189
272
|
}
|
|
190
273
|
return useRead;
|
|
@@ -257,10 +340,6 @@ function createUseWrite(options) {
|
|
|
257
340
|
);
|
|
258
341
|
const [lastTriggerOptions, setLastTriggerOptions] = useState2(void 0);
|
|
259
342
|
const [requestState, setRequestState] = useState2({ isPending: false, error: void 0 });
|
|
260
|
-
const reset = useCallback2(() => {
|
|
261
|
-
stateManager.deleteCache(queryKey);
|
|
262
|
-
setRequestState({ isPending: false, error: void 0 });
|
|
263
|
-
}, [queryKey]);
|
|
264
343
|
const abort = useCallback2(() => {
|
|
265
344
|
controller.abort();
|
|
266
345
|
}, []);
|
|
@@ -312,7 +391,6 @@ function createUseWrite(options) {
|
|
|
312
391
|
data: state.data,
|
|
313
392
|
error: requestState.error ?? state.error,
|
|
314
393
|
loading,
|
|
315
|
-
reset,
|
|
316
394
|
abort
|
|
317
395
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
318
396
|
};
|
|
@@ -502,7 +580,7 @@ function createUseInfiniteRead(options) {
|
|
|
502
580
|
canFetchPrev: state.canFetchPrev,
|
|
503
581
|
fetchNext: controller.fetchNext,
|
|
504
582
|
fetchPrev: controller.fetchPrev,
|
|
505
|
-
|
|
583
|
+
trigger: controller.refetch,
|
|
506
584
|
abort: controller.abort,
|
|
507
585
|
error: state.error
|
|
508
586
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spoosh/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React hooks for Spoosh API client",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"react": "^18 || ^19"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@spoosh/core": "0.
|
|
41
|
+
"@spoosh/core": "0.9.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"dev": "tsup --watch",
|