@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.cjs
CHANGED
|
@@ -30,12 +30,12 @@ __export(index_exports, {
|
|
|
30
30
|
configure: () => configure,
|
|
31
31
|
detectCapability: () => detectCapability,
|
|
32
32
|
detectRuntime: () => detectRuntime,
|
|
33
|
-
register: () => register,
|
|
34
33
|
resize: () => resize,
|
|
35
|
-
run: () => run,
|
|
36
34
|
select: () => select,
|
|
35
|
+
shutdown: () => shutdown,
|
|
37
36
|
spawn: () => spawn,
|
|
38
37
|
stats: () => stats,
|
|
38
|
+
task: () => task,
|
|
39
39
|
ticker: () => ticker
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -98,16 +98,15 @@ function getConfig() {
|
|
|
98
98
|
|
|
99
99
|
// src/runtime.ts
|
|
100
100
|
function detectRuntime() {
|
|
101
|
-
if (
|
|
102
|
-
if (
|
|
103
|
-
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node)
|
|
104
|
-
return "node";
|
|
101
|
+
if ("Bun" in globalThis) return "bun";
|
|
102
|
+
if ("Deno" in globalThis) return "deno";
|
|
103
|
+
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node) return "node";
|
|
105
104
|
return "browser";
|
|
106
105
|
}
|
|
107
106
|
function detectCapability() {
|
|
108
107
|
const runtime = detectRuntime();
|
|
109
108
|
if (runtime === "node" || runtime === "bun") return "full-threads";
|
|
110
|
-
if (
|
|
109
|
+
if ("Worker" in globalThis) return "full-threads";
|
|
111
110
|
return "single-thread";
|
|
112
111
|
}
|
|
113
112
|
|
|
@@ -374,7 +373,11 @@ var BunManagedWorker = class {
|
|
|
374
373
|
id;
|
|
375
374
|
constructor() {
|
|
376
375
|
this.id = ++workerIdCounter;
|
|
377
|
-
|
|
376
|
+
const WorkerConstructor = globalThis.Worker;
|
|
377
|
+
if (!WorkerConstructor) {
|
|
378
|
+
throw new Error("Bun Worker constructor is not available in this runtime");
|
|
379
|
+
}
|
|
380
|
+
this.worker = new WorkerConstructor(getBootstrapFile());
|
|
378
381
|
}
|
|
379
382
|
postMessage(data) {
|
|
380
383
|
this.worker.postMessage(data);
|
|
@@ -386,27 +389,28 @@ var BunManagedWorker = class {
|
|
|
386
389
|
on(event, handler) {
|
|
387
390
|
if (event === "message") {
|
|
388
391
|
this.worker.addEventListener("message", (e) => {
|
|
392
|
+
;
|
|
389
393
|
handler(e.data);
|
|
390
394
|
});
|
|
391
395
|
} else if (event === "error") {
|
|
392
396
|
this.worker.addEventListener("error", (e) => {
|
|
397
|
+
;
|
|
393
398
|
handler(e.error ?? new Error(e.message));
|
|
394
399
|
});
|
|
395
400
|
} else if (event === "exit") {
|
|
396
401
|
this.worker.addEventListener("close", (e) => {
|
|
402
|
+
;
|
|
397
403
|
handler(e.code ?? 0);
|
|
398
404
|
});
|
|
399
405
|
}
|
|
400
406
|
}
|
|
401
407
|
unref() {
|
|
402
408
|
if ("unref" in this.worker && typeof this.worker.unref === "function") {
|
|
403
|
-
;
|
|
404
409
|
this.worker.unref();
|
|
405
410
|
}
|
|
406
411
|
}
|
|
407
412
|
ref() {
|
|
408
413
|
if ("ref" in this.worker && typeof this.worker.ref === "function") {
|
|
409
|
-
;
|
|
410
414
|
this.worker.ref();
|
|
411
415
|
}
|
|
412
416
|
}
|
|
@@ -418,9 +422,11 @@ var BunWorkerAdapter = class {
|
|
|
418
422
|
};
|
|
419
423
|
|
|
420
424
|
// src/channel.ts
|
|
425
|
+
var CLOSED = /* @__PURE__ */ Symbol("puru.channel.closed");
|
|
421
426
|
var channelIdCounter = 0;
|
|
422
427
|
var channelRegistry = /* @__PURE__ */ new Map();
|
|
423
428
|
var ChannelImpl = class {
|
|
429
|
+
// constraint: can't create channels of nullable type
|
|
424
430
|
/** @internal */
|
|
425
431
|
_id;
|
|
426
432
|
buffer = [];
|
|
@@ -469,14 +475,16 @@ var ChannelImpl = class {
|
|
|
469
475
|
return Promise.resolve(null);
|
|
470
476
|
}
|
|
471
477
|
return new Promise((resolve) => {
|
|
472
|
-
this.recvQueue.push({
|
|
478
|
+
this.recvQueue.push({
|
|
479
|
+
resolve: (v) => resolve(v === CLOSED ? null : v)
|
|
480
|
+
});
|
|
473
481
|
});
|
|
474
482
|
}
|
|
475
483
|
close() {
|
|
476
484
|
if (this.closed) return;
|
|
477
485
|
this.closed = true;
|
|
478
486
|
for (const receiver of this.recvQueue) {
|
|
479
|
-
receiver.resolve(
|
|
487
|
+
receiver.resolve(CLOSED);
|
|
480
488
|
}
|
|
481
489
|
this.recvQueue = [];
|
|
482
490
|
for (const sender of this.sendQueue) {
|
|
@@ -501,6 +509,9 @@ function chan(capacity = 0) {
|
|
|
501
509
|
function getChannelById(id) {
|
|
502
510
|
return channelRegistry.get(id);
|
|
503
511
|
}
|
|
512
|
+
function getChannelId(channel) {
|
|
513
|
+
return channel._id;
|
|
514
|
+
}
|
|
504
515
|
|
|
505
516
|
// src/adapters/inline.ts
|
|
506
517
|
var inlineIdCounter = 0;
|
|
@@ -517,15 +528,14 @@ var InlineManagedWorker = class {
|
|
|
517
528
|
this.emit("message", { type: "ready" });
|
|
518
529
|
});
|
|
519
530
|
}
|
|
520
|
-
postMessage(
|
|
531
|
+
postMessage(msg) {
|
|
521
532
|
if (this.terminated) return;
|
|
522
|
-
const msg = data;
|
|
523
533
|
if (msg.type === "execute") {
|
|
524
|
-
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent
|
|
534
|
+
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent, msg.channels);
|
|
525
535
|
} else if (msg.type === "cancel") {
|
|
526
536
|
this.cancelledTasks.add(msg.taskId);
|
|
527
537
|
} else if (msg.type === "channel-result") {
|
|
528
|
-
|
|
538
|
+
return;
|
|
529
539
|
} else if (msg.type === "shutdown") {
|
|
530
540
|
this.terminated = true;
|
|
531
541
|
this.emit("exit", 0);
|
|
@@ -555,7 +565,6 @@ var InlineManagedWorker = class {
|
|
|
555
565
|
}
|
|
556
566
|
}
|
|
557
567
|
buildChannelProxies(channels) {
|
|
558
|
-
const self = this;
|
|
559
568
|
const proxies = {};
|
|
560
569
|
for (const [name, channelId] of Object.entries(channels)) {
|
|
561
570
|
proxies[name] = {
|
|
@@ -577,13 +586,7 @@ var InlineManagedWorker = class {
|
|
|
577
586
|
[Symbol.asyncIterator]() {
|
|
578
587
|
const ch = getChannelById(channelId);
|
|
579
588
|
if (!ch) throw new Error(`Channel ${channelId} not found`);
|
|
580
|
-
return
|
|
581
|
-
async next() {
|
|
582
|
-
const value = await ch.recv();
|
|
583
|
-
if (value === null) return { done: true, value: void 0 };
|
|
584
|
-
return { done: false, value };
|
|
585
|
-
}
|
|
586
|
-
};
|
|
589
|
+
return ch[Symbol.asyncIterator]();
|
|
587
590
|
}
|
|
588
591
|
};
|
|
589
592
|
}
|
|
@@ -662,14 +665,14 @@ var WorkerPool = class {
|
|
|
662
665
|
this.adapter = adapter;
|
|
663
666
|
}
|
|
664
667
|
// --- Queue helpers ---
|
|
665
|
-
enqueue(
|
|
666
|
-
this.queues[
|
|
668
|
+
enqueue(task2) {
|
|
669
|
+
this.queues[task2.priority].push(task2);
|
|
667
670
|
}
|
|
668
671
|
dequeue() {
|
|
669
672
|
return this.queues.high.shift() ?? this.queues.normal.shift() ?? this.queues.low.shift();
|
|
670
673
|
}
|
|
671
|
-
enqueueConcurrent(
|
|
672
|
-
this.concurrentQueues[
|
|
674
|
+
enqueueConcurrent(task2) {
|
|
675
|
+
this.concurrentQueues[task2.priority].push(task2);
|
|
673
676
|
}
|
|
674
677
|
dequeueConcurrent() {
|
|
675
678
|
return this.concurrentQueues.high.shift() ?? this.concurrentQueues.normal.shift() ?? this.concurrentQueues.low.shift();
|
|
@@ -695,73 +698,73 @@ var WorkerPool = class {
|
|
|
695
698
|
return void 0;
|
|
696
699
|
}
|
|
697
700
|
// --- Submit ---
|
|
698
|
-
submit(
|
|
701
|
+
submit(task2) {
|
|
699
702
|
if (this.draining) {
|
|
700
|
-
|
|
703
|
+
task2.reject(new Error("Pool is shutting down"));
|
|
701
704
|
return;
|
|
702
705
|
}
|
|
703
|
-
if (
|
|
704
|
-
this.submitConcurrent(
|
|
706
|
+
if (task2.concurrent) {
|
|
707
|
+
this.submitConcurrent(task2);
|
|
705
708
|
} else {
|
|
706
|
-
this.submitExclusive(
|
|
709
|
+
this.submitExclusive(task2);
|
|
707
710
|
}
|
|
708
711
|
}
|
|
709
|
-
submitExclusive(
|
|
712
|
+
submitExclusive(task2) {
|
|
710
713
|
const worker = this.idleWorkers.pop();
|
|
711
714
|
if (worker) {
|
|
712
|
-
this.dispatch(worker,
|
|
715
|
+
this.dispatch(worker, task2);
|
|
713
716
|
return;
|
|
714
717
|
}
|
|
715
718
|
const totalWorkers = this.allWorkers.size + this.pendingWorkerCount;
|
|
716
719
|
if (totalWorkers < this.config.maxThreads) {
|
|
717
720
|
this.pendingWorkerCount++;
|
|
718
|
-
this.pendingTasksForWorkers.push(
|
|
721
|
+
this.pendingTasksForWorkers.push(task2);
|
|
719
722
|
this.createAndReadyWorker();
|
|
720
723
|
return;
|
|
721
724
|
}
|
|
722
|
-
this.enqueue(
|
|
725
|
+
this.enqueue(task2);
|
|
723
726
|
}
|
|
724
|
-
submitConcurrent(
|
|
727
|
+
submitConcurrent(task2) {
|
|
725
728
|
for (const [worker, tasks] of this.sharedWorkers) {
|
|
726
729
|
if (tasks.size < this.config.concurrency) {
|
|
727
|
-
this.dispatchConcurrent(worker,
|
|
730
|
+
this.dispatchConcurrent(worker, task2);
|
|
728
731
|
return;
|
|
729
732
|
}
|
|
730
733
|
}
|
|
731
734
|
const idleWorker = this.idleWorkers.pop();
|
|
732
735
|
if (idleWorker) {
|
|
733
|
-
this.dispatchConcurrent(idleWorker,
|
|
736
|
+
this.dispatchConcurrent(idleWorker, task2);
|
|
734
737
|
return;
|
|
735
738
|
}
|
|
736
739
|
const totalWorkers = this.allWorkers.size + this.pendingWorkerCount;
|
|
737
740
|
if (totalWorkers < this.config.maxThreads) {
|
|
738
741
|
this.pendingWorkerCount++;
|
|
739
|
-
this.pendingTasksForWorkers.push(
|
|
742
|
+
this.pendingTasksForWorkers.push(task2);
|
|
740
743
|
this.createAndReadyWorker();
|
|
741
744
|
return;
|
|
742
745
|
}
|
|
743
|
-
this.enqueueConcurrent(
|
|
746
|
+
this.enqueueConcurrent(task2);
|
|
744
747
|
}
|
|
745
748
|
// --- Dispatch ---
|
|
746
|
-
dispatch(worker,
|
|
749
|
+
dispatch(worker, task2) {
|
|
747
750
|
const timer = this.idleTimers.get(worker);
|
|
748
751
|
if (timer) {
|
|
749
752
|
clearTimeout(timer);
|
|
750
753
|
this.idleTimers.delete(worker);
|
|
751
754
|
}
|
|
752
755
|
worker.ref();
|
|
753
|
-
this.exclusiveWorkers.set(
|
|
754
|
-
this.taskMap.set(
|
|
756
|
+
this.exclusiveWorkers.set(task2.id, worker);
|
|
757
|
+
this.taskMap.set(task2.id, task2);
|
|
755
758
|
const msg = {
|
|
756
759
|
type: "execute",
|
|
757
|
-
taskId:
|
|
758
|
-
fnStr:
|
|
760
|
+
taskId: task2.id,
|
|
761
|
+
fnStr: task2.fnStr,
|
|
759
762
|
concurrent: false,
|
|
760
|
-
channels:
|
|
763
|
+
channels: task2.channels
|
|
761
764
|
};
|
|
762
765
|
worker.postMessage(msg);
|
|
763
766
|
}
|
|
764
|
-
dispatchConcurrent(worker,
|
|
767
|
+
dispatchConcurrent(worker, task2) {
|
|
765
768
|
const timer = this.idleTimers.get(worker);
|
|
766
769
|
if (timer) {
|
|
767
770
|
clearTimeout(timer);
|
|
@@ -771,14 +774,14 @@ var WorkerPool = class {
|
|
|
771
774
|
if (!this.sharedWorkers.has(worker)) {
|
|
772
775
|
this.sharedWorkers.set(worker, /* @__PURE__ */ new Set());
|
|
773
776
|
}
|
|
774
|
-
this.sharedWorkers.get(worker).add(
|
|
775
|
-
this.taskMap.set(
|
|
777
|
+
this.sharedWorkers.get(worker).add(task2.id);
|
|
778
|
+
this.taskMap.set(task2.id, task2);
|
|
776
779
|
const msg = {
|
|
777
780
|
type: "execute",
|
|
778
|
-
taskId:
|
|
779
|
-
fnStr:
|
|
781
|
+
taskId: task2.id,
|
|
782
|
+
fnStr: task2.fnStr,
|
|
780
783
|
concurrent: true,
|
|
781
|
-
channels:
|
|
784
|
+
channels: task2.channels
|
|
782
785
|
};
|
|
783
786
|
worker.postMessage(msg);
|
|
784
787
|
}
|
|
@@ -805,6 +808,9 @@ var WorkerPool = class {
|
|
|
805
808
|
const taskId = response.taskId;
|
|
806
809
|
const err = new Error(response.message);
|
|
807
810
|
if (response.stack) err.stack = response.stack;
|
|
811
|
+
if (response.message.match(/^ReferenceError:/) || response.message.match(/ is not defined$/)) {
|
|
812
|
+
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.";
|
|
813
|
+
}
|
|
808
814
|
this.rejectTask(taskId, err);
|
|
809
815
|
if (this.exclusiveWorkers.has(taskId)) {
|
|
810
816
|
this.exclusiveWorkers.delete(taskId);
|
|
@@ -913,19 +919,28 @@ var WorkerPool = class {
|
|
|
913
919
|
}
|
|
914
920
|
// --- Task resolution ---
|
|
915
921
|
resolveTask(taskId, value) {
|
|
916
|
-
const
|
|
917
|
-
if (
|
|
922
|
+
const task2 = this.taskMap.get(taskId);
|
|
923
|
+
if (task2) {
|
|
918
924
|
this.taskMap.delete(taskId);
|
|
919
925
|
this.totalCompleted++;
|
|
920
|
-
|
|
926
|
+
task2.resolve(value);
|
|
921
927
|
}
|
|
922
928
|
}
|
|
923
929
|
rejectTask(taskId, reason) {
|
|
924
|
-
const
|
|
925
|
-
if (
|
|
930
|
+
const task2 = this.taskMap.get(taskId);
|
|
931
|
+
if (task2) {
|
|
926
932
|
this.taskMap.delete(taskId);
|
|
927
933
|
this.totalFailed++;
|
|
928
|
-
|
|
934
|
+
task2.reject(reason);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
rejectExclusiveTaskForWorker(worker, reason) {
|
|
938
|
+
for (const [taskId, assignedWorker] of this.exclusiveWorkers) {
|
|
939
|
+
if (assignedWorker === worker) {
|
|
940
|
+
this.exclusiveWorkers.delete(taskId);
|
|
941
|
+
this.rejectTask(taskId, reason);
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
929
944
|
}
|
|
930
945
|
}
|
|
931
946
|
// --- Cancellation ---
|
|
@@ -965,17 +980,21 @@ var WorkerPool = class {
|
|
|
965
980
|
async drain() {
|
|
966
981
|
this.draining = true;
|
|
967
982
|
for (const priority of ["high", "normal", "low"]) {
|
|
968
|
-
for (const
|
|
969
|
-
|
|
983
|
+
for (const task2 of this.queues[priority]) {
|
|
984
|
+
task2.reject(new Error("Pool is shutting down"));
|
|
970
985
|
}
|
|
971
986
|
this.queues[priority] = [];
|
|
972
987
|
}
|
|
973
988
|
for (const priority of ["high", "normal", "low"]) {
|
|
974
|
-
for (const
|
|
975
|
-
|
|
989
|
+
for (const task2 of this.concurrentQueues[priority]) {
|
|
990
|
+
task2.reject(new Error("Pool is shutting down"));
|
|
976
991
|
}
|
|
977
992
|
this.concurrentQueues[priority] = [];
|
|
978
993
|
}
|
|
994
|
+
for (const [taskId] of this.exclusiveWorkers) {
|
|
995
|
+
this.taskMap.delete(taskId);
|
|
996
|
+
}
|
|
997
|
+
this.exclusiveWorkers.clear();
|
|
979
998
|
for (const [, taskSet] of this.sharedWorkers) {
|
|
980
999
|
for (const taskId of taskSet) {
|
|
981
1000
|
this.taskMap.delete(taskId);
|
|
@@ -999,10 +1018,10 @@ var WorkerPool = class {
|
|
|
999
1018
|
while (true) {
|
|
1000
1019
|
const totalWorkers = this.allWorkers.size + this.pendingWorkerCount;
|
|
1001
1020
|
if (totalWorkers >= maxThreads) break;
|
|
1002
|
-
const
|
|
1003
|
-
if (!
|
|
1021
|
+
const task2 = this.dequeue() ?? this.dequeueConcurrent();
|
|
1022
|
+
if (!task2) break;
|
|
1004
1023
|
this.pendingWorkerCount++;
|
|
1005
|
-
this.pendingTasksForWorkers.push(
|
|
1024
|
+
this.pendingTasksForWorkers.push(task2);
|
|
1006
1025
|
this.createAndReadyWorker();
|
|
1007
1026
|
}
|
|
1008
1027
|
while (this.allWorkers.size > maxThreads && this.idleWorkers.length > 0) {
|
|
@@ -1022,19 +1041,18 @@ var WorkerPool = class {
|
|
|
1022
1041
|
const onReady = () => {
|
|
1023
1042
|
this.pendingWorkerCount--;
|
|
1024
1043
|
this.allWorkers.add(worker);
|
|
1025
|
-
const
|
|
1026
|
-
if (
|
|
1027
|
-
if (
|
|
1028
|
-
this.dispatchConcurrent(worker,
|
|
1044
|
+
const task2 = this.pendingTasksForWorkers.shift();
|
|
1045
|
+
if (task2) {
|
|
1046
|
+
if (task2.concurrent) {
|
|
1047
|
+
this.dispatchConcurrent(worker, task2);
|
|
1029
1048
|
} else {
|
|
1030
|
-
this.dispatch(worker,
|
|
1049
|
+
this.dispatch(worker, task2);
|
|
1031
1050
|
}
|
|
1032
1051
|
} else {
|
|
1033
1052
|
this.makeIdle(worker);
|
|
1034
1053
|
}
|
|
1035
1054
|
};
|
|
1036
|
-
worker.on("message", (
|
|
1037
|
-
const response = msg;
|
|
1055
|
+
worker.on("message", (response) => {
|
|
1038
1056
|
if (response.type === "ready") {
|
|
1039
1057
|
onReady();
|
|
1040
1058
|
return;
|
|
@@ -1042,12 +1060,7 @@ var WorkerPool = class {
|
|
|
1042
1060
|
this.handleWorkerMessage(worker, response);
|
|
1043
1061
|
});
|
|
1044
1062
|
worker.on("error", (err) => {
|
|
1045
|
-
|
|
1046
|
-
if (w === worker) {
|
|
1047
|
-
this.exclusiveWorkers.delete(taskId);
|
|
1048
|
-
break;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1063
|
+
this.rejectExclusiveTaskForWorker(worker, err);
|
|
1051
1064
|
const taskSet = this.sharedWorkers.get(worker);
|
|
1052
1065
|
if (taskSet) {
|
|
1053
1066
|
for (const taskId of taskSet) {
|
|
@@ -1067,12 +1080,7 @@ var WorkerPool = class {
|
|
|
1067
1080
|
if (idleIdx !== -1) {
|
|
1068
1081
|
this.idleWorkers.splice(idleIdx, 1);
|
|
1069
1082
|
}
|
|
1070
|
-
|
|
1071
|
-
if (w === worker) {
|
|
1072
|
-
this.exclusiveWorkers.delete(taskId);
|
|
1073
|
-
break;
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1083
|
+
this.rejectExclusiveTaskForWorker(worker, new Error("Worker exited unexpectedly"));
|
|
1076
1084
|
const taskSet = this.sharedWorkers.get(worker);
|
|
1077
1085
|
if (taskSet) {
|
|
1078
1086
|
for (const taskId of taskSet) {
|
|
@@ -1142,6 +1150,12 @@ function stats() {
|
|
|
1142
1150
|
function resize(maxThreads) {
|
|
1143
1151
|
getPool().resize(maxThreads);
|
|
1144
1152
|
}
|
|
1153
|
+
async function shutdown() {
|
|
1154
|
+
if (poolInstance) {
|
|
1155
|
+
await poolInstance.drain();
|
|
1156
|
+
poolInstance = null;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1145
1159
|
|
|
1146
1160
|
// src/spawn.ts
|
|
1147
1161
|
var taskCounter = 0;
|
|
@@ -1160,14 +1174,10 @@ function spawn(fn, opts) {
|
|
|
1160
1174
|
if (opts?.channels) {
|
|
1161
1175
|
channelMap = {};
|
|
1162
1176
|
for (const [name, ch] of Object.entries(opts.channels)) {
|
|
1163
|
-
|
|
1164
|
-
if (!impl._id) {
|
|
1165
|
-
throw new Error(`Channel "${name}" is not a valid puru channel`);
|
|
1166
|
-
}
|
|
1167
|
-
channelMap[name] = impl._id;
|
|
1177
|
+
channelMap[name] = getChannelId(ch);
|
|
1168
1178
|
}
|
|
1169
1179
|
}
|
|
1170
|
-
const
|
|
1180
|
+
const task2 = {
|
|
1171
1181
|
id: taskId,
|
|
1172
1182
|
fnStr,
|
|
1173
1183
|
priority: opts?.priority ?? "normal",
|
|
@@ -1190,7 +1200,7 @@ function spawn(fn, opts) {
|
|
|
1190
1200
|
}
|
|
1191
1201
|
}
|
|
1192
1202
|
};
|
|
1193
|
-
getPool().submit(
|
|
1203
|
+
getPool().submit(task2);
|
|
1194
1204
|
const cancel = () => {
|
|
1195
1205
|
if (settled) return;
|
|
1196
1206
|
settled = true;
|
|
@@ -1204,9 +1214,18 @@ function spawn(fn, opts) {
|
|
|
1204
1214
|
var WaitGroup = class {
|
|
1205
1215
|
tasks = [];
|
|
1206
1216
|
controller = new AbortController();
|
|
1217
|
+
/**
|
|
1218
|
+
* An `AbortSignal` shared across all tasks in this group.
|
|
1219
|
+
* Pass it into spawned functions so they can stop early when `cancel()` is called.
|
|
1220
|
+
*/
|
|
1207
1221
|
get signal() {
|
|
1208
1222
|
return this.controller.signal;
|
|
1209
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Spawns a function on a worker thread and adds it to the group.
|
|
1226
|
+
*
|
|
1227
|
+
* @throws If the group has already been cancelled.
|
|
1228
|
+
*/
|
|
1210
1229
|
spawn(fn, opts) {
|
|
1211
1230
|
if (this.controller.signal.aborted) {
|
|
1212
1231
|
throw new Error("WaitGroup has been cancelled");
|
|
@@ -1214,16 +1233,28 @@ var WaitGroup = class {
|
|
|
1214
1233
|
const handle = spawn(fn, opts);
|
|
1215
1234
|
this.tasks.push(handle);
|
|
1216
1235
|
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Waits for all tasks to complete successfully.
|
|
1238
|
+
* Rejects as soon as any task throws.
|
|
1239
|
+
*/
|
|
1217
1240
|
async wait() {
|
|
1218
1241
|
return Promise.all(this.tasks.map((t) => t.result));
|
|
1219
1242
|
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Waits for all tasks to settle (fulfilled or rejected) and returns each outcome.
|
|
1245
|
+
* Never rejects — inspect each `PromiseSettledResult` to handle failures individually.
|
|
1246
|
+
*/
|
|
1220
1247
|
async waitSettled() {
|
|
1221
1248
|
return Promise.allSettled(this.tasks.map((t) => t.result));
|
|
1222
1249
|
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Cancels all tasks in the group and signals the shared `AbortSignal`.
|
|
1252
|
+
* Already-settled tasks are unaffected.
|
|
1253
|
+
*/
|
|
1223
1254
|
cancel() {
|
|
1224
1255
|
this.controller.abort();
|
|
1225
|
-
for (const
|
|
1226
|
-
|
|
1256
|
+
for (const task2 of this.tasks) {
|
|
1257
|
+
task2.cancel();
|
|
1227
1258
|
}
|
|
1228
1259
|
}
|
|
1229
1260
|
};
|
|
@@ -1232,7 +1263,7 @@ var WaitGroup = class {
|
|
|
1232
1263
|
var ErrGroup = class {
|
|
1233
1264
|
tasks = [];
|
|
1234
1265
|
controller = new AbortController();
|
|
1235
|
-
firstError =
|
|
1266
|
+
firstError = null;
|
|
1236
1267
|
hasError = false;
|
|
1237
1268
|
get signal() {
|
|
1238
1269
|
return this.controller.signal;
|
|
@@ -1253,7 +1284,7 @@ var ErrGroup = class {
|
|
|
1253
1284
|
}
|
|
1254
1285
|
async wait() {
|
|
1255
1286
|
const settled = await Promise.allSettled(this.tasks.map((t) => t.result));
|
|
1256
|
-
if (this.hasError) {
|
|
1287
|
+
if (this.hasError && this.firstError) {
|
|
1257
1288
|
throw this.firstError;
|
|
1258
1289
|
}
|
|
1259
1290
|
return settled.map((r) => {
|
|
@@ -1263,8 +1294,8 @@ var ErrGroup = class {
|
|
|
1263
1294
|
}
|
|
1264
1295
|
cancel() {
|
|
1265
1296
|
this.controller.abort();
|
|
1266
|
-
for (const
|
|
1267
|
-
|
|
1297
|
+
for (const task2 of this.tasks) {
|
|
1298
|
+
task2.cancel();
|
|
1268
1299
|
}
|
|
1269
1300
|
}
|
|
1270
1301
|
};
|
|
@@ -1409,7 +1440,7 @@ var Ticker = class {
|
|
|
1409
1440
|
if (this.resolve) {
|
|
1410
1441
|
const r = this.resolve;
|
|
1411
1442
|
this.resolve = null;
|
|
1412
|
-
r();
|
|
1443
|
+
r(true);
|
|
1413
1444
|
}
|
|
1414
1445
|
}, ms);
|
|
1415
1446
|
if (this.interval.unref) this.interval.unref();
|
|
@@ -1417,7 +1448,11 @@ var Ticker = class {
|
|
|
1417
1448
|
async tick() {
|
|
1418
1449
|
if (this.stopped) return false;
|
|
1419
1450
|
return new Promise((resolve) => {
|
|
1420
|
-
this.resolve =
|
|
1451
|
+
this.resolve = resolve;
|
|
1452
|
+
if (this.stopped) {
|
|
1453
|
+
this.resolve = null;
|
|
1454
|
+
resolve(false);
|
|
1455
|
+
}
|
|
1421
1456
|
});
|
|
1422
1457
|
}
|
|
1423
1458
|
stop() {
|
|
@@ -1429,7 +1464,7 @@ var Ticker = class {
|
|
|
1429
1464
|
if (this.resolve) {
|
|
1430
1465
|
const r = this.resolve;
|
|
1431
1466
|
this.resolve = null;
|
|
1432
|
-
r();
|
|
1467
|
+
r(false);
|
|
1433
1468
|
}
|
|
1434
1469
|
}
|
|
1435
1470
|
async *[Symbol.asyncIterator]() {
|
|
@@ -1443,54 +1478,45 @@ function ticker(ms) {
|
|
|
1443
1478
|
}
|
|
1444
1479
|
|
|
1445
1480
|
// src/registry.ts
|
|
1446
|
-
var registry = /* @__PURE__ */ new Map();
|
|
1447
1481
|
var taskCounter2 = 0;
|
|
1448
|
-
function
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
throw new Error(`Task "${name}" is not registered. Call register() first.`);
|
|
1458
|
-
}
|
|
1459
|
-
const fnStr = serializeFunction(fn);
|
|
1460
|
-
const serializedArgs = args.map((a) => {
|
|
1461
|
-
const json = JSON.stringify(a);
|
|
1462
|
-
if (json === void 0) {
|
|
1463
|
-
throw new TypeError(
|
|
1464
|
-
`Argument of type ${typeof a} is not JSON-serializable. run() args must be JSON-serializable (no undefined, functions, symbols, or BigInt).`
|
|
1465
|
-
);
|
|
1466
|
-
}
|
|
1467
|
-
return json;
|
|
1468
|
-
});
|
|
1469
|
-
const wrapperStr = `() => (${fnStr})(${serializedArgs.join(", ")})`;
|
|
1470
|
-
const taskId = `reg_${++taskCounter2}`;
|
|
1471
|
-
const spawnStack = new Error().stack;
|
|
1472
|
-
let resolveFn;
|
|
1473
|
-
let rejectFn;
|
|
1474
|
-
const result = new Promise((resolve, reject) => {
|
|
1475
|
-
resolveFn = resolve;
|
|
1476
|
-
rejectFn = reject;
|
|
1477
|
-
});
|
|
1478
|
-
const task = {
|
|
1479
|
-
id: taskId,
|
|
1480
|
-
fnStr: wrapperStr,
|
|
1481
|
-
priority: "normal",
|
|
1482
|
-
concurrent: false,
|
|
1483
|
-
resolve: (value) => resolveFn(value),
|
|
1484
|
-
reject: (reason) => {
|
|
1485
|
-
if (reason instanceof Error && spawnStack) {
|
|
1486
|
-
const callerLine = spawnStack.split("\n").slice(2).join("\n");
|
|
1487
|
-
reason.stack = (reason.stack ?? reason.message) + "\n --- spawned at ---\n" + callerLine;
|
|
1482
|
+
function task(fn) {
|
|
1483
|
+
return (...args) => {
|
|
1484
|
+
const fnStr = serializeFunction(fn);
|
|
1485
|
+
const serializedArgs = args.map((a) => {
|
|
1486
|
+
const json = JSON.stringify(a);
|
|
1487
|
+
if (json === void 0) {
|
|
1488
|
+
throw new TypeError(
|
|
1489
|
+
`Argument of type ${typeof a} is not JSON-serializable. task() args must be JSON-serializable (no undefined, functions, symbols, or BigInt).`
|
|
1490
|
+
);
|
|
1488
1491
|
}
|
|
1489
|
-
|
|
1490
|
-
}
|
|
1492
|
+
return json;
|
|
1493
|
+
});
|
|
1494
|
+
const wrapperStr = `() => (${fnStr})(${serializedArgs.join(", ")})`;
|
|
1495
|
+
const taskId = `task_${++taskCounter2}`;
|
|
1496
|
+
const spawnStack = new Error().stack;
|
|
1497
|
+
let resolveFn;
|
|
1498
|
+
let rejectFn;
|
|
1499
|
+
const result = new Promise((resolve, reject) => {
|
|
1500
|
+
resolveFn = resolve;
|
|
1501
|
+
rejectFn = reject;
|
|
1502
|
+
});
|
|
1503
|
+
const taskObj = {
|
|
1504
|
+
id: taskId,
|
|
1505
|
+
fnStr: wrapperStr,
|
|
1506
|
+
priority: "normal",
|
|
1507
|
+
concurrent: false,
|
|
1508
|
+
resolve: (value) => resolveFn(value),
|
|
1509
|
+
reject: (reason) => {
|
|
1510
|
+
if (reason instanceof Error && spawnStack) {
|
|
1511
|
+
const callerLine = spawnStack.split("\n").slice(2).join("\n");
|
|
1512
|
+
reason.stack = (reason.stack ?? reason.message) + "\n --- spawned at ---\n" + callerLine;
|
|
1513
|
+
}
|
|
1514
|
+
rejectFn(reason);
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
getPool().submit(taskObj);
|
|
1518
|
+
return result;
|
|
1491
1519
|
};
|
|
1492
|
-
getPool().submit(task);
|
|
1493
|
-
return result;
|
|
1494
1520
|
}
|
|
1495
1521
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1496
1522
|
0 && (module.exports = {
|
|
@@ -1504,12 +1530,12 @@ function run(name, ...args) {
|
|
|
1504
1530
|
configure,
|
|
1505
1531
|
detectCapability,
|
|
1506
1532
|
detectRuntime,
|
|
1507
|
-
register,
|
|
1508
1533
|
resize,
|
|
1509
|
-
run,
|
|
1510
1534
|
select,
|
|
1535
|
+
shutdown,
|
|
1511
1536
|
spawn,
|
|
1512
1537
|
stats,
|
|
1538
|
+
task,
|
|
1513
1539
|
ticker
|
|
1514
1540
|
});
|
|
1515
1541
|
//# sourceMappingURL=index.cjs.map
|