@spoosh/angular 0.10.2 → 0.11.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.mjs DELETED
@@ -1,950 +0,0 @@
1
- // src/injectRead/index.ts
2
- import {
3
- signal,
4
- effect,
5
- DestroyRef,
6
- inject,
7
- untracked
8
- } from "@angular/core";
9
- import {
10
- createOperationController,
11
- createSelectorProxy,
12
- resolvePath,
13
- resolveTags
14
- } from "@spoosh/core";
15
- function createInjectRead(options) {
16
- const { api, stateManager, eventEmitter, pluginExecutor } = options;
17
- return function injectRead(readFn, readOptions) {
18
- const destroyRef = inject(DestroyRef);
19
- const {
20
- enabled: enabledOption = true,
21
- tags,
22
- ...pluginOpts
23
- } = readOptions ?? {};
24
- const getEnabled = () => typeof enabledOption === "function" ? enabledOption() : enabledOption;
25
- const dataSignal = signal(void 0);
26
- const errorSignal = signal(void 0);
27
- const loadingSignal = signal(true);
28
- const fetchingSignal = signal(false);
29
- const inputSignal = signal({});
30
- const metaSignal = signal({});
31
- let currentController = null;
32
- let currentQueryKey = null;
33
- let baseQueryKey = null;
34
- let currentSubscription = null;
35
- let currentResolvedTags = [];
36
- let prevContext = null;
37
- let isMounted = false;
38
- const instanceId = `angular-${Math.random().toString(36).slice(2)}`;
39
- const captureSelector = () => {
40
- const selectorResult = {
41
- call: null,
42
- selector: null
43
- };
44
- const selectorProxy = createSelectorProxy(
45
- (result2) => {
46
- selectorResult.call = result2.call;
47
- selectorResult.selector = result2.selector;
48
- }
49
- );
50
- readFn(selectorProxy);
51
- return selectorResult;
52
- };
53
- const createController = (capturedCall, resolvedTags, queryKey) => {
54
- if (currentSubscription) {
55
- currentSubscription();
56
- }
57
- const controller = createOperationController({
58
- operationType: "read",
59
- path: capturedCall.path,
60
- method: capturedCall.method,
61
- tags: resolvedTags,
62
- requestOptions: capturedCall.options,
63
- stateManager,
64
- eventEmitter,
65
- pluginExecutor,
66
- instanceId,
67
- fetchFn: async (fetchOpts) => {
68
- const pathMethods = api(capturedCall.path);
69
- const method = pathMethods[capturedCall.method];
70
- return method(fetchOpts);
71
- }
72
- });
73
- controller.setPluginOptions(pluginOpts);
74
- currentSubscription = controller.subscribe(() => {
75
- const state = controller.getState();
76
- dataSignal.set(state.data);
77
- errorSignal.set(state.error);
78
- const entry = stateManager.getCache(queryKey);
79
- const newMeta = entry?.meta ? Object.fromEntries(entry.meta) : {};
80
- metaSignal.set(newMeta);
81
- });
82
- currentController = controller;
83
- currentQueryKey = queryKey;
84
- currentResolvedTags = resolvedTags;
85
- return controller;
86
- };
87
- const executeWithTracking = async (controller, force = false, overrideOptions) => {
88
- const hasData = dataSignal() !== void 0;
89
- loadingSignal.set(!hasData);
90
- fetchingSignal.set(true);
91
- errorSignal.set(void 0);
92
- try {
93
- const execOptions = overrideOptions ? {
94
- ...currentController?.getContext().request,
95
- ...overrideOptions
96
- } : void 0;
97
- const response = await controller.execute(execOptions, { force });
98
- if (response.error) {
99
- errorSignal.set(response.error);
100
- } else {
101
- errorSignal.set(void 0);
102
- }
103
- if (response.data !== void 0) {
104
- dataSignal.set(response.data);
105
- }
106
- return response;
107
- } catch (err) {
108
- errorSignal.set(err);
109
- return { error: err };
110
- } finally {
111
- loadingSignal.set(false);
112
- fetchingSignal.set(false);
113
- }
114
- };
115
- const initialSelectorResult = captureSelector();
116
- const initialCapturedCall = initialSelectorResult.call;
117
- if (!initialCapturedCall) {
118
- throw new Error(
119
- 'injectRead requires calling an HTTP method (GET). Example: injectRead((api) => api("posts").GET())'
120
- );
121
- }
122
- const initialRequestOptions = initialCapturedCall.options;
123
- const initialPathSegments = initialCapturedCall.path.split("/").filter(Boolean);
124
- const initialResolvedPath = resolvePath(
125
- initialPathSegments,
126
- initialRequestOptions?.params
127
- );
128
- const initialResolvedTags = resolveTags(
129
- tags !== void 0 ? { tags } : void 0,
130
- initialResolvedPath
131
- );
132
- const initialQueryKey = stateManager.createQueryKey({
133
- path: initialCapturedCall.path,
134
- method: initialCapturedCall.method,
135
- options: initialCapturedCall.options
136
- });
137
- createController(initialCapturedCall, initialResolvedTags, initialQueryKey);
138
- baseQueryKey = initialQueryKey;
139
- loadingSignal.set(false);
140
- let wasEnabled = false;
141
- effect(
142
- () => {
143
- const isEnabled = getEnabled();
144
- const selectorResult = captureSelector();
145
- const capturedCall = selectorResult.call;
146
- if (!capturedCall) {
147
- throw new Error(
148
- 'injectRead requires calling an HTTP method (GET). Example: injectRead((api) => api("posts").GET())'
149
- );
150
- }
151
- const requestOptions = capturedCall.options;
152
- const pathSegments = capturedCall.path.split("/").filter(Boolean);
153
- const resolvedPath = resolvePath(pathSegments, requestOptions?.params);
154
- const resolvedTags = resolveTags(
155
- tags !== void 0 ? { tags } : void 0,
156
- resolvedPath
157
- );
158
- const queryKey = stateManager.createQueryKey({
159
- path: capturedCall.path,
160
- method: capturedCall.method,
161
- options: capturedCall.options
162
- });
163
- const opts = capturedCall.options;
164
- const inputInner = {};
165
- if (opts?.query !== void 0) {
166
- inputInner.query = opts.query;
167
- }
168
- if (opts?.body !== void 0) {
169
- inputInner.body = opts.body;
170
- }
171
- if (opts?.params !== void 0) {
172
- inputInner.params = opts.params;
173
- }
174
- inputSignal.set(inputInner);
175
- const baseQueryKeyChanged = queryKey !== baseQueryKey;
176
- const enabledChanged = isEnabled !== wasEnabled;
177
- wasEnabled = isEnabled;
178
- if (baseQueryKeyChanged) {
179
- baseQueryKey = queryKey;
180
- if (currentController) {
181
- prevContext = currentController.getContext();
182
- if (isMounted) {
183
- currentController.unmount();
184
- isMounted = false;
185
- }
186
- }
187
- const controller = createController(
188
- capturedCall,
189
- resolvedTags,
190
- queryKey
191
- );
192
- if (prevContext) {
193
- controller.update(prevContext);
194
- prevContext = null;
195
- }
196
- if (isEnabled) {
197
- controller.mount();
198
- isMounted = true;
199
- untracked(() => {
200
- executeWithTracking(controller, false);
201
- });
202
- } else {
203
- loadingSignal.set(false);
204
- }
205
- } else if (enabledChanged && currentController) {
206
- if (isEnabled && !isMounted) {
207
- currentController.mount();
208
- isMounted = true;
209
- untracked(() => {
210
- executeWithTracking(currentController, false);
211
- });
212
- } else if (!isEnabled && isMounted) {
213
- currentController.unmount();
214
- isMounted = false;
215
- }
216
- }
217
- if (!isEnabled) {
218
- loadingSignal.set(false);
219
- return;
220
- }
221
- const unsubRefetch = eventEmitter.on(
222
- "refetch",
223
- (event) => {
224
- if (event.queryKey === currentQueryKey && currentController) {
225
- untracked(() => {
226
- executeWithTracking(currentController, true);
227
- });
228
- }
229
- }
230
- );
231
- const unsubInvalidate = eventEmitter.on(
232
- "invalidate",
233
- (invalidatedTags) => {
234
- const hasMatch = invalidatedTags.some(
235
- (tag) => currentResolvedTags.includes(tag)
236
- );
237
- if (hasMatch && currentController) {
238
- untracked(() => {
239
- executeWithTracking(currentController, true);
240
- });
241
- }
242
- }
243
- );
244
- const unsubRefetchAll = eventEmitter.on("refetchAll", () => {
245
- if (currentController) {
246
- untracked(() => {
247
- executeWithTracking(currentController, true);
248
- });
249
- }
250
- });
251
- return () => {
252
- unsubRefetch();
253
- unsubInvalidate();
254
- unsubRefetchAll();
255
- };
256
- },
257
- { allowSignalWrites: true }
258
- );
259
- destroyRef.onDestroy(() => {
260
- if (currentSubscription) {
261
- currentSubscription();
262
- }
263
- if (currentController && isMounted) {
264
- currentController.unmount();
265
- }
266
- });
267
- const abort = () => {
268
- currentController?.abort();
269
- };
270
- const trigger = async (triggerOptions) => {
271
- const { force = true, ...overrideOptions } = triggerOptions ?? {};
272
- const hasOverrides = Object.keys(overrideOptions).length > 0;
273
- if (!hasOverrides) {
274
- if (!currentController) {
275
- return Promise.resolve({ data: void 0, error: void 0 });
276
- }
277
- return executeWithTracking(currentController, force, void 0);
278
- }
279
- const selectorResult = captureSelector();
280
- const capturedCall = selectorResult.call;
281
- if (!capturedCall) {
282
- return Promise.resolve({ data: void 0, error: void 0 });
283
- }
284
- const mergedOptions = {
285
- ...capturedCall.options ?? {},
286
- ...overrideOptions
287
- };
288
- const pathSegments = capturedCall.path.split("/").filter(Boolean);
289
- const newQueryKey = stateManager.createQueryKey({
290
- path: capturedCall.path,
291
- method: capturedCall.method,
292
- options: mergedOptions
293
- });
294
- if (newQueryKey === currentQueryKey && currentController) {
295
- return executeWithTracking(currentController, force, overrideOptions);
296
- }
297
- const params = mergedOptions?.params;
298
- const newResolvedPath = resolvePath(pathSegments, params);
299
- const newResolvedTags = resolveTags(
300
- tags !== void 0 ? { tags } : void 0,
301
- newResolvedPath
302
- );
303
- const newController = createController(
304
- { ...capturedCall, options: mergedOptions },
305
- newResolvedTags,
306
- newQueryKey
307
- );
308
- newController.mount();
309
- isMounted = true;
310
- return executeWithTracking(newController, force, void 0);
311
- };
312
- const result = {
313
- meta: metaSignal,
314
- get input() {
315
- return inputSignal();
316
- },
317
- data: dataSignal,
318
- error: errorSignal,
319
- loading: loadingSignal,
320
- fetching: fetchingSignal,
321
- abort,
322
- trigger
323
- };
324
- return result;
325
- };
326
- }
327
-
328
- // src/injectWrite/index.ts
329
- import { signal as signal2, effect as effect2, DestroyRef as DestroyRef2, inject as inject2 } from "@angular/core";
330
- import {
331
- createOperationController as createOperationController2,
332
- createSelectorProxy as createSelectorProxy2,
333
- resolvePath as resolvePath2,
334
- resolveTags as resolveTags2
335
- } from "@spoosh/core";
336
- function createInjectWrite(options) {
337
- const { api, stateManager, pluginExecutor, eventEmitter } = options;
338
- function injectWrite(writeFn, writeOptions) {
339
- const destroyRef = inject2(DestroyRef2);
340
- const captureSelector = () => {
341
- const selectorResult = {
342
- call: null,
343
- selector: null
344
- };
345
- const selectorProxy = createSelectorProxy2(
346
- (result2) => {
347
- selectorResult.call = result2.call;
348
- selectorResult.selector = result2.selector;
349
- }
350
- );
351
- writeFn(selectorProxy);
352
- if (!selectorResult.call) {
353
- throw new Error(
354
- 'injectWrite requires calling an HTTP method (POST, PUT, PATCH, DELETE). Example: injectWrite((api) => api("posts").POST())'
355
- );
356
- }
357
- return selectorResult.call;
358
- };
359
- const instanceId = `angular-${Math.random().toString(36).slice(2)}`;
360
- let currentQueryKey = null;
361
- let currentController = null;
362
- let currentSubscription = null;
363
- const dataSignal = signal2(void 0);
364
- const errorSignal = signal2(void 0);
365
- const loadingSignal = signal2(false);
366
- const lastTriggerOptionsSignal = signal2(void 0);
367
- const metaSignal = signal2({});
368
- destroyRef.onDestroy(() => {
369
- if (currentSubscription) {
370
- currentSubscription();
371
- }
372
- });
373
- const abort = () => {
374
- currentController?.abort();
375
- };
376
- const trigger = async (triggerOptions) => {
377
- const selectedEndpoint = captureSelector();
378
- const params = triggerOptions?.params;
379
- const pathSegments = selectedEndpoint.path.split("/").filter(Boolean);
380
- const resolvedPath = resolvePath2(pathSegments, params);
381
- const tags = resolveTags2(triggerOptions, resolvedPath);
382
- const queryKey = stateManager.createQueryKey({
383
- path: selectedEndpoint.path,
384
- method: selectedEndpoint.method,
385
- options: triggerOptions
386
- });
387
- const needsNewController = !currentController || currentQueryKey !== queryKey;
388
- if (needsNewController) {
389
- if (currentSubscription) {
390
- currentSubscription();
391
- }
392
- const controller = createOperationController2({
393
- operationType: "write",
394
- path: selectedEndpoint.path,
395
- method: selectedEndpoint.method,
396
- tags,
397
- stateManager,
398
- eventEmitter,
399
- pluginExecutor,
400
- instanceId,
401
- fetchFn: async (fetchOpts) => {
402
- const pathMethods = api(selectedEndpoint.path);
403
- const method = pathMethods[selectedEndpoint.method];
404
- return method(fetchOpts);
405
- }
406
- });
407
- currentSubscription = controller.subscribe(() => {
408
- const state = controller.getState();
409
- dataSignal.set(state.data);
410
- errorSignal.set(state.error);
411
- const entry = stateManager.getCache(queryKey);
412
- const newMeta = entry?.meta ? Object.fromEntries(entry.meta) : {};
413
- metaSignal.set(newMeta);
414
- });
415
- currentController = controller;
416
- currentQueryKey = queryKey;
417
- }
418
- lastTriggerOptionsSignal.set(triggerOptions);
419
- loadingSignal.set(true);
420
- errorSignal.set(void 0);
421
- const mergedOptions = { ...writeOptions, ...triggerOptions, tags };
422
- currentController.setPluginOptions(mergedOptions);
423
- try {
424
- const response = await currentController.execute(triggerOptions, {
425
- force: true
426
- });
427
- if (response.error) {
428
- errorSignal.set(response.error);
429
- } else {
430
- errorSignal.set(void 0);
431
- }
432
- return response;
433
- } catch (err) {
434
- errorSignal.set(err);
435
- return { error: err };
436
- } finally {
437
- loadingSignal.set(false);
438
- }
439
- };
440
- const inputSignal = signal2({});
441
- effect2(
442
- () => {
443
- const opts = lastTriggerOptionsSignal();
444
- const inputInner = {};
445
- if (opts?.query !== void 0) {
446
- inputInner.query = opts.query;
447
- }
448
- if (opts?.body !== void 0) {
449
- inputInner.body = opts.body;
450
- }
451
- if (opts?.params !== void 0) {
452
- inputInner.params = opts.params;
453
- }
454
- inputSignal.set(inputInner);
455
- },
456
- { allowSignalWrites: true }
457
- );
458
- const result = {
459
- trigger,
460
- meta: metaSignal,
461
- input: inputSignal,
462
- data: dataSignal,
463
- error: errorSignal,
464
- loading: loadingSignal,
465
- abort
466
- };
467
- return result;
468
- }
469
- return injectWrite;
470
- }
471
-
472
- // src/injectPages/index.ts
473
- import {
474
- signal as signal3,
475
- computed,
476
- effect as effect3,
477
- DestroyRef as DestroyRef3,
478
- inject as inject3,
479
- untracked as untracked2
480
- } from "@angular/core";
481
- import {
482
- createInfiniteReadController,
483
- createSelectorProxy as createSelectorProxy3,
484
- resolvePath as resolvePath3,
485
- resolveTags as resolveTags3
486
- } from "@spoosh/core";
487
- function createInjectPages(options) {
488
- const { api, stateManager, eventEmitter, pluginExecutor } = options;
489
- return function injectPages(readFn, readOptions) {
490
- const destroyRef = inject3(DestroyRef3);
491
- const {
492
- enabled: enabledOption = true,
493
- tags,
494
- canFetchNext,
495
- nextPageRequest,
496
- merger,
497
- canFetchPrev,
498
- prevPageRequest,
499
- ...pluginOpts
500
- } = readOptions;
501
- const getEnabled = () => typeof enabledOption === "function" ? enabledOption() : enabledOption;
502
- const callbackRefs = {
503
- canFetchNext,
504
- canFetchPrev,
505
- nextPageRequest,
506
- prevPageRequest,
507
- merger
508
- };
509
- const captureSelector = () => {
510
- const selectorResult = {
511
- call: null,
512
- selector: null
513
- };
514
- const selectorProxy = createSelectorProxy3(
515
- (result2) => {
516
- selectorResult.call = result2.call;
517
- selectorResult.selector = result2.selector;
518
- }
519
- );
520
- readFn(selectorProxy);
521
- if (!selectorResult.call) {
522
- throw new Error(
523
- 'injectPages requires calling an HTTP method (GET). Example: injectPages((api) => api("posts").GET())'
524
- );
525
- }
526
- return selectorResult.call;
527
- };
528
- const instanceId = `angular-${Math.random().toString(36).slice(2)}`;
529
- const dataSignal = signal3(void 0);
530
- const pagesSignal = signal3([]);
531
- const errorSignal = signal3(void 0);
532
- const loadingSignal = signal3(false);
533
- const canFetchNextSignal = signal3(false);
534
- const canFetchPrevSignal = signal3(false);
535
- const fetchingNextSignal = signal3(false);
536
- const fetchingPrevSignal = signal3(false);
537
- let currentController = null;
538
- let currentQueryKey = null;
539
- let currentSubscription = null;
540
- let currentResolvedTags = [];
541
- let prevContext = null;
542
- let isMounted = false;
543
- let unsubInvalidate = null;
544
- let unsubRefetchAll = null;
545
- const updateSignalsFromState = () => {
546
- if (!currentController) return;
547
- const state = currentController.getState();
548
- dataSignal.set(state.data);
549
- pagesSignal.set(
550
- state.pages
551
- );
552
- errorSignal.set(state.error);
553
- canFetchNextSignal.set(state.canFetchNext);
554
- canFetchPrevSignal.set(state.canFetchPrev);
555
- };
556
- const triggerFetch = () => {
557
- if (!currentController) return;
558
- const currentState = currentController.getState();
559
- const isFetching = untracked2(
560
- () => fetchingNextSignal() || fetchingPrevSignal()
561
- );
562
- if (currentState.data === void 0 && !isFetching) {
563
- loadingSignal.set(true);
564
- fetchingNextSignal.set(true);
565
- errorSignal.set(void 0);
566
- currentController.fetchNext().finally(() => {
567
- updateSignalsFromState();
568
- loadingSignal.set(false);
569
- fetchingNextSignal.set(false);
570
- });
571
- } else if (currentState.data !== void 0) {
572
- updateSignalsFromState();
573
- }
574
- };
575
- const createController = (capturedCall, resolvedTags, queryKey) => {
576
- if (currentSubscription) {
577
- currentSubscription();
578
- }
579
- if (unsubInvalidate) {
580
- unsubInvalidate();
581
- }
582
- if (unsubRefetchAll) {
583
- unsubRefetchAll();
584
- }
585
- const requestOptions = capturedCall.options;
586
- const initialRequest = {
587
- query: requestOptions?.query,
588
- params: requestOptions?.params,
589
- body: requestOptions?.body
590
- };
591
- const controller = createInfiniteReadController({
592
- path: capturedCall.path,
593
- method: capturedCall.method,
594
- tags: resolvedTags,
595
- initialRequest,
596
- canFetchNext: canFetchNext ? (ctx) => callbackRefs.canFetchNext?.(ctx) ?? false : void 0,
597
- canFetchPrev: canFetchPrev ? (ctx) => callbackRefs.canFetchPrev?.(ctx) ?? false : void 0,
598
- nextPageRequest: nextPageRequest ? (ctx) => callbackRefs.nextPageRequest?.(ctx) ?? {} : void 0,
599
- prevPageRequest: prevPageRequest ? (ctx) => callbackRefs.prevPageRequest?.(ctx) ?? {} : void 0,
600
- merger: (pages) => callbackRefs.merger(pages),
601
- stateManager,
602
- eventEmitter,
603
- pluginExecutor,
604
- instanceId,
605
- fetchFn: async (opts, abortSignal) => {
606
- const pathMethods = api(capturedCall.path);
607
- const method = pathMethods[capturedCall.method];
608
- const fetchOptions = {
609
- ...capturedCall.options,
610
- ...opts,
611
- signal: abortSignal
612
- };
613
- return method(fetchOptions);
614
- }
615
- });
616
- controller.setPluginOptions(pluginOpts);
617
- currentSubscription = controller.subscribe(() => {
618
- const state = controller.getState();
619
- dataSignal.set(state.data);
620
- pagesSignal.set(
621
- state.pages
622
- );
623
- errorSignal.set(state.error);
624
- canFetchNextSignal.set(state.canFetchNext);
625
- canFetchPrevSignal.set(state.canFetchPrev);
626
- });
627
- currentController = controller;
628
- currentQueryKey = queryKey;
629
- currentResolvedTags = resolvedTags;
630
- unsubInvalidate = eventEmitter.on(
631
- "invalidate",
632
- (invalidatedTags) => {
633
- if (!getEnabled() || !currentController) return;
634
- const hasMatch = invalidatedTags.some(
635
- (tag) => currentResolvedTags.includes(tag)
636
- );
637
- if (hasMatch) {
638
- loadingSignal.set(true);
639
- currentController.trigger().finally(() => {
640
- updateSignalsFromState();
641
- loadingSignal.set(false);
642
- });
643
- }
644
- }
645
- );
646
- unsubRefetchAll = eventEmitter.on("refetchAll", () => {
647
- if (!getEnabled() || !currentController) return;
648
- loadingSignal.set(true);
649
- currentController.trigger().finally(() => {
650
- updateSignalsFromState();
651
- loadingSignal.set(false);
652
- });
653
- });
654
- return controller;
655
- };
656
- const initialCapturedCall = captureSelector();
657
- const initialRequestOptions = initialCapturedCall.options;
658
- const initialPathSegments = initialCapturedCall.path.split("/").filter(Boolean);
659
- const initialResolvedPath = resolvePath3(
660
- initialPathSegments,
661
- initialRequestOptions?.params
662
- );
663
- const initialResolvedTags = resolveTags3(
664
- tags !== void 0 ? { tags } : void 0,
665
- initialResolvedPath
666
- );
667
- const initialQueryKey = stateManager.createQueryKey({
668
- path: initialCapturedCall.path,
669
- method: initialCapturedCall.method,
670
- options: initialCapturedCall.options
671
- });
672
- createController(initialCapturedCall, initialResolvedTags, initialQueryKey);
673
- effect3(
674
- () => {
675
- const isEnabled = getEnabled();
676
- const capturedCall = captureSelector();
677
- const requestOptions = capturedCall.options;
678
- const pathSegments = capturedCall.path.split("/").filter(Boolean);
679
- const resolvedPath = resolvePath3(pathSegments, requestOptions?.params);
680
- const resolvedTags = resolveTags3(
681
- tags !== void 0 ? { tags } : void 0,
682
- resolvedPath
683
- );
684
- const queryKey = stateManager.createQueryKey({
685
- path: capturedCall.path,
686
- method: capturedCall.method,
687
- options: capturedCall.options
688
- });
689
- const queryKeyChanged = queryKey !== currentQueryKey;
690
- if (!isEnabled) {
691
- if (isMounted && currentController) {
692
- currentController.unmount();
693
- isMounted = false;
694
- }
695
- loadingSignal.set(false);
696
- return;
697
- }
698
- if (queryKeyChanged) {
699
- if (currentController && isMounted) {
700
- prevContext = currentController.getContext();
701
- currentController.unmount();
702
- isMounted = false;
703
- }
704
- const controller = createController(
705
- capturedCall,
706
- resolvedTags,
707
- queryKey
708
- );
709
- if (prevContext) {
710
- controller.update(prevContext);
711
- prevContext = null;
712
- }
713
- controller.mount();
714
- isMounted = true;
715
- untracked2(() => {
716
- loadingSignal.set(true);
717
- fetchingNextSignal.set(true);
718
- errorSignal.set(void 0);
719
- controller.trigger({ force: false }).finally(() => {
720
- updateSignalsFromState();
721
- loadingSignal.set(false);
722
- fetchingNextSignal.set(false);
723
- });
724
- });
725
- } else if (!isMounted && currentController) {
726
- currentController.mount();
727
- isMounted = true;
728
- untracked2(() => {
729
- triggerFetch();
730
- });
731
- }
732
- },
733
- { allowSignalWrites: true }
734
- );
735
- destroyRef.onDestroy(() => {
736
- if (unsubInvalidate) {
737
- unsubInvalidate();
738
- }
739
- if (unsubRefetchAll) {
740
- unsubRefetchAll();
741
- }
742
- if (currentSubscription) {
743
- currentSubscription();
744
- }
745
- if (currentController && isMounted) {
746
- currentController.unmount();
747
- }
748
- });
749
- const fetchNext = async () => {
750
- if (!currentController) return;
751
- if (!isMounted) {
752
- currentController.mount();
753
- isMounted = true;
754
- }
755
- fetchingNextSignal.set(true);
756
- try {
757
- await currentController.fetchNext();
758
- updateSignalsFromState();
759
- } finally {
760
- fetchingNextSignal.set(false);
761
- }
762
- };
763
- const fetchPrev = async () => {
764
- if (!currentController) return;
765
- if (!isMounted) {
766
- currentController.mount();
767
- isMounted = true;
768
- }
769
- fetchingPrevSignal.set(true);
770
- try {
771
- await currentController.fetchPrev();
772
- updateSignalsFromState();
773
- } finally {
774
- fetchingPrevSignal.set(false);
775
- }
776
- };
777
- const trigger = async (options2) => {
778
- if (!currentController) return;
779
- if (!isMounted) {
780
- currentController.mount();
781
- isMounted = true;
782
- }
783
- loadingSignal.set(true);
784
- try {
785
- await currentController.trigger(options2);
786
- updateSignalsFromState();
787
- } finally {
788
- loadingSignal.set(false);
789
- }
790
- };
791
- const abort = () => {
792
- currentController?.abort();
793
- };
794
- const fetchingSignal = computed(
795
- () => fetchingNextSignal() || fetchingPrevSignal()
796
- );
797
- const result = {
798
- data: dataSignal,
799
- pages: pagesSignal,
800
- error: errorSignal,
801
- loading: loadingSignal,
802
- fetching: fetchingSignal,
803
- fetchingNext: fetchingNextSignal,
804
- fetchingPrev: fetchingPrevSignal,
805
- canFetchNext: canFetchNextSignal,
806
- canFetchPrev: canFetchPrevSignal,
807
- fetchNext,
808
- fetchPrev,
809
- trigger,
810
- abort
811
- };
812
- return result;
813
- };
814
- }
815
-
816
- // src/injectQueue/index.ts
817
- import { signal as signal4, DestroyRef as DestroyRef4, inject as inject4 } from "@angular/core";
818
- import {
819
- createSelectorProxy as createSelectorProxy4,
820
- createQueueController
821
- } from "@spoosh/core";
822
- function createInjectQueue(options) {
823
- const { api, stateManager, pluginExecutor, eventEmitter } = options;
824
- function injectQueue(queueFn, queueOptions) {
825
- const destroyRef = inject4(DestroyRef4);
826
- const captureSelector = () => {
827
- const selectorResult = {
828
- call: null,
829
- selector: null
830
- };
831
- const selectorProxy = createSelectorProxy4(
832
- (result) => {
833
- selectorResult.call = result.call;
834
- selectorResult.selector = result.selector;
835
- }
836
- );
837
- queueFn(selectorProxy);
838
- const captured = selectorResult.call ?? selectorResult.selector;
839
- if (!captured) {
840
- throw new Error(
841
- 'injectQueue requires selecting an HTTP method. Example: injectQueue((api) => api("uploads").POST())'
842
- );
843
- }
844
- return captured;
845
- };
846
- const selectedEndpoint = captureSelector();
847
- const { concurrency, ...hookOptions } = queueOptions ?? {};
848
- const config = {
849
- path: selectedEndpoint.path,
850
- method: selectedEndpoint.method,
851
- concurrency,
852
- operationType: "queue",
853
- hookOptions
854
- };
855
- const controller = createQueueController(config, {
856
- api,
857
- stateManager,
858
- eventEmitter,
859
- pluginExecutor
860
- });
861
- const tasksSignal = signal4(controller.getQueue());
862
- const statsSignal = signal4(controller.getStats());
863
- const unsubscribe = controller.subscribe(() => {
864
- tasksSignal.set(controller.getQueue());
865
- statsSignal.set(controller.getStats());
866
- });
867
- destroyRef.onDestroy(() => {
868
- unsubscribe();
869
- controller.clear();
870
- });
871
- const trigger = (input) => {
872
- return controller.trigger(input ?? {});
873
- };
874
- return {
875
- trigger,
876
- tasks: tasksSignal.asReadonly(),
877
- stats: statsSignal.asReadonly(),
878
- abort: controller.abort,
879
- retry: controller.retry,
880
- remove: controller.remove,
881
- removeSettled: controller.removeSettled,
882
- clear: controller.clear,
883
- setConcurrency: controller.setConcurrency
884
- };
885
- }
886
- return injectQueue;
887
- }
888
-
889
- // src/create/index.ts
890
- function create(instance) {
891
- const { api, stateManager, eventEmitter, pluginExecutor } = instance;
892
- const injectRead = createInjectRead({
893
- api,
894
- stateManager,
895
- eventEmitter,
896
- pluginExecutor
897
- });
898
- const injectWrite = createInjectWrite({
899
- api,
900
- stateManager,
901
- eventEmitter,
902
- pluginExecutor
903
- });
904
- const injectPages = createInjectPages({
905
- api,
906
- stateManager,
907
- eventEmitter,
908
- pluginExecutor
909
- });
910
- const injectQueue = createInjectQueue({
911
- api,
912
- stateManager,
913
- eventEmitter,
914
- pluginExecutor
915
- });
916
- const plugins = pluginExecutor.getPlugins();
917
- const setupContext = {
918
- stateManager,
919
- eventEmitter,
920
- pluginExecutor
921
- };
922
- for (const plugin of plugins) {
923
- plugin.setup?.(setupContext);
924
- }
925
- const instanceApiContext = {
926
- api,
927
- stateManager,
928
- eventEmitter,
929
- pluginExecutor
930
- };
931
- const instanceApis = plugins.reduce(
932
- (acc, plugin) => {
933
- if (plugin.instanceApi) {
934
- return { ...acc, ...plugin.instanceApi(instanceApiContext) };
935
- }
936
- return acc;
937
- },
938
- {}
939
- );
940
- return {
941
- injectRead,
942
- injectWrite,
943
- injectPages,
944
- injectQueue,
945
- ...instanceApis
946
- };
947
- }
948
- export {
949
- create
950
- };