@vobs/queue 1.1.0 → 1.2.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/dist/index.cjs +615 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +61 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +609 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -6
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
|
|
6
|
+
// packages/reactivity/src/debug.ts
|
|
7
|
+
var activeDebugHooks = null;
|
|
8
|
+
var signalNames = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function hasDebugHooks() {
|
|
10
|
+
return activeDebugHooks !== null;
|
|
11
|
+
}
|
|
12
|
+
__name(hasDebugHooks, "hasDebugHooks");
|
|
13
|
+
function setSignalDebugName(signal, name) {
|
|
14
|
+
signalNames.set(signal, name);
|
|
15
|
+
invokeDebug("signalNamed", signal, name);
|
|
16
|
+
}
|
|
17
|
+
__name(setSignalDebugName, "setSignalDebugName");
|
|
18
|
+
function invokeDebug(name, ...args) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
__name(invokeDebug, "invokeDebug");
|
|
22
|
+
|
|
23
|
+
// packages/reactivity/src/owner.ts
|
|
24
|
+
var currentOwner = null;
|
|
25
|
+
function getCurrentOwner() {
|
|
26
|
+
return currentOwner;
|
|
27
|
+
}
|
|
28
|
+
__name(getCurrentOwner, "getCurrentOwner");
|
|
29
|
+
function onDispose(cleanup) {
|
|
30
|
+
throw new Error("Vobs: onDispose \u5FC5\u987B\u5728 Owner \u4F5C\u7528\u57DF\u5185\u8C03\u7528");
|
|
31
|
+
}
|
|
32
|
+
__name(onDispose, "onDispose");
|
|
33
|
+
|
|
34
|
+
// packages/reactivity/src/signal.ts
|
|
35
|
+
var currentSubscriber = null;
|
|
36
|
+
function getCurrentSubscriber() {
|
|
37
|
+
return currentSubscriber;
|
|
38
|
+
}
|
|
39
|
+
__name(getCurrentSubscriber, "getCurrentSubscriber");
|
|
40
|
+
function trackDependency(dependency) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
__name(trackDependency, "trackDependency");
|
|
44
|
+
function state(initialValue, debugName) {
|
|
45
|
+
let value = initialValue;
|
|
46
|
+
let disposed = false;
|
|
47
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
48
|
+
const signalInstance = {
|
|
49
|
+
get value() {
|
|
50
|
+
return value;
|
|
51
|
+
},
|
|
52
|
+
set value(nextValue) {
|
|
53
|
+
if (disposed || Object.is(value, nextValue)) return;
|
|
54
|
+
value = nextValue;
|
|
55
|
+
for (const subscriber of [...subscribers]) subscriber.notify();
|
|
56
|
+
},
|
|
57
|
+
unsubscribe(subscriber) {
|
|
58
|
+
subscribers.delete(subscriber);
|
|
59
|
+
},
|
|
60
|
+
// 与 `.value =` 赋值同一条路径:判等短路、debug hook、notify 全部一致。
|
|
61
|
+
// 以闭包实现,可安全地作为回调直接传递(无 this 绑定问题)。
|
|
62
|
+
set(next) {
|
|
63
|
+
signalInstance.value = next;
|
|
64
|
+
},
|
|
65
|
+
dispose() {
|
|
66
|
+
if (disposed) return;
|
|
67
|
+
disposed = true;
|
|
68
|
+
subscribers.clear();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
if (debugName?.trim()) setSignalDebugName(signalInstance, debugName.trim());
|
|
72
|
+
return signalInstance;
|
|
73
|
+
}
|
|
74
|
+
__name(state, "state");
|
|
75
|
+
|
|
76
|
+
// packages/reactivity/src/scheduler.ts
|
|
77
|
+
var _Scheduler = class _Scheduler {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.dirtyEffects = /* @__PURE__ */ new Set();
|
|
80
|
+
this.lowPriorityEffects = /* @__PURE__ */ new Set();
|
|
81
|
+
// flush 不可重入(flushing 标志保证),缓冲数组可在轮次间安全复用,避免每轮分配。
|
|
82
|
+
this.normalBuffer = [];
|
|
83
|
+
this.lowBuffer = [];
|
|
84
|
+
this.flushing = false;
|
|
85
|
+
this.scheduled = false;
|
|
86
|
+
this.batchDepth = 0;
|
|
87
|
+
}
|
|
88
|
+
schedule(effect2) {
|
|
89
|
+
if (effect2.disposed) return;
|
|
90
|
+
this.dirtyEffects.add(effect2);
|
|
91
|
+
this.lowPriorityEffects.delete(effect2);
|
|
92
|
+
this.ensureScheduled();
|
|
93
|
+
}
|
|
94
|
+
/** Queue an effect behind normal updates while preserving deterministic order. */
|
|
95
|
+
scheduleLow(effect2) {
|
|
96
|
+
if (effect2.disposed) return;
|
|
97
|
+
if (!this.dirtyEffects.has(effect2)) this.lowPriorityEffects.add(effect2);
|
|
98
|
+
this.ensureScheduled();
|
|
99
|
+
}
|
|
100
|
+
ensureScheduled() {
|
|
101
|
+
if (this.batchDepth === 0 && !this.flushing && !this.scheduled) {
|
|
102
|
+
this.scheduled = true;
|
|
103
|
+
queueMicrotask(() => {
|
|
104
|
+
this.scheduled = false;
|
|
105
|
+
this.flush();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
remove(effect2) {
|
|
110
|
+
this.dirtyEffects.delete(effect2);
|
|
111
|
+
this.lowPriorityEffects.delete(effect2);
|
|
112
|
+
}
|
|
113
|
+
batch(fn) {
|
|
114
|
+
this.batchDepth++;
|
|
115
|
+
try {
|
|
116
|
+
return fn();
|
|
117
|
+
} finally {
|
|
118
|
+
this.batchDepth--;
|
|
119
|
+
if (this.batchDepth === 0) this.flush();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
flush() {
|
|
123
|
+
if (this.flushing || this.batchDepth > 0) return;
|
|
124
|
+
this.flushing = true;
|
|
125
|
+
let rounds = 0;
|
|
126
|
+
let firstError;
|
|
127
|
+
let hasError = false;
|
|
128
|
+
try {
|
|
129
|
+
while (this.dirtyEffects.size > 0 || this.lowPriorityEffects.size > 0) {
|
|
130
|
+
if (++rounds > 100) {
|
|
131
|
+
this.dirtyEffects.clear();
|
|
132
|
+
this.lowPriorityEffects.clear();
|
|
133
|
+
throw new Error("Vobs: \u54CD\u5E94\u5F0F\u66F4\u65B0\u8D85\u8FC7 100 \u8F6E\uFF0C\u53EF\u80FD\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56");
|
|
134
|
+
}
|
|
135
|
+
this.collectRunnable(this.dirtyEffects, this.normalBuffer);
|
|
136
|
+
this.collectRunnable(this.lowPriorityEffects, this.lowBuffer);
|
|
137
|
+
sortEffects(this.normalBuffer);
|
|
138
|
+
sortEffects(this.lowBuffer);
|
|
139
|
+
for (const effect2 of this.normalBuffer) {
|
|
140
|
+
try {
|
|
141
|
+
effect2.run();
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (!hasError) {
|
|
144
|
+
firstError = error;
|
|
145
|
+
hasError = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const effect2 of this.lowBuffer) {
|
|
150
|
+
try {
|
|
151
|
+
effect2.run();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (!hasError) {
|
|
154
|
+
firstError = error;
|
|
155
|
+
hasError = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
this.normalBuffer.length = 0;
|
|
160
|
+
this.lowBuffer.length = 0;
|
|
161
|
+
}
|
|
162
|
+
} finally {
|
|
163
|
+
this.normalBuffer.length = 0;
|
|
164
|
+
this.lowBuffer.length = 0;
|
|
165
|
+
this.flushing = false;
|
|
166
|
+
}
|
|
167
|
+
if (hasError) throw firstError;
|
|
168
|
+
}
|
|
169
|
+
/** 收集未 disposed 的 effect 并清空源集合;run() 期间新调度的 effect 留给下一轮。 */
|
|
170
|
+
collectRunnable(source, target) {
|
|
171
|
+
for (const effect2 of source) {
|
|
172
|
+
if (!effect2.disposed) target.push(effect2);
|
|
173
|
+
}
|
|
174
|
+
source.clear();
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
__name(_Scheduler, "Scheduler");
|
|
178
|
+
function sortEffects(effects) {
|
|
179
|
+
if (effects.length > 1) {
|
|
180
|
+
effects.sort((a, b) => b.depth - a.depth || a.order - b.order);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
__name(sortEffects, "sortEffects");
|
|
184
|
+
|
|
185
|
+
// packages/runtime/src/hmr.ts
|
|
186
|
+
var globalTarget = globalThis;
|
|
187
|
+
var hmrGlobal = globalTarget.__VOBS_HMR__ ?? { modules: /* @__PURE__ */ new Map() };
|
|
188
|
+
globalTarget.__VOBS_HMR__ = hmrGlobal;
|
|
189
|
+
|
|
190
|
+
// packages/vobs/src/context.ts
|
|
191
|
+
var ownerProviders = /* @__PURE__ */ new WeakMap();
|
|
192
|
+
function inject(key, fallback) {
|
|
193
|
+
const owner = getCurrentOwner();
|
|
194
|
+
const value = owner ? injectFromOwner(owner, key) : void 0;
|
|
195
|
+
return value === void 0 ? fallback : value;
|
|
196
|
+
}
|
|
197
|
+
__name(inject, "inject");
|
|
198
|
+
function injectFromOwner(owner, key) {
|
|
199
|
+
let current = owner;
|
|
200
|
+
while (current) {
|
|
201
|
+
const providers = ownerProviders.get(current);
|
|
202
|
+
if (providers?.has(key)) return providers.get(key);
|
|
203
|
+
current = current.parent;
|
|
204
|
+
}
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
207
|
+
__name(injectFromOwner, "injectFromOwner");
|
|
208
|
+
|
|
209
|
+
// packages/vobs/src/app.ts
|
|
210
|
+
function createInjectionKey(description) {
|
|
211
|
+
return Symbol(description);
|
|
212
|
+
}
|
|
213
|
+
__name(createInjectionKey, "createInjectionKey");
|
|
214
|
+
|
|
215
|
+
// packages/queue/src/index.ts
|
|
216
|
+
var _QueueError = class _QueueError extends Error {
|
|
217
|
+
constructor(code, message, cause) {
|
|
218
|
+
super(message);
|
|
219
|
+
this.name = "QueueError";
|
|
220
|
+
this.code = code;
|
|
221
|
+
this.cause = cause;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
__name(_QueueError, "QueueError");
|
|
225
|
+
var QueueError = _QueueError;
|
|
226
|
+
var QUEUE_KEY = createInjectionKey("vobs.queue");
|
|
227
|
+
var PRIORITIES = {
|
|
228
|
+
low: 0,
|
|
229
|
+
normal: 1,
|
|
230
|
+
high: 2,
|
|
231
|
+
critical: 3
|
|
232
|
+
};
|
|
233
|
+
function createTaskQueue(options = {}) {
|
|
234
|
+
const concurrency = resolveConcurrency(options);
|
|
235
|
+
const tasks = state([]);
|
|
236
|
+
const pending = state(0);
|
|
237
|
+
const processing = state(0);
|
|
238
|
+
const completed = state(0);
|
|
239
|
+
const failed = state(0);
|
|
240
|
+
const total = state(0);
|
|
241
|
+
const paused = state(false);
|
|
242
|
+
const queue = [];
|
|
243
|
+
const ownedTasks = /* @__PURE__ */ new Set();
|
|
244
|
+
let sequence = 0;
|
|
245
|
+
let active = 0;
|
|
246
|
+
let nextId = 0;
|
|
247
|
+
let disposed = false;
|
|
248
|
+
const context = {
|
|
249
|
+
pending,
|
|
250
|
+
processing,
|
|
251
|
+
completed,
|
|
252
|
+
failed,
|
|
253
|
+
total,
|
|
254
|
+
paused,
|
|
255
|
+
tasks,
|
|
256
|
+
add(fn, taskOptions = {}) {
|
|
257
|
+
ensureActive();
|
|
258
|
+
if (typeof fn !== "function") {
|
|
259
|
+
throw new QueueError("INVALID_TASK", "Vobs Queue: task \u5FC5\u987B\u662F\u51FD\u6570");
|
|
260
|
+
}
|
|
261
|
+
const id = taskOptions.id ?? options.idFactory?.() ?? `task-${++nextId}`;
|
|
262
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
263
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: task id \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
264
|
+
}
|
|
265
|
+
if (tasks.value.some((task2) => task2.id === id)) {
|
|
266
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", `Vobs Queue: \u5DF2\u5B58\u5728\u4EFB\u52A1 ${id}`);
|
|
267
|
+
}
|
|
268
|
+
const task = createTask(fn, id, taskOptions);
|
|
269
|
+
ownedTasks.add(task);
|
|
270
|
+
tasks.value = Object.freeze([...tasks.value, task]);
|
|
271
|
+
queue.push(task);
|
|
272
|
+
sortQueue();
|
|
273
|
+
refreshStats();
|
|
274
|
+
drain();
|
|
275
|
+
return task;
|
|
276
|
+
},
|
|
277
|
+
pause() {
|
|
278
|
+
ensureActive();
|
|
279
|
+
paused.value = true;
|
|
280
|
+
},
|
|
281
|
+
resume() {
|
|
282
|
+
ensureActive();
|
|
283
|
+
if (!paused.value) return;
|
|
284
|
+
paused.value = false;
|
|
285
|
+
drain();
|
|
286
|
+
},
|
|
287
|
+
clear() {
|
|
288
|
+
ensureActive();
|
|
289
|
+
for (const task of [...queue]) task.cancel();
|
|
290
|
+
queue.length = 0;
|
|
291
|
+
refreshStats();
|
|
292
|
+
},
|
|
293
|
+
dispose() {
|
|
294
|
+
if (disposed) return;
|
|
295
|
+
disposed = true;
|
|
296
|
+
for (const task of [...ownedTasks]) task.cancel();
|
|
297
|
+
queue.length = 0;
|
|
298
|
+
for (const task of ownedTasks) task.dispose();
|
|
299
|
+
ownedTasks.clear();
|
|
300
|
+
tasks.value = Object.freeze([]);
|
|
301
|
+
pending.dispose();
|
|
302
|
+
processing.dispose();
|
|
303
|
+
completed.dispose();
|
|
304
|
+
failed.dispose();
|
|
305
|
+
total.dispose();
|
|
306
|
+
paused.dispose();
|
|
307
|
+
tasks.dispose();
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
return context;
|
|
311
|
+
function createTask(fn, id, taskOptions) {
|
|
312
|
+
const priority = taskOptions.priority ?? "normal";
|
|
313
|
+
if (!(priority in PRIORITIES)) {
|
|
314
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", `Vobs Queue: \u4E0D\u652F\u6301\u4EFB\u52A1\u4F18\u5148\u7EA7 ${String(priority)}`);
|
|
315
|
+
}
|
|
316
|
+
const maxRetries = taskOptions.retry ?? 0;
|
|
317
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 0) {
|
|
318
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: retry \u5FC5\u987B\u662F\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6574\u6570");
|
|
319
|
+
}
|
|
320
|
+
const retryDelay = taskOptions.retryDelay ?? 0;
|
|
321
|
+
validateRetryDelay(retryDelay);
|
|
322
|
+
const status = state("pending");
|
|
323
|
+
const attempt = state(0);
|
|
324
|
+
const result = state(null);
|
|
325
|
+
const error = state(null);
|
|
326
|
+
let promise;
|
|
327
|
+
const task = {
|
|
328
|
+
id,
|
|
329
|
+
priority,
|
|
330
|
+
status,
|
|
331
|
+
attempt,
|
|
332
|
+
result,
|
|
333
|
+
error,
|
|
334
|
+
get promise() {
|
|
335
|
+
return promise;
|
|
336
|
+
},
|
|
337
|
+
fn,
|
|
338
|
+
maxRetries,
|
|
339
|
+
retryDelay,
|
|
340
|
+
sequence: sequence++,
|
|
341
|
+
controller: null,
|
|
342
|
+
settled: false,
|
|
343
|
+
executing: false,
|
|
344
|
+
retryQueued: false,
|
|
345
|
+
resolve: null,
|
|
346
|
+
reject: null,
|
|
347
|
+
cancel() {
|
|
348
|
+
if (status.value === "success" || status.value === "error" || status.value === "cancelled") return;
|
|
349
|
+
task.controller?.abort();
|
|
350
|
+
removeFromQueue(task);
|
|
351
|
+
status.value = "cancelled";
|
|
352
|
+
const cancellation = new QueueError("QUEUE_TASK_CANCELLED", `Vobs Queue: \u4EFB\u52A1 ${id} \u5DF2\u53D6\u6D88`);
|
|
353
|
+
error.value = cancellation;
|
|
354
|
+
settleReject(cancellation);
|
|
355
|
+
refreshStats();
|
|
356
|
+
},
|
|
357
|
+
retry() {
|
|
358
|
+
ensureActive();
|
|
359
|
+
if (status.value === "pending" || status.value === "running" || status.value === "retrying") return promise;
|
|
360
|
+
if (status.value === "success") return promise;
|
|
361
|
+
status.value = "pending";
|
|
362
|
+
attempt.value = 0;
|
|
363
|
+
result.value = null;
|
|
364
|
+
error.value = null;
|
|
365
|
+
task.controller = null;
|
|
366
|
+
task.settled = false;
|
|
367
|
+
task.retryQueued = true;
|
|
368
|
+
promise = createPromise();
|
|
369
|
+
if (!task.executing) queue.push(task);
|
|
370
|
+
sortQueue();
|
|
371
|
+
refreshStats();
|
|
372
|
+
drain();
|
|
373
|
+
return promise;
|
|
374
|
+
},
|
|
375
|
+
run() {
|
|
376
|
+
task.retryQueued = false;
|
|
377
|
+
task.executing = true;
|
|
378
|
+
return execute(task).finally(() => {
|
|
379
|
+
task.executing = false;
|
|
380
|
+
if (task.retryQueued && task.status.value === "pending" && !disposed) {
|
|
381
|
+
task.retryQueued = false;
|
|
382
|
+
queue.push(task);
|
|
383
|
+
sortQueue();
|
|
384
|
+
drain();
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
},
|
|
388
|
+
dispose() {
|
|
389
|
+
task.controller?.abort();
|
|
390
|
+
status.dispose();
|
|
391
|
+
attempt.dispose();
|
|
392
|
+
result.dispose();
|
|
393
|
+
error.dispose();
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
promise = createPromise();
|
|
397
|
+
return task;
|
|
398
|
+
function createPromise() {
|
|
399
|
+
return new Promise((resolve, reject) => {
|
|
400
|
+
task.resolve = resolve;
|
|
401
|
+
task.reject = reject;
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function settleReject(reason) {
|
|
405
|
+
if (task.settled) return;
|
|
406
|
+
task.settled = true;
|
|
407
|
+
task.reject?.(reason);
|
|
408
|
+
task.resolve = null;
|
|
409
|
+
task.reject = null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async function execute(task) {
|
|
413
|
+
while (!disposed && (task.status.value === "pending" || task.status.value === "retrying")) {
|
|
414
|
+
if (task.status.value === "retrying") task.status.value = "pending";
|
|
415
|
+
task.status.value = "running";
|
|
416
|
+
task.attempt.value++;
|
|
417
|
+
const controller = new AbortController();
|
|
418
|
+
task.controller = controller;
|
|
419
|
+
refreshStats();
|
|
420
|
+
try {
|
|
421
|
+
const value = await task.fn(controller.signal);
|
|
422
|
+
if (isCancelled(task) || disposed) return;
|
|
423
|
+
task.result.value = value;
|
|
424
|
+
task.status.value = "success";
|
|
425
|
+
settleTask(task, value);
|
|
426
|
+
refreshStats();
|
|
427
|
+
return;
|
|
428
|
+
} catch (reason) {
|
|
429
|
+
if (isCancelled(task) || disposed) return;
|
|
430
|
+
if (isAbortError(reason)) {
|
|
431
|
+
const cancellation = new QueueError("QUEUE_TASK_CANCELLED", `Vobs Queue: \u4EFB\u52A1 ${task.id} \u5DF2\u53D6\u6D88`, reason);
|
|
432
|
+
task.error.value = cancellation;
|
|
433
|
+
task.status.value = "cancelled";
|
|
434
|
+
settleTask(task, void 0, cancellation);
|
|
435
|
+
refreshStats();
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const failure = toQueueError(reason, task.id);
|
|
439
|
+
let delay;
|
|
440
|
+
try {
|
|
441
|
+
if (task.attempt.value <= task.maxRetries) {
|
|
442
|
+
delay = resolveRetryDelay(task.retryDelay, task.attempt.value, failure);
|
|
443
|
+
}
|
|
444
|
+
} catch (delayError) {
|
|
445
|
+
const invalidDelay = toQueueError(delayError, task.id);
|
|
446
|
+
task.error.value = invalidDelay;
|
|
447
|
+
task.status.value = "error";
|
|
448
|
+
settleTask(task, void 0, invalidDelay);
|
|
449
|
+
report(invalidDelay, task);
|
|
450
|
+
refreshStats();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (delay !== void 0) {
|
|
454
|
+
task.status.value = "retrying";
|
|
455
|
+
refreshStats();
|
|
456
|
+
try {
|
|
457
|
+
await wait(delay, controller.signal);
|
|
458
|
+
} catch (delayError) {
|
|
459
|
+
if (isCancelled(task) || isAbortError(delayError)) return;
|
|
460
|
+
throw delayError;
|
|
461
|
+
}
|
|
462
|
+
if (isCancelled(task) || disposed) return;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
task.error.value = failure;
|
|
466
|
+
task.status.value = "error";
|
|
467
|
+
settleTask(task, void 0, failure);
|
|
468
|
+
report(failure, task);
|
|
469
|
+
refreshStats();
|
|
470
|
+
return;
|
|
471
|
+
} finally {
|
|
472
|
+
if (task.controller === controller) task.controller = null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function drain() {
|
|
477
|
+
while (!disposed && !paused.value && active < concurrency && queue.length > 0) {
|
|
478
|
+
const task = queue.shift();
|
|
479
|
+
if (task.status.value !== "pending") continue;
|
|
480
|
+
active++;
|
|
481
|
+
void task.run().finally(() => {
|
|
482
|
+
active--;
|
|
483
|
+
refreshStats();
|
|
484
|
+
drain();
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
refreshStats();
|
|
488
|
+
}
|
|
489
|
+
function sortQueue() {
|
|
490
|
+
queue.sort((left, right) => PRIORITIES[right.priority] - PRIORITIES[left.priority] || left.sequence - right.sequence);
|
|
491
|
+
}
|
|
492
|
+
function removeFromQueue(task) {
|
|
493
|
+
const index = queue.indexOf(task);
|
|
494
|
+
if (index >= 0) queue.splice(index, 1);
|
|
495
|
+
}
|
|
496
|
+
function refreshStats() {
|
|
497
|
+
let nextPending = 0;
|
|
498
|
+
let nextProcessing = 0;
|
|
499
|
+
let nextCompleted = 0;
|
|
500
|
+
let nextFailed = 0;
|
|
501
|
+
for (const task of tasks.value) {
|
|
502
|
+
if (task.status.value === "pending") nextPending++;
|
|
503
|
+
else if (task.status.value === "running" || task.status.value === "retrying") nextProcessing++;
|
|
504
|
+
else if (task.status.value === "success") nextCompleted++;
|
|
505
|
+
else if (task.status.value === "error") nextFailed++;
|
|
506
|
+
}
|
|
507
|
+
pending.value = nextPending;
|
|
508
|
+
processing.value = nextProcessing;
|
|
509
|
+
completed.value = nextCompleted;
|
|
510
|
+
failed.value = nextFailed;
|
|
511
|
+
total.value = tasks.value.length;
|
|
512
|
+
}
|
|
513
|
+
function settleTask(task, value, reason) {
|
|
514
|
+
if (task.settled) return;
|
|
515
|
+
task.settled = true;
|
|
516
|
+
if (reason !== void 0) task.reject?.(reason);
|
|
517
|
+
else task.resolve?.(value);
|
|
518
|
+
task.resolve = null;
|
|
519
|
+
task.reject = null;
|
|
520
|
+
}
|
|
521
|
+
function report(queueError, task) {
|
|
522
|
+
try {
|
|
523
|
+
options.onError?.(queueError, task);
|
|
524
|
+
} catch {
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function ensureActive() {
|
|
528
|
+
if (disposed) throw new QueueError("QUEUE_CONTEXT_DISPOSED", "Vobs Queue: \u4E0A\u4E0B\u6587\u5DF2\u9500\u6BC1");
|
|
529
|
+
}
|
|
530
|
+
function isCancelled(task) {
|
|
531
|
+
return task.status.value === "cancelled";
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
__name(createTaskQueue, "createTaskQueue");
|
|
535
|
+
function queuePlugin(options = {}) {
|
|
536
|
+
return {
|
|
537
|
+
name: "@vobs/queue",
|
|
538
|
+
version: "0.1.0",
|
|
539
|
+
install(context) {
|
|
540
|
+
const ownedQueue = options.queue ? void 0 : createTaskQueue(options);
|
|
541
|
+
context.provide(QUEUE_KEY, options.queue ?? ownedQueue);
|
|
542
|
+
return () => ownedQueue?.dispose();
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
__name(queuePlugin, "queuePlugin");
|
|
547
|
+
function useQueue() {
|
|
548
|
+
const queue = inject(QUEUE_KEY);
|
|
549
|
+
if (!queue) throw new QueueError("QUEUE_CONTEXT_MISSING", "Vobs Queue: \u627E\u4E0D\u5230\u4E0A\u4E0B\u6587\uFF0C\u8BF7\u5B89\u88C5 queuePlugin");
|
|
550
|
+
return queue;
|
|
551
|
+
}
|
|
552
|
+
__name(useQueue, "useQueue");
|
|
553
|
+
function resolveConcurrency(options) {
|
|
554
|
+
if (options.concurrency !== void 0 && options.concurrent !== void 0 && options.concurrency !== options.concurrent) {
|
|
555
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: concurrency \u4E0E concurrent \u4E0D\u80FD\u8BBE\u7F6E\u4E3A\u4E0D\u540C\u503C");
|
|
556
|
+
}
|
|
557
|
+
const value = options.concurrency ?? options.concurrent ?? 3;
|
|
558
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
559
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: concurrency \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
560
|
+
}
|
|
561
|
+
return value;
|
|
562
|
+
}
|
|
563
|
+
__name(resolveConcurrency, "resolveConcurrency");
|
|
564
|
+
function validateRetryDelay(value) {
|
|
565
|
+
if (typeof value === "number" && (!Number.isFinite(value) || value < 0)) {
|
|
566
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: retryDelay \u5FC5\u987B\u662F\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6709\u9650\u6570\u5B57\u6216\u51FD\u6570");
|
|
567
|
+
}
|
|
568
|
+
if (typeof value !== "number" && typeof value !== "function") {
|
|
569
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: retryDelay \u5FC5\u987B\u662F\u6570\u5B57\u6216\u51FD\u6570");
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
__name(validateRetryDelay, "validateRetryDelay");
|
|
573
|
+
function resolveRetryDelay(value, attempt, error) {
|
|
574
|
+
const delay = typeof value === "function" ? value(attempt, error) : value;
|
|
575
|
+
if (!Number.isFinite(delay) || delay < 0) {
|
|
576
|
+
throw new QueueError("INVALID_QUEUE_OPTIONS", "Vobs Queue: retryDelay \u51FD\u6570\u5FC5\u987B\u8FD4\u56DE\u5927\u4E8E\u7B49\u4E8E 0 \u7684\u6709\u9650\u6570\u5B57");
|
|
577
|
+
}
|
|
578
|
+
return delay;
|
|
579
|
+
}
|
|
580
|
+
__name(resolveRetryDelay, "resolveRetryDelay");
|
|
581
|
+
function wait(delay, signal) {
|
|
582
|
+
if (delay === 0) return Promise.resolve();
|
|
583
|
+
return new Promise((resolve, reject) => {
|
|
584
|
+
const timer = setTimeout(done, delay);
|
|
585
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
586
|
+
function done() {
|
|
587
|
+
signal.removeEventListener("abort", abort);
|
|
588
|
+
resolve();
|
|
589
|
+
}
|
|
590
|
+
__name(done, "done");
|
|
591
|
+
function abort() {
|
|
592
|
+
clearTimeout(timer);
|
|
593
|
+
reject(Object.assign(new Error("Aborted"), { name: "AbortError" }));
|
|
594
|
+
}
|
|
595
|
+
__name(abort, "abort");
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
__name(wait, "wait");
|
|
599
|
+
function toQueueError(reason, id) {
|
|
600
|
+
if (reason instanceof QueueError) return reason;
|
|
601
|
+
return new QueueError("QUEUE_TASK_FAILED", `Vobs Queue: \u4EFB\u52A1 ${id} \u6267\u884C\u5931\u8D25`, reason);
|
|
602
|
+
}
|
|
603
|
+
__name(toQueueError, "toQueueError");
|
|
604
|
+
function isAbortError(value) {
|
|
605
|
+
return Boolean(value) && typeof value === "object" && value.name === "AbortError";
|
|
606
|
+
}
|
|
607
|
+
__name(isAbortError, "isAbortError");
|
|
608
|
+
|
|
609
|
+
exports.QUEUE_KEY = QUEUE_KEY;
|
|
610
|
+
exports.QueueError = QueueError;
|
|
611
|
+
exports.createTaskQueue = createTaskQueue;
|
|
612
|
+
exports.queuePlugin = queuePlugin;
|
|
613
|
+
exports.useQueue = useQueue;
|
|
614
|
+
//# sourceMappingURL=index.cjs.map
|
|
615
|
+
//# sourceMappingURL=index.cjs.map
|