@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/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 (typeof globalThis.Bun !== "undefined") return "bun";
102
- if (typeof globalThis.Deno !== "undefined") return "deno";
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 (typeof globalThis.Worker !== "undefined") return "full-threads";
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
- this.worker = new Worker(getBootstrapFile());
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({ resolve });
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(null);
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(data) {
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 ?? false, msg.channels);
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
- this.emit("message", msg);
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(task) {
666
- this.queues[task.priority].push(task);
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(task) {
672
- this.concurrentQueues[task.priority].push(task);
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(task) {
701
+ submit(task2) {
699
702
  if (this.draining) {
700
- task.reject(new Error("Pool is shutting down"));
703
+ task2.reject(new Error("Pool is shutting down"));
701
704
  return;
702
705
  }
703
- if (task.concurrent) {
704
- this.submitConcurrent(task);
706
+ if (task2.concurrent) {
707
+ this.submitConcurrent(task2);
705
708
  } else {
706
- this.submitExclusive(task);
709
+ this.submitExclusive(task2);
707
710
  }
708
711
  }
709
- submitExclusive(task) {
712
+ submitExclusive(task2) {
710
713
  const worker = this.idleWorkers.pop();
711
714
  if (worker) {
712
- this.dispatch(worker, task);
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(task);
721
+ this.pendingTasksForWorkers.push(task2);
719
722
  this.createAndReadyWorker();
720
723
  return;
721
724
  }
722
- this.enqueue(task);
725
+ this.enqueue(task2);
723
726
  }
724
- submitConcurrent(task) {
727
+ submitConcurrent(task2) {
725
728
  for (const [worker, tasks] of this.sharedWorkers) {
726
729
  if (tasks.size < this.config.concurrency) {
727
- this.dispatchConcurrent(worker, task);
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, task);
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(task);
742
+ this.pendingTasksForWorkers.push(task2);
740
743
  this.createAndReadyWorker();
741
744
  return;
742
745
  }
743
- this.enqueueConcurrent(task);
746
+ this.enqueueConcurrent(task2);
744
747
  }
745
748
  // --- Dispatch ---
746
- dispatch(worker, task) {
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(task.id, worker);
754
- this.taskMap.set(task.id, task);
756
+ this.exclusiveWorkers.set(task2.id, worker);
757
+ this.taskMap.set(task2.id, task2);
755
758
  const msg = {
756
759
  type: "execute",
757
- taskId: task.id,
758
- fnStr: task.fnStr,
760
+ taskId: task2.id,
761
+ fnStr: task2.fnStr,
759
762
  concurrent: false,
760
- channels: task.channels
763
+ channels: task2.channels
761
764
  };
762
765
  worker.postMessage(msg);
763
766
  }
764
- dispatchConcurrent(worker, task) {
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(task.id);
775
- this.taskMap.set(task.id, task);
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: task.id,
779
- fnStr: task.fnStr,
781
+ taskId: task2.id,
782
+ fnStr: task2.fnStr,
780
783
  concurrent: true,
781
- channels: task.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 task = this.taskMap.get(taskId);
917
- if (task) {
922
+ const task2 = this.taskMap.get(taskId);
923
+ if (task2) {
918
924
  this.taskMap.delete(taskId);
919
925
  this.totalCompleted++;
920
- task.resolve(value);
926
+ task2.resolve(value);
921
927
  }
922
928
  }
923
929
  rejectTask(taskId, reason) {
924
- const task = this.taskMap.get(taskId);
925
- if (task) {
930
+ const task2 = this.taskMap.get(taskId);
931
+ if (task2) {
926
932
  this.taskMap.delete(taskId);
927
933
  this.totalFailed++;
928
- task.reject(reason);
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 task of this.queues[priority]) {
969
- task.reject(new Error("Pool is shutting down"));
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 task of this.concurrentQueues[priority]) {
975
- task.reject(new Error("Pool is shutting down"));
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 task = this.dequeue() ?? this.dequeueConcurrent();
1003
- if (!task) break;
1021
+ const task2 = this.dequeue() ?? this.dequeueConcurrent();
1022
+ if (!task2) break;
1004
1023
  this.pendingWorkerCount++;
1005
- this.pendingTasksForWorkers.push(task);
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 task = this.pendingTasksForWorkers.shift();
1026
- if (task) {
1027
- if (task.concurrent) {
1028
- this.dispatchConcurrent(worker, task);
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, task);
1049
+ this.dispatch(worker, task2);
1031
1050
  }
1032
1051
  } else {
1033
1052
  this.makeIdle(worker);
1034
1053
  }
1035
1054
  };
1036
- worker.on("message", (msg) => {
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
- for (const [taskId, w] of this.exclusiveWorkers) {
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
- for (const [taskId, w] of this.exclusiveWorkers) {
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
- const impl = ch;
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 task = {
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(task);
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 task of this.tasks) {
1226
- task.cancel();
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 = void 0;
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 task of this.tasks) {
1267
- task.cancel();
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 = () => resolve(true);
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 register(name, fn) {
1449
- if (registry.has(name)) {
1450
- throw new Error(`Task "${name}" is already registered`);
1451
- }
1452
- registry.set(name, fn);
1453
- }
1454
- function run(name, ...args) {
1455
- const fn = registry.get(name);
1456
- if (!fn) {
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
- rejectFn(reason);
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