@ametie/vue-muza-use 1.0.0 → 1.1.1
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 +5 -3
- package/dist/index.cjs +74 -19
- package/dist/index.d.cts +33 -7
- package/dist/index.d.ts +33 -7
- package/dist/index.mjs +74 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -798,13 +798,15 @@ const { data } = useApi('/messages', {
|
|
|
798
798
|
Apply to all `useApi` instances at once:
|
|
799
799
|
|
|
800
800
|
```typescript
|
|
801
|
-
createApiClient({
|
|
802
|
-
|
|
801
|
+
const api = createApiClient({ baseURL: 'https://api.example.com' })
|
|
802
|
+
|
|
803
|
+
app.use(createApi({
|
|
804
|
+
axios: api,
|
|
803
805
|
globalOptions: {
|
|
804
806
|
refetchOnFocus: true,
|
|
805
807
|
refetchOnReconnect: true,
|
|
806
808
|
},
|
|
807
|
-
})
|
|
809
|
+
}))
|
|
808
810
|
```
|
|
809
811
|
|
|
810
812
|
Opt individual requests out with `refetchOnFocus: false`:
|
package/dist/index.cjs
CHANGED
|
@@ -374,6 +374,16 @@ function useApi(url, options = {}) {
|
|
|
374
374
|
if (abortController2.value) abortController2.value.abort("Cancelled by new request");
|
|
375
375
|
const controller = new AbortController();
|
|
376
376
|
abortController2.value = controller;
|
|
377
|
+
if (config?.signal) {
|
|
378
|
+
const signal = config.signal;
|
|
379
|
+
if (signal.aborted) {
|
|
380
|
+
controller.abort(signal.reason);
|
|
381
|
+
} else {
|
|
382
|
+
signal.addEventListener("abort", () => {
|
|
383
|
+
controller.abort(signal.reason);
|
|
384
|
+
}, { once: true });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
377
387
|
let globalAbortHandler = null;
|
|
378
388
|
let subscribedSignal = null;
|
|
379
389
|
if (globalAbort) {
|
|
@@ -608,6 +618,8 @@ function useApiBatch(requests, options = {}) {
|
|
|
608
618
|
concurrency,
|
|
609
619
|
immediate = false,
|
|
610
620
|
skipErrorNotification = true,
|
|
621
|
+
lazy = false,
|
|
622
|
+
poll = 0,
|
|
611
623
|
watch: watchSource,
|
|
612
624
|
onItemSuccess,
|
|
613
625
|
onItemError,
|
|
@@ -632,13 +644,21 @@ function useApiBatch(requests, options = {}) {
|
|
|
632
644
|
);
|
|
633
645
|
const abortControllers = (0, import_vue6.ref)([]);
|
|
634
646
|
let isAborted = false;
|
|
635
|
-
|
|
636
|
-
|
|
647
|
+
let pollTimer = null;
|
|
648
|
+
const getPollConfig = () => {
|
|
649
|
+
const val = (0, import_vue6.toValue)(poll);
|
|
650
|
+
if (typeof val === "number") return { interval: val, whenHidden: false };
|
|
651
|
+
return {
|
|
652
|
+
interval: (0, import_vue6.toValue)(val.interval),
|
|
653
|
+
whenHidden: (0, import_vue6.toValue)(val.whenHidden) ?? false
|
|
654
|
+
};
|
|
655
|
+
};
|
|
656
|
+
const updateProgress = (succeeded, failed, total) => {
|
|
637
657
|
const completed = succeeded + failed;
|
|
638
658
|
const newProgress = {
|
|
639
659
|
completed,
|
|
640
|
-
total
|
|
641
|
-
percentage:
|
|
660
|
+
total,
|
|
661
|
+
percentage: total > 0 ? Math.round(completed / total * 100) : 0,
|
|
642
662
|
succeeded,
|
|
643
663
|
failed
|
|
644
664
|
};
|
|
@@ -655,7 +675,8 @@ function useApiBatch(requests, options = {}) {
|
|
|
655
675
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
656
676
|
...config.headers && { headers: config.headers },
|
|
657
677
|
useGlobalAbort: false,
|
|
658
|
-
skipErrorNotification
|
|
678
|
+
skipErrorNotification,
|
|
679
|
+
lazy: true
|
|
659
680
|
}));
|
|
660
681
|
const { execute: execute2, error: reqError, statusCode, response } = api;
|
|
661
682
|
try {
|
|
@@ -710,7 +731,7 @@ function useApiBatch(requests, options = {}) {
|
|
|
710
731
|
scope.stop();
|
|
711
732
|
}
|
|
712
733
|
};
|
|
713
|
-
const executeWithConcurrency = async (requests2, limit) => {
|
|
734
|
+
const executeWithConcurrency = async (requests2, limit, total) => {
|
|
714
735
|
const results = new Array(requests2.length);
|
|
715
736
|
let succeededCount = 0;
|
|
716
737
|
let failedCount = 0;
|
|
@@ -728,8 +749,9 @@ function useApiBatch(requests, options = {}) {
|
|
|
728
749
|
errors.value.push(result.error);
|
|
729
750
|
}
|
|
730
751
|
}
|
|
731
|
-
updateProgress(succeededCount, failedCount);
|
|
752
|
+
updateProgress(succeededCount, failedCount, total);
|
|
732
753
|
if (!settled && !result.success && result.error) {
|
|
754
|
+
abort("First request failed in non-settled mode");
|
|
733
755
|
throw result.error;
|
|
734
756
|
}
|
|
735
757
|
return result;
|
|
@@ -758,7 +780,7 @@ function useApiBatch(requests, options = {}) {
|
|
|
758
780
|
errors.value.push(result.error);
|
|
759
781
|
}
|
|
760
782
|
}
|
|
761
|
-
updateProgress(succeededCount, failedCount);
|
|
783
|
+
updateProgress(succeededCount, failedCount, total);
|
|
762
784
|
if (!settled && !result.success && result.error) {
|
|
763
785
|
abort("First request failed in non-settled mode");
|
|
764
786
|
throw result.error;
|
|
@@ -775,6 +797,9 @@ function useApiBatch(requests, options = {}) {
|
|
|
775
797
|
return results;
|
|
776
798
|
};
|
|
777
799
|
const execute = async () => {
|
|
800
|
+
if (loading.value) {
|
|
801
|
+
abort("Replaced by new execution");
|
|
802
|
+
}
|
|
778
803
|
const currentRequests = getRequests();
|
|
779
804
|
isAborted = false;
|
|
780
805
|
loading.value = true;
|
|
@@ -782,20 +807,21 @@ function useApiBatch(requests, options = {}) {
|
|
|
782
807
|
errors.value = [];
|
|
783
808
|
data.value = [];
|
|
784
809
|
abortControllers.value = [];
|
|
785
|
-
|
|
810
|
+
const total = currentRequests.length;
|
|
811
|
+
updateProgress(0, 0, total);
|
|
812
|
+
let finalResults = [];
|
|
786
813
|
try {
|
|
787
|
-
|
|
788
|
-
data.value =
|
|
789
|
-
const allFailed =
|
|
790
|
-
if (allFailed &&
|
|
814
|
+
finalResults = await executeWithConcurrency(currentRequests, concurrency, total);
|
|
815
|
+
data.value = finalResults;
|
|
816
|
+
const allFailed = finalResults.every((r) => !r.success);
|
|
817
|
+
if (allFailed && finalResults.length > 0) {
|
|
791
818
|
error.value = {
|
|
792
|
-
message: `All ${
|
|
819
|
+
message: `All ${finalResults.length} requests failed`,
|
|
793
820
|
status: 0,
|
|
794
821
|
code: "BATCH_ALL_FAILED"
|
|
795
822
|
};
|
|
796
823
|
}
|
|
797
|
-
|
|
798
|
-
return results;
|
|
824
|
+
return finalResults;
|
|
799
825
|
} catch (err) {
|
|
800
826
|
if (!settled) {
|
|
801
827
|
error.value = err;
|
|
@@ -804,10 +830,27 @@ function useApiBatch(requests, options = {}) {
|
|
|
804
830
|
} finally {
|
|
805
831
|
loading.value = false;
|
|
806
832
|
abortControllers.value = [];
|
|
833
|
+
onFinish?.(finalResults);
|
|
834
|
+
if (!isAborted) {
|
|
835
|
+
const { interval, whenHidden } = getPollConfig();
|
|
836
|
+
if (interval > 0) {
|
|
837
|
+
const hidden = typeof document !== "undefined" && document.hidden;
|
|
838
|
+
if (whenHidden || !hidden) {
|
|
839
|
+
pollTimer = setTimeout(() => {
|
|
840
|
+
pollTimer = null;
|
|
841
|
+
execute();
|
|
842
|
+
}, interval);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
807
846
|
}
|
|
808
847
|
};
|
|
809
848
|
const abort = (message = "Batch aborted") => {
|
|
810
849
|
isAborted = true;
|
|
850
|
+
if (pollTimer) {
|
|
851
|
+
clearTimeout(pollTimer);
|
|
852
|
+
pollTimer = null;
|
|
853
|
+
}
|
|
811
854
|
for (const controller of abortControllers.value) {
|
|
812
855
|
controller.abort(message);
|
|
813
856
|
}
|
|
@@ -830,14 +873,26 @@ function useApiBatch(requests, options = {}) {
|
|
|
830
873
|
if ((0, import_vue6.getCurrentScope)()) {
|
|
831
874
|
(0, import_vue6.onScopeDispose)(() => abort("Scope disposed"));
|
|
832
875
|
}
|
|
876
|
+
if (!lazy && typeof requests === "function") {
|
|
877
|
+
const trackingScope = (0, import_vue6.effectScope)();
|
|
878
|
+
trackingScope.run(() => {
|
|
879
|
+
const requestsComputed = (0, import_vue6.computed)(
|
|
880
|
+
() => requests().map(normalizeRequest)
|
|
881
|
+
);
|
|
882
|
+
(0, import_vue6.watch)(requestsComputed, () => {
|
|
883
|
+
execute();
|
|
884
|
+
}, { deep: true });
|
|
885
|
+
});
|
|
886
|
+
if ((0, import_vue6.getCurrentScope)()) (0, import_vue6.onScopeDispose)(() => trackingScope.stop());
|
|
887
|
+
execute();
|
|
888
|
+
} else if (immediate) {
|
|
889
|
+
execute();
|
|
890
|
+
}
|
|
833
891
|
if (watchSource) {
|
|
834
892
|
(0, import_vue6.watch)(watchSource, () => {
|
|
835
893
|
execute();
|
|
836
894
|
}, { deep: true });
|
|
837
895
|
}
|
|
838
|
-
if (immediate) {
|
|
839
|
-
execute();
|
|
840
|
-
}
|
|
841
896
|
return {
|
|
842
897
|
data,
|
|
843
898
|
successfulData,
|
package/dist/index.d.cts
CHANGED
|
@@ -139,7 +139,7 @@ interface UseApiOptions<T = unknown, D = unknown, TSelected = T> extends ApiRequ
|
|
|
139
139
|
* Compatible with `lazy: true` — focus is a browser trigger, not a reactive dep.
|
|
140
140
|
* Compatible with `poll` — both register separate listeners; `!loading` guard prevents duplicates.
|
|
141
141
|
*
|
|
142
|
-
* Can be set globally via `
|
|
142
|
+
* Can be set globally via `createApi({ axios, globalOptions: { refetchOnFocus: true } })`.
|
|
143
143
|
* Per-request value takes precedence over global (including `false` to opt-out).
|
|
144
144
|
*/
|
|
145
145
|
refetchOnFocus?: boolean | {
|
|
@@ -151,7 +151,7 @@ interface UseApiOptions<T = unknown, D = unknown, TSelected = T> extends ApiRequ
|
|
|
151
151
|
* No throttle is applied — reconnect is already a rare event.
|
|
152
152
|
* No refetch fires if a request is already in-flight (`loading: true`).
|
|
153
153
|
*
|
|
154
|
-
* Can be set globally via `
|
|
154
|
+
* Can be set globally via `createApi({ axios, globalOptions: { refetchOnReconnect: true } })`.
|
|
155
155
|
* Per-request value takes precedence over global (including `false` to opt-out).
|
|
156
156
|
*/
|
|
157
157
|
refetchOnReconnect?: boolean;
|
|
@@ -336,7 +336,34 @@ interface UseApiBatchOptions<T = unknown, D = unknown> extends Omit<ApiRequestCo
|
|
|
336
336
|
immediate?: boolean;
|
|
337
337
|
/** Skip individual error notifications */
|
|
338
338
|
skipErrorNotification?: boolean;
|
|
339
|
-
/**
|
|
339
|
+
/**
|
|
340
|
+
* Disable auto-tracking. When true, reactive changes to the `requests` getter
|
|
341
|
+
* will NOT trigger re-execution. Use when you want full manual control via execute().
|
|
342
|
+
* Default: false — auto-tracks when `requests` is a function.
|
|
343
|
+
*/
|
|
344
|
+
lazy?: boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Polling interval in ms, or advanced config object.
|
|
347
|
+
* - Pass a number: `poll: 5000` — re-execute every 5 seconds.
|
|
348
|
+
* - Pass an object: `poll: { interval: 5000, whenHidden: false }` — skip polling when tab is hidden.
|
|
349
|
+
* Properties inside the object can also be Refs.
|
|
350
|
+
*/
|
|
351
|
+
poll?: MaybeRefOrGetter<number | {
|
|
352
|
+
interval: MaybeRefOrGetter<number>;
|
|
353
|
+
whenHidden?: MaybeRefOrGetter<boolean>;
|
|
354
|
+
}>;
|
|
355
|
+
/**
|
|
356
|
+
* @deprecated Use a reactive getter for `requests` with `lazy: false` (default).
|
|
357
|
+
* Auto-tracking will re-execute when the getter's dependencies change.
|
|
358
|
+
* Will be removed in v2.0.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* // Before (deprecated):
|
|
362
|
+
* useApiBatch(() => ids.value.map(id => `/items/${id}`), { watch: ids })
|
|
363
|
+
*
|
|
364
|
+
* // After (preferred):
|
|
365
|
+
* useApiBatch(() => ids.value.map(id => `/items/${id}`))
|
|
366
|
+
*/
|
|
340
367
|
watch?: WatchSource | WatchSource[];
|
|
341
368
|
/** Callback when a single request succeeds */
|
|
342
369
|
onItemSuccess?: (item: BatchResultItem<T>, index: number) => void;
|
|
@@ -448,7 +475,7 @@ declare function useApiDelete<T = unknown, TSelected = T>(url: MaybeRefOrGetter<
|
|
|
448
475
|
* - Abort support for all pending requests
|
|
449
476
|
* - Detailed per-request results with URL mapping
|
|
450
477
|
* - Progress tracking
|
|
451
|
-
* -
|
|
478
|
+
* - Auto-tracking for reactive getter requests (lazy: false default)
|
|
452
479
|
*
|
|
453
480
|
* @example
|
|
454
481
|
* ```ts
|
|
@@ -466,11 +493,10 @@ declare function useApiDelete<T = unknown, TSelected = T>(url: MaybeRefOrGetter<
|
|
|
466
493
|
* const ids = [1, 2, 3]
|
|
467
494
|
* useApiBatch(ids.map(id => ({ url: `/users/${id}`, method: 'DELETE' })))
|
|
468
495
|
*
|
|
469
|
-
* // Reactive getter
|
|
496
|
+
* // Reactive getter — auto-tracks deps, re-executes when pages changes
|
|
470
497
|
* const pages = ref([1, 2, 3])
|
|
471
498
|
* const { successfulData } = useApiBatch(
|
|
472
|
-
* () => pages.value.map(page => ({ url: '/users', params: { page } }))
|
|
473
|
-
* { watch: pages, immediate: true }
|
|
499
|
+
* () => pages.value.map(page => ({ url: '/users', params: { page } }))
|
|
474
500
|
* )
|
|
475
501
|
* ```
|
|
476
502
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -139,7 +139,7 @@ interface UseApiOptions<T = unknown, D = unknown, TSelected = T> extends ApiRequ
|
|
|
139
139
|
* Compatible with `lazy: true` — focus is a browser trigger, not a reactive dep.
|
|
140
140
|
* Compatible with `poll` — both register separate listeners; `!loading` guard prevents duplicates.
|
|
141
141
|
*
|
|
142
|
-
* Can be set globally via `
|
|
142
|
+
* Can be set globally via `createApi({ axios, globalOptions: { refetchOnFocus: true } })`.
|
|
143
143
|
* Per-request value takes precedence over global (including `false` to opt-out).
|
|
144
144
|
*/
|
|
145
145
|
refetchOnFocus?: boolean | {
|
|
@@ -151,7 +151,7 @@ interface UseApiOptions<T = unknown, D = unknown, TSelected = T> extends ApiRequ
|
|
|
151
151
|
* No throttle is applied — reconnect is already a rare event.
|
|
152
152
|
* No refetch fires if a request is already in-flight (`loading: true`).
|
|
153
153
|
*
|
|
154
|
-
* Can be set globally via `
|
|
154
|
+
* Can be set globally via `createApi({ axios, globalOptions: { refetchOnReconnect: true } })`.
|
|
155
155
|
* Per-request value takes precedence over global (including `false` to opt-out).
|
|
156
156
|
*/
|
|
157
157
|
refetchOnReconnect?: boolean;
|
|
@@ -336,7 +336,34 @@ interface UseApiBatchOptions<T = unknown, D = unknown> extends Omit<ApiRequestCo
|
|
|
336
336
|
immediate?: boolean;
|
|
337
337
|
/** Skip individual error notifications */
|
|
338
338
|
skipErrorNotification?: boolean;
|
|
339
|
-
/**
|
|
339
|
+
/**
|
|
340
|
+
* Disable auto-tracking. When true, reactive changes to the `requests` getter
|
|
341
|
+
* will NOT trigger re-execution. Use when you want full manual control via execute().
|
|
342
|
+
* Default: false — auto-tracks when `requests` is a function.
|
|
343
|
+
*/
|
|
344
|
+
lazy?: boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Polling interval in ms, or advanced config object.
|
|
347
|
+
* - Pass a number: `poll: 5000` — re-execute every 5 seconds.
|
|
348
|
+
* - Pass an object: `poll: { interval: 5000, whenHidden: false }` — skip polling when tab is hidden.
|
|
349
|
+
* Properties inside the object can also be Refs.
|
|
350
|
+
*/
|
|
351
|
+
poll?: MaybeRefOrGetter<number | {
|
|
352
|
+
interval: MaybeRefOrGetter<number>;
|
|
353
|
+
whenHidden?: MaybeRefOrGetter<boolean>;
|
|
354
|
+
}>;
|
|
355
|
+
/**
|
|
356
|
+
* @deprecated Use a reactive getter for `requests` with `lazy: false` (default).
|
|
357
|
+
* Auto-tracking will re-execute when the getter's dependencies change.
|
|
358
|
+
* Will be removed in v2.0.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* // Before (deprecated):
|
|
362
|
+
* useApiBatch(() => ids.value.map(id => `/items/${id}`), { watch: ids })
|
|
363
|
+
*
|
|
364
|
+
* // After (preferred):
|
|
365
|
+
* useApiBatch(() => ids.value.map(id => `/items/${id}`))
|
|
366
|
+
*/
|
|
340
367
|
watch?: WatchSource | WatchSource[];
|
|
341
368
|
/** Callback when a single request succeeds */
|
|
342
369
|
onItemSuccess?: (item: BatchResultItem<T>, index: number) => void;
|
|
@@ -448,7 +475,7 @@ declare function useApiDelete<T = unknown, TSelected = T>(url: MaybeRefOrGetter<
|
|
|
448
475
|
* - Abort support for all pending requests
|
|
449
476
|
* - Detailed per-request results with URL mapping
|
|
450
477
|
* - Progress tracking
|
|
451
|
-
* -
|
|
478
|
+
* - Auto-tracking for reactive getter requests (lazy: false default)
|
|
452
479
|
*
|
|
453
480
|
* @example
|
|
454
481
|
* ```ts
|
|
@@ -466,11 +493,10 @@ declare function useApiDelete<T = unknown, TSelected = T>(url: MaybeRefOrGetter<
|
|
|
466
493
|
* const ids = [1, 2, 3]
|
|
467
494
|
* useApiBatch(ids.map(id => ({ url: `/users/${id}`, method: 'DELETE' })))
|
|
468
495
|
*
|
|
469
|
-
* // Reactive getter
|
|
496
|
+
* // Reactive getter — auto-tracks deps, re-executes when pages changes
|
|
470
497
|
* const pages = ref([1, 2, 3])
|
|
471
498
|
* const { successfulData } = useApiBatch(
|
|
472
|
-
* () => pages.value.map(page => ({ url: '/users', params: { page } }))
|
|
473
|
-
* { watch: pages, immediate: true }
|
|
499
|
+
* () => pages.value.map(page => ({ url: '/users', params: { page } }))
|
|
474
500
|
* )
|
|
475
501
|
* ```
|
|
476
502
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -321,6 +321,16 @@ function useApi(url, options = {}) {
|
|
|
321
321
|
if (abortController2.value) abortController2.value.abort("Cancelled by new request");
|
|
322
322
|
const controller = new AbortController();
|
|
323
323
|
abortController2.value = controller;
|
|
324
|
+
if (config?.signal) {
|
|
325
|
+
const signal = config.signal;
|
|
326
|
+
if (signal.aborted) {
|
|
327
|
+
controller.abort(signal.reason);
|
|
328
|
+
} else {
|
|
329
|
+
signal.addEventListener("abort", () => {
|
|
330
|
+
controller.abort(signal.reason);
|
|
331
|
+
}, { once: true });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
324
334
|
let globalAbortHandler = null;
|
|
325
335
|
let subscribedSignal = null;
|
|
326
336
|
if (globalAbort) {
|
|
@@ -555,6 +565,8 @@ function useApiBatch(requests, options = {}) {
|
|
|
555
565
|
concurrency,
|
|
556
566
|
immediate = false,
|
|
557
567
|
skipErrorNotification = true,
|
|
568
|
+
lazy = false,
|
|
569
|
+
poll = 0,
|
|
558
570
|
watch: watchSource,
|
|
559
571
|
onItemSuccess,
|
|
560
572
|
onItemError,
|
|
@@ -579,13 +591,21 @@ function useApiBatch(requests, options = {}) {
|
|
|
579
591
|
);
|
|
580
592
|
const abortControllers = ref4([]);
|
|
581
593
|
let isAborted = false;
|
|
582
|
-
|
|
583
|
-
|
|
594
|
+
let pollTimer = null;
|
|
595
|
+
const getPollConfig = () => {
|
|
596
|
+
const val = toValue2(poll);
|
|
597
|
+
if (typeof val === "number") return { interval: val, whenHidden: false };
|
|
598
|
+
return {
|
|
599
|
+
interval: toValue2(val.interval),
|
|
600
|
+
whenHidden: toValue2(val.whenHidden) ?? false
|
|
601
|
+
};
|
|
602
|
+
};
|
|
603
|
+
const updateProgress = (succeeded, failed, total) => {
|
|
584
604
|
const completed = succeeded + failed;
|
|
585
605
|
const newProgress = {
|
|
586
606
|
completed,
|
|
587
|
-
total
|
|
588
|
-
percentage:
|
|
607
|
+
total,
|
|
608
|
+
percentage: total > 0 ? Math.round(completed / total * 100) : 0,
|
|
589
609
|
succeeded,
|
|
590
610
|
failed
|
|
591
611
|
};
|
|
@@ -602,7 +622,8 @@ function useApiBatch(requests, options = {}) {
|
|
|
602
622
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
603
623
|
...config.headers && { headers: config.headers },
|
|
604
624
|
useGlobalAbort: false,
|
|
605
|
-
skipErrorNotification
|
|
625
|
+
skipErrorNotification,
|
|
626
|
+
lazy: true
|
|
606
627
|
}));
|
|
607
628
|
const { execute: execute2, error: reqError, statusCode, response } = api;
|
|
608
629
|
try {
|
|
@@ -657,7 +678,7 @@ function useApiBatch(requests, options = {}) {
|
|
|
657
678
|
scope.stop();
|
|
658
679
|
}
|
|
659
680
|
};
|
|
660
|
-
const executeWithConcurrency = async (requests2, limit) => {
|
|
681
|
+
const executeWithConcurrency = async (requests2, limit, total) => {
|
|
661
682
|
const results = new Array(requests2.length);
|
|
662
683
|
let succeededCount = 0;
|
|
663
684
|
let failedCount = 0;
|
|
@@ -675,8 +696,9 @@ function useApiBatch(requests, options = {}) {
|
|
|
675
696
|
errors.value.push(result.error);
|
|
676
697
|
}
|
|
677
698
|
}
|
|
678
|
-
updateProgress(succeededCount, failedCount);
|
|
699
|
+
updateProgress(succeededCount, failedCount, total);
|
|
679
700
|
if (!settled && !result.success && result.error) {
|
|
701
|
+
abort("First request failed in non-settled mode");
|
|
680
702
|
throw result.error;
|
|
681
703
|
}
|
|
682
704
|
return result;
|
|
@@ -705,7 +727,7 @@ function useApiBatch(requests, options = {}) {
|
|
|
705
727
|
errors.value.push(result.error);
|
|
706
728
|
}
|
|
707
729
|
}
|
|
708
|
-
updateProgress(succeededCount, failedCount);
|
|
730
|
+
updateProgress(succeededCount, failedCount, total);
|
|
709
731
|
if (!settled && !result.success && result.error) {
|
|
710
732
|
abort("First request failed in non-settled mode");
|
|
711
733
|
throw result.error;
|
|
@@ -722,6 +744,9 @@ function useApiBatch(requests, options = {}) {
|
|
|
722
744
|
return results;
|
|
723
745
|
};
|
|
724
746
|
const execute = async () => {
|
|
747
|
+
if (loading.value) {
|
|
748
|
+
abort("Replaced by new execution");
|
|
749
|
+
}
|
|
725
750
|
const currentRequests = getRequests();
|
|
726
751
|
isAborted = false;
|
|
727
752
|
loading.value = true;
|
|
@@ -729,20 +754,21 @@ function useApiBatch(requests, options = {}) {
|
|
|
729
754
|
errors.value = [];
|
|
730
755
|
data.value = [];
|
|
731
756
|
abortControllers.value = [];
|
|
732
|
-
|
|
757
|
+
const total = currentRequests.length;
|
|
758
|
+
updateProgress(0, 0, total);
|
|
759
|
+
let finalResults = [];
|
|
733
760
|
try {
|
|
734
|
-
|
|
735
|
-
data.value =
|
|
736
|
-
const allFailed =
|
|
737
|
-
if (allFailed &&
|
|
761
|
+
finalResults = await executeWithConcurrency(currentRequests, concurrency, total);
|
|
762
|
+
data.value = finalResults;
|
|
763
|
+
const allFailed = finalResults.every((r) => !r.success);
|
|
764
|
+
if (allFailed && finalResults.length > 0) {
|
|
738
765
|
error.value = {
|
|
739
|
-
message: `All ${
|
|
766
|
+
message: `All ${finalResults.length} requests failed`,
|
|
740
767
|
status: 0,
|
|
741
768
|
code: "BATCH_ALL_FAILED"
|
|
742
769
|
};
|
|
743
770
|
}
|
|
744
|
-
|
|
745
|
-
return results;
|
|
771
|
+
return finalResults;
|
|
746
772
|
} catch (err) {
|
|
747
773
|
if (!settled) {
|
|
748
774
|
error.value = err;
|
|
@@ -751,10 +777,27 @@ function useApiBatch(requests, options = {}) {
|
|
|
751
777
|
} finally {
|
|
752
778
|
loading.value = false;
|
|
753
779
|
abortControllers.value = [];
|
|
780
|
+
onFinish?.(finalResults);
|
|
781
|
+
if (!isAborted) {
|
|
782
|
+
const { interval, whenHidden } = getPollConfig();
|
|
783
|
+
if (interval > 0) {
|
|
784
|
+
const hidden = typeof document !== "undefined" && document.hidden;
|
|
785
|
+
if (whenHidden || !hidden) {
|
|
786
|
+
pollTimer = setTimeout(() => {
|
|
787
|
+
pollTimer = null;
|
|
788
|
+
execute();
|
|
789
|
+
}, interval);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
754
793
|
}
|
|
755
794
|
};
|
|
756
795
|
const abort = (message = "Batch aborted") => {
|
|
757
796
|
isAborted = true;
|
|
797
|
+
if (pollTimer) {
|
|
798
|
+
clearTimeout(pollTimer);
|
|
799
|
+
pollTimer = null;
|
|
800
|
+
}
|
|
758
801
|
for (const controller of abortControllers.value) {
|
|
759
802
|
controller.abort(message);
|
|
760
803
|
}
|
|
@@ -777,14 +820,26 @@ function useApiBatch(requests, options = {}) {
|
|
|
777
820
|
if (getCurrentScope3()) {
|
|
778
821
|
onScopeDispose3(() => abort("Scope disposed"));
|
|
779
822
|
}
|
|
823
|
+
if (!lazy && typeof requests === "function") {
|
|
824
|
+
const trackingScope = effectScope2();
|
|
825
|
+
trackingScope.run(() => {
|
|
826
|
+
const requestsComputed = computed2(
|
|
827
|
+
() => requests().map(normalizeRequest)
|
|
828
|
+
);
|
|
829
|
+
watch2(requestsComputed, () => {
|
|
830
|
+
execute();
|
|
831
|
+
}, { deep: true });
|
|
832
|
+
});
|
|
833
|
+
if (getCurrentScope3()) onScopeDispose3(() => trackingScope.stop());
|
|
834
|
+
execute();
|
|
835
|
+
} else if (immediate) {
|
|
836
|
+
execute();
|
|
837
|
+
}
|
|
780
838
|
if (watchSource) {
|
|
781
839
|
watch2(watchSource, () => {
|
|
782
840
|
execute();
|
|
783
841
|
}, { deep: true });
|
|
784
842
|
}
|
|
785
|
-
if (immediate) {
|
|
786
|
-
execute();
|
|
787
|
-
}
|
|
788
843
|
return {
|
|
789
844
|
data,
|
|
790
845
|
successfulData,
|