@spoosh/react 0.5.1 → 0.6.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
@@ -1,6 +1,6 @@
1
1
  # @spoosh/react
2
2
 
3
- React hooks for Spoosh - `useRead`, `useWrite`, and `useInfiniteRead`.
3
+ React hooks for Spoosh - `useRead`, `useLazyRead`, `useWrite`, and `useInfiniteRead`.
4
4
 
5
5
  **[Documentation](https://spoosh.dev/docs/integrations/react)** · **Requirements:** TypeScript >= 5.0, React >= 18.0
6
6
 
@@ -23,7 +23,8 @@ const spoosh = new Spoosh<ApiSchema, Error>("/api").use([
23
23
  cachePlugin({ staleTime: 5000 }),
24
24
  ]);
25
25
 
26
- export const { useRead, useWrite, useInfiniteRead } = createReactSpoosh(spoosh);
26
+ export const { useRead, useLazyRead, useWrite, useInfiniteRead } =
27
+ createReactSpoosh(spoosh);
27
28
  ```
28
29
 
29
30
  ### useRead
@@ -32,7 +33,7 @@ Fetch data with automatic caching and refetching.
32
33
 
33
34
  ```typescript
34
35
  function UserList() {
35
- const { data, loading, error, refetch } = useRead(
36
+ const { data, loading, error, trigger } = useRead(
36
37
  (api) => api("users").GET()
37
38
  );
38
39
 
@@ -62,6 +63,27 @@ const { data: user } = useRead(
62
63
  );
63
64
  ```
64
65
 
66
+ ### useLazyRead
67
+
68
+ Lazy data fetching for print/download/export scenarios. Does not auto-fetch on mount.
69
+
70
+ ```typescript
71
+ function PrintOrder() {
72
+ const { trigger, loading } = useLazyRead((api) => api("orders/:id").GET);
73
+
74
+ const handlePrint = async (orderId: string) => {
75
+ const { data } = await trigger({ params: { id: orderId } });
76
+ if (data) printReceipt(data);
77
+ };
78
+
79
+ return (
80
+ <button onClick={() => handlePrint("123")} disabled={loading}>
81
+ {loading ? "Loading..." : "Print"}
82
+ </button>
83
+ );
84
+ }
85
+ ```
86
+
65
87
  ### useWrite
66
88
 
67
89
  Trigger mutations with loading and error states.
@@ -177,7 +199,7 @@ function PostList() {
177
199
  | `error` | `TError \| undefined` | Error if request failed |
178
200
  | `loading` | `boolean` | True during initial load |
179
201
  | `fetching` | `boolean` | True during any fetch |
180
- | `refetch` | `() => Promise` | Manually trigger refetch |
202
+ | `trigger` | `() => Promise` | Manually trigger fetch |
181
203
  | `abort` | `() => void` | Abort current request |
182
204
 
183
205
  ### useWrite(writeFn)
@@ -190,7 +212,6 @@ function PostList() {
190
212
  | `data` | `TData \| undefined` | Response data |
191
213
  | `error` | `TError \| undefined` | Error if request failed |
192
214
  | `loading` | `boolean` | True while mutation is in progress |
193
- | `reset` | `() => void` | Reset state |
194
215
  | `abort` | `() => void` | Abort current request |
195
216
 
196
217
  ### useInfiniteRead(readFn, options)
package/dist/index.d.mts CHANGED
@@ -41,8 +41,8 @@ type BaseReadResult<TData, TError, TMeta = Record<string, unknown>> = {
41
41
  meta: TMeta;
42
42
  /** Abort the current fetch operation */
43
43
  abort: () => void;
44
- /** Manually trigger a refetch */
45
- refetch: () => Promise<SpooshResponse<TData, TError>>;
44
+ /** Manually trigger a fetch */
45
+ trigger: () => Promise<SpooshResponse<TData, TError>>;
46
46
  };
47
47
  /**
48
48
  * Result returned by `useWrite` hook.
@@ -63,11 +63,28 @@ type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> =
63
63
  error: TError | undefined;
64
64
  /** Plugin-provided metadata */
65
65
  meta: TMeta;
66
- /** Reset the state to initial values */
67
- reset: () => void;
68
66
  /** Abort the current mutation */
69
67
  abort: () => void;
70
68
  };
69
+ /**
70
+ * Result returned by `useLazyRead` hook.
71
+ *
72
+ * @template TData - The response data type
73
+ * @template TError - The error type
74
+ * @template TOptions - The trigger options type
75
+ */
76
+ type BaseLazyReadResult<TData, TError, TOptions> = {
77
+ /** Execute the fetch with optional options */
78
+ trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
79
+ /** True while the fetch is in progress */
80
+ loading: boolean;
81
+ /** Response data from the API */
82
+ data: TData | undefined;
83
+ /** Error from the last failed request */
84
+ error: TError | undefined;
85
+ /** Abort the current fetch */
86
+ abort: () => void;
87
+ };
71
88
  type OptionalQueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
72
89
  query?: Exclude<TQuery, undefined>;
73
90
  } : {
@@ -106,6 +123,7 @@ type ExtractMethodError<T> = T extends (...args: never[]) => infer R ? ErrorResp
106
123
  error: infer E;
107
124
  } ? E : unknown : unknown;
108
125
  type ExtractMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] : never;
126
+ type ExtractCoreMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] extends object ? Pick<A[0], Extract<keyof A[0], "query" | "params" | "body">> : object : object;
109
127
  type ExtractMethodQuery<T> = ExtractMethodOptions<T> extends {
110
128
  query: infer Q;
111
129
  } ? Q : never;
@@ -237,8 +255,8 @@ type BaseInfiniteReadResult<TData, TError, TItem, TPluginResult = Record<string,
237
255
  fetchNext: () => Promise<void>;
238
256
  /** Fetch the previous page */
239
257
  fetchPrev: () => Promise<void>;
240
- /** Refetch all pages from the beginning */
241
- refetch: () => Promise<void>;
258
+ /** Trigger refetch of all pages from the beginning */
259
+ trigger: () => Promise<void>;
242
260
  /** Abort the current fetch operation */
243
261
  abort: () => void;
244
262
  /** Error from the last failed request */
@@ -263,6 +281,9 @@ type UseReadFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
263
281
  type UseWriteFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
264
282
  <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>>;
265
283
  };
284
+ type UseLazyReadFn<TDefaultError, TSchema> = {
285
+ <TMethod extends (...args: never) => Promise<SpooshResponse<unknown, unknown>>>(readFn: (api: ReadApiClient<TSchema, TDefaultError>) => TMethod): BaseLazyReadResult<ExtractMethodData<TMethod>, InferError<ExtractMethodError<TMethod>, TDefaultError>, ExtractCoreMethodOptions<TMethod>> & WriteResponseInputFields<ExtractResponseQuery<TMethod>, ExtractResponseBody<TMethod>, ExtractResponseParamNames<TMethod>>;
286
+ };
266
287
  type InfiniteReadResolverContext<TSchema, TData, TError, TRequest> = ResolverContext<TSchema, TData, TError, TRequest extends {
267
288
  query: infer Q;
268
289
  } ? Q : never, TRequest extends {
@@ -285,7 +306,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
285
306
  *
286
307
  * @param readFn - Function that selects the API endpoint to call (e.g., `(api) => api("posts").GET()`)
287
308
  * @param readOptions - Optional configuration including `enabled`, `tags`, and plugin-specific options
288
- * @returns Object containing `data`, `error`, `loading`, `fetching`, `refetch`, and `abort`
309
+ * @returns Object containing `data`, `error`, `loading`, `fetching`, `trigger`, and `abort`
289
310
  *
290
311
  * @example
291
312
  * ```tsx
@@ -302,7 +323,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
302
323
  * React hook for mutations (POST, PUT, PATCH, DELETE) with manual triggering.
303
324
  *
304
325
  * @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`
326
+ * @returns Object containing `trigger`, `data`, `error`, `loading`, and `abort`
306
327
  *
307
328
  * @example
308
329
  * ```tsx
@@ -315,6 +336,23 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
315
336
  * ```
316
337
  */
317
338
  useWrite: UseWriteFn<TDefaultError, TSchema, TPlugins>;
339
+ /**
340
+ * React hook for lazy GET requests with manual triggering (does not auto-fetch on mount).
341
+ *
342
+ * @param readFn - Function that selects the API endpoint (e.g., `(api) => api("posts").GET`)
343
+ * @returns Object containing `trigger`, `data`, `error`, `loading`, and `abort`
344
+ *
345
+ * @example
346
+ * ```tsx
347
+ * const { trigger, loading, data } = useLazyRead((api) => api("posts/:id").GET);
348
+ *
349
+ * const handleClick = async (id) => {
350
+ * const { data, error } = await trigger({ params: { id } });
351
+ * if (data) console.log('Fetched:', data);
352
+ * };
353
+ * ```
354
+ */
355
+ useLazyRead: UseLazyReadFn<TDefaultError, TSchema>;
318
356
  /**
319
357
  * React hook for infinite/paginated data fetching with automatic pagination control.
320
358
  *
@@ -411,6 +449,8 @@ declare function createUseRead<TSchema, TDefaultError, TPlugins extends readonly
411
449
  }> ? E : unknown, MergePluginResults<TPlugins>["read"]> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
412
450
  };
413
451
 
452
+ declare function createUseLazyRead<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): <TMethod extends (...args: never[]) => Promise<SpooshResponse<unknown, unknown>>>(readFn: (api: ReadApiClient<TSchema, TDefaultError>) => TMethod) => BaseLazyReadResult<ExtractMethodData<TMethod>, [ExtractMethodError<TMethod>] extends [unknown] ? TDefaultError : ExtractMethodError<TMethod>, ExtractCoreMethodOptions<TMethod>> & WriteResponseInputFields<ExtractResponseQuery<TMethod>, ExtractResponseBody<TMethod>, ExtractResponseParamNames<TMethod>>;
453
+
414
454
  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
455
  writeOptions: infer W;
416
456
  } ? W : 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 {
@@ -427,4 +467,4 @@ declare function createUseInfiniteRead<TSchema, TDefaultError, TPlugins extends
427
467
  readResult: infer R;
428
468
  } ? R : object : object : never : never) ? T_1 extends unknown ? (x: T_1) => void : never : never : never) extends (x: infer I) => void ? I : never>;
429
469
 
430
- export { type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type PluginHooksConfig, type SpooshReactHooks, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, createReactSpoosh, createUseInfiniteRead, createUseRead, createUseWrite };
470
+ export { type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseLazyReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type PluginHooksConfig, type SpooshReactHooks, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, createReactSpoosh, createUseInfiniteRead, createUseLazyRead, createUseRead, createUseWrite };
package/dist/index.d.ts CHANGED
@@ -41,8 +41,8 @@ type BaseReadResult<TData, TError, TMeta = Record<string, unknown>> = {
41
41
  meta: TMeta;
42
42
  /** Abort the current fetch operation */
43
43
  abort: () => void;
44
- /** Manually trigger a refetch */
45
- refetch: () => Promise<SpooshResponse<TData, TError>>;
44
+ /** Manually trigger a fetch */
45
+ trigger: () => Promise<SpooshResponse<TData, TError>>;
46
46
  };
47
47
  /**
48
48
  * Result returned by `useWrite` hook.
@@ -63,11 +63,28 @@ type BaseWriteResult<TData, TError, TOptions, TMeta = Record<string, unknown>> =
63
63
  error: TError | undefined;
64
64
  /** Plugin-provided metadata */
65
65
  meta: TMeta;
66
- /** Reset the state to initial values */
67
- reset: () => void;
68
66
  /** Abort the current mutation */
69
67
  abort: () => void;
70
68
  };
69
+ /**
70
+ * Result returned by `useLazyRead` hook.
71
+ *
72
+ * @template TData - The response data type
73
+ * @template TError - The error type
74
+ * @template TOptions - The trigger options type
75
+ */
76
+ type BaseLazyReadResult<TData, TError, TOptions> = {
77
+ /** Execute the fetch with optional options */
78
+ trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
79
+ /** True while the fetch is in progress */
80
+ loading: boolean;
81
+ /** Response data from the API */
82
+ data: TData | undefined;
83
+ /** Error from the last failed request */
84
+ error: TError | undefined;
85
+ /** Abort the current fetch */
86
+ abort: () => void;
87
+ };
71
88
  type OptionalQueryField<TQuery> = [TQuery] extends [never] ? object : undefined extends TQuery ? {
72
89
  query?: Exclude<TQuery, undefined>;
73
90
  } : {
@@ -106,6 +123,7 @@ type ExtractMethodError<T> = T extends (...args: never[]) => infer R ? ErrorResp
106
123
  error: infer E;
107
124
  } ? E : unknown : unknown;
108
125
  type ExtractMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] : never;
126
+ type ExtractCoreMethodOptions<T> = T extends (...args: infer A) => unknown ? A[0] extends object ? Pick<A[0], Extract<keyof A[0], "query" | "params" | "body">> : object : object;
109
127
  type ExtractMethodQuery<T> = ExtractMethodOptions<T> extends {
110
128
  query: infer Q;
111
129
  } ? Q : never;
@@ -237,8 +255,8 @@ type BaseInfiniteReadResult<TData, TError, TItem, TPluginResult = Record<string,
237
255
  fetchNext: () => Promise<void>;
238
256
  /** Fetch the previous page */
239
257
  fetchPrev: () => Promise<void>;
240
- /** Refetch all pages from the beginning */
241
- refetch: () => Promise<void>;
258
+ /** Trigger refetch of all pages from the beginning */
259
+ trigger: () => Promise<void>;
242
260
  /** Abort the current fetch operation */
243
261
  abort: () => void;
244
262
  /** Error from the last failed request */
@@ -263,6 +281,9 @@ type UseReadFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
263
281
  type UseWriteFn<TDefaultError, TSchema, TPlugins extends PluginArray> = {
264
282
  <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>>;
265
283
  };
284
+ type UseLazyReadFn<TDefaultError, TSchema> = {
285
+ <TMethod extends (...args: never) => Promise<SpooshResponse<unknown, unknown>>>(readFn: (api: ReadApiClient<TSchema, TDefaultError>) => TMethod): BaseLazyReadResult<ExtractMethodData<TMethod>, InferError<ExtractMethodError<TMethod>, TDefaultError>, ExtractCoreMethodOptions<TMethod>> & WriteResponseInputFields<ExtractResponseQuery<TMethod>, ExtractResponseBody<TMethod>, ExtractResponseParamNames<TMethod>>;
286
+ };
266
287
  type InfiniteReadResolverContext<TSchema, TData, TError, TRequest> = ResolverContext<TSchema, TData, TError, TRequest extends {
267
288
  query: infer Q;
268
289
  } ? Q : never, TRequest extends {
@@ -285,7 +306,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
285
306
  *
286
307
  * @param readFn - Function that selects the API endpoint to call (e.g., `(api) => api("posts").GET()`)
287
308
  * @param readOptions - Optional configuration including `enabled`, `tags`, and plugin-specific options
288
- * @returns Object containing `data`, `error`, `loading`, `fetching`, `refetch`, and `abort`
309
+ * @returns Object containing `data`, `error`, `loading`, `fetching`, `trigger`, and `abort`
289
310
  *
290
311
  * @example
291
312
  * ```tsx
@@ -302,7 +323,7 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
302
323
  * React hook for mutations (POST, PUT, PATCH, DELETE) with manual triggering.
303
324
  *
304
325
  * @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`
326
+ * @returns Object containing `trigger`, `data`, `error`, `loading`, and `abort`
306
327
  *
307
328
  * @example
308
329
  * ```tsx
@@ -315,6 +336,23 @@ type SpooshReactHooks<TDefaultError, TSchema, TPlugins extends PluginArray> = {
315
336
  * ```
316
337
  */
317
338
  useWrite: UseWriteFn<TDefaultError, TSchema, TPlugins>;
339
+ /**
340
+ * React hook for lazy GET requests with manual triggering (does not auto-fetch on mount).
341
+ *
342
+ * @param readFn - Function that selects the API endpoint (e.g., `(api) => api("posts").GET`)
343
+ * @returns Object containing `trigger`, `data`, `error`, `loading`, and `abort`
344
+ *
345
+ * @example
346
+ * ```tsx
347
+ * const { trigger, loading, data } = useLazyRead((api) => api("posts/:id").GET);
348
+ *
349
+ * const handleClick = async (id) => {
350
+ * const { data, error } = await trigger({ params: { id } });
351
+ * if (data) console.log('Fetched:', data);
352
+ * };
353
+ * ```
354
+ */
355
+ useLazyRead: UseLazyReadFn<TDefaultError, TSchema>;
318
356
  /**
319
357
  * React hook for infinite/paginated data fetching with automatic pagination control.
320
358
  *
@@ -411,6 +449,8 @@ declare function createUseRead<TSchema, TDefaultError, TPlugins extends readonly
411
449
  }> ? E : unknown, MergePluginResults<TPlugins>["read"]> & ResponseInputFields<ExtractResponseQuery<TReadFn>, ExtractResponseBody<TReadFn>, ExtractResponseParamNames<TReadFn>>;
412
450
  };
413
451
 
452
+ declare function createUseLazyRead<TSchema, TDefaultError, TPlugins extends readonly SpooshPlugin<PluginTypeConfig>[]>(options: Omit<SpooshInstanceShape<unknown, TSchema, TDefaultError, TPlugins>, "_types">): <TMethod extends (...args: never[]) => Promise<SpooshResponse<unknown, unknown>>>(readFn: (api: ReadApiClient<TSchema, TDefaultError>) => TMethod) => BaseLazyReadResult<ExtractMethodData<TMethod>, [ExtractMethodError<TMethod>] extends [unknown] ? TDefaultError : ExtractMethodError<TMethod>, ExtractCoreMethodOptions<TMethod>> & WriteResponseInputFields<ExtractResponseQuery<TMethod>, ExtractResponseBody<TMethod>, ExtractResponseParamNames<TMethod>>;
453
+
414
454
  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
455
  writeOptions: infer W;
416
456
  } ? W : 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 {
@@ -427,4 +467,4 @@ declare function createUseInfiniteRead<TSchema, TDefaultError, TPlugins extends
427
467
  readResult: infer R;
428
468
  } ? R : object : object : never : never) ? T_1 extends unknown ? (x: T_1) => void : never : never : never) extends (x: infer I) => void ? I : never>;
429
469
 
430
- export { type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type PluginHooksConfig, type SpooshReactHooks, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, createReactSpoosh, createUseInfiniteRead, createUseRead, createUseWrite };
470
+ export { type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseLazyReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type PluginHooksConfig, type SpooshReactHooks, type UseInfiniteReadResult, type UseReadResult, type UseWriteResult, createReactSpoosh, createUseInfiniteRead, createUseLazyRead, createUseRead, createUseWrite };
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ var src_exports = {};
24
24
  __export(src_exports, {
25
25
  createReactSpoosh: () => createReactSpoosh,
26
26
  createUseInfiniteRead: () => createUseInfiniteRead,
27
+ createUseLazyRead: () => createUseLazyRead,
27
28
  createUseRead: () => createUseRead,
28
29
  createUseWrite: () => createUseWrite
29
30
  });
@@ -171,7 +172,7 @@ function createUseRead(options) {
171
172
  const abort = (0, import_react.useCallback)(() => {
172
173
  abortRef.current();
173
174
  }, []);
174
- const refetch = (0, import_react.useCallback)(() => {
175
+ const trigger = (0, import_react.useCallback)(() => {
175
176
  return executeWithTracking(true);
176
177
  }, [executeWithTracking]);
177
178
  const entry = stateManager.getCache(queryKey);
@@ -200,7 +201,7 @@ function createUseRead(options) {
200
201
  loading,
201
202
  fetching,
202
203
  abort,
203
- refetch
204
+ trigger
204
205
  };
205
206
  }
206
207
  return useRead;
@@ -262,10 +263,6 @@ function createUseWrite(options) {
262
263
  );
263
264
  const [lastTriggerOptions, setLastTriggerOptions] = (0, import_react2.useState)(void 0);
264
265
  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
266
  const abort = (0, import_react2.useCallback)(() => {
270
267
  controller.abort();
271
268
  }, []);
@@ -317,7 +314,6 @@ function createUseWrite(options) {
317
314
  data: state.data,
318
315
  error: requestState.error ?? state.error,
319
316
  loading,
320
- reset,
321
317
  abort
322
318
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
323
319
  };
@@ -325,9 +321,141 @@ function createUseWrite(options) {
325
321
  return useWrite;
326
322
  }
327
323
 
328
- // src/useInfiniteRead/index.ts
324
+ // src/useLazyRead/index.ts
329
325
  var import_react3 = require("react");
330
326
  var import_core3 = require("@spoosh/core");
327
+ function createUseLazyRead(options) {
328
+ const { api, stateManager, pluginExecutor, eventEmitter } = options;
329
+ function useLazyRead(readFn) {
330
+ const hookId = (0, import_react3.useId)();
331
+ const selectorResultRef = (0, import_react3.useRef)({
332
+ call: null,
333
+ selector: null
334
+ });
335
+ const selectorProxy = (0, import_core3.createSelectorProxy)((result) => {
336
+ selectorResultRef.current = result;
337
+ });
338
+ readFn(selectorProxy);
339
+ const selectedEndpoint = selectorResultRef.current.selector;
340
+ if (!selectedEndpoint) {
341
+ throw new Error(
342
+ 'useLazyRead requires selecting an HTTP method (GET). Example: useLazyRead((api) => api("posts").GET)'
343
+ );
344
+ }
345
+ if (selectedEndpoint.method !== "GET") {
346
+ throw new Error(
347
+ "useLazyRead only supports GET method. Use useWrite for POST, PUT, PATCH, DELETE methods."
348
+ );
349
+ }
350
+ const pathSegments = selectedEndpoint.path.split("/").filter(Boolean);
351
+ const controllerRef = (0, import_react3.useRef)(null);
352
+ const emptyStateRef = (0, import_react3.useRef)({ data: void 0, error: void 0 });
353
+ const [currentQueryKey, setCurrentQueryKey] = (0, import_react3.useState)(null);
354
+ const [, forceUpdate] = (0, import_react3.useState)(0);
355
+ const getOrCreateController = (0, import_react3.useCallback)(
356
+ (triggerOptions) => {
357
+ const queryKey = stateManager.createQueryKey({
358
+ path: pathSegments,
359
+ method: selectedEndpoint.method,
360
+ options: triggerOptions
361
+ });
362
+ if (controllerRef.current?.queryKey === queryKey) {
363
+ return { controller: controllerRef.current.controller, queryKey };
364
+ }
365
+ const controller2 = (0, import_core3.createOperationController)({
366
+ operationType: "read",
367
+ path: pathSegments,
368
+ method: "GET",
369
+ tags: [],
370
+ stateManager,
371
+ eventEmitter,
372
+ pluginExecutor,
373
+ hookId,
374
+ requestOptions: triggerOptions,
375
+ fetchFn: async (fetchOpts) => {
376
+ const pathMethods = api(selectedEndpoint.path);
377
+ const method = pathMethods[selectedEndpoint.method];
378
+ return method(fetchOpts);
379
+ }
380
+ });
381
+ controllerRef.current = { controller: controller2, queryKey };
382
+ setCurrentQueryKey(queryKey);
383
+ forceUpdate((n) => n + 1);
384
+ return { controller: controller2, queryKey };
385
+ },
386
+ [pathSegments, selectedEndpoint.method, selectedEndpoint.path, hookId]
387
+ );
388
+ const controller = controllerRef.current?.controller;
389
+ const subscribe = (0, import_react3.useCallback)(
390
+ (callback) => {
391
+ if (!controller) return () => {
392
+ };
393
+ return controller.subscribe(callback);
394
+ },
395
+ [controller]
396
+ );
397
+ const getSnapshot = (0, import_react3.useCallback)(() => {
398
+ if (!controller) return emptyStateRef.current;
399
+ return controller.getState();
400
+ }, [controller]);
401
+ const state = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
402
+ const [lastTriggerOptions, setLastTriggerOptions] = (0, import_react3.useState)(void 0);
403
+ const [requestState, setRequestState] = (0, import_react3.useState)({ isPending: false, error: void 0 });
404
+ const abort = (0, import_react3.useCallback)(() => {
405
+ controllerRef.current?.controller.abort();
406
+ }, []);
407
+ const trigger = (0, import_react3.useCallback)(
408
+ async (triggerOptions) => {
409
+ setLastTriggerOptions(triggerOptions);
410
+ setRequestState((prev) => ({ ...prev, isPending: true }));
411
+ const params = triggerOptions?.params;
412
+ (0, import_core3.resolvePath)(pathSegments, params);
413
+ const { controller: ctrl } = getOrCreateController(triggerOptions);
414
+ ctrl.setPluginOptions(triggerOptions);
415
+ try {
416
+ const response = await ctrl.execute(triggerOptions);
417
+ if (response.error) {
418
+ setRequestState({ isPending: false, error: response.error });
419
+ } else {
420
+ setRequestState({ isPending: false, error: void 0 });
421
+ }
422
+ return response;
423
+ } catch (err) {
424
+ setRequestState({ isPending: false, error: err });
425
+ throw err;
426
+ }
427
+ },
428
+ [pathSegments, getOrCreateController]
429
+ );
430
+ const opts = lastTriggerOptions;
431
+ const inputInner = {};
432
+ if (opts?.query !== void 0) {
433
+ inputInner.query = opts.query;
434
+ }
435
+ if (opts?.body !== void 0) {
436
+ inputInner.body = opts.body;
437
+ }
438
+ if (opts?.params !== void 0) {
439
+ inputInner.params = opts.params;
440
+ }
441
+ const inputField = Object.keys(inputInner).length > 0 ? { input: inputInner } : {};
442
+ const loading = requestState.isPending;
443
+ return {
444
+ trigger,
445
+ ...inputField,
446
+ data: state.data,
447
+ error: requestState.error ?? state.error,
448
+ loading,
449
+ abort
450
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
451
+ };
452
+ }
453
+ return useLazyRead;
454
+ }
455
+
456
+ // src/useInfiniteRead/index.ts
457
+ var import_react4 = require("react");
458
+ var import_core4 = require("@spoosh/core");
331
459
  function createUseInfiniteRead(options) {
332
460
  const { api, stateManager, eventEmitter, pluginExecutor } = options;
333
461
  return function useInfiniteRead(readFn, readOptions) {
@@ -341,12 +469,12 @@ function createUseInfiniteRead(options) {
341
469
  prevPageRequest,
342
470
  ...pluginOpts
343
471
  } = readOptions;
344
- const hookId = (0, import_react3.useId)();
345
- const selectorResultRef = (0, import_react3.useRef)({
472
+ const hookId = (0, import_react4.useId)();
473
+ const selectorResultRef = (0, import_react4.useRef)({
346
474
  call: null,
347
475
  selector: null
348
476
  });
349
- const selectorProxy = (0, import_core3.createSelectorProxy)((result2) => {
477
+ const selectorProxy = (0, import_core4.createSelectorProxy)((result2) => {
350
478
  selectorResultRef.current = result2;
351
479
  });
352
480
  readFn(selectorProxy);
@@ -369,13 +497,13 @@ function createUseInfiniteRead(options) {
369
497
  params: void 0,
370
498
  body: void 0
371
499
  };
372
- const resolvedPath = (0, import_core3.resolvePath)(pathSegments, requestOptions?.params);
373
- const resolvedTags = (0, import_core3.resolveTags)({ tags }, resolvedPath);
374
- const canFetchNextRef = (0, import_react3.useRef)(canFetchNext);
375
- const canFetchPrevRef = (0, import_react3.useRef)(canFetchPrev);
376
- const nextPageRequestRef = (0, import_react3.useRef)(nextPageRequest);
377
- const prevPageRequestRef = (0, import_react3.useRef)(prevPageRequest);
378
- const mergerRef = (0, import_react3.useRef)(merger);
500
+ const resolvedPath = (0, import_core4.resolvePath)(pathSegments, requestOptions?.params);
501
+ const resolvedTags = (0, import_core4.resolveTags)({ tags }, resolvedPath);
502
+ const canFetchNextRef = (0, import_react4.useRef)(canFetchNext);
503
+ const canFetchPrevRef = (0, import_react4.useRef)(canFetchPrev);
504
+ const nextPageRequestRef = (0, import_react4.useRef)(nextPageRequest);
505
+ const prevPageRequestRef = (0, import_react4.useRef)(prevPageRequest);
506
+ const mergerRef = (0, import_react4.useRef)(merger);
379
507
  canFetchNextRef.current = canFetchNext;
380
508
  canFetchPrevRef.current = canFetchPrev;
381
509
  nextPageRequestRef.current = nextPageRequest;
@@ -386,10 +514,10 @@ function createUseInfiniteRead(options) {
386
514
  method: capturedCall.method,
387
515
  options: baseOptionsForKey
388
516
  });
389
- const controllerRef = (0, import_react3.useRef)(null);
517
+ const controllerRef = (0, import_react4.useRef)(null);
390
518
  if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
391
519
  controllerRef.current = {
392
- controller: (0, import_core3.createInfiniteReadController)({
520
+ controller: (0, import_core4.createInfiniteReadController)({
393
521
  path: pathSegments,
394
522
  method: capturedCall.method,
395
523
  tags: resolvedTags,
@@ -422,12 +550,12 @@ function createUseInfiniteRead(options) {
422
550
  }
423
551
  const controller = controllerRef.current.controller;
424
552
  controller.setPluginOptions(pluginOpts);
425
- const state = (0, import_react3.useSyncExternalStore)(
553
+ const state = (0, import_react4.useSyncExternalStore)(
426
554
  controller.subscribe,
427
555
  controller.getState,
428
556
  controller.getState
429
557
  );
430
- const [isPending, setIsPending] = (0, import_react3.useState)(() => {
558
+ const [isPending, setIsPending] = (0, import_react4.useState)(() => {
431
559
  return enabled && state.data === void 0;
432
560
  });
433
561
  const fetchingDirection = controller.getFetchingDirection();
@@ -436,18 +564,18 @@ function createUseInfiniteRead(options) {
436
564
  const fetchingPrev = fetchingDirection === "prev";
437
565
  const hasData = state.data !== void 0;
438
566
  const loading = (isPending || fetching) && !hasData;
439
- const lifecycleRef = (0, import_react3.useRef)({
567
+ const lifecycleRef = (0, import_react4.useRef)({
440
568
  initialized: false,
441
569
  prevContext: null
442
570
  });
443
571
  const tagsKey = JSON.stringify(tags);
444
- (0, import_react3.useEffect)(() => {
572
+ (0, import_react4.useEffect)(() => {
445
573
  return () => {
446
574
  controllerRef.current?.controller.unmount();
447
575
  lifecycleRef.current.initialized = false;
448
576
  };
449
577
  }, []);
450
- (0, import_react3.useEffect)(() => {
578
+ (0, import_react4.useEffect)(() => {
451
579
  controller.mount();
452
580
  lifecycleRef.current.initialized = true;
453
581
  const unsubInvalidate = eventEmitter.on(
@@ -466,7 +594,7 @@ function createUseInfiniteRead(options) {
466
594
  unsubInvalidate();
467
595
  };
468
596
  }, [tagsKey]);
469
- (0, import_react3.useEffect)(() => {
597
+ (0, import_react4.useEffect)(() => {
470
598
  if (!lifecycleRef.current.initialized) return;
471
599
  if (enabled) {
472
600
  const currentState = controller.getState();
@@ -477,7 +605,7 @@ function createUseInfiniteRead(options) {
477
605
  }
478
606
  }
479
607
  }, [enabled]);
480
- (0, import_react3.useEffect)(() => {
608
+ (0, import_react4.useEffect)(() => {
481
609
  if (!enabled || !lifecycleRef.current.initialized) return;
482
610
  const prevContext = controller.getContext();
483
611
  controller.update(prevContext);
@@ -496,7 +624,7 @@ function createUseInfiniteRead(options) {
496
624
  canFetchPrev: state.canFetchPrev,
497
625
  fetchNext: controller.fetchNext,
498
626
  fetchPrev: controller.fetchPrev,
499
- refetch: controller.refetch,
627
+ trigger: controller.refetch,
500
628
  abort: controller.abort,
501
629
  error: state.error
502
630
  };
@@ -519,6 +647,12 @@ function createReactSpoosh(instance) {
519
647
  eventEmitter,
520
648
  pluginExecutor
521
649
  });
650
+ const useLazyRead = createUseLazyRead({
651
+ api,
652
+ stateManager,
653
+ eventEmitter,
654
+ pluginExecutor
655
+ });
522
656
  const useInfiniteRead = createUseInfiniteRead({
523
657
  api,
524
658
  stateManager,
@@ -544,6 +678,7 @@ function createReactSpoosh(instance) {
544
678
  return {
545
679
  useRead,
546
680
  useWrite,
681
+ useLazyRead,
547
682
  useInfiniteRead,
548
683
  ...instanceApis
549
684
  };
package/dist/index.mjs CHANGED
@@ -155,7 +155,7 @@ function createUseRead(options) {
155
155
  const abort = useCallback(() => {
156
156
  abortRef.current();
157
157
  }, []);
158
- const refetch = useCallback(() => {
158
+ const trigger = useCallback(() => {
159
159
  return executeWithTracking(true);
160
160
  }, [executeWithTracking]);
161
161
  const entry = stateManager.getCache(queryKey);
@@ -184,7 +184,7 @@ function createUseRead(options) {
184
184
  loading,
185
185
  fetching,
186
186
  abort,
187
- refetch
187
+ trigger
188
188
  };
189
189
  }
190
190
  return useRead;
@@ -257,10 +257,6 @@ function createUseWrite(options) {
257
257
  );
258
258
  const [lastTriggerOptions, setLastTriggerOptions] = useState2(void 0);
259
259
  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
260
  const abort = useCallback2(() => {
265
261
  controller.abort();
266
262
  }, []);
@@ -312,7 +308,6 @@ function createUseWrite(options) {
312
308
  data: state.data,
313
309
  error: requestState.error ?? state.error,
314
310
  loading,
315
- reset,
316
311
  abort
317
312
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
318
313
  };
@@ -320,18 +315,160 @@ function createUseWrite(options) {
320
315
  return useWrite;
321
316
  }
322
317
 
323
- // src/useInfiniteRead/index.ts
318
+ // src/useLazyRead/index.ts
324
319
  import {
320
+ useSyncExternalStore as useSyncExternalStore3,
325
321
  useRef as useRef3,
322
+ useCallback as useCallback3,
323
+ useState as useState3,
324
+ useId as useId3
325
+ } from "react";
326
+ import {
327
+ createOperationController as createOperationController3,
328
+ createSelectorProxy as createSelectorProxy3,
329
+ resolvePath as resolvePath3
330
+ } from "@spoosh/core";
331
+ function createUseLazyRead(options) {
332
+ const { api, stateManager, pluginExecutor, eventEmitter } = options;
333
+ function useLazyRead(readFn) {
334
+ const hookId = useId3();
335
+ const selectorResultRef = useRef3({
336
+ call: null,
337
+ selector: null
338
+ });
339
+ const selectorProxy = createSelectorProxy3((result) => {
340
+ selectorResultRef.current = result;
341
+ });
342
+ readFn(selectorProxy);
343
+ const selectedEndpoint = selectorResultRef.current.selector;
344
+ if (!selectedEndpoint) {
345
+ throw new Error(
346
+ 'useLazyRead requires selecting an HTTP method (GET). Example: useLazyRead((api) => api("posts").GET)'
347
+ );
348
+ }
349
+ if (selectedEndpoint.method !== "GET") {
350
+ throw new Error(
351
+ "useLazyRead only supports GET method. Use useWrite for POST, PUT, PATCH, DELETE methods."
352
+ );
353
+ }
354
+ const pathSegments = selectedEndpoint.path.split("/").filter(Boolean);
355
+ const controllerRef = useRef3(null);
356
+ const emptyStateRef = useRef3({ data: void 0, error: void 0 });
357
+ const [currentQueryKey, setCurrentQueryKey] = useState3(null);
358
+ const [, forceUpdate] = useState3(0);
359
+ const getOrCreateController = useCallback3(
360
+ (triggerOptions) => {
361
+ const queryKey = stateManager.createQueryKey({
362
+ path: pathSegments,
363
+ method: selectedEndpoint.method,
364
+ options: triggerOptions
365
+ });
366
+ if (controllerRef.current?.queryKey === queryKey) {
367
+ return { controller: controllerRef.current.controller, queryKey };
368
+ }
369
+ const controller2 = createOperationController3({
370
+ operationType: "read",
371
+ path: pathSegments,
372
+ method: "GET",
373
+ tags: [],
374
+ stateManager,
375
+ eventEmitter,
376
+ pluginExecutor,
377
+ hookId,
378
+ requestOptions: triggerOptions,
379
+ fetchFn: async (fetchOpts) => {
380
+ const pathMethods = api(selectedEndpoint.path);
381
+ const method = pathMethods[selectedEndpoint.method];
382
+ return method(fetchOpts);
383
+ }
384
+ });
385
+ controllerRef.current = { controller: controller2, queryKey };
386
+ setCurrentQueryKey(queryKey);
387
+ forceUpdate((n) => n + 1);
388
+ return { controller: controller2, queryKey };
389
+ },
390
+ [pathSegments, selectedEndpoint.method, selectedEndpoint.path, hookId]
391
+ );
392
+ const controller = controllerRef.current?.controller;
393
+ const subscribe = useCallback3(
394
+ (callback) => {
395
+ if (!controller) return () => {
396
+ };
397
+ return controller.subscribe(callback);
398
+ },
399
+ [controller]
400
+ );
401
+ const getSnapshot = useCallback3(() => {
402
+ if (!controller) return emptyStateRef.current;
403
+ return controller.getState();
404
+ }, [controller]);
405
+ const state = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
406
+ const [lastTriggerOptions, setLastTriggerOptions] = useState3(void 0);
407
+ const [requestState, setRequestState] = useState3({ isPending: false, error: void 0 });
408
+ const abort = useCallback3(() => {
409
+ controllerRef.current?.controller.abort();
410
+ }, []);
411
+ const trigger = useCallback3(
412
+ async (triggerOptions) => {
413
+ setLastTriggerOptions(triggerOptions);
414
+ setRequestState((prev) => ({ ...prev, isPending: true }));
415
+ const params = triggerOptions?.params;
416
+ resolvePath3(pathSegments, params);
417
+ const { controller: ctrl } = getOrCreateController(triggerOptions);
418
+ ctrl.setPluginOptions(triggerOptions);
419
+ try {
420
+ const response = await ctrl.execute(triggerOptions);
421
+ if (response.error) {
422
+ setRequestState({ isPending: false, error: response.error });
423
+ } else {
424
+ setRequestState({ isPending: false, error: void 0 });
425
+ }
426
+ return response;
427
+ } catch (err) {
428
+ setRequestState({ isPending: false, error: err });
429
+ throw err;
430
+ }
431
+ },
432
+ [pathSegments, getOrCreateController]
433
+ );
434
+ const opts = lastTriggerOptions;
435
+ const inputInner = {};
436
+ if (opts?.query !== void 0) {
437
+ inputInner.query = opts.query;
438
+ }
439
+ if (opts?.body !== void 0) {
440
+ inputInner.body = opts.body;
441
+ }
442
+ if (opts?.params !== void 0) {
443
+ inputInner.params = opts.params;
444
+ }
445
+ const inputField = Object.keys(inputInner).length > 0 ? { input: inputInner } : {};
446
+ const loading = requestState.isPending;
447
+ return {
448
+ trigger,
449
+ ...inputField,
450
+ data: state.data,
451
+ error: requestState.error ?? state.error,
452
+ loading,
453
+ abort
454
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
455
+ };
456
+ }
457
+ return useLazyRead;
458
+ }
459
+
460
+ // src/useInfiniteRead/index.ts
461
+ import {
462
+ useRef as useRef4,
326
463
  useEffect as useEffect2,
327
- useSyncExternalStore as useSyncExternalStore3,
328
- useId as useId3,
329
- useState as useState3
464
+ useSyncExternalStore as useSyncExternalStore4,
465
+ useId as useId4,
466
+ useState as useState4
330
467
  } from "react";
331
468
  import {
332
469
  createInfiniteReadController,
333
- createSelectorProxy as createSelectorProxy3,
334
- resolvePath as resolvePath3,
470
+ createSelectorProxy as createSelectorProxy4,
471
+ resolvePath as resolvePath4,
335
472
  resolveTags as resolveTags3
336
473
  } from "@spoosh/core";
337
474
  function createUseInfiniteRead(options) {
@@ -347,12 +484,12 @@ function createUseInfiniteRead(options) {
347
484
  prevPageRequest,
348
485
  ...pluginOpts
349
486
  } = readOptions;
350
- const hookId = useId3();
351
- const selectorResultRef = useRef3({
487
+ const hookId = useId4();
488
+ const selectorResultRef = useRef4({
352
489
  call: null,
353
490
  selector: null
354
491
  });
355
- const selectorProxy = createSelectorProxy3((result2) => {
492
+ const selectorProxy = createSelectorProxy4((result2) => {
356
493
  selectorResultRef.current = result2;
357
494
  });
358
495
  readFn(selectorProxy);
@@ -375,13 +512,13 @@ function createUseInfiniteRead(options) {
375
512
  params: void 0,
376
513
  body: void 0
377
514
  };
378
- const resolvedPath = resolvePath3(pathSegments, requestOptions?.params);
515
+ const resolvedPath = resolvePath4(pathSegments, requestOptions?.params);
379
516
  const resolvedTags = resolveTags3({ tags }, resolvedPath);
380
- const canFetchNextRef = useRef3(canFetchNext);
381
- const canFetchPrevRef = useRef3(canFetchPrev);
382
- const nextPageRequestRef = useRef3(nextPageRequest);
383
- const prevPageRequestRef = useRef3(prevPageRequest);
384
- const mergerRef = useRef3(merger);
517
+ const canFetchNextRef = useRef4(canFetchNext);
518
+ const canFetchPrevRef = useRef4(canFetchPrev);
519
+ const nextPageRequestRef = useRef4(nextPageRequest);
520
+ const prevPageRequestRef = useRef4(prevPageRequest);
521
+ const mergerRef = useRef4(merger);
385
522
  canFetchNextRef.current = canFetchNext;
386
523
  canFetchPrevRef.current = canFetchPrev;
387
524
  nextPageRequestRef.current = nextPageRequest;
@@ -392,7 +529,7 @@ function createUseInfiniteRead(options) {
392
529
  method: capturedCall.method,
393
530
  options: baseOptionsForKey
394
531
  });
395
- const controllerRef = useRef3(null);
532
+ const controllerRef = useRef4(null);
396
533
  if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
397
534
  controllerRef.current = {
398
535
  controller: createInfiniteReadController({
@@ -428,12 +565,12 @@ function createUseInfiniteRead(options) {
428
565
  }
429
566
  const controller = controllerRef.current.controller;
430
567
  controller.setPluginOptions(pluginOpts);
431
- const state = useSyncExternalStore3(
568
+ const state = useSyncExternalStore4(
432
569
  controller.subscribe,
433
570
  controller.getState,
434
571
  controller.getState
435
572
  );
436
- const [isPending, setIsPending] = useState3(() => {
573
+ const [isPending, setIsPending] = useState4(() => {
437
574
  return enabled && state.data === void 0;
438
575
  });
439
576
  const fetchingDirection = controller.getFetchingDirection();
@@ -442,7 +579,7 @@ function createUseInfiniteRead(options) {
442
579
  const fetchingPrev = fetchingDirection === "prev";
443
580
  const hasData = state.data !== void 0;
444
581
  const loading = (isPending || fetching) && !hasData;
445
- const lifecycleRef = useRef3({
582
+ const lifecycleRef = useRef4({
446
583
  initialized: false,
447
584
  prevContext: null
448
585
  });
@@ -502,7 +639,7 @@ function createUseInfiniteRead(options) {
502
639
  canFetchPrev: state.canFetchPrev,
503
640
  fetchNext: controller.fetchNext,
504
641
  fetchPrev: controller.fetchPrev,
505
- refetch: controller.refetch,
642
+ trigger: controller.refetch,
506
643
  abort: controller.abort,
507
644
  error: state.error
508
645
  };
@@ -525,6 +662,12 @@ function createReactSpoosh(instance) {
525
662
  eventEmitter,
526
663
  pluginExecutor
527
664
  });
665
+ const useLazyRead = createUseLazyRead({
666
+ api,
667
+ stateManager,
668
+ eventEmitter,
669
+ pluginExecutor
670
+ });
528
671
  const useInfiniteRead = createUseInfiniteRead({
529
672
  api,
530
673
  stateManager,
@@ -550,6 +693,7 @@ function createReactSpoosh(instance) {
550
693
  return {
551
694
  useRead,
552
695
  useWrite,
696
+ useLazyRead,
553
697
  useInfiniteRead,
554
698
  ...instanceApis
555
699
  };
@@ -557,6 +701,7 @@ function createReactSpoosh(instance) {
557
701
  export {
558
702
  createReactSpoosh,
559
703
  createUseInfiniteRead,
704
+ createUseLazyRead,
560
705
  createUseRead,
561
706
  createUseWrite
562
707
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/react",
3
- "version": "0.5.1",
3
+ "version": "0.6.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",