@spoosh/react 0.6.0 → 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/dist/index.js CHANGED
@@ -24,7 +24,6 @@ var src_exports = {};
24
24
  __export(src_exports, {
25
25
  createReactSpoosh: () => createReactSpoosh,
26
26
  createUseInfiniteRead: () => createUseInfiniteRead,
27
- createUseLazyRead: () => createUseLazyRead,
28
27
  createUseRead: () => createUseRead,
29
28
  createUseWrite: () => createUseWrite
30
29
  });
@@ -70,10 +69,11 @@ function createUseRead(options) {
70
69
  initialized: false,
71
70
  prevContext: null
72
71
  });
73
- if (controllerRef.current && controllerRef.current.queryKey !== queryKey) {
72
+ const baseQueryKeyChanged = controllerRef.current && controllerRef.current.baseQueryKey !== queryKey;
73
+ if (baseQueryKeyChanged) {
74
74
  lifecycleRef.current.prevContext = controllerRef.current.controller.getContext();
75
75
  }
76
- if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
76
+ if (!controllerRef.current || baseQueryKeyChanged) {
77
77
  const controller2 = (0, import_core.createOperationController)({
78
78
  operationType: "read",
79
79
  path: pathSegments,
@@ -90,29 +90,36 @@ function createUseRead(options) {
90
90
  return method(fetchOpts);
91
91
  }
92
92
  });
93
- controllerRef.current = { controller: controller2, queryKey };
93
+ controllerRef.current = { controller: controller2, queryKey, baseQueryKey: queryKey };
94
94
  }
95
95
  const controller = controllerRef.current.controller;
96
96
  controller.setPluginOptions(pluginOpts);
97
- const state = (0, import_react.useSyncExternalStore)(
98
- controller.subscribe,
99
- controller.getState,
100
- controller.getState
97
+ const subscribe = (0, import_react.useCallback)(
98
+ (callback) => {
99
+ return controller.subscribe(callback);
100
+ },
101
+ [controller]
101
102
  );
103
+ const getSnapshot = (0, import_react.useCallback)(() => {
104
+ return controller.getState();
105
+ }, [controller]);
106
+ const state = (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
102
107
  const [requestState, setRequestState] = (0, import_react.useState)(() => {
103
108
  const cachedEntry = stateManager.getCache(queryKey);
104
109
  const hasCachedData = cachedEntry?.state?.data !== void 0;
105
110
  return { isPending: enabled && !hasCachedData, error: void 0 };
106
111
  });
112
+ const [, forceUpdate] = (0, import_react.useState)(0);
107
113
  const abortRef = (0, import_react.useRef)(controller.abort);
108
114
  abortRef.current = controller.abort;
109
115
  const pluginOptsKey = JSON.stringify(pluginOpts);
110
116
  const tagsKey = JSON.stringify(tags);
111
117
  const executeWithTracking = (0, import_react.useCallback)(
112
- async (force = false) => {
118
+ async (force = false, overrideOptions) => {
113
119
  setRequestState((prev) => ({ ...prev, isPending: true }));
114
120
  try {
115
- 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 });
116
123
  if (response.error) {
117
124
  setRequestState({ isPending: false, error: response.error });
118
125
  } else {
@@ -124,7 +131,7 @@ function createUseRead(options) {
124
131
  throw err;
125
132
  }
126
133
  },
127
- [controller]
134
+ [controller, capturedCall.options]
128
135
  );
129
136
  (0, import_react.useEffect)(() => {
130
137
  return () => {
@@ -172,9 +179,84 @@ function createUseRead(options) {
172
179
  const abort = (0, import_react.useCallback)(() => {
173
180
  abortRef.current();
174
181
  }, []);
175
- const trigger = (0, import_react.useCallback)(() => {
176
- return executeWithTracking(true);
177
- }, [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
+ );
178
260
  const entry = stateManager.getCache(queryKey);
179
261
  const pluginResultData = entry?.meta ? Object.fromEntries(entry.meta) : {};
180
262
  const opts = capturedCall.options;
@@ -321,141 +403,9 @@ function createUseWrite(options) {
321
403
  return useWrite;
322
404
  }
323
405
 
324
- // src/useLazyRead/index.ts
406
+ // src/useInfiniteRead/index.ts
325
407
  var import_react3 = require("react");
326
408
  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");
459
409
  function createUseInfiniteRead(options) {
460
410
  const { api, stateManager, eventEmitter, pluginExecutor } = options;
461
411
  return function useInfiniteRead(readFn, readOptions) {
@@ -469,12 +419,12 @@ function createUseInfiniteRead(options) {
469
419
  prevPageRequest,
470
420
  ...pluginOpts
471
421
  } = readOptions;
472
- const hookId = (0, import_react4.useId)();
473
- const selectorResultRef = (0, import_react4.useRef)({
422
+ const hookId = (0, import_react3.useId)();
423
+ const selectorResultRef = (0, import_react3.useRef)({
474
424
  call: null,
475
425
  selector: null
476
426
  });
477
- const selectorProxy = (0, import_core4.createSelectorProxy)((result2) => {
427
+ const selectorProxy = (0, import_core3.createSelectorProxy)((result2) => {
478
428
  selectorResultRef.current = result2;
479
429
  });
480
430
  readFn(selectorProxy);
@@ -497,13 +447,13 @@ function createUseInfiniteRead(options) {
497
447
  params: void 0,
498
448
  body: void 0
499
449
  };
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);
450
+ const resolvedPath = (0, import_core3.resolvePath)(pathSegments, requestOptions?.params);
451
+ const resolvedTags = (0, import_core3.resolveTags)({ tags }, resolvedPath);
452
+ const canFetchNextRef = (0, import_react3.useRef)(canFetchNext);
453
+ const canFetchPrevRef = (0, import_react3.useRef)(canFetchPrev);
454
+ const nextPageRequestRef = (0, import_react3.useRef)(nextPageRequest);
455
+ const prevPageRequestRef = (0, import_react3.useRef)(prevPageRequest);
456
+ const mergerRef = (0, import_react3.useRef)(merger);
507
457
  canFetchNextRef.current = canFetchNext;
508
458
  canFetchPrevRef.current = canFetchPrev;
509
459
  nextPageRequestRef.current = nextPageRequest;
@@ -514,10 +464,10 @@ function createUseInfiniteRead(options) {
514
464
  method: capturedCall.method,
515
465
  options: baseOptionsForKey
516
466
  });
517
- const controllerRef = (0, import_react4.useRef)(null);
467
+ const controllerRef = (0, import_react3.useRef)(null);
518
468
  if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
519
469
  controllerRef.current = {
520
- controller: (0, import_core4.createInfiniteReadController)({
470
+ controller: (0, import_core3.createInfiniteReadController)({
521
471
  path: pathSegments,
522
472
  method: capturedCall.method,
523
473
  tags: resolvedTags,
@@ -550,12 +500,12 @@ function createUseInfiniteRead(options) {
550
500
  }
551
501
  const controller = controllerRef.current.controller;
552
502
  controller.setPluginOptions(pluginOpts);
553
- const state = (0, import_react4.useSyncExternalStore)(
503
+ const state = (0, import_react3.useSyncExternalStore)(
554
504
  controller.subscribe,
555
505
  controller.getState,
556
506
  controller.getState
557
507
  );
558
- const [isPending, setIsPending] = (0, import_react4.useState)(() => {
508
+ const [isPending, setIsPending] = (0, import_react3.useState)(() => {
559
509
  return enabled && state.data === void 0;
560
510
  });
561
511
  const fetchingDirection = controller.getFetchingDirection();
@@ -564,18 +514,18 @@ function createUseInfiniteRead(options) {
564
514
  const fetchingPrev = fetchingDirection === "prev";
565
515
  const hasData = state.data !== void 0;
566
516
  const loading = (isPending || fetching) && !hasData;
567
- const lifecycleRef = (0, import_react4.useRef)({
517
+ const lifecycleRef = (0, import_react3.useRef)({
568
518
  initialized: false,
569
519
  prevContext: null
570
520
  });
571
521
  const tagsKey = JSON.stringify(tags);
572
- (0, import_react4.useEffect)(() => {
522
+ (0, import_react3.useEffect)(() => {
573
523
  return () => {
574
524
  controllerRef.current?.controller.unmount();
575
525
  lifecycleRef.current.initialized = false;
576
526
  };
577
527
  }, []);
578
- (0, import_react4.useEffect)(() => {
528
+ (0, import_react3.useEffect)(() => {
579
529
  controller.mount();
580
530
  lifecycleRef.current.initialized = true;
581
531
  const unsubInvalidate = eventEmitter.on(
@@ -594,7 +544,7 @@ function createUseInfiniteRead(options) {
594
544
  unsubInvalidate();
595
545
  };
596
546
  }, [tagsKey]);
597
- (0, import_react4.useEffect)(() => {
547
+ (0, import_react3.useEffect)(() => {
598
548
  if (!lifecycleRef.current.initialized) return;
599
549
  if (enabled) {
600
550
  const currentState = controller.getState();
@@ -605,7 +555,7 @@ function createUseInfiniteRead(options) {
605
555
  }
606
556
  }
607
557
  }, [enabled]);
608
- (0, import_react4.useEffect)(() => {
558
+ (0, import_react3.useEffect)(() => {
609
559
  if (!enabled || !lifecycleRef.current.initialized) return;
610
560
  const prevContext = controller.getContext();
611
561
  controller.update(prevContext);
@@ -647,12 +597,6 @@ function createReactSpoosh(instance) {
647
597
  eventEmitter,
648
598
  pluginExecutor
649
599
  });
650
- const useLazyRead = createUseLazyRead({
651
- api,
652
- stateManager,
653
- eventEmitter,
654
- pluginExecutor
655
- });
656
600
  const useInfiniteRead = createUseInfiniteRead({
657
601
  api,
658
602
  stateManager,
@@ -678,7 +622,6 @@ function createReactSpoosh(instance) {
678
622
  return {
679
623
  useRead,
680
624
  useWrite,
681
- useLazyRead,
682
625
  useInfiniteRead,
683
626
  ...instanceApis
684
627
  };