vue-reactive-queue 0.0.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 ADDED
@@ -0,0 +1,276 @@
1
+ # vue-reactive-queue
2
+
3
+ Reactive async task queue for Vue 3 with concurrency control.
4
+
5
+ ## Features
6
+
7
+ - πŸš€ **Concurrency Control** - Limit parallel task execution
8
+ - πŸ“Š **Reactive State** - Track task status in real-time with Vue reactivity
9
+ - ⏸️ **Pause/Resume** - Control queue execution flow
10
+ - πŸ”„ **Retry** - Retry failed tasks with one call
11
+ - πŸ“ **Task History** - Keep track of completed tasks
12
+ - 🎯 **Manual Execution** - Start specific tasks on demand
13
+ - πŸ’ͺ **TypeScript** - Full type support with generics
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install vue-reactive-queue
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Basic
24
+
25
+ ```ts
26
+ import { useQueue } from 'vue-reactive-queue'
27
+
28
+ const queue = useQueue()
29
+
30
+ // Add tasks to the queue
31
+ queue.add(async () => {
32
+ await processFile('document.pdf')
33
+ })
34
+
35
+ queue.add(async () => {
36
+ await processFile('image.png')
37
+ })
38
+ ```
39
+
40
+ ### Concurrency Control
41
+
42
+ ```ts
43
+ const queue = useQueue({ concurrency: 3 })
44
+
45
+ // Only 3 tasks will run in parallel
46
+ queue.add(() => fetchUser(1))
47
+ queue.add(() => fetchUser(2))
48
+ queue.add(() => fetchUser(3))
49
+ queue.add(() => fetchUser(4)) // waits for a slot
50
+ ```
51
+
52
+ ### Reactive State
53
+
54
+ ```vue
55
+ <script setup>
56
+ import { useQueue } from 'vue-reactive-queue'
57
+
58
+ const queue = useQueue({ concurrency: 2 })
59
+
60
+ function addTask() {
61
+ queue.add(async () => {
62
+ await new Promise(r => setTimeout(r, 1000))
63
+ return 'done'
64
+ })
65
+ }
66
+ </script>
67
+
68
+ <template>
69
+ <div>
70
+ <p>Pending: {{ queue.stats.value.pending }}</p>
71
+ <p>Running: {{ queue.stats.value.running }}</p>
72
+ <p>Completed: {{ queue.stats.value.completed }}</p>
73
+
74
+ <button @click="addTask">Add Task</button>
75
+
76
+ <div v-for="task in queue.tasks.value" :key="task.id">
77
+ <span>Task #{{ task.id }}: {{ task.status }}</span>
78
+ <button v-if="task.status === 'rejected'" @click="queue.retry(task.id)">
79
+ Retry
80
+ </button>
81
+ </div>
82
+ </div>
83
+ </template>
84
+ ```
85
+
86
+ ### Pause and Resume
87
+
88
+ ```ts
89
+ const queue = useQueue()
90
+
91
+ queue.add(() => task1())
92
+ queue.add(() => task2())
93
+
94
+ queue.pause() // Stop executing new tasks
95
+ queue.resume() // Continue execution
96
+ ```
97
+
98
+ ### Start Paused
99
+
100
+ ```ts
101
+ const queue = useQueue({ immediate: false })
102
+
103
+ queue.add(() => task1())
104
+ queue.add(() => task2())
105
+
106
+ // Tasks are pending, not running
107
+ console.log(queue.stats.value.isPaused) // true
108
+
109
+ // Start when ready
110
+ queue.resume()
111
+ ```
112
+
113
+ ### Manual Task Execution
114
+
115
+ Start a specific task immediately, ignoring the pause state:
116
+
117
+ ```ts
118
+ const queue = useQueue({ immediate: false })
119
+
120
+ const { id: idA } = queue.add(() => taskA())
121
+ const { id: idB } = queue.add(() => taskB())
122
+ const { id: idC } = queue.add(() => taskC())
123
+
124
+ // Only execute taskB, leave others pending
125
+ await queue.start(idB)
126
+ ```
127
+
128
+ ### Retry Failed Tasks
129
+
130
+ ```ts
131
+ const queue = useQueue()
132
+
133
+ const { id } = queue.add(async () => {
134
+ await uploadFile(file) // might fail due to network issues
135
+ })
136
+
137
+ // Later, if the task failed
138
+ const result = queue.retry(id)
139
+ if (result.success) {
140
+ await result.promise
141
+ }
142
+ ```
143
+
144
+ ### Task Metadata
145
+
146
+ Attach custom data to tasks for UI rendering:
147
+
148
+ ```ts
149
+ interface TaskMeta {
150
+ filename: string
151
+ size: number
152
+ }
153
+
154
+ const queue = useQueue<TaskMeta>()
155
+
156
+ queue.add(
157
+ () => uploadFile(file),
158
+ { filename: file.name, size: file.size }
159
+ )
160
+ ```
161
+
162
+ ```vue
163
+ <template>
164
+ <div v-for="task in queue.tasks.value" :key="task.id">
165
+ <span>{{ task.meta?.filename }}</span>
166
+ <span>{{ task.status }}</span>
167
+ </div>
168
+ </template>
169
+ ```
170
+
171
+ ### Callbacks
172
+
173
+ ```ts
174
+ const queue = useQueue({
175
+ onSuccess(task) {
176
+ console.log('Task completed:', task.result)
177
+ },
178
+ onError(task, error) {
179
+ console.error('Task failed:', error)
180
+ },
181
+ onFinished() {
182
+ console.log('All tasks done!')
183
+ },
184
+ })
185
+ ```
186
+
187
+ ### Limit History
188
+
189
+ ```ts
190
+ // Only keep the last 10 completed tasks
191
+ const queue = useQueue({ maxHistory: 10 })
192
+
193
+ // Keep no history (only pending/running tasks visible)
194
+ const queue = useQueue({ maxHistory: 0 })
195
+ ```
196
+
197
+ ### Wait for Idle
198
+
199
+ ```ts
200
+ const queue = useQueue()
201
+
202
+ queue.add(() => task1())
203
+ queue.add(() => task2())
204
+ queue.add(() => task3())
205
+
206
+ await queue.waitForIdle()
207
+ console.log('All tasks completed!')
208
+ ```
209
+
210
+ ### Dynamic Concurrency
211
+
212
+ ```ts
213
+ import { ref } from 'vue'
214
+
215
+ const limit = ref(2)
216
+ const queue = useQueue({ concurrency: limit })
217
+
218
+ // Reactively update concurrency
219
+ limit.value = 5
220
+
221
+ // Or use setConcurrency
222
+ queue.setConcurrency(10)
223
+ ```
224
+
225
+ ## Type Declarations
226
+
227
+ ```ts
228
+ export interface QueueTask<T = unknown, M = unknown> {
229
+ readonly id: number
230
+ status: 'pending' | 'running' | 'fulfilled' | 'rejected'
231
+ readonly createdAt: number
232
+ startedAt?: number
233
+ finishedAt?: number
234
+ result?: T
235
+ error?: unknown
236
+ readonly meta?: M
237
+ }
238
+
239
+ export interface QueueStats {
240
+ readonly pending: number
241
+ readonly running: number
242
+ readonly completed: number
243
+ readonly failed: number
244
+ readonly total: number
245
+ readonly isIdle: boolean
246
+ readonly isPaused: boolean
247
+ }
248
+
249
+ export interface UseQueueOptions<M = unknown> {
250
+ concurrency?: MaybeRefOrGetter<number> // default: 1
251
+ immediate?: boolean // default: true
252
+ maxHistory?: number // default: undefined (keep all)
253
+ onSuccess?: (task: QueueTask<unknown, M>) => void
254
+ onError?: (task: QueueTask<unknown, M>, error: unknown) => void
255
+ onFinished?: () => void
256
+ }
257
+
258
+ export interface UseQueueReturn<M = unknown> {
259
+ tasks: Readonly<ShallowRef<QueueTask<unknown, M>[]>>
260
+ stats: ComputedRef<QueueStats>
261
+ concurrency: Readonly<ShallowRef<number>>
262
+ add: <T>(fn: () => T | Promise<T>, meta?: M) => { id: number; task: QueueTask<T, M>; promise: Promise<T> }
263
+ start: (id: number) => Promise<unknown> | undefined
264
+ retry: (id: number) => RetryResult
265
+ remove: (id: number) => boolean
266
+ clear: () => void
267
+ pause: () => void
268
+ resume: () => void
269
+ setConcurrency: (value: number) => void
270
+ waitForIdle: () => Promise<void>
271
+ }
272
+ ```
273
+
274
+ ## License
275
+
276
+ MIT
@@ -0,0 +1,128 @@
1
+ import { ComputedRef, MaybeRefOrGetter, ShallowRef } from "vue";
2
+
3
+ //#region src/index.d.ts
4
+ type TaskStatus = "pending" | "running" | "fulfilled" | "rejected";
5
+ type TaskFunction<T = unknown> = () => T | Promise<T>;
6
+ interface QueueTask<T = unknown, M = unknown> {
7
+ readonly id: number;
8
+ status: TaskStatus;
9
+ readonly createdAt: number;
10
+ startedAt?: number;
11
+ finishedAt?: number;
12
+ result?: T;
13
+ error?: unknown;
14
+ readonly run: TaskFunction<T>;
15
+ readonly meta?: M;
16
+ }
17
+ interface QueueStats {
18
+ readonly pending: number;
19
+ readonly running: number;
20
+ readonly completed: number;
21
+ readonly failed: number;
22
+ readonly total: number;
23
+ readonly isIdle: boolean;
24
+ readonly isPaused: boolean;
25
+ }
26
+ interface UseQueueOptions<M = unknown> {
27
+ /**
28
+ * Maximum number of concurrent tasks
29
+ *
30
+ * @default 1
31
+ */
32
+ concurrency?: MaybeRefOrGetter<number>;
33
+ /**
34
+ * Start the queue immediately
35
+ *
36
+ * @default true
37
+ */
38
+ immediate?: boolean;
39
+ /**
40
+ * Maximum number of completed tasks to keep in history
41
+ * Set to 0 to keep no history, undefined to keep all
42
+ *
43
+ * @default undefined
44
+ */
45
+ maxHistory?: number;
46
+ /**
47
+ * Callback when a task completes successfully
48
+ */
49
+ onSuccess?: (task: QueueTask<unknown, M>) => void;
50
+ /**
51
+ * Callback when a task fails
52
+ */
53
+ onError?: (task: QueueTask<unknown, M>, error: unknown) => void;
54
+ /**
55
+ * Callback when all tasks are finished and queue becomes idle
56
+ */
57
+ onFinished?: () => void;
58
+ }
59
+ type RetryResult = {
60
+ success: true;
61
+ promise: Promise<unknown>;
62
+ } | {
63
+ success: false;
64
+ reason: "not-found" | "not-rejected" | "already-queued";
65
+ };
66
+ interface UseQueueReturn<M = unknown> {
67
+ /**
68
+ * All tasks in the queue (including history)
69
+ */
70
+ tasks: Readonly<ShallowRef<QueueTask<unknown, M>[]>>;
71
+ /**
72
+ * Queue statistics
73
+ */
74
+ stats: ComputedRef<QueueStats>;
75
+ /**
76
+ * Current concurrency limit
77
+ */
78
+ concurrency: Readonly<ShallowRef<number>>;
79
+ /**
80
+ * Add a task to the queue
81
+ */
82
+ add: <T = unknown>(fn: TaskFunction<T>, meta?: M) => {
83
+ id: number;
84
+ task: QueueTask<T, M>;
85
+ promise: Promise<T>;
86
+ };
87
+ /**
88
+ * Start a specific pending task immediately (ignores pause state)
89
+ */
90
+ start: (id: number) => Promise<unknown> | undefined;
91
+ /**
92
+ * Retry a failed task
93
+ */
94
+ retry: (id: number) => RetryResult;
95
+ /**
96
+ * Remove a pending task from the queue
97
+ */
98
+ remove: (id: number) => boolean;
99
+ /**
100
+ * Clear all pending tasks
101
+ */
102
+ clear: () => void;
103
+ /**
104
+ * Pause the queue (running tasks will continue)
105
+ */
106
+ pause: () => void;
107
+ /**
108
+ * Resume the queue
109
+ */
110
+ resume: () => void;
111
+ /**
112
+ * Set the concurrency limit
113
+ */
114
+ setConcurrency: (value: number) => void;
115
+ /**
116
+ * Wait for the queue to become idle
117
+ */
118
+ waitForIdle: () => Promise<void>;
119
+ }
120
+ /**
121
+ * Reactive async task queue with concurrency control.
122
+ *
123
+ * @see https://github.com/ruibaby/vue-reactive-queue
124
+ */
125
+ declare function useQueue<M = unknown>(options?: UseQueueOptions<M>): UseQueueReturn<M>;
126
+ //#endregion
127
+ export { type MaybeRefOrGetter, QueueStats, QueueTask, RetryResult, TaskFunction, TaskStatus, UseQueueOptions, UseQueueReturn, useQueue };
128
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;KAcY,UAAA;AAAA,KAEA,YAAA,sBAAkC,CAAA,GAAI,OAAA,CAAQ,CAAA;AAAA,UAEzC,SAAA;EAAA,SACN,EAAA;EACT,MAAA,EAAQ,UAAA;EAAA,SACC,SAAA;EACT,SAAA;EACA,UAAA;EACA,MAAA,GAAS,CAAA;EACT,KAAA;EAAA,SACS,GAAA,EAAK,YAAA,CAAa,CAAA;EAAA,SAClB,IAAA,GAAO,CAAA;AAAA;AAAA,UAGD,UAAA;EAAA,SACN,OAAA;EAAA,SACA,OAAA;EAAA,SACA,SAAA;EAAA,SACA,MAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA;EAAA,SACA,QAAA;AAAA;AAAA,UAGM,eAAA;EAtBS;;;;;EA4BxB,WAAA,GAAc,gBAAA;EAnBG;;;;;EA0BjB,SAAA;EAjCQ;;;;;;EAyCR,UAAA;EAnCS;;;EAwCT,SAAA,IAAa,IAAA,EAAM,SAAA,UAAmB,CAAA;EAvCtB;;;EA4ChB,OAAA,IAAW,IAAA,EAAM,SAAA,UAAmB,CAAA,GAAI,KAAA;EAzCf;;;EA8CzB,UAAA;AAAA;AAAA,KAGU,WAAA;EACN,OAAA;EAAe,OAAA,EAAS,OAAA;AAAA;EACxB,OAAA;EAAgB,MAAA;AAAA;AAAA,UAEL,cAAA;EA3Ce;;;EA+C9B,KAAA,EAAO,QAAA,CAAS,UAAA,CAAW,SAAA,UAAmB,CAAA;EArB3B;;;EA0BnB,KAAA,EAAO,WAAA,CAAY,UAAA;EArBO;;;EA0B1B,WAAA,EAAa,QAAA,CAAS,UAAA;EA5CtB;;;EAiDA,GAAA,gBACE,EAAA,EAAI,YAAA,CAAa,CAAA,GACjB,IAAA,GAAO,CAAA;IAEP,EAAA;IACA,IAAA,EAAM,SAAA,CAAU,CAAA,EAAG,CAAA;IACnB,OAAA,EAAS,OAAA,CAAQ,CAAA;EAAA;EArCiB;;;EA2CpC,KAAA,GAAQ,EAAA,aAAe,OAAA;EAtCb;;AAGZ;EAwCE,KAAA,GAAQ,EAAA,aAAe,WAAA;;;;EAKvB,MAAA,GAAS,EAAA;EA5CmB;;;EAiD5B,KAAA;EAhD0B;AAE5B;;EAmDE,KAAA;EA/C8C;;;EAoD9C,MAAA;EA/CmB;;;EAoDnB,cAAA,GAAiB,KAAA;EAzCE;;;EA8CnB,WAAA,QAAmB,OAAA;AAAA;;;;;;iBAiCL,QAAA,aAAA,CACd,OAAA,GAAS,eAAA,CAAgB,CAAA,IACxB,cAAA,CAAe,CAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,290 @@
1
+ import { computed, shallowReadonly, shallowRef, toValue, triggerRef, watch } from "vue";
2
+
3
+ //#region src/index.ts
4
+ function clampConcurrency(value) {
5
+ if (!Number.isFinite(value)) return 1;
6
+ return Math.max(1, Math.floor(value));
7
+ }
8
+ function createDeferred() {
9
+ let resolve;
10
+ let reject;
11
+ return {
12
+ promise: new Promise((res, rej) => {
13
+ resolve = res;
14
+ reject = rej;
15
+ }),
16
+ resolve,
17
+ reject
18
+ };
19
+ }
20
+ /**
21
+ * Reactive async task queue with concurrency control.
22
+ *
23
+ * @see https://github.com/ruibaby/vue-reactive-queue
24
+ */
25
+ function useQueue(options = {}) {
26
+ const { concurrency: initialConcurrency = 1, immediate = true, maxHistory, onSuccess, onError, onFinished } = options;
27
+ const concurrency = shallowRef(clampConcurrency(toValue(initialConcurrency)));
28
+ const isPaused = shallowRef(!immediate);
29
+ const tasks = shallowRef([]);
30
+ const pendingQueue = shallowRef([]);
31
+ const deferredMap = /* @__PURE__ */ new Map();
32
+ let taskId = 0;
33
+ const runningCount = shallowRef(0);
34
+ const pendingCount = shallowRef(0);
35
+ const completedCount = shallowRef(0);
36
+ const failedCount = shallowRef(0);
37
+ const stats = computed(() => ({
38
+ pending: pendingCount.value,
39
+ running: runningCount.value,
40
+ completed: completedCount.value,
41
+ failed: failedCount.value,
42
+ total: tasks.value.length,
43
+ isPaused: isPaused.value,
44
+ isIdle: pendingCount.value === 0 && runningCount.value === 0
45
+ }));
46
+ function touchTasks() {
47
+ triggerRef(tasks);
48
+ }
49
+ function touchQueue() {
50
+ triggerRef(pendingQueue);
51
+ }
52
+ function trimHistory() {
53
+ if (maxHistory === void 0 || maxHistory < 0) return;
54
+ const completedTasks = tasks.value.filter((t) => t.status === "fulfilled" || t.status === "rejected");
55
+ if (completedTasks.length <= maxHistory) return;
56
+ const toRemove = completedTasks.length - maxHistory;
57
+ const idsToRemove = new Set(completedTasks.slice(0, toRemove).map((t) => t.id));
58
+ tasks.value = tasks.value.filter((t) => !idsToRemove.has(t.id));
59
+ touchTasks();
60
+ }
61
+ function checkIdle() {
62
+ if (stats.value.isIdle) onFinished?.();
63
+ }
64
+ function executeTask(task, deferred) {
65
+ runningCount.value += 1;
66
+ task.status = "running";
67
+ task.startedAt = Date.now();
68
+ touchTasks();
69
+ let runPromise;
70
+ try {
71
+ runPromise = Promise.resolve(task.run());
72
+ } catch (error) {
73
+ runPromise = Promise.reject(error);
74
+ }
75
+ runPromise.then((result) => {
76
+ task.status = "fulfilled";
77
+ task.result = result;
78
+ task.finishedAt = Date.now();
79
+ completedCount.value += 1;
80
+ runningCount.value -= 1;
81
+ touchTasks();
82
+ trimHistory();
83
+ deferred?.resolve(result);
84
+ onSuccess?.(task);
85
+ checkIdle();
86
+ }).catch((error) => {
87
+ task.status = "rejected";
88
+ task.error = error;
89
+ task.finishedAt = Date.now();
90
+ failedCount.value += 1;
91
+ runningCount.value -= 1;
92
+ touchTasks();
93
+ trimHistory();
94
+ deferred?.reject(error);
95
+ onError?.(task, error);
96
+ checkIdle();
97
+ }).finally(() => {
98
+ deferredMap.delete(task.id);
99
+ schedule();
100
+ });
101
+ }
102
+ function schedule() {
103
+ if (isPaused.value) return;
104
+ while (runningCount.value < concurrency.value && pendingQueue.value.length > 0) {
105
+ const nextTask = pendingQueue.value.shift();
106
+ touchQueue();
107
+ if (!nextTask) break;
108
+ if (nextTask.status === "pending") pendingCount.value -= 1;
109
+ executeTask(nextTask, deferredMap.get(nextTask.id));
110
+ }
111
+ }
112
+ function add(fn, meta) {
113
+ const id = ++taskId;
114
+ const task = {
115
+ id,
116
+ status: "pending",
117
+ createdAt: Date.now(),
118
+ run: fn,
119
+ meta
120
+ };
121
+ tasks.value.push(task);
122
+ triggerRef(tasks);
123
+ pendingQueue.value.push(task);
124
+ triggerRef(pendingQueue);
125
+ pendingCount.value += 1;
126
+ const deferred = createDeferred();
127
+ deferredMap.set(id, deferred);
128
+ schedule();
129
+ return {
130
+ id,
131
+ task,
132
+ promise: deferred.promise
133
+ };
134
+ }
135
+ function start(id) {
136
+ const task = tasks.value.find((t) => t.id === id);
137
+ if (!task || task.status !== "pending") return void 0;
138
+ const queueIndex = pendingQueue.value.findIndex((t) => t.id === id);
139
+ if (queueIndex !== -1) {
140
+ pendingQueue.value.splice(queueIndex, 1);
141
+ touchQueue();
142
+ }
143
+ const existingDeferred = deferredMap.get(id);
144
+ deferredMap.delete(id);
145
+ pendingCount.value -= 1;
146
+ const deferred = createDeferred();
147
+ let runPromise;
148
+ try {
149
+ runPromise = Promise.resolve(task.run());
150
+ } catch (error) {
151
+ runPromise = Promise.reject(error);
152
+ }
153
+ runningCount.value += 1;
154
+ task.status = "running";
155
+ task.startedAt = Date.now();
156
+ touchTasks();
157
+ runPromise.then((result) => {
158
+ task.status = "fulfilled";
159
+ task.result = result;
160
+ task.finishedAt = Date.now();
161
+ completedCount.value += 1;
162
+ runningCount.value -= 1;
163
+ touchTasks();
164
+ trimHistory();
165
+ deferred.resolve(result);
166
+ existingDeferred?.resolve(result);
167
+ onSuccess?.(task);
168
+ checkIdle();
169
+ }).catch((error) => {
170
+ task.status = "rejected";
171
+ task.error = error;
172
+ task.finishedAt = Date.now();
173
+ failedCount.value += 1;
174
+ runningCount.value -= 1;
175
+ touchTasks();
176
+ trimHistory();
177
+ deferred.reject(error);
178
+ existingDeferred?.reject(error);
179
+ onError?.(task, error);
180
+ checkIdle();
181
+ });
182
+ return deferred.promise;
183
+ }
184
+ function retry(id) {
185
+ const task = tasks.value.find((t) => t.id === id);
186
+ if (!task) return {
187
+ success: false,
188
+ reason: "not-found"
189
+ };
190
+ if (task.status !== "rejected") return {
191
+ success: false,
192
+ reason: "not-rejected"
193
+ };
194
+ if (pendingQueue.value.some((t) => t.id === id)) return {
195
+ success: false,
196
+ reason: "already-queued"
197
+ };
198
+ failedCount.value -= 1;
199
+ pendingCount.value += 1;
200
+ task.status = "pending";
201
+ task.startedAt = void 0;
202
+ task.finishedAt = void 0;
203
+ task.result = void 0;
204
+ task.error = void 0;
205
+ touchTasks();
206
+ const deferred = createDeferred();
207
+ deferredMap.set(id, deferred);
208
+ pendingQueue.value.push(task);
209
+ triggerRef(pendingQueue);
210
+ schedule();
211
+ return {
212
+ success: true,
213
+ promise: deferred.promise
214
+ };
215
+ }
216
+ function remove(id) {
217
+ const queueIndex = pendingQueue.value.findIndex((t) => t.id === id);
218
+ if (queueIndex === -1) return false;
219
+ pendingQueue.value.splice(queueIndex, 1);
220
+ touchQueue();
221
+ const taskIndex = tasks.value.findIndex((t) => t.id === id);
222
+ if (taskIndex !== -1) {
223
+ tasks.value.splice(taskIndex, 1);
224
+ touchTasks();
225
+ }
226
+ pendingCount.value -= 1;
227
+ deferredMap.get(id)?.reject(/* @__PURE__ */ new Error("Task removed"));
228
+ deferredMap.delete(id);
229
+ return true;
230
+ }
231
+ function clear() {
232
+ if (pendingQueue.value.length === 0) return;
233
+ const pendingIds = new Set(pendingQueue.value.map((t) => t.id));
234
+ const clearedCount = pendingIds.size;
235
+ pendingQueue.value = [];
236
+ triggerRef(pendingQueue);
237
+ tasks.value = tasks.value.filter((t) => !pendingIds.has(t.id));
238
+ triggerRef(tasks);
239
+ pendingCount.value -= clearedCount;
240
+ for (const id of pendingIds) {
241
+ deferredMap.get(id)?.reject(/* @__PURE__ */ new Error("Queue cleared"));
242
+ deferredMap.delete(id);
243
+ }
244
+ }
245
+ function pause() {
246
+ isPaused.value = true;
247
+ }
248
+ function resume() {
249
+ if (!isPaused.value) return;
250
+ isPaused.value = false;
251
+ schedule();
252
+ }
253
+ function setConcurrency(value) {
254
+ concurrency.value = clampConcurrency(value);
255
+ schedule();
256
+ }
257
+ function waitForIdle() {
258
+ if (stats.value.isIdle) return Promise.resolve();
259
+ return new Promise((resolve) => {
260
+ const stop = watch(() => stats.value.isIdle, (idle) => {
261
+ if (idle) {
262
+ stop();
263
+ resolve();
264
+ }
265
+ });
266
+ });
267
+ }
268
+ if (typeof initialConcurrency === "function" || typeof initialConcurrency === "object") watch(() => toValue(initialConcurrency), (value) => {
269
+ concurrency.value = clampConcurrency(value);
270
+ schedule();
271
+ });
272
+ return {
273
+ tasks: shallowReadonly(tasks),
274
+ stats,
275
+ concurrency: shallowReadonly(concurrency),
276
+ add,
277
+ start,
278
+ retry,
279
+ remove,
280
+ clear,
281
+ pause,
282
+ resume,
283
+ setConcurrency,
284
+ waitForIdle
285
+ };
286
+ }
287
+
288
+ //#endregion
289
+ export { useQueue };
290
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, ShallowRef } from \"vue\";\nimport {\n computed,\n shallowReadonly,\n shallowRef,\n toValue,\n triggerRef,\n watch,\n} from \"vue\";\n\n// =====================\n// Types\n// =====================\n\nexport type TaskStatus = \"pending\" | \"running\" | \"fulfilled\" | \"rejected\";\n\nexport type TaskFunction<T = unknown> = () => T | Promise<T>;\n\nexport interface QueueTask<T = unknown, M = unknown> {\n readonly id: number;\n status: TaskStatus;\n readonly createdAt: number;\n startedAt?: number;\n finishedAt?: number;\n result?: T;\n error?: unknown;\n readonly run: TaskFunction<T>;\n readonly meta?: M;\n}\n\nexport interface QueueStats {\n readonly pending: number;\n readonly running: number;\n readonly completed: number;\n readonly failed: number;\n readonly total: number;\n readonly isIdle: boolean;\n readonly isPaused: boolean;\n}\n\nexport interface UseQueueOptions<M = unknown> {\n /**\n * Maximum number of concurrent tasks\n *\n * @default 1\n */\n concurrency?: MaybeRefOrGetter<number>;\n\n /**\n * Start the queue immediately\n *\n * @default true\n */\n immediate?: boolean;\n\n /**\n * Maximum number of completed tasks to keep in history\n * Set to 0 to keep no history, undefined to keep all\n *\n * @default undefined\n */\n maxHistory?: number;\n\n /**\n * Callback when a task completes successfully\n */\n onSuccess?: (task: QueueTask<unknown, M>) => void;\n\n /**\n * Callback when a task fails\n */\n onError?: (task: QueueTask<unknown, M>, error: unknown) => void;\n\n /**\n * Callback when all tasks are finished and queue becomes idle\n */\n onFinished?: () => void;\n}\n\nexport type RetryResult =\n | { success: true; promise: Promise<unknown> }\n | { success: false; reason: \"not-found\" | \"not-rejected\" | \"already-queued\" };\n\nexport interface UseQueueReturn<M = unknown> {\n /**\n * All tasks in the queue (including history)\n */\n tasks: Readonly<ShallowRef<QueueTask<unknown, M>[]>>;\n\n /**\n * Queue statistics\n */\n stats: ComputedRef<QueueStats>;\n\n /**\n * Current concurrency limit\n */\n concurrency: Readonly<ShallowRef<number>>;\n\n /**\n * Add a task to the queue\n */\n add: <T = unknown>(\n fn: TaskFunction<T>,\n meta?: M,\n ) => {\n id: number;\n task: QueueTask<T, M>;\n promise: Promise<T>;\n };\n\n /**\n * Start a specific pending task immediately (ignores pause state)\n */\n start: (id: number) => Promise<unknown> | undefined;\n\n /**\n * Retry a failed task\n */\n retry: (id: number) => RetryResult;\n\n /**\n * Remove a pending task from the queue\n */\n remove: (id: number) => boolean;\n\n /**\n * Clear all pending tasks\n */\n clear: () => void;\n\n /**\n * Pause the queue (running tasks will continue)\n */\n pause: () => void;\n\n /**\n * Resume the queue\n */\n resume: () => void;\n\n /**\n * Set the concurrency limit\n */\n setConcurrency: (value: number) => void;\n\n /**\n * Wait for the queue to become idle\n */\n waitForIdle: () => Promise<void>;\n}\n\n// =====================\n// Implementation\n// =====================\n\ninterface Deferred<T> {\n resolve: (value: T) => void;\n reject: (reason?: unknown) => void;\n promise: Promise<T>;\n}\n\nfunction clampConcurrency(value: number): number {\n if (!Number.isFinite(value)) return 1;\n return Math.max(1, Math.floor(value));\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve: (value: T) => void;\n let reject: (reason?: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve: resolve!, reject: reject! };\n}\n\n/**\n * Reactive async task queue with concurrency control.\n *\n * @see https://github.com/ruibaby/vue-reactive-queue\n */\nexport function useQueue<M = unknown>(\n options: UseQueueOptions<M> = {},\n): UseQueueReturn<M> {\n const {\n concurrency: initialConcurrency = 1,\n immediate = true,\n maxHistory,\n onSuccess,\n onError,\n onFinished,\n } = options;\n\n const concurrency = shallowRef(clampConcurrency(toValue(initialConcurrency)));\n const isPaused = shallowRef(!immediate);\n const tasks = shallowRef<QueueTask<unknown, M>[]>([]);\n const pendingQueue = shallowRef<QueueTask<unknown, M>[]>([]);\n const deferredMap = new Map<number, Deferred<unknown>>();\n\n let taskId = 0;\n const runningCount = shallowRef(0);\n const pendingCount = shallowRef(0);\n const completedCount = shallowRef(0);\n const failedCount = shallowRef(0);\n\n const stats = computed<QueueStats>(() => ({\n pending: pendingCount.value,\n running: runningCount.value,\n completed: completedCount.value,\n failed: failedCount.value,\n total: tasks.value.length,\n isPaused: isPaused.value,\n isIdle: pendingCount.value === 0 && runningCount.value === 0,\n }));\n\n function touchTasks() {\n triggerRef(tasks);\n }\n\n function touchQueue() {\n triggerRef(pendingQueue);\n }\n\n function trimHistory() {\n if (maxHistory === undefined || maxHistory < 0) return;\n\n const completedTasks = tasks.value.filter(\n (t) => t.status === \"fulfilled\" || t.status === \"rejected\",\n );\n if (completedTasks.length <= maxHistory) return;\n\n const toRemove = completedTasks.length - maxHistory;\n const idsToRemove = new Set(\n completedTasks.slice(0, toRemove).map((t) => t.id),\n );\n tasks.value = tasks.value.filter((t) => !idsToRemove.has(t.id));\n touchTasks();\n }\n\n function checkIdle() {\n if (stats.value.isIdle) onFinished?.();\n }\n\n function executeTask(\n task: QueueTask<unknown, M>,\n deferred?: Deferred<unknown>,\n ) {\n runningCount.value += 1;\n task.status = \"running\";\n task.startedAt = Date.now();\n touchTasks();\n\n let runPromise: Promise<unknown>;\n try {\n runPromise = Promise.resolve(task.run());\n } catch (error) {\n runPromise = Promise.reject(error);\n }\n\n runPromise\n .then((result) => {\n task.status = \"fulfilled\";\n task.result = result;\n task.finishedAt = Date.now();\n completedCount.value += 1;\n runningCount.value -= 1;\n touchTasks();\n trimHistory();\n deferred?.resolve(result);\n onSuccess?.(task);\n checkIdle();\n })\n .catch((error) => {\n task.status = \"rejected\";\n task.error = error;\n task.finishedAt = Date.now();\n failedCount.value += 1;\n runningCount.value -= 1;\n touchTasks();\n trimHistory();\n deferred?.reject(error);\n onError?.(task, error);\n checkIdle();\n })\n .finally(() => {\n deferredMap.delete(task.id);\n schedule();\n });\n }\n\n function schedule() {\n if (isPaused.value) return;\n\n while (\n runningCount.value < concurrency.value &&\n pendingQueue.value.length > 0\n ) {\n const nextTask = pendingQueue.value.shift();\n touchQueue();\n if (!nextTask) break;\n\n if (nextTask.status === \"pending\") pendingCount.value -= 1;\n\n const deferred = deferredMap.get(nextTask.id);\n executeTask(nextTask, deferred);\n }\n }\n\n function add<T>(fn: TaskFunction<T>, meta?: M) {\n const id = ++taskId;\n const task: QueueTask<T, M> = {\n id,\n status: \"pending\",\n createdAt: Date.now(),\n run: fn,\n meta,\n };\n\n tasks.value.push(task);\n triggerRef(tasks);\n pendingQueue.value.push(task);\n triggerRef(pendingQueue);\n pendingCount.value += 1;\n\n const deferred = createDeferred<T>();\n deferredMap.set(id, deferred as Deferred<unknown>);\n\n schedule();\n\n return { id, task, promise: deferred.promise };\n }\n\n function start(id: number): Promise<unknown> | undefined {\n const task = tasks.value.find((t) => t.id === id);\n if (!task || task.status !== \"pending\") return undefined;\n\n const queueIndex = pendingQueue.value.findIndex((t) => t.id === id);\n if (queueIndex !== -1) {\n pendingQueue.value.splice(queueIndex, 1);\n touchQueue();\n }\n\n const existingDeferred = deferredMap.get(id);\n deferredMap.delete(id);\n\n pendingCount.value -= 1;\n\n const deferred = createDeferred<unknown>();\n\n let runPromise: Promise<unknown>;\n try {\n runPromise = Promise.resolve(task.run());\n } catch (error) {\n runPromise = Promise.reject(error);\n }\n\n runningCount.value += 1;\n task.status = \"running\";\n task.startedAt = Date.now();\n touchTasks();\n\n runPromise\n .then((result) => {\n task.status = \"fulfilled\";\n task.result = result;\n task.finishedAt = Date.now();\n completedCount.value += 1;\n runningCount.value -= 1;\n touchTasks();\n trimHistory();\n deferred.resolve(result);\n existingDeferred?.resolve(result);\n onSuccess?.(task);\n checkIdle();\n })\n .catch((error) => {\n task.status = \"rejected\";\n task.error = error;\n task.finishedAt = Date.now();\n failedCount.value += 1;\n runningCount.value -= 1;\n touchTasks();\n trimHistory();\n deferred.reject(error);\n existingDeferred?.reject(error);\n onError?.(task, error);\n checkIdle();\n });\n\n return deferred.promise;\n }\n\n function retry(id: number): RetryResult {\n const task = tasks.value.find((t) => t.id === id);\n if (!task) return { success: false, reason: \"not-found\" };\n\n if (task.status !== \"rejected\")\n return { success: false, reason: \"not-rejected\" };\n\n if (pendingQueue.value.some((t) => t.id === id))\n return { success: false, reason: \"already-queued\" };\n\n failedCount.value -= 1;\n pendingCount.value += 1;\n task.status = \"pending\";\n task.startedAt = undefined;\n task.finishedAt = undefined;\n task.result = undefined;\n task.error = undefined;\n touchTasks();\n\n const deferred = createDeferred<unknown>();\n deferredMap.set(id, deferred);\n pendingQueue.value.push(task);\n triggerRef(pendingQueue);\n schedule();\n\n return { success: true, promise: deferred.promise };\n }\n\n function remove(id: number): boolean {\n const queueIndex = pendingQueue.value.findIndex((t) => t.id === id);\n if (queueIndex === -1) return false;\n\n pendingQueue.value.splice(queueIndex, 1);\n touchQueue();\n\n const taskIndex = tasks.value.findIndex((t) => t.id === id);\n if (taskIndex !== -1) {\n tasks.value.splice(taskIndex, 1);\n touchTasks();\n }\n\n pendingCount.value -= 1;\n deferredMap.get(id)?.reject(new Error(\"Task removed\"));\n deferredMap.delete(id);\n\n return true;\n }\n\n function clear() {\n if (pendingQueue.value.length === 0) return;\n\n const pendingIds = new Set(pendingQueue.value.map((t) => t.id));\n const clearedCount = pendingIds.size;\n\n pendingQueue.value = [];\n triggerRef(pendingQueue);\n\n tasks.value = tasks.value.filter((t) => !pendingIds.has(t.id));\n triggerRef(tasks);\n\n pendingCount.value -= clearedCount;\n\n for (const id of pendingIds) {\n deferredMap.get(id)?.reject(new Error(\"Queue cleared\"));\n deferredMap.delete(id);\n }\n }\n\n function pause() {\n isPaused.value = true;\n }\n\n function resume() {\n if (!isPaused.value) return;\n isPaused.value = false;\n schedule();\n }\n\n function setConcurrency(value: number) {\n concurrency.value = clampConcurrency(value);\n schedule();\n }\n\n function waitForIdle(): Promise<void> {\n if (stats.value.isIdle) return Promise.resolve();\n\n return new Promise((resolve) => {\n const stop = watch(\n () => stats.value.isIdle,\n (idle) => {\n if (idle) {\n stop();\n resolve();\n }\n },\n );\n });\n }\n\n // Watch for dynamic concurrency changes\n if (\n typeof initialConcurrency === \"function\" ||\n typeof initialConcurrency === \"object\"\n ) {\n watch(\n () => toValue(initialConcurrency),\n (value) => {\n concurrency.value = clampConcurrency(value);\n schedule();\n },\n );\n }\n\n return {\n tasks: shallowReadonly(tasks),\n stats,\n concurrency: shallowReadonly(concurrency),\n add,\n start,\n retry,\n remove,\n clear,\n pause,\n resume,\n setConcurrency,\n waitForIdle,\n };\n}\n\nexport type { MaybeRefOrGetter };\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "vue-reactive-queue",
3
+ "version": "0.0.0",
4
+ "description": "A Vue 3 composable for managing async task queues with reactive state, concurrency control, pause/resume, and retry support",
5
+ "homepage": "https://github.com/ruibaby/vue-reactive-queue",
6
+ "bugs": {
7
+ "url": "https://github.com/ruibaby/vue-reactive-queue/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/ruibaby/vue-reactive-queue"
12
+ },
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": "./dist/index.mjs",
17
+ "./package.json": "./package.json"
18
+ },
19
+ "types": "./dist/index.d.mts",
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "devDependencies": {
24
+ "@biomejs/biome": "^2.3.13",
25
+ "@types/node": "^22.19.7",
26
+ "@vitest/coverage-v8": "4.0.18",
27
+ "tsdown": "^0.20.1",
28
+ "typescript": "^5.9.3",
29
+ "vitest": "^4.0.18"
30
+ },
31
+ "peerDependencies": {
32
+ "vue": ">=3.0.0"
33
+ },
34
+ "scripts": {
35
+ "build": "tsdown",
36
+ "dev": "tsdown --watch",
37
+ "test": "vitest run",
38
+ "test:coverage": "vitest run --coverage",
39
+ "test:watch": "vitest",
40
+ "typecheck": "tsc --noEmit",
41
+ "check": "biome check . --write",
42
+ "prepublish": "pnpm run build"
43
+ }
44
+ }