@spoosh/react 0.5.0 → 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/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
  });
@@ -34,15 +35,19 @@ var import_react = require("react");
34
35
  var import_core = require("@spoosh/core");
35
36
  function createUseRead(options) {
36
37
  const { api, stateManager, eventEmitter, pluginExecutor } = options;
37
- return function useRead(readFn, readOptions) {
38
- const { enabled = true, tags, ...pluginOpts } = readOptions ?? {};
38
+ function useRead(readFn, readOptions) {
39
+ const {
40
+ enabled = true,
41
+ tags,
42
+ ...pluginOpts
43
+ } = readOptions ?? {};
39
44
  const hookId = (0, import_react.useId)();
40
45
  const selectorResultRef = (0, import_react.useRef)({
41
46
  call: null,
42
47
  selector: null
43
48
  });
44
- const selectorProxy = (0, import_core.createSelectorProxy)((result2) => {
45
- selectorResultRef.current = result2;
49
+ const selectorProxy = (0, import_core.createSelectorProxy)((result) => {
50
+ selectorResultRef.current = result;
46
51
  });
47
52
  readFn(selectorProxy);
48
53
  const capturedCall = selectorResultRef.current.call;
@@ -102,6 +107,7 @@ function createUseRead(options) {
102
107
  const abortRef = (0, import_react.useRef)(controller.abort);
103
108
  abortRef.current = controller.abort;
104
109
  const pluginOptsKey = JSON.stringify(pluginOpts);
110
+ const tagsKey = JSON.stringify(tags);
105
111
  const executeWithTracking = (0, import_react.useCallback)(
106
112
  async (force = false) => {
107
113
  setRequestState((prev) => ({ ...prev, isPending: true }));
@@ -157,7 +163,7 @@ function createUseRead(options) {
157
163
  unsubRefetch();
158
164
  unsubInvalidate();
159
165
  };
160
- }, [queryKey, enabled]);
166
+ }, [queryKey, enabled, tagsKey]);
161
167
  (0, import_react.useEffect)(() => {
162
168
  if (!enabled || !lifecycleRef.current.initialized) return;
163
169
  const prevContext = controller.getContext();
@@ -166,7 +172,7 @@ function createUseRead(options) {
166
172
  const abort = (0, import_react.useCallback)(() => {
167
173
  abortRef.current();
168
174
  }, []);
169
- const refetch = (0, import_react.useCallback)(() => {
175
+ const trigger = (0, import_react.useCallback)(() => {
170
176
  return executeWithTracking(true);
171
177
  }, [executeWithTracking]);
172
178
  const entry = stateManager.getCache(queryKey);
@@ -186,7 +192,8 @@ function createUseRead(options) {
186
192
  const hasData = state.data !== void 0;
187
193
  const loading = requestState.isPending && !hasData;
188
194
  const fetching = requestState.isPending;
189
- const result = {
195
+ return {
196
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
197
  meta: pluginResultData,
191
198
  ...inputField,
192
199
  data: state.data,
@@ -194,10 +201,10 @@ function createUseRead(options) {
194
201
  loading,
195
202
  fetching,
196
203
  abort,
197
- refetch
204
+ trigger
198
205
  };
199
- return result;
200
- };
206
+ }
207
+ return useRead;
201
208
  }
202
209
 
203
210
  // src/useWrite/index.ts
@@ -205,14 +212,14 @@ var import_react2 = require("react");
205
212
  var import_core2 = require("@spoosh/core");
206
213
  function createUseWrite(options) {
207
214
  const { api, stateManager, pluginExecutor, eventEmitter } = options;
208
- return function useWrite(writeFn) {
215
+ function useWrite(writeFn) {
209
216
  const hookId = (0, import_react2.useId)();
210
217
  const selectorResultRef = (0, import_react2.useRef)({
211
218
  call: null,
212
219
  selector: null
213
220
  });
214
- const selectorProxy = (0, import_core2.createSelectorProxy)((result2) => {
215
- selectorResultRef.current = result2;
221
+ const selectorProxy = (0, import_core2.createSelectorProxy)((result) => {
222
+ selectorResultRef.current = result;
216
223
  });
217
224
  writeFn(selectorProxy);
218
225
  const selectedEndpoint = selectorResultRef.current.selector;
@@ -256,10 +263,6 @@ function createUseWrite(options) {
256
263
  );
257
264
  const [lastTriggerOptions, setLastTriggerOptions] = (0, import_react2.useState)(void 0);
258
265
  const [requestState, setRequestState] = (0, import_react2.useState)({ isPending: false, error: void 0 });
259
- const reset = (0, import_react2.useCallback)(() => {
260
- stateManager.deleteCache(queryKey);
261
- setRequestState({ isPending: false, error: void 0 });
262
- }, [queryKey]);
263
266
  const abort = (0, import_react2.useCallback)(() => {
264
267
  controller.abort();
265
268
  }, []);
@@ -303,23 +306,156 @@ function createUseWrite(options) {
303
306
  }
304
307
  const inputField = Object.keys(inputInner).length > 0 ? { input: inputInner } : {};
305
308
  const loading = requestState.isPending;
306
- const result = {
309
+ return {
307
310
  trigger,
311
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
308
312
  meta: pluginResultData,
309
313
  ...inputField,
310
314
  data: state.data,
311
315
  error: requestState.error ?? state.error,
312
316
  loading,
313
- reset,
314
317
  abort
318
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
315
319
  };
316
- return result;
317
- };
320
+ }
321
+ return useWrite;
318
322
  }
319
323
 
320
- // src/useInfiniteRead/index.ts
324
+ // src/useLazyRead/index.ts
321
325
  var import_react3 = require("react");
322
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");
323
459
  function createUseInfiniteRead(options) {
324
460
  const { api, stateManager, eventEmitter, pluginExecutor } = options;
325
461
  return function useInfiniteRead(readFn, readOptions) {
@@ -333,12 +469,12 @@ function createUseInfiniteRead(options) {
333
469
  prevPageRequest,
334
470
  ...pluginOpts
335
471
  } = readOptions;
336
- const hookId = (0, import_react3.useId)();
337
- const selectorResultRef = (0, import_react3.useRef)({
472
+ const hookId = (0, import_react4.useId)();
473
+ const selectorResultRef = (0, import_react4.useRef)({
338
474
  call: null,
339
475
  selector: null
340
476
  });
341
- const selectorProxy = (0, import_core3.createSelectorProxy)((result2) => {
477
+ const selectorProxy = (0, import_core4.createSelectorProxy)((result2) => {
342
478
  selectorResultRef.current = result2;
343
479
  });
344
480
  readFn(selectorProxy);
@@ -361,13 +497,13 @@ function createUseInfiniteRead(options) {
361
497
  params: void 0,
362
498
  body: void 0
363
499
  };
364
- const resolvedPath = (0, import_core3.resolvePath)(pathSegments, requestOptions?.params);
365
- const resolvedTags = (0, import_core3.resolveTags)({ tags }, resolvedPath);
366
- const canFetchNextRef = (0, import_react3.useRef)(canFetchNext);
367
- const canFetchPrevRef = (0, import_react3.useRef)(canFetchPrev);
368
- const nextPageRequestRef = (0, import_react3.useRef)(nextPageRequest);
369
- const prevPageRequestRef = (0, import_react3.useRef)(prevPageRequest);
370
- 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);
371
507
  canFetchNextRef.current = canFetchNext;
372
508
  canFetchPrevRef.current = canFetchPrev;
373
509
  nextPageRequestRef.current = nextPageRequest;
@@ -378,10 +514,10 @@ function createUseInfiniteRead(options) {
378
514
  method: capturedCall.method,
379
515
  options: baseOptionsForKey
380
516
  });
381
- const controllerRef = (0, import_react3.useRef)(null);
517
+ const controllerRef = (0, import_react4.useRef)(null);
382
518
  if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
383
519
  controllerRef.current = {
384
- controller: (0, import_core3.createInfiniteReadController)({
520
+ controller: (0, import_core4.createInfiniteReadController)({
385
521
  path: pathSegments,
386
522
  method: capturedCall.method,
387
523
  tags: resolvedTags,
@@ -414,12 +550,12 @@ function createUseInfiniteRead(options) {
414
550
  }
415
551
  const controller = controllerRef.current.controller;
416
552
  controller.setPluginOptions(pluginOpts);
417
- const state = (0, import_react3.useSyncExternalStore)(
553
+ const state = (0, import_react4.useSyncExternalStore)(
418
554
  controller.subscribe,
419
555
  controller.getState,
420
556
  controller.getState
421
557
  );
422
- const [isPending, setIsPending] = (0, import_react3.useState)(() => {
558
+ const [isPending, setIsPending] = (0, import_react4.useState)(() => {
423
559
  return enabled && state.data === void 0;
424
560
  });
425
561
  const fetchingDirection = controller.getFetchingDirection();
@@ -428,17 +564,18 @@ function createUseInfiniteRead(options) {
428
564
  const fetchingPrev = fetchingDirection === "prev";
429
565
  const hasData = state.data !== void 0;
430
566
  const loading = (isPending || fetching) && !hasData;
431
- const lifecycleRef = (0, import_react3.useRef)({
567
+ const lifecycleRef = (0, import_react4.useRef)({
432
568
  initialized: false,
433
569
  prevContext: null
434
570
  });
435
- (0, import_react3.useEffect)(() => {
571
+ const tagsKey = JSON.stringify(tags);
572
+ (0, import_react4.useEffect)(() => {
436
573
  return () => {
437
574
  controllerRef.current?.controller.unmount();
438
575
  lifecycleRef.current.initialized = false;
439
576
  };
440
577
  }, []);
441
- (0, import_react3.useEffect)(() => {
578
+ (0, import_react4.useEffect)(() => {
442
579
  controller.mount();
443
580
  lifecycleRef.current.initialized = true;
444
581
  const unsubInvalidate = eventEmitter.on(
@@ -456,8 +593,8 @@ function createUseInfiniteRead(options) {
456
593
  return () => {
457
594
  unsubInvalidate();
458
595
  };
459
- }, []);
460
- (0, import_react3.useEffect)(() => {
596
+ }, [tagsKey]);
597
+ (0, import_react4.useEffect)(() => {
461
598
  if (!lifecycleRef.current.initialized) return;
462
599
  if (enabled) {
463
600
  const currentState = controller.getState();
@@ -468,7 +605,7 @@ function createUseInfiniteRead(options) {
468
605
  }
469
606
  }
470
607
  }, [enabled]);
471
- (0, import_react3.useEffect)(() => {
608
+ (0, import_react4.useEffect)(() => {
472
609
  if (!enabled || !lifecycleRef.current.initialized) return;
473
610
  const prevContext = controller.getContext();
474
611
  controller.update(prevContext);
@@ -487,7 +624,7 @@ function createUseInfiniteRead(options) {
487
624
  canFetchPrev: state.canFetchPrev,
488
625
  fetchNext: controller.fetchNext,
489
626
  fetchPrev: controller.fetchPrev,
490
- refetch: controller.refetch,
627
+ trigger: controller.refetch,
491
628
  abort: controller.abort,
492
629
  error: state.error
493
630
  };
@@ -510,6 +647,12 @@ function createReactSpoosh(instance) {
510
647
  eventEmitter,
511
648
  pluginExecutor
512
649
  });
650
+ const useLazyRead = createUseLazyRead({
651
+ api,
652
+ stateManager,
653
+ eventEmitter,
654
+ pluginExecutor
655
+ });
513
656
  const useInfiniteRead = createUseInfiniteRead({
514
657
  api,
515
658
  stateManager,
@@ -535,6 +678,7 @@ function createReactSpoosh(instance) {
535
678
  return {
536
679
  useRead,
537
680
  useWrite,
681
+ useLazyRead,
538
682
  useInfiniteRead,
539
683
  ...instanceApis
540
684
  };