@dmop/puru 0.1.4 → 0.1.10
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/AGENTS.md +184 -0
- package/README.md +160 -372
- package/dist/index.cjs +175 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +398 -17
- package/dist/index.d.ts +398 -17
- package/dist/index.js +175 -149
- package/dist/index.js.map +1 -1
- package/llms-full.txt +286 -0
- package/llms.txt +45 -0
- package/package.json +38 -5
package/dist/index.js
CHANGED
|
@@ -56,21 +56,20 @@ function getConfig() {
|
|
|
56
56
|
|
|
57
57
|
// src/runtime.ts
|
|
58
58
|
function detectRuntime() {
|
|
59
|
-
if (
|
|
60
|
-
if (
|
|
61
|
-
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node)
|
|
62
|
-
return "node";
|
|
59
|
+
if ("Bun" in globalThis) return "bun";
|
|
60
|
+
if ("Deno" in globalThis) return "deno";
|
|
61
|
+
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node) return "node";
|
|
63
62
|
return "browser";
|
|
64
63
|
}
|
|
65
64
|
function detectCapability() {
|
|
66
65
|
const runtime = detectRuntime();
|
|
67
66
|
if (runtime === "node" || runtime === "bun") return "full-threads";
|
|
68
|
-
if (
|
|
67
|
+
if ("Worker" in globalThis) return "full-threads";
|
|
69
68
|
return "single-thread";
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
// src/adapters/node.ts
|
|
73
|
-
import { Worker
|
|
72
|
+
import { Worker } from "worker_threads";
|
|
74
73
|
|
|
75
74
|
// src/bootstrap.ts
|
|
76
75
|
var CHANNEL_PROXY_CODE = `
|
|
@@ -286,7 +285,7 @@ self.postMessage({ type: 'ready' });
|
|
|
286
285
|
var NodeManagedWorker = class {
|
|
287
286
|
worker;
|
|
288
287
|
constructor() {
|
|
289
|
-
this.worker = new
|
|
288
|
+
this.worker = new Worker(NODE_BOOTSTRAP_CODE, { eval: true });
|
|
290
289
|
}
|
|
291
290
|
get id() {
|
|
292
291
|
return this.worker.threadId;
|
|
@@ -332,7 +331,11 @@ var BunManagedWorker = class {
|
|
|
332
331
|
id;
|
|
333
332
|
constructor() {
|
|
334
333
|
this.id = ++workerIdCounter;
|
|
335
|
-
|
|
334
|
+
const WorkerConstructor = globalThis.Worker;
|
|
335
|
+
if (!WorkerConstructor) {
|
|
336
|
+
throw new Error("Bun Worker constructor is not available in this runtime");
|
|
337
|
+
}
|
|
338
|
+
this.worker = new WorkerConstructor(getBootstrapFile());
|
|
336
339
|
}
|
|
337
340
|
postMessage(data) {
|
|
338
341
|
this.worker.postMessage(data);
|
|
@@ -344,27 +347,28 @@ var BunManagedWorker = class {
|
|
|
344
347
|
on(event, handler) {
|
|
345
348
|
if (event === "message") {
|
|
346
349
|
this.worker.addEventListener("message", (e) => {
|
|
350
|
+
;
|
|
347
351
|
handler(e.data);
|
|
348
352
|
});
|
|
349
353
|
} else if (event === "error") {
|
|
350
354
|
this.worker.addEventListener("error", (e) => {
|
|
355
|
+
;
|
|
351
356
|
handler(e.error ?? new Error(e.message));
|
|
352
357
|
});
|
|
353
358
|
} else if (event === "exit") {
|
|
354
359
|
this.worker.addEventListener("close", (e) => {
|
|
360
|
+
;
|
|
355
361
|
handler(e.code ?? 0);
|
|
356
362
|
});
|
|
357
363
|
}
|
|
358
364
|
}
|
|
359
365
|
unref() {
|
|
360
366
|
if ("unref" in this.worker && typeof this.worker.unref === "function") {
|
|
361
|
-
;
|
|
362
367
|
this.worker.unref();
|
|
363
368
|
}
|
|
364
369
|
}
|
|
365
370
|
ref() {
|
|
366
371
|
if ("ref" in this.worker && typeof this.worker.ref === "function") {
|
|
367
|
-
;
|
|
368
372
|
this.worker.ref();
|
|
369
373
|
}
|
|
370
374
|
}
|
|
@@ -376,9 +380,11 @@ var BunWorkerAdapter = class {
|
|
|
376
380
|
};
|
|
377
381
|
|
|
378
382
|
// src/channel.ts
|
|
383
|
+
var CLOSED = /* @__PURE__ */ Symbol("puru.channel.closed");
|
|
379
384
|
var channelIdCounter = 0;
|
|
380
385
|
var channelRegistry = /* @__PURE__ */ new Map();
|
|
381
386
|
var ChannelImpl = class {
|
|
387
|
+
// constraint: can't create channels of nullable type
|
|
382
388
|
/** @internal */
|
|
383
389
|
_id;
|
|
384
390
|
buffer = [];
|
|
@@ -427,14 +433,16 @@ var ChannelImpl = class {
|
|
|
427
433
|
return Promise.resolve(null);
|
|
428
434
|
}
|
|
429
435
|
return new Promise((resolve) => {
|
|
430
|
-
this.recvQueue.push({
|
|
436
|
+
this.recvQueue.push({
|
|
437
|
+
resolve: (v) => resolve(v === CLOSED ? null : v)
|
|
438
|
+
});
|
|
431
439
|
});
|
|
432
440
|
}
|
|
433
441
|
close() {
|
|
434
442
|
if (this.closed) return;
|
|
435
443
|
this.closed = true;
|
|
436
444
|
for (const receiver of this.recvQueue) {
|
|
437
|
-
receiver.resolve(
|
|
445
|
+
receiver.resolve(CLOSED);
|
|
438
446
|
}
|
|
439
447
|
this.recvQueue = [];
|
|
440
448
|
for (const sender of this.sendQueue) {
|
|
@@ -459,6 +467,9 @@ function chan(capacity = 0) {
|
|
|
459
467
|
function getChannelById(id) {
|
|
460
468
|
return channelRegistry.get(id);
|
|
461
469
|
}
|
|
470
|
+
function getChannelId(channel) {
|
|
471
|
+
return channel._id;
|
|
472
|
+
}
|
|
462
473
|
|
|
463
474
|
// src/adapters/inline.ts
|
|
464
475
|
var inlineIdCounter = 0;
|
|
@@ -475,15 +486,14 @@ var InlineManagedWorker = class {
|
|
|
475
486
|
this.emit("message", { type: "ready" });
|
|
476
487
|
});
|
|
477
488
|
}
|
|
478
|
-
postMessage(
|
|
489
|
+
postMessage(msg) {
|
|
479
490
|
if (this.terminated) return;
|
|
480
|
-
const msg = data;
|
|
481
491
|
if (msg.type === "execute") {
|
|
482
|
-
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent
|
|
492
|
+
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent, msg.channels);
|
|
483
493
|
} else if (msg.type === "cancel") {
|
|
484
494
|
this.cancelledTasks.add(msg.taskId);
|
|
485
495
|
} else if (msg.type === "channel-result") {
|
|
486
|
-
|
|
496
|
+
return;
|
|
487
497
|
} else if (msg.type === "shutdown") {
|
|
488
498
|
this.terminated = true;
|
|
489
499
|
this.emit("exit", 0);
|
|
@@ -513,7 +523,6 @@ var InlineManagedWorker = class {
|
|
|
513
523
|
}
|
|
514
524
|
}
|
|
515
525
|
buildChannelProxies(channels) {
|
|
516
|
-
const self = this;
|
|
517
526
|
const proxies = {};
|
|
518
527
|
for (const [name, channelId] of Object.entries(channels)) {
|
|
519
528
|
proxies[name] = {
|
|
@@ -535,13 +544,7 @@ var InlineManagedWorker = class {
|
|
|
535
544
|
[Symbol.asyncIterator]() {
|
|
536
545
|
const ch = getChannelById(channelId);
|
|
537
546
|
if (!ch) throw new Error(`Channel ${channelId} not found`);
|
|
538
|
-
return
|
|
539
|
-
async next() {
|
|
540
|
-
const value = await ch.recv();
|
|
541
|
-
if (value === null) return { done: true, value: void 0 };
|
|
542
|
-
return { done: false, value };
|
|
543
|
-
}
|
|
544
|
-
};
|
|
547
|
+
return ch[Symbol.asyncIterator]();
|
|
545
548
|
}
|
|
546
549
|
};
|
|
547
550
|
}
|
|
@@ -620,14 +623,14 @@ var WorkerPool = class {
|
|
|
620
623
|
this.adapter = adapter;
|
|
621
624
|
}
|
|
622
625
|
// --- Queue helpers ---
|
|
623
|
-
enqueue(
|
|
624
|
-
this.queues[
|
|
626
|
+
enqueue(task2) {
|
|
627
|
+
this.queues[task2.priority].push(task2);
|
|
625
628
|
}
|
|
626
629
|
dequeue() {
|
|
627
630
|
return this.queues.high.shift() ?? this.queues.normal.shift() ?? this.queues.low.shift();
|
|
628
631
|
}
|
|
629
|
-
enqueueConcurrent(
|
|
630
|
-
this.concurrentQueues[
|
|
632
|
+
enqueueConcurrent(task2) {
|
|
633
|
+
this.concurrentQueues[task2.priority].push(task2);
|
|
631
634
|
}
|
|
632
635
|
dequeueConcurrent() {
|
|
633
636
|
return this.concurrentQueues.high.shift() ?? this.concurrentQueues.normal.shift() ?? this.concurrentQueues.low.shift();
|
|
@@ -653,73 +656,73 @@ var WorkerPool = class {
|
|
|
653
656
|
return void 0;
|
|
654
657
|
}
|
|
655
658
|
// --- Submit ---
|
|
656
|
-
submit(
|
|
659
|
+
submit(task2) {
|
|
657
660
|
if (this.draining) {
|
|
658
|
-
|
|
661
|
+
task2.reject(new Error("Pool is shutting down"));
|
|
659
662
|
return;
|
|
660
663
|
}
|
|
661
|
-
if (
|
|
662
|
-
this.submitConcurrent(
|
|
664
|
+
if (task2.concurrent) {
|
|
665
|
+
this.submitConcurrent(task2);
|
|
663
666
|
} else {
|
|
664
|
-
this.submitExclusive(
|
|
667
|
+
this.submitExclusive(task2);
|
|
665
668
|
}
|
|
666
669
|
}
|
|
667
|
-
submitExclusive(
|
|
670
|
+
submitExclusive(task2) {
|
|
668
671
|
const worker = this.idleWorkers.pop();
|
|
669
672
|
if (worker) {
|
|
670
|
-
this.dispatch(worker,
|
|
673
|
+
this.dispatch(worker, task2);
|
|
671
674
|
return;
|
|
672
675
|
}
|
|
673
676
|
const totalWorkers = this.allWorkers.size + this.pendingWorkerCount;
|
|
674
677
|
if (totalWorkers < this.config.maxThreads) {
|
|
675
678
|
this.pendingWorkerCount++;
|
|
676
|
-
this.pendingTasksForWorkers.push(
|
|
679
|
+
this.pendingTasksForWorkers.push(task2);
|
|
677
680
|
this.createAndReadyWorker();
|
|
678
681
|
return;
|
|
679
682
|
}
|
|
680
|
-
this.enqueue(
|
|
683
|
+
this.enqueue(task2);
|
|
681
684
|
}
|
|
682
|
-
submitConcurrent(
|
|
685
|
+
submitConcurrent(task2) {
|
|
683
686
|
for (const [worker, tasks] of this.sharedWorkers) {
|
|
684
687
|
if (tasks.size < this.config.concurrency) {
|
|
685
|
-
this.dispatchConcurrent(worker,
|
|
688
|
+
this.dispatchConcurrent(worker, task2);
|
|
686
689
|
return;
|
|
687
690
|
}
|
|
688
691
|
}
|
|
689
692
|
const idleWorker = this.idleWorkers.pop();
|
|
690
693
|
if (idleWorker) {
|
|
691
|
-
this.dispatchConcurrent(idleWorker,
|
|
694
|
+
this.dispatchConcurrent(idleWorker, task2);
|
|
692
695
|
return;
|
|
693
696
|
}
|
|
694
697
|
const totalWorkers = this.allWorkers.size + this.pendingWorkerCount;
|
|
695
698
|
if (totalWorkers < this.config.maxThreads) {
|
|
696
699
|
this.pendingWorkerCount++;
|
|
697
|
-
this.pendingTasksForWorkers.push(
|
|
700
|
+
this.pendingTasksForWorkers.push(task2);
|
|
698
701
|
this.createAndReadyWorker();
|
|
699
702
|
return;
|
|
700
703
|
}
|
|
701
|
-
this.enqueueConcurrent(
|
|
704
|
+
this.enqueueConcurrent(task2);
|
|
702
705
|
}
|
|
703
706
|
// --- Dispatch ---
|
|
704
|
-
dispatch(worker,
|
|
707
|
+
dispatch(worker, task2) {
|
|
705
708
|
const timer = this.idleTimers.get(worker);
|
|
706
709
|
if (timer) {
|
|
707
710
|
clearTimeout(timer);
|
|
708
711
|
this.idleTimers.delete(worker);
|
|
709
712
|
}
|
|
710
713
|
worker.ref();
|
|
711
|
-
this.exclusiveWorkers.set(
|
|
712
|
-
this.taskMap.set(
|
|
714
|
+
this.exclusiveWorkers.set(task2.id, worker);
|
|
715
|
+
this.taskMap.set(task2.id, task2);
|
|
713
716
|
const msg = {
|
|
714
717
|
type: "execute",
|
|
715
|
-
taskId:
|
|
716
|
-
fnStr:
|
|
718
|
+
taskId: task2.id,
|
|
719
|
+
fnStr: task2.fnStr,
|
|
717
720
|
concurrent: false,
|
|
718
|
-
channels:
|
|
721
|
+
channels: task2.channels
|
|
719
722
|
};
|
|
720
723
|
worker.postMessage(msg);
|
|
721
724
|
}
|
|
722
|
-
dispatchConcurrent(worker,
|
|
725
|
+
dispatchConcurrent(worker, task2) {
|
|
723
726
|
const timer = this.idleTimers.get(worker);
|
|
724
727
|
if (timer) {
|
|
725
728
|
clearTimeout(timer);
|
|
@@ -729,14 +732,14 @@ var WorkerPool = class {
|
|
|
729
732
|
if (!this.sharedWorkers.has(worker)) {
|
|
730
733
|
this.sharedWorkers.set(worker, /* @__PURE__ */ new Set());
|
|
731
734
|
}
|
|
732
|
-
this.sharedWorkers.get(worker).add(
|
|
733
|
-
this.taskMap.set(
|
|
735
|
+
this.sharedWorkers.get(worker).add(task2.id);
|
|
736
|
+
this.taskMap.set(task2.id, task2);
|
|
734
737
|
const msg = {
|
|
735
738
|
type: "execute",
|
|
736
|
-
taskId:
|
|
737
|
-
fnStr:
|
|
739
|
+
taskId: task2.id,
|
|
740
|
+
fnStr: task2.fnStr,
|
|
738
741
|
concurrent: true,
|
|
739
|
-
channels:
|
|
742
|
+
channels: task2.channels
|
|
740
743
|
};
|
|
741
744
|
worker.postMessage(msg);
|
|
742
745
|
}
|
|
@@ -763,6 +766,9 @@ var WorkerPool = class {
|
|
|
763
766
|
const taskId = response.taskId;
|
|
764
767
|
const err = new Error(response.message);
|
|
765
768
|
if (response.stack) err.stack = response.stack;
|
|
769
|
+
if (response.message.match(/^ReferenceError:/) || response.message.match(/ is not defined$/)) {
|
|
770
|
+
err.message += "\n Hint: functions passed to spawn() cannot access variables from the enclosing scope. Inline all required values directly in the function body, or pass them via the channels option.";
|
|
771
|
+
}
|
|
766
772
|
this.rejectTask(taskId, err);
|
|
767
773
|
if (this.exclusiveWorkers.has(taskId)) {
|
|
768
774
|
this.exclusiveWorkers.delete(taskId);
|
|
@@ -871,19 +877,28 @@ var WorkerPool = class {
|
|
|
871
877
|
}
|
|
872
878
|
// --- Task resolution ---
|
|
873
879
|
resolveTask(taskId, value) {
|
|
874
|
-
const
|
|
875
|
-
if (
|
|
880
|
+
const task2 = this.taskMap.get(taskId);
|
|
881
|
+
if (task2) {
|
|
876
882
|
this.taskMap.delete(taskId);
|
|
877
883
|
this.totalCompleted++;
|
|
878
|
-
|
|
884
|
+
task2.resolve(value);
|
|
879
885
|
}
|
|
880
886
|
}
|
|
881
887
|
rejectTask(taskId, reason) {
|
|
882
|
-
const
|
|
883
|
-
if (
|
|
888
|
+
const task2 = this.taskMap.get(taskId);
|
|
889
|
+
if (task2) {
|
|
884
890
|
this.taskMap.delete(taskId);
|
|
885
891
|
this.totalFailed++;
|
|
886
|
-
|
|
892
|
+
task2.reject(reason);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
rejectExclusiveTaskForWorker(worker, reason) {
|
|
896
|
+
for (const [taskId, assignedWorker] of this.exclusiveWorkers) {
|
|
897
|
+
if (assignedWorker === worker) {
|
|
898
|
+
this.exclusiveWorkers.delete(taskId);
|
|
899
|
+
this.rejectTask(taskId, reason);
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
887
902
|
}
|
|
888
903
|
}
|
|
889
904
|
// --- Cancellation ---
|
|
@@ -923,17 +938,21 @@ var WorkerPool = class {
|
|
|
923
938
|
async drain() {
|
|
924
939
|
this.draining = true;
|
|
925
940
|
for (const priority of ["high", "normal", "low"]) {
|
|
926
|
-
for (const
|
|
927
|
-
|
|
941
|
+
for (const task2 of this.queues[priority]) {
|
|
942
|
+
task2.reject(new Error("Pool is shutting down"));
|
|
928
943
|
}
|
|
929
944
|
this.queues[priority] = [];
|
|
930
945
|
}
|
|
931
946
|
for (const priority of ["high", "normal", "low"]) {
|
|
932
|
-
for (const
|
|
933
|
-
|
|
947
|
+
for (const task2 of this.concurrentQueues[priority]) {
|
|
948
|
+
task2.reject(new Error("Pool is shutting down"));
|
|
934
949
|
}
|
|
935
950
|
this.concurrentQueues[priority] = [];
|
|
936
951
|
}
|
|
952
|
+
for (const [taskId] of this.exclusiveWorkers) {
|
|
953
|
+
this.taskMap.delete(taskId);
|
|
954
|
+
}
|
|
955
|
+
this.exclusiveWorkers.clear();
|
|
937
956
|
for (const [, taskSet] of this.sharedWorkers) {
|
|
938
957
|
for (const taskId of taskSet) {
|
|
939
958
|
this.taskMap.delete(taskId);
|
|
@@ -957,10 +976,10 @@ var WorkerPool = class {
|
|
|
957
976
|
while (true) {
|
|
958
977
|
const totalWorkers = this.allWorkers.size + this.pendingWorkerCount;
|
|
959
978
|
if (totalWorkers >= maxThreads) break;
|
|
960
|
-
const
|
|
961
|
-
if (!
|
|
979
|
+
const task2 = this.dequeue() ?? this.dequeueConcurrent();
|
|
980
|
+
if (!task2) break;
|
|
962
981
|
this.pendingWorkerCount++;
|
|
963
|
-
this.pendingTasksForWorkers.push(
|
|
982
|
+
this.pendingTasksForWorkers.push(task2);
|
|
964
983
|
this.createAndReadyWorker();
|
|
965
984
|
}
|
|
966
985
|
while (this.allWorkers.size > maxThreads && this.idleWorkers.length > 0) {
|
|
@@ -980,19 +999,18 @@ var WorkerPool = class {
|
|
|
980
999
|
const onReady = () => {
|
|
981
1000
|
this.pendingWorkerCount--;
|
|
982
1001
|
this.allWorkers.add(worker);
|
|
983
|
-
const
|
|
984
|
-
if (
|
|
985
|
-
if (
|
|
986
|
-
this.dispatchConcurrent(worker,
|
|
1002
|
+
const task2 = this.pendingTasksForWorkers.shift();
|
|
1003
|
+
if (task2) {
|
|
1004
|
+
if (task2.concurrent) {
|
|
1005
|
+
this.dispatchConcurrent(worker, task2);
|
|
987
1006
|
} else {
|
|
988
|
-
this.dispatch(worker,
|
|
1007
|
+
this.dispatch(worker, task2);
|
|
989
1008
|
}
|
|
990
1009
|
} else {
|
|
991
1010
|
this.makeIdle(worker);
|
|
992
1011
|
}
|
|
993
1012
|
};
|
|
994
|
-
worker.on("message", (
|
|
995
|
-
const response = msg;
|
|
1013
|
+
worker.on("message", (response) => {
|
|
996
1014
|
if (response.type === "ready") {
|
|
997
1015
|
onReady();
|
|
998
1016
|
return;
|
|
@@ -1000,12 +1018,7 @@ var WorkerPool = class {
|
|
|
1000
1018
|
this.handleWorkerMessage(worker, response);
|
|
1001
1019
|
});
|
|
1002
1020
|
worker.on("error", (err) => {
|
|
1003
|
-
|
|
1004
|
-
if (w === worker) {
|
|
1005
|
-
this.exclusiveWorkers.delete(taskId);
|
|
1006
|
-
break;
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1021
|
+
this.rejectExclusiveTaskForWorker(worker, err);
|
|
1009
1022
|
const taskSet = this.sharedWorkers.get(worker);
|
|
1010
1023
|
if (taskSet) {
|
|
1011
1024
|
for (const taskId of taskSet) {
|
|
@@ -1025,12 +1038,7 @@ var WorkerPool = class {
|
|
|
1025
1038
|
if (idleIdx !== -1) {
|
|
1026
1039
|
this.idleWorkers.splice(idleIdx, 1);
|
|
1027
1040
|
}
|
|
1028
|
-
|
|
1029
|
-
if (w === worker) {
|
|
1030
|
-
this.exclusiveWorkers.delete(taskId);
|
|
1031
|
-
break;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1041
|
+
this.rejectExclusiveTaskForWorker(worker, new Error("Worker exited unexpectedly"));
|
|
1034
1042
|
const taskSet = this.sharedWorkers.get(worker);
|
|
1035
1043
|
if (taskSet) {
|
|
1036
1044
|
for (const taskId of taskSet) {
|
|
@@ -1100,6 +1108,12 @@ function stats() {
|
|
|
1100
1108
|
function resize(maxThreads) {
|
|
1101
1109
|
getPool().resize(maxThreads);
|
|
1102
1110
|
}
|
|
1111
|
+
async function shutdown() {
|
|
1112
|
+
if (poolInstance) {
|
|
1113
|
+
await poolInstance.drain();
|
|
1114
|
+
poolInstance = null;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1103
1117
|
|
|
1104
1118
|
// src/spawn.ts
|
|
1105
1119
|
var taskCounter = 0;
|
|
@@ -1118,14 +1132,10 @@ function spawn(fn, opts) {
|
|
|
1118
1132
|
if (opts?.channels) {
|
|
1119
1133
|
channelMap = {};
|
|
1120
1134
|
for (const [name, ch] of Object.entries(opts.channels)) {
|
|
1121
|
-
|
|
1122
|
-
if (!impl._id) {
|
|
1123
|
-
throw new Error(`Channel "${name}" is not a valid puru channel`);
|
|
1124
|
-
}
|
|
1125
|
-
channelMap[name] = impl._id;
|
|
1135
|
+
channelMap[name] = getChannelId(ch);
|
|
1126
1136
|
}
|
|
1127
1137
|
}
|
|
1128
|
-
const
|
|
1138
|
+
const task2 = {
|
|
1129
1139
|
id: taskId,
|
|
1130
1140
|
fnStr,
|
|
1131
1141
|
priority: opts?.priority ?? "normal",
|
|
@@ -1148,7 +1158,7 @@ function spawn(fn, opts) {
|
|
|
1148
1158
|
}
|
|
1149
1159
|
}
|
|
1150
1160
|
};
|
|
1151
|
-
getPool().submit(
|
|
1161
|
+
getPool().submit(task2);
|
|
1152
1162
|
const cancel = () => {
|
|
1153
1163
|
if (settled) return;
|
|
1154
1164
|
settled = true;
|
|
@@ -1162,9 +1172,18 @@ function spawn(fn, opts) {
|
|
|
1162
1172
|
var WaitGroup = class {
|
|
1163
1173
|
tasks = [];
|
|
1164
1174
|
controller = new AbortController();
|
|
1175
|
+
/**
|
|
1176
|
+
* An `AbortSignal` shared across all tasks in this group.
|
|
1177
|
+
* Pass it into spawned functions so they can stop early when `cancel()` is called.
|
|
1178
|
+
*/
|
|
1165
1179
|
get signal() {
|
|
1166
1180
|
return this.controller.signal;
|
|
1167
1181
|
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Spawns a function on a worker thread and adds it to the group.
|
|
1184
|
+
*
|
|
1185
|
+
* @throws If the group has already been cancelled.
|
|
1186
|
+
*/
|
|
1168
1187
|
spawn(fn, opts) {
|
|
1169
1188
|
if (this.controller.signal.aborted) {
|
|
1170
1189
|
throw new Error("WaitGroup has been cancelled");
|
|
@@ -1172,16 +1191,28 @@ var WaitGroup = class {
|
|
|
1172
1191
|
const handle = spawn(fn, opts);
|
|
1173
1192
|
this.tasks.push(handle);
|
|
1174
1193
|
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Waits for all tasks to complete successfully.
|
|
1196
|
+
* Rejects as soon as any task throws.
|
|
1197
|
+
*/
|
|
1175
1198
|
async wait() {
|
|
1176
1199
|
return Promise.all(this.tasks.map((t) => t.result));
|
|
1177
1200
|
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Waits for all tasks to settle (fulfilled or rejected) and returns each outcome.
|
|
1203
|
+
* Never rejects — inspect each `PromiseSettledResult` to handle failures individually.
|
|
1204
|
+
*/
|
|
1178
1205
|
async waitSettled() {
|
|
1179
1206
|
return Promise.allSettled(this.tasks.map((t) => t.result));
|
|
1180
1207
|
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Cancels all tasks in the group and signals the shared `AbortSignal`.
|
|
1210
|
+
* Already-settled tasks are unaffected.
|
|
1211
|
+
*/
|
|
1181
1212
|
cancel() {
|
|
1182
1213
|
this.controller.abort();
|
|
1183
|
-
for (const
|
|
1184
|
-
|
|
1214
|
+
for (const task2 of this.tasks) {
|
|
1215
|
+
task2.cancel();
|
|
1185
1216
|
}
|
|
1186
1217
|
}
|
|
1187
1218
|
};
|
|
@@ -1190,7 +1221,7 @@ var WaitGroup = class {
|
|
|
1190
1221
|
var ErrGroup = class {
|
|
1191
1222
|
tasks = [];
|
|
1192
1223
|
controller = new AbortController();
|
|
1193
|
-
firstError =
|
|
1224
|
+
firstError = null;
|
|
1194
1225
|
hasError = false;
|
|
1195
1226
|
get signal() {
|
|
1196
1227
|
return this.controller.signal;
|
|
@@ -1211,7 +1242,7 @@ var ErrGroup = class {
|
|
|
1211
1242
|
}
|
|
1212
1243
|
async wait() {
|
|
1213
1244
|
const settled = await Promise.allSettled(this.tasks.map((t) => t.result));
|
|
1214
|
-
if (this.hasError) {
|
|
1245
|
+
if (this.hasError && this.firstError) {
|
|
1215
1246
|
throw this.firstError;
|
|
1216
1247
|
}
|
|
1217
1248
|
return settled.map((r) => {
|
|
@@ -1221,8 +1252,8 @@ var ErrGroup = class {
|
|
|
1221
1252
|
}
|
|
1222
1253
|
cancel() {
|
|
1223
1254
|
this.controller.abort();
|
|
1224
|
-
for (const
|
|
1225
|
-
|
|
1255
|
+
for (const task2 of this.tasks) {
|
|
1256
|
+
task2.cancel();
|
|
1226
1257
|
}
|
|
1227
1258
|
}
|
|
1228
1259
|
};
|
|
@@ -1367,7 +1398,7 @@ var Ticker = class {
|
|
|
1367
1398
|
if (this.resolve) {
|
|
1368
1399
|
const r = this.resolve;
|
|
1369
1400
|
this.resolve = null;
|
|
1370
|
-
r();
|
|
1401
|
+
r(true);
|
|
1371
1402
|
}
|
|
1372
1403
|
}, ms);
|
|
1373
1404
|
if (this.interval.unref) this.interval.unref();
|
|
@@ -1375,7 +1406,11 @@ var Ticker = class {
|
|
|
1375
1406
|
async tick() {
|
|
1376
1407
|
if (this.stopped) return false;
|
|
1377
1408
|
return new Promise((resolve) => {
|
|
1378
|
-
this.resolve =
|
|
1409
|
+
this.resolve = resolve;
|
|
1410
|
+
if (this.stopped) {
|
|
1411
|
+
this.resolve = null;
|
|
1412
|
+
resolve(false);
|
|
1413
|
+
}
|
|
1379
1414
|
});
|
|
1380
1415
|
}
|
|
1381
1416
|
stop() {
|
|
@@ -1387,7 +1422,7 @@ var Ticker = class {
|
|
|
1387
1422
|
if (this.resolve) {
|
|
1388
1423
|
const r = this.resolve;
|
|
1389
1424
|
this.resolve = null;
|
|
1390
|
-
r();
|
|
1425
|
+
r(false);
|
|
1391
1426
|
}
|
|
1392
1427
|
}
|
|
1393
1428
|
async *[Symbol.asyncIterator]() {
|
|
@@ -1401,54 +1436,45 @@ function ticker(ms) {
|
|
|
1401
1436
|
}
|
|
1402
1437
|
|
|
1403
1438
|
// src/registry.ts
|
|
1404
|
-
var registry = /* @__PURE__ */ new Map();
|
|
1405
1439
|
var taskCounter2 = 0;
|
|
1406
|
-
function
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
throw new Error(`Task "${name}" is not registered. Call register() first.`);
|
|
1416
|
-
}
|
|
1417
|
-
const fnStr = serializeFunction(fn);
|
|
1418
|
-
const serializedArgs = args.map((a) => {
|
|
1419
|
-
const json = JSON.stringify(a);
|
|
1420
|
-
if (json === void 0) {
|
|
1421
|
-
throw new TypeError(
|
|
1422
|
-
`Argument of type ${typeof a} is not JSON-serializable. run() args must be JSON-serializable (no undefined, functions, symbols, or BigInt).`
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
return json;
|
|
1426
|
-
});
|
|
1427
|
-
const wrapperStr = `() => (${fnStr})(${serializedArgs.join(", ")})`;
|
|
1428
|
-
const taskId = `reg_${++taskCounter2}`;
|
|
1429
|
-
const spawnStack = new Error().stack;
|
|
1430
|
-
let resolveFn;
|
|
1431
|
-
let rejectFn;
|
|
1432
|
-
const result = new Promise((resolve, reject) => {
|
|
1433
|
-
resolveFn = resolve;
|
|
1434
|
-
rejectFn = reject;
|
|
1435
|
-
});
|
|
1436
|
-
const task = {
|
|
1437
|
-
id: taskId,
|
|
1438
|
-
fnStr: wrapperStr,
|
|
1439
|
-
priority: "normal",
|
|
1440
|
-
concurrent: false,
|
|
1441
|
-
resolve: (value) => resolveFn(value),
|
|
1442
|
-
reject: (reason) => {
|
|
1443
|
-
if (reason instanceof Error && spawnStack) {
|
|
1444
|
-
const callerLine = spawnStack.split("\n").slice(2).join("\n");
|
|
1445
|
-
reason.stack = (reason.stack ?? reason.message) + "\n --- spawned at ---\n" + callerLine;
|
|
1440
|
+
function task(fn) {
|
|
1441
|
+
return (...args) => {
|
|
1442
|
+
const fnStr = serializeFunction(fn);
|
|
1443
|
+
const serializedArgs = args.map((a) => {
|
|
1444
|
+
const json = JSON.stringify(a);
|
|
1445
|
+
if (json === void 0) {
|
|
1446
|
+
throw new TypeError(
|
|
1447
|
+
`Argument of type ${typeof a} is not JSON-serializable. task() args must be JSON-serializable (no undefined, functions, symbols, or BigInt).`
|
|
1448
|
+
);
|
|
1446
1449
|
}
|
|
1447
|
-
|
|
1448
|
-
}
|
|
1450
|
+
return json;
|
|
1451
|
+
});
|
|
1452
|
+
const wrapperStr = `() => (${fnStr})(${serializedArgs.join(", ")})`;
|
|
1453
|
+
const taskId = `task_${++taskCounter2}`;
|
|
1454
|
+
const spawnStack = new Error().stack;
|
|
1455
|
+
let resolveFn;
|
|
1456
|
+
let rejectFn;
|
|
1457
|
+
const result = new Promise((resolve, reject) => {
|
|
1458
|
+
resolveFn = resolve;
|
|
1459
|
+
rejectFn = reject;
|
|
1460
|
+
});
|
|
1461
|
+
const taskObj = {
|
|
1462
|
+
id: taskId,
|
|
1463
|
+
fnStr: wrapperStr,
|
|
1464
|
+
priority: "normal",
|
|
1465
|
+
concurrent: false,
|
|
1466
|
+
resolve: (value) => resolveFn(value),
|
|
1467
|
+
reject: (reason) => {
|
|
1468
|
+
if (reason instanceof Error && spawnStack) {
|
|
1469
|
+
const callerLine = spawnStack.split("\n").slice(2).join("\n");
|
|
1470
|
+
reason.stack = (reason.stack ?? reason.message) + "\n --- spawned at ---\n" + callerLine;
|
|
1471
|
+
}
|
|
1472
|
+
rejectFn(reason);
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
getPool().submit(taskObj);
|
|
1476
|
+
return result;
|
|
1449
1477
|
};
|
|
1450
|
-
getPool().submit(task);
|
|
1451
|
-
return result;
|
|
1452
1478
|
}
|
|
1453
1479
|
export {
|
|
1454
1480
|
ErrGroup,
|
|
@@ -1461,12 +1487,12 @@ export {
|
|
|
1461
1487
|
configure,
|
|
1462
1488
|
detectCapability,
|
|
1463
1489
|
detectRuntime,
|
|
1464
|
-
register,
|
|
1465
1490
|
resize,
|
|
1466
|
-
run,
|
|
1467
1491
|
select,
|
|
1492
|
+
shutdown,
|
|
1468
1493
|
spawn,
|
|
1469
1494
|
stats,
|
|
1495
|
+
task,
|
|
1470
1496
|
ticker
|
|
1471
1497
|
};
|
|
1472
1498
|
//# sourceMappingURL=index.js.map
|