@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 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, refetch } = useRead(
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
- | `refetch` | `() => Promise` | Manually trigger refetch |
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
- /** Manually trigger a refetch */
45
- refetch: () => Promise<SpooshResponse<TData, TError>>;
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
- /** Refetch all pages from the beginning */
241
- refetch: () => Promise<void>;
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
- data?: unknown;
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`, `refetch`, and `abort`
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`, `reset`, and `abort`
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
- <TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<{
383
- data?: unknown;
384
- error?: unknown;
385
- }>, TReadOpts>(readFn: TReadFn, readOptions: TReadOpts & BaseReadOptions & ResolveTypes<((TPlugins[number] extends infer T ? T extends TPlugins[number] ? T extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
386
- readOptions: infer R;
387
- } ? 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 {
388
- readOptions: infer R;
389
- } ? 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<{
390
- data: infer D;
391
- }> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
392
- error: infer E;
393
- }> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
394
- error: infer E;
395
- }> ? E : unknown, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>): BaseReadResult<TReadFn extends (...args: unknown[]) => Promise<{
396
- data: infer D;
397
- }> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
398
- error: infer E;
399
- }> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
400
- error: infer E;
401
- }> ? E : unknown, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
402
- <TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<{
403
- data?: unknown;
404
- error?: unknown;
405
- }>>(readFn: TReadFn): BaseReadResult<TReadFn extends (...args: unknown[]) => Promise<{
406
- data: infer D;
407
- }> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
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
- /** Manually trigger a refetch */
45
- refetch: () => Promise<SpooshResponse<TData, TError>>;
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
- /** Refetch all pages from the beginning */
241
- refetch: () => Promise<void>;
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
- data?: unknown;
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`, `refetch`, and `abort`
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`, `reset`, and `abort`
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
- <TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<{
383
- data?: unknown;
384
- error?: unknown;
385
- }>, TReadOpts>(readFn: TReadFn, readOptions: TReadOpts & BaseReadOptions & ResolveTypes<((TPlugins[number] extends infer T ? T extends TPlugins[number] ? T extends SpooshPlugin<infer Types extends PluginTypeConfig> ? Types extends {
386
- readOptions: infer R;
387
- } ? 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 {
388
- readOptions: infer R;
389
- } ? 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<{
390
- data: infer D;
391
- }> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
392
- error: infer E;
393
- }> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
394
- error: infer E;
395
- }> ? E : unknown, ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn> extends never ? never : Record<ExtractResponseParamNames<TReadFn>, string | number>>>): BaseReadResult<TReadFn extends (...args: unknown[]) => Promise<{
396
- data: infer D;
397
- }> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
398
- error: infer E;
399
- }> ? E : unknown] extends [unknown] ? TDefaultError : TReadFn extends (...args: unknown[]) => Promise<{
400
- error: infer E;
401
- }> ? E : unknown, ResolveResultTypes<MergePluginResults<TPlugins>["read"], TReadOpts>> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
402
- <TReadFn extends (api: ReadApiClient<TSchema, TDefaultError>) => Promise<{
403
- data?: unknown;
404
- error?: unknown;
405
- }>>(readFn: TReadFn): BaseReadResult<TReadFn extends (...args: unknown[]) => Promise<{
406
- data: infer D;
407
- }> ? D : unknown, [TReadFn extends (...args: unknown[]) => Promise<{
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
- if (controllerRef.current && controllerRef.current.queryKey !== queryKey) {
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 || controllerRef.current.queryKey !== queryKey) {
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 state = (0, import_react.useSyncExternalStore)(
97
- controller.subscribe,
98
- controller.getState,
99
- controller.getState
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 response = await controller.execute(void 0, { force });
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 refetch = (0, import_react.useCallback)(() => {
175
- return executeWithTracking(true);
176
- }, [executeWithTracking]);
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
- refetch
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
- refetch: controller.refetch,
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
- if (controllerRef.current && controllerRef.current.queryKey !== queryKey) {
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 || controllerRef.current.queryKey !== queryKey) {
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 state = useSyncExternalStore(
81
- controller.subscribe,
82
- controller.getState,
83
- controller.getState
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 response = await controller.execute(void 0, { force });
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 refetch = useCallback(() => {
159
- return executeWithTracking(true);
160
- }, [executeWithTracking]);
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
- refetch
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
- refetch: controller.refetch,
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.5.1",
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.7.0"
41
+ "@spoosh/core": "0.9.0"
42
42
  },
43
43
  "scripts": {
44
44
  "dev": "tsup --watch",