@spoosh/angular 0.4.0 → 0.5.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/angular
2
2
 
3
- Angular signals integration for Spoosh - `injectRead`, `injectWrite`, and `injectInfiniteRead`.
3
+ Angular signals integration for Spoosh - `injectRead`, `injectLazyRead`, `injectWrite`, and `injectInfiniteRead`.
4
4
 
5
5
  **[Documentation](https://spoosh.dev/docs/integrations/angular)** · **Requirements:** TypeScript >= 5.0, Angular >= 16.0
6
6
 
@@ -23,13 +23,13 @@ const spoosh = new Spoosh<ApiSchema, Error>("/api").use([
23
23
  cachePlugin({ staleTime: 5000 }),
24
24
  ]);
25
25
 
26
- export const { injectRead, injectWrite, injectInfiniteRead } =
26
+ export const { injectRead, injectLazyRead, injectWrite, injectInfiniteRead } =
27
27
  createAngularSpoosh(spoosh);
28
28
  ```
29
29
 
30
30
  ### injectRead
31
31
 
32
- Fetch data with automatic caching and refetching using Angular signals.
32
+ Fetch data with automatic caching and triggering using Angular signals.
33
33
 
34
34
  ```typescript
35
35
  @Component({
@@ -74,6 +74,28 @@ export class UserListComponent {
74
74
  }
75
75
  ```
76
76
 
77
+ ### injectLazyRead
78
+
79
+ Lazy data fetching for print/download/export scenarios. Does not auto-fetch on mount.
80
+
81
+ ```typescript
82
+ @Component({
83
+ template: `
84
+ <button (click)="handlePrint('123')" [disabled]="order.loading()">
85
+ {{ order.loading() ? "Loading..." : "Print" }}
86
+ </button>
87
+ `,
88
+ })
89
+ export class PrintOrderComponent {
90
+ order = injectLazyRead((api) => api("orders/:id").GET);
91
+
92
+ async handlePrint(orderId: string) {
93
+ const { data } = await this.order.trigger({ params: { id: orderId } });
94
+ if (data) this.printService.printReceipt(data);
95
+ }
96
+ }
97
+ ```
98
+
77
99
  ### injectWrite
78
100
 
79
101
  Trigger mutations with loading and error states.
@@ -187,7 +209,7 @@ export class PostListComponent {
187
209
  | `loading` | `Signal<boolean>` | True during initial load |
188
210
  | `fetching` | `Signal<boolean>` | True during any fetch |
189
211
  | `meta` | `Signal<PluginResults>` | Plugin metadata (e.g., `transformedData`) |
190
- | `refetch` | `() => Promise` | Manually trigger refetch |
212
+ | `trigger` | `() => Promise` | Manually trigger fetch |
191
213
  | `abort` | `() => void` | Abort current request |
192
214
 
193
215
  ### injectWrite(writeFn)
@@ -202,7 +224,6 @@ export class PostListComponent {
202
224
  | `loading` | `Signal<boolean>` | True while mutation is in progress |
203
225
  | `meta` | `Signal<PluginResults>` | Plugin metadata |
204
226
  | `input` | `TriggerOptions \| undefined` | Last trigger input |
205
- | `reset` | `() => void` | Reset state |
206
227
  | `abort` | `() => void` | Abort current request |
207
228
 
208
229
  ### injectInfiniteRead(readFn, options)
package/dist/index.d.mts CHANGED
@@ -24,7 +24,7 @@ interface BaseReadResult<TData, TError, TPluginResult = Record<string, unknown>>
24
24
  fetching: Signal<boolean>;
25
25
  meta: Signal<TPluginResult>;
26
26
  abort: () => void;
27
- refetch: () => Promise<SpooshResponse<TData, TError>>;
27
+ trigger: () => Promise<SpooshResponse<TData, TError>>;
28
28
  }
29
29
  interface BaseWriteResult<TData, TError, TOptions, TPluginResult = Record<string, unknown>> {
30
30
  trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
@@ -32,7 +32,13 @@ interface BaseWriteResult<TData, TError, TOptions, TPluginResult = Record<string
32
32
  error: Signal<TError | undefined>;
33
33
  loading: Signal<boolean>;
34
34
  meta: Signal<TPluginResult>;
35
- reset: () => void;
35
+ abort: () => void;
36
+ }
37
+ interface BaseLazyReadResult<TData, TError, TOptions> {
38
+ trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
39
+ data: Signal<TData | undefined>;
40
+ error: Signal<TError | undefined>;
41
+ loading: Signal<boolean>;
36
42
  abort: () => void;
37
43
  }
38
44
  type PageContext<TData, TRequest> = {
@@ -61,7 +67,7 @@ interface BaseInfiniteReadResult<TData, TError, TItem, TPluginResult = Record<st
61
67
  meta: Signal<TPluginResult>;
62
68
  fetchNext: () => Promise<void>;
63
69
  fetchPrev: () => Promise<void>;
64
- refetch: () => Promise<void>;
70
+ trigger: () => Promise<void>;
65
71
  abort: () => void;
66
72
  }
67
73
  type QueryRequestOptions = CoreRequestOptionsBase;
@@ -148,6 +154,23 @@ type ExtractMethodBody<T> = ExtractMethodOptions<T> extends {
148
154
  body: infer B;
149
155
  } ? B : never;
150
156
 
157
+ declare function createInjectLazyRead<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<TMethod extends (...args: never[]) => infer R ? Extract<Awaited<R>, {
158
+ data: unknown;
159
+ error?: undefined;
160
+ }> extends {
161
+ data: infer D;
162
+ } ? D : unknown : unknown, [TMethod extends (...args: never[]) => infer R_1 ? Extract<Awaited<R_1>, {
163
+ error: unknown;
164
+ data?: undefined;
165
+ }> extends {
166
+ error: infer E;
167
+ } ? E : unknown : unknown] extends [unknown] ? TDefaultError : TMethod extends (...args: never[]) => infer R_1 ? Extract<Awaited<R_1>, {
168
+ error: unknown;
169
+ data?: undefined;
170
+ }> extends {
171
+ error: infer E;
172
+ } ? E : unknown : unknown, TMethod extends (...args: infer A) => unknown ? A[0] extends object ? Pick<A[0], Extract<keyof A[0], "query" | "body" | "params">> : object : object> & WriteResponseInputFields<ExtractResponseQuery<TMethod>, ExtractResponseBody<TMethod>, ExtractResponseParamNames<TMethod>>;
173
+
151
174
  type AnyInfiniteRequestOptions = InfiniteRequestOptions;
152
175
  declare function createInjectInfiniteRead<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>>, TRequest extends AnyInfiniteRequestOptions = AnyInfiniteRequestOptions, TItem = unknown>(readFn: TReadFn, readOptions: BaseInfiniteReadOptions<TReadFn extends (...args: never[]) => infer R ? Extract<Awaited<R>, {
153
176
  data: unknown;
@@ -319,8 +342,9 @@ type SpooshAngularFunctions<TDefaultError, TSchema, TPlugins extends PluginArray
319
342
  injectRead: ReturnType<typeof createInjectRead<TSchema, TDefaultError, TPlugins>>;
320
343
  injectWrite: ReturnType<typeof createInjectWrite<TSchema, TDefaultError, TPlugins>>;
321
344
  injectInfiniteRead: ReturnType<typeof createInjectInfiniteRead<TSchema, TDefaultError, TPlugins>>;
345
+ injectLazyRead: ReturnType<typeof createInjectLazyRead<TSchema, TDefaultError, TPlugins>>;
322
346
  } & MergePluginInstanceApi<TPlugins, TSchema>;
323
347
 
324
348
  declare function createAngularSpoosh<TSchema, TDefaultError, TPlugins extends PluginArray, TApi>(instance: SpooshInstanceShape<TApi, TSchema, TDefaultError, TPlugins>): SpooshAngularFunctions<TDefaultError, TSchema, TPlugins>;
325
349
 
326
- export { type AngularOptionsMap, type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type EnabledOption, type ExtractMethodBody, type ExtractMethodData, type ExtractMethodError, type ExtractMethodOptions, type ExtractMethodQuery, type ExtractResponseBody, type ExtractResponseParamNames, type ExtractResponseQuery, type PageContext, type ReadApiClient, type ResponseInputFields, type SpooshInstanceShape, type WriteApiClient, type WriteResponseInputFields, createAngularSpoosh };
350
+ export { type AngularOptionsMap, type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseLazyReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type EnabledOption, type ExtractMethodBody, type ExtractMethodData, type ExtractMethodError, type ExtractMethodOptions, type ExtractMethodQuery, type ExtractResponseBody, type ExtractResponseParamNames, type ExtractResponseQuery, type PageContext, type ReadApiClient, type ResponseInputFields, type SpooshInstanceShape, type WriteApiClient, type WriteResponseInputFields, createAngularSpoosh };
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ interface BaseReadResult<TData, TError, TPluginResult = Record<string, unknown>>
24
24
  fetching: Signal<boolean>;
25
25
  meta: Signal<TPluginResult>;
26
26
  abort: () => void;
27
- refetch: () => Promise<SpooshResponse<TData, TError>>;
27
+ trigger: () => Promise<SpooshResponse<TData, TError>>;
28
28
  }
29
29
  interface BaseWriteResult<TData, TError, TOptions, TPluginResult = Record<string, unknown>> {
30
30
  trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
@@ -32,7 +32,13 @@ interface BaseWriteResult<TData, TError, TOptions, TPluginResult = Record<string
32
32
  error: Signal<TError | undefined>;
33
33
  loading: Signal<boolean>;
34
34
  meta: Signal<TPluginResult>;
35
- reset: () => void;
35
+ abort: () => void;
36
+ }
37
+ interface BaseLazyReadResult<TData, TError, TOptions> {
38
+ trigger: (options?: TOptions) => Promise<SpooshResponse<TData, TError>>;
39
+ data: Signal<TData | undefined>;
40
+ error: Signal<TError | undefined>;
41
+ loading: Signal<boolean>;
36
42
  abort: () => void;
37
43
  }
38
44
  type PageContext<TData, TRequest> = {
@@ -61,7 +67,7 @@ interface BaseInfiniteReadResult<TData, TError, TItem, TPluginResult = Record<st
61
67
  meta: Signal<TPluginResult>;
62
68
  fetchNext: () => Promise<void>;
63
69
  fetchPrev: () => Promise<void>;
64
- refetch: () => Promise<void>;
70
+ trigger: () => Promise<void>;
65
71
  abort: () => void;
66
72
  }
67
73
  type QueryRequestOptions = CoreRequestOptionsBase;
@@ -148,6 +154,23 @@ type ExtractMethodBody<T> = ExtractMethodOptions<T> extends {
148
154
  body: infer B;
149
155
  } ? B : never;
150
156
 
157
+ declare function createInjectLazyRead<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<TMethod extends (...args: never[]) => infer R ? Extract<Awaited<R>, {
158
+ data: unknown;
159
+ error?: undefined;
160
+ }> extends {
161
+ data: infer D;
162
+ } ? D : unknown : unknown, [TMethod extends (...args: never[]) => infer R_1 ? Extract<Awaited<R_1>, {
163
+ error: unknown;
164
+ data?: undefined;
165
+ }> extends {
166
+ error: infer E;
167
+ } ? E : unknown : unknown] extends [unknown] ? TDefaultError : TMethod extends (...args: never[]) => infer R_1 ? Extract<Awaited<R_1>, {
168
+ error: unknown;
169
+ data?: undefined;
170
+ }> extends {
171
+ error: infer E;
172
+ } ? E : unknown : unknown, TMethod extends (...args: infer A) => unknown ? A[0] extends object ? Pick<A[0], Extract<keyof A[0], "query" | "body" | "params">> : object : object> & WriteResponseInputFields<ExtractResponseQuery<TMethod>, ExtractResponseBody<TMethod>, ExtractResponseParamNames<TMethod>>;
173
+
151
174
  type AnyInfiniteRequestOptions = InfiniteRequestOptions;
152
175
  declare function createInjectInfiniteRead<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>>, TRequest extends AnyInfiniteRequestOptions = AnyInfiniteRequestOptions, TItem = unknown>(readFn: TReadFn, readOptions: BaseInfiniteReadOptions<TReadFn extends (...args: never[]) => infer R ? Extract<Awaited<R>, {
153
176
  data: unknown;
@@ -319,8 +342,9 @@ type SpooshAngularFunctions<TDefaultError, TSchema, TPlugins extends PluginArray
319
342
  injectRead: ReturnType<typeof createInjectRead<TSchema, TDefaultError, TPlugins>>;
320
343
  injectWrite: ReturnType<typeof createInjectWrite<TSchema, TDefaultError, TPlugins>>;
321
344
  injectInfiniteRead: ReturnType<typeof createInjectInfiniteRead<TSchema, TDefaultError, TPlugins>>;
345
+ injectLazyRead: ReturnType<typeof createInjectLazyRead<TSchema, TDefaultError, TPlugins>>;
322
346
  } & MergePluginInstanceApi<TPlugins, TSchema>;
323
347
 
324
348
  declare function createAngularSpoosh<TSchema, TDefaultError, TPlugins extends PluginArray, TApi>(instance: SpooshInstanceShape<TApi, TSchema, TDefaultError, TPlugins>): SpooshAngularFunctions<TDefaultError, TSchema, TPlugins>;
325
349
 
326
- export { type AngularOptionsMap, type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type EnabledOption, type ExtractMethodBody, type ExtractMethodData, type ExtractMethodError, type ExtractMethodOptions, type ExtractMethodQuery, type ExtractResponseBody, type ExtractResponseParamNames, type ExtractResponseQuery, type PageContext, type ReadApiClient, type ResponseInputFields, type SpooshInstanceShape, type WriteApiClient, type WriteResponseInputFields, createAngularSpoosh };
350
+ export { type AngularOptionsMap, type BaseInfiniteReadOptions, type BaseInfiniteReadResult, type BaseLazyReadResult, type BaseReadOptions, type BaseReadResult, type BaseWriteResult, type EnabledOption, type ExtractMethodBody, type ExtractMethodData, type ExtractMethodError, type ExtractMethodOptions, type ExtractMethodQuery, type ExtractResponseBody, type ExtractResponseParamNames, type ExtractResponseQuery, type PageContext, type ReadApiClient, type ResponseInputFields, type SpooshInstanceShape, type WriteApiClient, type WriteResponseInputFields, createAngularSpoosh };
package/dist/index.js CHANGED
@@ -267,7 +267,7 @@ function createInjectRead(options) {
267
267
  const abort = () => {
268
268
  currentController?.abort();
269
269
  };
270
- const refetch = () => {
270
+ const trigger = () => {
271
271
  if (currentController) {
272
272
  if (!isMounted) {
273
273
  currentController.mount();
@@ -287,7 +287,7 @@ function createInjectRead(options) {
287
287
  loading: loadingSignal,
288
288
  fetching: fetchingSignal,
289
289
  abort,
290
- refetch
290
+ trigger
291
291
  };
292
292
  return result;
293
293
  };
@@ -333,14 +333,6 @@ function createInjectWrite(options) {
333
333
  currentSubscription();
334
334
  }
335
335
  });
336
- const reset = () => {
337
- if (currentQueryKey) {
338
- stateManager.deleteCache(currentQueryKey);
339
- }
340
- dataSignal.set(void 0);
341
- errorSignal.set(void 0);
342
- loadingSignal.set(false);
343
- };
344
336
  const abort = () => {
345
337
  currentController?.abort();
346
338
  };
@@ -431,7 +423,6 @@ function createInjectWrite(options) {
431
423
  data: dataSignal,
432
424
  error: errorSignal,
433
425
  loading: loadingSignal,
434
- reset,
435
426
  abort
436
427
  };
437
428
  return result;
@@ -730,7 +721,7 @@ function createInjectInfiniteRead(options) {
730
721
  fetchingPrevSignal.set(false);
731
722
  }
732
723
  };
733
- const refetch = async () => {
724
+ const trigger = async () => {
734
725
  if (!currentController) return;
735
726
  if (!isMounted) {
736
727
  currentController.mount();
@@ -763,7 +754,131 @@ function createInjectInfiniteRead(options) {
763
754
  canFetchPrev: canFetchPrevSignal,
764
755
  fetchNext,
765
756
  fetchPrev,
766
- refetch,
757
+ trigger,
758
+ abort
759
+ };
760
+ return result;
761
+ };
762
+ }
763
+
764
+ // src/injectLazyRead/index.ts
765
+ var import_core7 = require("@angular/core");
766
+ var import_core8 = require("@spoosh/core");
767
+ function createInjectLazyRead(options) {
768
+ const { api, stateManager, pluginExecutor, eventEmitter } = options;
769
+ return function injectLazyRead(readFn) {
770
+ const destroyRef = (0, import_core7.inject)(import_core7.DestroyRef);
771
+ const captureSelector = () => {
772
+ const selectorResult = {
773
+ call: null,
774
+ selector: null
775
+ };
776
+ const selectorProxy = (0, import_core8.createSelectorProxy)(
777
+ (result2) => {
778
+ selectorResult.call = result2.call;
779
+ selectorResult.selector = result2.selector;
780
+ }
781
+ );
782
+ readFn(selectorProxy);
783
+ if (!selectorResult.selector) {
784
+ throw new Error(
785
+ 'injectLazyRead requires selecting an HTTP method (GET). Example: injectLazyRead((api) => api("posts").GET)'
786
+ );
787
+ }
788
+ return selectorResult.selector;
789
+ };
790
+ const hookId = `angular-${Math.random().toString(36).slice(2)}`;
791
+ let currentQueryKey = null;
792
+ let currentController = null;
793
+ let currentSubscription = null;
794
+ const dataSignal = (0, import_core7.signal)(void 0);
795
+ const errorSignal = (0, import_core7.signal)(void 0);
796
+ const loadingSignal = (0, import_core7.signal)(false);
797
+ const lastTriggerOptionsSignal = (0, import_core7.signal)(void 0);
798
+ const inputSignal = (0, import_core7.signal)({});
799
+ destroyRef.onDestroy(() => {
800
+ if (currentSubscription) {
801
+ currentSubscription();
802
+ }
803
+ });
804
+ const abort = () => {
805
+ currentController?.abort();
806
+ };
807
+ const trigger = async (triggerOptions) => {
808
+ const selectedEndpoint = captureSelector();
809
+ const params = triggerOptions?.params;
810
+ const pathSegments = selectedEndpoint.path.split("/").filter(Boolean);
811
+ (0, import_core8.resolvePath)(pathSegments, params);
812
+ const queryKey = stateManager.createQueryKey({
813
+ path: pathSegments,
814
+ method: selectedEndpoint.method,
815
+ options: triggerOptions
816
+ });
817
+ const needsNewController = !currentController || currentQueryKey !== queryKey;
818
+ if (needsNewController) {
819
+ if (currentSubscription) {
820
+ currentSubscription();
821
+ }
822
+ const controller = (0, import_core8.createOperationController)({
823
+ operationType: "read",
824
+ path: pathSegments,
825
+ method: selectedEndpoint.method,
826
+ tags: [],
827
+ stateManager,
828
+ eventEmitter,
829
+ pluginExecutor,
830
+ hookId,
831
+ requestOptions: triggerOptions,
832
+ fetchFn: async (fetchOpts) => {
833
+ const pathMethods = api(selectedEndpoint.path);
834
+ const method = pathMethods[selectedEndpoint.method];
835
+ return method(fetchOpts);
836
+ }
837
+ });
838
+ currentSubscription = controller.subscribe(() => {
839
+ const state = controller.getState();
840
+ dataSignal.set(state.data);
841
+ errorSignal.set(state.error);
842
+ });
843
+ currentController = controller;
844
+ currentQueryKey = queryKey;
845
+ }
846
+ lastTriggerOptionsSignal.set(triggerOptions);
847
+ const opts = triggerOptions;
848
+ const inputInner = {};
849
+ if (opts?.query !== void 0) {
850
+ inputInner.query = opts.query;
851
+ }
852
+ if (opts?.body !== void 0) {
853
+ inputInner.body = opts.body;
854
+ }
855
+ if (opts?.params !== void 0) {
856
+ inputInner.params = opts.params;
857
+ }
858
+ inputSignal.set(inputInner);
859
+ loadingSignal.set(true);
860
+ currentController.setPluginOptions(triggerOptions);
861
+ try {
862
+ const response = await currentController.execute(triggerOptions);
863
+ if (response.error) {
864
+ errorSignal.set(response.error);
865
+ } else {
866
+ errorSignal.set(void 0);
867
+ }
868
+ return response;
869
+ } catch (err) {
870
+ errorSignal.set(err);
871
+ throw err;
872
+ } finally {
873
+ loadingSignal.set(false);
874
+ }
875
+ };
876
+ const result = {
877
+ trigger,
878
+ input: inputSignal,
879
+ data: dataSignal,
880
+ error: errorSignal,
881
+ loading: loadingSignal,
767
882
  abort
768
883
  };
769
884
  return result;
@@ -791,6 +906,14 @@ function createAngularSpoosh(instance) {
791
906
  eventEmitter,
792
907
  pluginExecutor
793
908
  });
909
+ const injectLazyRead = createInjectLazyRead(
910
+ {
911
+ api,
912
+ stateManager,
913
+ eventEmitter,
914
+ pluginExecutor
915
+ }
916
+ );
794
917
  const instanceApiContext = {
795
918
  api,
796
919
  stateManager,
@@ -811,6 +934,7 @@ function createAngularSpoosh(instance) {
811
934
  injectRead,
812
935
  injectWrite,
813
936
  injectInfiniteRead,
937
+ injectLazyRead,
814
938
  ...instanceApis
815
939
  };
816
940
  }
package/dist/index.mjs CHANGED
@@ -252,7 +252,7 @@ function createInjectRead(options) {
252
252
  const abort = () => {
253
253
  currentController?.abort();
254
254
  };
255
- const refetch = () => {
255
+ const trigger = () => {
256
256
  if (currentController) {
257
257
  if (!isMounted) {
258
258
  currentController.mount();
@@ -272,7 +272,7 @@ function createInjectRead(options) {
272
272
  loading: loadingSignal,
273
273
  fetching: fetchingSignal,
274
274
  abort,
275
- refetch
275
+ trigger
276
276
  };
277
277
  return result;
278
278
  };
@@ -323,14 +323,6 @@ function createInjectWrite(options) {
323
323
  currentSubscription();
324
324
  }
325
325
  });
326
- const reset = () => {
327
- if (currentQueryKey) {
328
- stateManager.deleteCache(currentQueryKey);
329
- }
330
- dataSignal.set(void 0);
331
- errorSignal.set(void 0);
332
- loadingSignal.set(false);
333
- };
334
326
  const abort = () => {
335
327
  currentController?.abort();
336
328
  };
@@ -421,7 +413,6 @@ function createInjectWrite(options) {
421
413
  data: dataSignal,
422
414
  error: errorSignal,
423
415
  loading: loadingSignal,
424
- reset,
425
416
  abort
426
417
  };
427
418
  return result;
@@ -732,7 +723,7 @@ function createInjectInfiniteRead(options) {
732
723
  fetchingPrevSignal.set(false);
733
724
  }
734
725
  };
735
- const refetch = async () => {
726
+ const trigger = async () => {
736
727
  if (!currentController) return;
737
728
  if (!isMounted) {
738
729
  currentController.mount();
@@ -765,7 +756,135 @@ function createInjectInfiniteRead(options) {
765
756
  canFetchPrev: canFetchPrevSignal,
766
757
  fetchNext,
767
758
  fetchPrev,
768
- refetch,
759
+ trigger,
760
+ abort
761
+ };
762
+ return result;
763
+ };
764
+ }
765
+
766
+ // src/injectLazyRead/index.ts
767
+ import { signal as signal4, DestroyRef as DestroyRef4, inject as inject4 } from "@angular/core";
768
+ import {
769
+ createOperationController as createOperationController3,
770
+ createSelectorProxy as createSelectorProxy4,
771
+ resolvePath as resolvePath4
772
+ } from "@spoosh/core";
773
+ function createInjectLazyRead(options) {
774
+ const { api, stateManager, pluginExecutor, eventEmitter } = options;
775
+ return function injectLazyRead(readFn) {
776
+ const destroyRef = inject4(DestroyRef4);
777
+ const captureSelector = () => {
778
+ const selectorResult = {
779
+ call: null,
780
+ selector: null
781
+ };
782
+ const selectorProxy = createSelectorProxy4(
783
+ (result2) => {
784
+ selectorResult.call = result2.call;
785
+ selectorResult.selector = result2.selector;
786
+ }
787
+ );
788
+ readFn(selectorProxy);
789
+ if (!selectorResult.selector) {
790
+ throw new Error(
791
+ 'injectLazyRead requires selecting an HTTP method (GET). Example: injectLazyRead((api) => api("posts").GET)'
792
+ );
793
+ }
794
+ return selectorResult.selector;
795
+ };
796
+ const hookId = `angular-${Math.random().toString(36).slice(2)}`;
797
+ let currentQueryKey = null;
798
+ let currentController = null;
799
+ let currentSubscription = null;
800
+ const dataSignal = signal4(void 0);
801
+ const errorSignal = signal4(void 0);
802
+ const loadingSignal = signal4(false);
803
+ const lastTriggerOptionsSignal = signal4(void 0);
804
+ const inputSignal = signal4({});
805
+ destroyRef.onDestroy(() => {
806
+ if (currentSubscription) {
807
+ currentSubscription();
808
+ }
809
+ });
810
+ const abort = () => {
811
+ currentController?.abort();
812
+ };
813
+ const trigger = async (triggerOptions) => {
814
+ const selectedEndpoint = captureSelector();
815
+ const params = triggerOptions?.params;
816
+ const pathSegments = selectedEndpoint.path.split("/").filter(Boolean);
817
+ resolvePath4(pathSegments, params);
818
+ const queryKey = stateManager.createQueryKey({
819
+ path: pathSegments,
820
+ method: selectedEndpoint.method,
821
+ options: triggerOptions
822
+ });
823
+ const needsNewController = !currentController || currentQueryKey !== queryKey;
824
+ if (needsNewController) {
825
+ if (currentSubscription) {
826
+ currentSubscription();
827
+ }
828
+ const controller = createOperationController3({
829
+ operationType: "read",
830
+ path: pathSegments,
831
+ method: selectedEndpoint.method,
832
+ tags: [],
833
+ stateManager,
834
+ eventEmitter,
835
+ pluginExecutor,
836
+ hookId,
837
+ requestOptions: triggerOptions,
838
+ fetchFn: async (fetchOpts) => {
839
+ const pathMethods = api(selectedEndpoint.path);
840
+ const method = pathMethods[selectedEndpoint.method];
841
+ return method(fetchOpts);
842
+ }
843
+ });
844
+ currentSubscription = controller.subscribe(() => {
845
+ const state = controller.getState();
846
+ dataSignal.set(state.data);
847
+ errorSignal.set(state.error);
848
+ });
849
+ currentController = controller;
850
+ currentQueryKey = queryKey;
851
+ }
852
+ lastTriggerOptionsSignal.set(triggerOptions);
853
+ const opts = triggerOptions;
854
+ const inputInner = {};
855
+ if (opts?.query !== void 0) {
856
+ inputInner.query = opts.query;
857
+ }
858
+ if (opts?.body !== void 0) {
859
+ inputInner.body = opts.body;
860
+ }
861
+ if (opts?.params !== void 0) {
862
+ inputInner.params = opts.params;
863
+ }
864
+ inputSignal.set(inputInner);
865
+ loadingSignal.set(true);
866
+ currentController.setPluginOptions(triggerOptions);
867
+ try {
868
+ const response = await currentController.execute(triggerOptions);
869
+ if (response.error) {
870
+ errorSignal.set(response.error);
871
+ } else {
872
+ errorSignal.set(void 0);
873
+ }
874
+ return response;
875
+ } catch (err) {
876
+ errorSignal.set(err);
877
+ throw err;
878
+ } finally {
879
+ loadingSignal.set(false);
880
+ }
881
+ };
882
+ const result = {
883
+ trigger,
884
+ input: inputSignal,
885
+ data: dataSignal,
886
+ error: errorSignal,
887
+ loading: loadingSignal,
769
888
  abort
770
889
  };
771
890
  return result;
@@ -793,6 +912,14 @@ function createAngularSpoosh(instance) {
793
912
  eventEmitter,
794
913
  pluginExecutor
795
914
  });
915
+ const injectLazyRead = createInjectLazyRead(
916
+ {
917
+ api,
918
+ stateManager,
919
+ eventEmitter,
920
+ pluginExecutor
921
+ }
922
+ );
796
923
  const instanceApiContext = {
797
924
  api,
798
925
  stateManager,
@@ -813,6 +940,7 @@ function createAngularSpoosh(instance) {
813
940
  injectRead,
814
941
  injectWrite,
815
942
  injectInfiniteRead,
943
+ injectLazyRead,
816
944
  ...instanceApis
817
945
  };
818
946
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoosh/angular",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "description": "Angular signals integration for Spoosh API client",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  "@angular/core": ">=16.0.0"
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",