@dmop/puru 0.1.5 → 0.1.11

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.js CHANGED
@@ -56,21 +56,20 @@ function getConfig() {
56
56
 
57
57
  // src/runtime.ts
58
58
  function detectRuntime() {
59
- if (typeof globalThis.Bun !== "undefined") return "bun";
60
- if (typeof globalThis.Deno !== "undefined") return "deno";
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 (typeof globalThis.Worker !== "undefined") return "full-threads";
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 as Worker2 } from "worker_threads";
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 Worker2(NODE_BOOTSTRAP_CODE, { eval: true });
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
- this.worker = new Worker(getBootstrapFile());
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
  }
@@ -393,6 +397,12 @@ var ChannelImpl = class {
393
397
  this.capacity = capacity;
394
398
  channelRegistry.set(this._id, this);
395
399
  }
400
+ get len() {
401
+ return this.buffer.length;
402
+ }
403
+ get cap() {
404
+ return this.capacity;
405
+ }
396
406
  send(value) {
397
407
  if (this.closed) {
398
408
  return Promise.reject(new Error("send on closed channel"));
@@ -446,6 +456,40 @@ var ChannelImpl = class {
446
456
  }
447
457
  this.sendQueue = [];
448
458
  }
459
+ sendOnly() {
460
+ const send = (value) => this.send(value);
461
+ const close = () => this.close();
462
+ const getLen = () => this.len;
463
+ const getCap = () => this.cap;
464
+ return {
465
+ send,
466
+ close,
467
+ get len() {
468
+ return getLen();
469
+ },
470
+ get cap() {
471
+ return getCap();
472
+ }
473
+ };
474
+ }
475
+ recvOnly() {
476
+ const recv = () => this.recv();
477
+ const getLen = () => this.len;
478
+ const getCap = () => this.cap;
479
+ const getIter = () => this[Symbol.asyncIterator]();
480
+ return {
481
+ recv,
482
+ get len() {
483
+ return getLen();
484
+ },
485
+ get cap() {
486
+ return getCap();
487
+ },
488
+ [Symbol.asyncIterator]() {
489
+ return getIter();
490
+ }
491
+ };
492
+ }
449
493
  async *[Symbol.asyncIterator]() {
450
494
  while (true) {
451
495
  const value = await this.recv();
@@ -463,6 +507,9 @@ function chan(capacity = 0) {
463
507
  function getChannelById(id) {
464
508
  return channelRegistry.get(id);
465
509
  }
510
+ function getChannelId(channel) {
511
+ return channel._id;
512
+ }
466
513
 
467
514
  // src/adapters/inline.ts
468
515
  var inlineIdCounter = 0;
@@ -479,15 +526,14 @@ var InlineManagedWorker = class {
479
526
  this.emit("message", { type: "ready" });
480
527
  });
481
528
  }
482
- postMessage(data) {
529
+ postMessage(msg) {
483
530
  if (this.terminated) return;
484
- const msg = data;
485
531
  if (msg.type === "execute") {
486
- this.executeTask(msg.taskId, msg.fnStr, msg.concurrent ?? false, msg.channels);
532
+ this.executeTask(msg.taskId, msg.fnStr, msg.concurrent, msg.channels);
487
533
  } else if (msg.type === "cancel") {
488
534
  this.cancelledTasks.add(msg.taskId);
489
535
  } else if (msg.type === "channel-result") {
490
- this.emit("message", msg);
536
+ return;
491
537
  } else if (msg.type === "shutdown") {
492
538
  this.terminated = true;
493
539
  this.emit("exit", 0);
@@ -517,7 +563,6 @@ var InlineManagedWorker = class {
517
563
  }
518
564
  }
519
565
  buildChannelProxies(channels) {
520
- const self = this;
521
566
  const proxies = {};
522
567
  for (const [name, channelId] of Object.entries(channels)) {
523
568
  proxies[name] = {
@@ -539,13 +584,7 @@ var InlineManagedWorker = class {
539
584
  [Symbol.asyncIterator]() {
540
585
  const ch = getChannelById(channelId);
541
586
  if (!ch) throw new Error(`Channel ${channelId} not found`);
542
- return {
543
- async next() {
544
- const value = await ch.recv();
545
- if (value === null) return { done: true, value: void 0 };
546
- return { done: false, value };
547
- }
548
- };
587
+ return ch[Symbol.asyncIterator]();
549
588
  }
550
589
  };
551
590
  }
@@ -893,6 +932,15 @@ var WorkerPool = class {
893
932
  task2.reject(reason);
894
933
  }
895
934
  }
935
+ rejectExclusiveTaskForWorker(worker, reason) {
936
+ for (const [taskId, assignedWorker] of this.exclusiveWorkers) {
937
+ if (assignedWorker === worker) {
938
+ this.exclusiveWorkers.delete(taskId);
939
+ this.rejectTask(taskId, reason);
940
+ break;
941
+ }
942
+ }
943
+ }
896
944
  // --- Cancellation ---
897
945
  cancelTask(taskId) {
898
946
  const removed = this.removeFromQueue(taskId);
@@ -941,6 +989,10 @@ var WorkerPool = class {
941
989
  }
942
990
  this.concurrentQueues[priority] = [];
943
991
  }
992
+ for (const [taskId] of this.exclusiveWorkers) {
993
+ this.taskMap.delete(taskId);
994
+ }
995
+ this.exclusiveWorkers.clear();
944
996
  for (const [, taskSet] of this.sharedWorkers) {
945
997
  for (const taskId of taskSet) {
946
998
  this.taskMap.delete(taskId);
@@ -998,8 +1050,7 @@ var WorkerPool = class {
998
1050
  this.makeIdle(worker);
999
1051
  }
1000
1052
  };
1001
- worker.on("message", (msg) => {
1002
- const response = msg;
1053
+ worker.on("message", (response) => {
1003
1054
  if (response.type === "ready") {
1004
1055
  onReady();
1005
1056
  return;
@@ -1007,12 +1058,7 @@ var WorkerPool = class {
1007
1058
  this.handleWorkerMessage(worker, response);
1008
1059
  });
1009
1060
  worker.on("error", (err) => {
1010
- for (const [taskId, w] of this.exclusiveWorkers) {
1011
- if (w === worker) {
1012
- this.exclusiveWorkers.delete(taskId);
1013
- break;
1014
- }
1015
- }
1061
+ this.rejectExclusiveTaskForWorker(worker, err);
1016
1062
  const taskSet = this.sharedWorkers.get(worker);
1017
1063
  if (taskSet) {
1018
1064
  for (const taskId of taskSet) {
@@ -1032,12 +1078,7 @@ var WorkerPool = class {
1032
1078
  if (idleIdx !== -1) {
1033
1079
  this.idleWorkers.splice(idleIdx, 1);
1034
1080
  }
1035
- for (const [taskId, w] of this.exclusiveWorkers) {
1036
- if (w === worker) {
1037
- this.exclusiveWorkers.delete(taskId);
1038
- break;
1039
- }
1040
- }
1081
+ this.rejectExclusiveTaskForWorker(worker, new Error("Worker exited unexpectedly"));
1041
1082
  const taskSet = this.sharedWorkers.get(worker);
1042
1083
  if (taskSet) {
1043
1084
  for (const taskId of taskSet) {
@@ -1131,11 +1172,7 @@ function spawn(fn, opts) {
1131
1172
  if (opts?.channels) {
1132
1173
  channelMap = {};
1133
1174
  for (const [name, ch] of Object.entries(opts.channels)) {
1134
- const impl = ch;
1135
- if (!impl._id) {
1136
- throw new Error(`Channel "${name}" is not a valid puru channel`);
1137
- }
1138
- channelMap[name] = impl._id;
1175
+ channelMap[name] = getChannelId(ch);
1139
1176
  }
1140
1177
  }
1141
1178
  const task2 = {
@@ -1161,6 +1198,18 @@ function spawn(fn, opts) {
1161
1198
  }
1162
1199
  }
1163
1200
  };
1201
+ const ctx = opts?.ctx;
1202
+ if (ctx) {
1203
+ if (ctx.signal.aborted) {
1204
+ settled = true;
1205
+ rejectFn(ctx.err ?? new DOMException("Task was cancelled", "AbortError"));
1206
+ return {
1207
+ result,
1208
+ cancel: () => {
1209
+ }
1210
+ };
1211
+ }
1212
+ }
1164
1213
  getPool().submit(task2);
1165
1214
  const cancel = () => {
1166
1215
  if (settled) return;
@@ -1168,6 +1217,14 @@ function spawn(fn, opts) {
1168
1217
  getPool().cancelTask(taskId);
1169
1218
  rejectFn(new DOMException("Task was cancelled", "AbortError"));
1170
1219
  };
1220
+ if (ctx) {
1221
+ const onAbort = () => cancel();
1222
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
1223
+ result.then(
1224
+ () => ctx.signal.removeEventListener("abort", onAbort),
1225
+ () => ctx.signal.removeEventListener("abort", onAbort)
1226
+ );
1227
+ }
1171
1228
  return { result, cancel };
1172
1229
  }
1173
1230
 
@@ -1175,6 +1232,17 @@ function spawn(fn, opts) {
1175
1232
  var WaitGroup = class {
1176
1233
  tasks = [];
1177
1234
  controller = new AbortController();
1235
+ ctx;
1236
+ constructor(ctx) {
1237
+ this.ctx = ctx;
1238
+ if (ctx) {
1239
+ if (ctx.signal.aborted) {
1240
+ this.controller.abort();
1241
+ } else {
1242
+ ctx.signal.addEventListener("abort", () => this.cancel(), { once: true });
1243
+ }
1244
+ }
1245
+ }
1178
1246
  /**
1179
1247
  * An `AbortSignal` shared across all tasks in this group.
1180
1248
  * Pass it into spawned functions so they can stop early when `cancel()` is called.
@@ -1191,7 +1259,7 @@ var WaitGroup = class {
1191
1259
  if (this.controller.signal.aborted) {
1192
1260
  throw new Error("WaitGroup has been cancelled");
1193
1261
  }
1194
- const handle = spawn(fn, opts);
1262
+ const handle = spawn(fn, { ...opts, ctx: this.ctx });
1195
1263
  this.tasks.push(handle);
1196
1264
  }
1197
1265
  /**
@@ -1224,28 +1292,77 @@ var WaitGroup = class {
1224
1292
  var ErrGroup = class {
1225
1293
  tasks = [];
1226
1294
  controller = new AbortController();
1227
- firstError = void 0;
1295
+ firstError = null;
1228
1296
  hasError = false;
1297
+ ctx;
1298
+ limit = 0;
1299
+ // 0 = unlimited
1300
+ inFlight = 0;
1301
+ waiting = [];
1302
+ constructor(ctx) {
1303
+ this.ctx = ctx;
1304
+ if (ctx) {
1305
+ if (ctx.signal.aborted) {
1306
+ this.controller.abort();
1307
+ } else {
1308
+ ctx.signal.addEventListener("abort", () => this.cancel(), { once: true });
1309
+ }
1310
+ }
1311
+ }
1229
1312
  get signal() {
1230
1313
  return this.controller.signal;
1231
1314
  }
1315
+ /**
1316
+ * Set the maximum number of tasks that can run concurrently.
1317
+ * Like Go's `errgroup.SetLimit()`. Must be called before any `spawn()`.
1318
+ * A value of 0 (default) means unlimited.
1319
+ */
1320
+ setLimit(n) {
1321
+ if (this.tasks.length > 0) {
1322
+ throw new Error("SetLimit must be called before any spawn()");
1323
+ }
1324
+ if (n < 0 || !Number.isInteger(n)) {
1325
+ throw new RangeError("Limit must be a non-negative integer");
1326
+ }
1327
+ this.limit = n;
1328
+ }
1232
1329
  spawn(fn, opts) {
1233
1330
  if (this.controller.signal.aborted) {
1234
1331
  throw new Error("ErrGroup has been cancelled");
1235
1332
  }
1236
- const handle = spawn(fn, opts);
1237
- handle.result.catch((err) => {
1333
+ if (this.limit > 0 && this.inFlight >= this.limit) {
1334
+ const result2 = new Promise((resolve) => {
1335
+ this.waiting.push(resolve);
1336
+ }).then(() => this.doSpawn(fn, opts));
1337
+ this.tasks.push({ result: result2, cancel: () => {
1338
+ } });
1339
+ return;
1340
+ }
1341
+ const result = this.doSpawn(fn, opts);
1342
+ this.tasks.push({ result, cancel: () => {
1343
+ } });
1344
+ }
1345
+ doSpawn(fn, opts) {
1346
+ this.inFlight++;
1347
+ const handle = spawn(fn, { ...opts, ctx: this.ctx });
1348
+ const onSettle = () => {
1349
+ this.inFlight--;
1350
+ const next = this.waiting.shift();
1351
+ if (next) next();
1352
+ };
1353
+ handle.result.then(onSettle, (err) => {
1354
+ onSettle();
1238
1355
  if (!this.hasError) {
1239
1356
  this.hasError = true;
1240
1357
  this.firstError = err;
1241
1358
  this.cancel();
1242
1359
  }
1243
1360
  });
1244
- this.tasks.push(handle);
1361
+ return handle.result;
1245
1362
  }
1246
1363
  async wait() {
1247
1364
  const settled = await Promise.allSettled(this.tasks.map((t) => t.result));
1248
- if (this.hasError) {
1365
+ if (this.hasError && this.firstError) {
1249
1366
  throw this.firstError;
1250
1367
  }
1251
1368
  return settled.map((r) => {
@@ -1297,6 +1414,121 @@ var Mutex = class {
1297
1414
  return this.locked;
1298
1415
  }
1299
1416
  };
1417
+ var RWMutex = class {
1418
+ readers = 0;
1419
+ writing = false;
1420
+ readQueue = [];
1421
+ writeQueue = [];
1422
+ async rLock() {
1423
+ if (!this.writing && this.writeQueue.length === 0) {
1424
+ this.readers++;
1425
+ return;
1426
+ }
1427
+ return new Promise((resolve) => {
1428
+ this.readQueue.push(() => {
1429
+ this.readers++;
1430
+ resolve();
1431
+ });
1432
+ });
1433
+ }
1434
+ rUnlock() {
1435
+ if (this.readers <= 0) {
1436
+ throw new Error("Cannot rUnlock a RWMutex that is not read-locked");
1437
+ }
1438
+ this.readers--;
1439
+ if (this.readers === 0) {
1440
+ this.wakeWriter();
1441
+ }
1442
+ }
1443
+ async lock() {
1444
+ if (!this.writing && this.readers === 0) {
1445
+ this.writing = true;
1446
+ return;
1447
+ }
1448
+ return new Promise((resolve) => {
1449
+ this.writeQueue.push(() => {
1450
+ this.writing = true;
1451
+ resolve();
1452
+ });
1453
+ });
1454
+ }
1455
+ unlock() {
1456
+ if (!this.writing) {
1457
+ throw new Error("Cannot unlock a RWMutex that is not write-locked");
1458
+ }
1459
+ this.writing = false;
1460
+ if (this.readQueue.length > 0) {
1461
+ this.wakeReaders();
1462
+ } else {
1463
+ this.wakeWriter();
1464
+ }
1465
+ }
1466
+ async withRLock(fn) {
1467
+ await this.rLock();
1468
+ try {
1469
+ return await fn();
1470
+ } finally {
1471
+ this.rUnlock();
1472
+ }
1473
+ }
1474
+ async withLock(fn) {
1475
+ await this.lock();
1476
+ try {
1477
+ return await fn();
1478
+ } finally {
1479
+ this.unlock();
1480
+ }
1481
+ }
1482
+ get isLocked() {
1483
+ return this.writing || this.readers > 0;
1484
+ }
1485
+ wakeReaders() {
1486
+ const queue = this.readQueue;
1487
+ this.readQueue = [];
1488
+ for (const wake of queue) {
1489
+ wake();
1490
+ }
1491
+ }
1492
+ wakeWriter() {
1493
+ const next = this.writeQueue.shift();
1494
+ if (next) next();
1495
+ }
1496
+ };
1497
+
1498
+ // src/cond.ts
1499
+ var Cond = class {
1500
+ mu;
1501
+ waiters = [];
1502
+ constructor(mu) {
1503
+ this.mu = mu;
1504
+ }
1505
+ /**
1506
+ * Atomically releases the mutex, suspends the caller until `signal()` or `broadcast()`
1507
+ * is called, then re-acquires the mutex before returning.
1508
+ *
1509
+ * Must be called while holding the mutex.
1510
+ */
1511
+ async wait() {
1512
+ this.mu.unlock();
1513
+ await new Promise((resolve) => {
1514
+ this.waiters.push(resolve);
1515
+ });
1516
+ await this.mu.lock();
1517
+ }
1518
+ /** Wake one waiting task (if any). */
1519
+ signal() {
1520
+ const next = this.waiters.shift();
1521
+ if (next) next();
1522
+ }
1523
+ /** Wake all waiting tasks. */
1524
+ broadcast() {
1525
+ const queue = this.waiters;
1526
+ this.waiters = [];
1527
+ for (const wake of queue) {
1528
+ wake();
1529
+ }
1530
+ }
1531
+ };
1300
1532
 
1301
1533
  // src/once.ts
1302
1534
  var Once = class {
@@ -1335,6 +1567,7 @@ function select(cases, opts) {
1335
1567
  if (settled) return;
1336
1568
  settled = true;
1337
1569
  try {
1570
+ ;
1338
1571
  handler(value);
1339
1572
  resolve();
1340
1573
  } catch (err) {
@@ -1368,6 +1601,7 @@ function select(cases, opts) {
1368
1601
  if (settled) return;
1369
1602
  settled = true;
1370
1603
  try {
1604
+ ;
1371
1605
  handler(value);
1372
1606
  resolve();
1373
1607
  } catch (err) {
@@ -1438,11 +1672,60 @@ function ticker(ms) {
1438
1672
  return new Ticker(ms);
1439
1673
  }
1440
1674
 
1675
+ // src/timer.ts
1676
+ var Timer = class {
1677
+ timer = null;
1678
+ _stopped = false;
1679
+ /** Promise that resolves when the timer fires. Replaced on `reset()`. */
1680
+ channel;
1681
+ constructor(ms) {
1682
+ this.channel = this.schedule(ms);
1683
+ }
1684
+ schedule(ms) {
1685
+ return new Promise((resolve) => {
1686
+ this.timer = setTimeout(() => {
1687
+ this._stopped = true;
1688
+ this.timer = null;
1689
+ resolve();
1690
+ }, ms);
1691
+ if (typeof this.timer === "object" && "unref" in this.timer) {
1692
+ this.timer.unref();
1693
+ }
1694
+ });
1695
+ }
1696
+ /**
1697
+ * Stop the timer. Returns `true` if the timer was pending (stopped before firing),
1698
+ * `false` if it had already fired or was already stopped.
1699
+ *
1700
+ * After stopping, the current `channel` promise will never resolve.
1701
+ */
1702
+ stop() {
1703
+ if (this.timer === null) return false;
1704
+ clearTimeout(this.timer);
1705
+ this.timer = null;
1706
+ this._stopped = true;
1707
+ return true;
1708
+ }
1709
+ /**
1710
+ * Reset the timer to fire after `ms` milliseconds.
1711
+ * If the timer was pending, it is stopped first. Creates a new `channel` promise.
1712
+ */
1713
+ reset(ms) {
1714
+ this.stop();
1715
+ this._stopped = false;
1716
+ this.channel = this.schedule(ms);
1717
+ }
1718
+ /** Whether the timer has fired or been stopped. */
1719
+ get stopped() {
1720
+ return this._stopped;
1721
+ }
1722
+ };
1723
+
1441
1724
  // src/registry.ts
1442
1725
  var taskCounter2 = 0;
1443
1726
  function task(fn) {
1727
+ const fnStr = serializeFunction(fn);
1444
1728
  return (...args) => {
1445
- const fnStr = serializeFunction(fn);
1446
1729
  const serializedArgs = args.map((a) => {
1447
1730
  const json = JSON.stringify(a);
1448
1731
  if (json === void 0) {
@@ -1479,13 +1762,178 @@ function task(fn) {
1479
1762
  return result;
1480
1763
  };
1481
1764
  }
1765
+
1766
+ // src/context.ts
1767
+ var CancelledError = class extends Error {
1768
+ constructor(message = "context cancelled") {
1769
+ super(message);
1770
+ this.name = "CancelledError";
1771
+ }
1772
+ };
1773
+ var DeadlineExceededError = class extends Error {
1774
+ constructor() {
1775
+ super("context deadline exceeded");
1776
+ this.name = "DeadlineExceededError";
1777
+ }
1778
+ };
1779
+ var BaseContext = class {
1780
+ _err = null;
1781
+ controller;
1782
+ parent;
1783
+ constructor(parent) {
1784
+ this.parent = parent;
1785
+ this.controller = new AbortController();
1786
+ if (parent) {
1787
+ if (parent.signal.aborted) {
1788
+ this._err = parent.err ?? new CancelledError();
1789
+ this.controller.abort();
1790
+ } else {
1791
+ parent.signal.addEventListener(
1792
+ "abort",
1793
+ () => {
1794
+ if (!this.controller.signal.aborted) {
1795
+ this._err = parent.err ?? new CancelledError();
1796
+ this.controller.abort();
1797
+ }
1798
+ },
1799
+ { once: true }
1800
+ );
1801
+ }
1802
+ }
1803
+ }
1804
+ get signal() {
1805
+ return this.controller.signal;
1806
+ }
1807
+ get deadline() {
1808
+ return this.parent?.deadline ?? null;
1809
+ }
1810
+ get err() {
1811
+ return this._err;
1812
+ }
1813
+ value(_key) {
1814
+ return this.parent?.value(_key);
1815
+ }
1816
+ done() {
1817
+ if (this.controller.signal.aborted) return Promise.resolve();
1818
+ return new Promise((resolve) => {
1819
+ this.controller.signal.addEventListener("abort", () => resolve(), { once: true });
1820
+ });
1821
+ }
1822
+ };
1823
+ var BackgroundContext = class {
1824
+ _signal = new AbortController().signal;
1825
+ get signal() {
1826
+ return this._signal;
1827
+ }
1828
+ get deadline() {
1829
+ return null;
1830
+ }
1831
+ get err() {
1832
+ return null;
1833
+ }
1834
+ value(_key) {
1835
+ return void 0;
1836
+ }
1837
+ done() {
1838
+ return new Promise(() => {
1839
+ });
1840
+ }
1841
+ };
1842
+ var bg = null;
1843
+ function background() {
1844
+ if (!bg) bg = new BackgroundContext();
1845
+ return bg;
1846
+ }
1847
+ var CancelContext = class extends BaseContext {
1848
+ cancel(reason) {
1849
+ if (!this.controller.signal.aborted) {
1850
+ this._err = new CancelledError(reason ?? "context cancelled");
1851
+ this.controller.abort();
1852
+ }
1853
+ }
1854
+ };
1855
+ function withCancel(parent) {
1856
+ const ctx = new CancelContext(parent);
1857
+ return [ctx, (reason) => ctx.cancel(reason)];
1858
+ }
1859
+ var DeadlineContext = class extends BaseContext {
1860
+ _deadline;
1861
+ timer = null;
1862
+ constructor(parent, deadline) {
1863
+ super(parent);
1864
+ this._deadline = deadline;
1865
+ if (parent.deadline && parent.deadline < deadline) {
1866
+ this._deadline = parent.deadline;
1867
+ }
1868
+ if (this.controller.signal.aborted) {
1869
+ return;
1870
+ }
1871
+ const ms = this._deadline.getTime() - Date.now();
1872
+ if (ms <= 0) {
1873
+ this._err = new DeadlineExceededError();
1874
+ this.controller.abort();
1875
+ } else {
1876
+ this.timer = setTimeout(() => {
1877
+ if (!this.controller.signal.aborted) {
1878
+ this._err = new DeadlineExceededError();
1879
+ this.controller.abort();
1880
+ }
1881
+ }, ms);
1882
+ if (typeof this.timer === "object" && "unref" in this.timer) {
1883
+ this.timer.unref();
1884
+ }
1885
+ }
1886
+ }
1887
+ get deadline() {
1888
+ return this._deadline;
1889
+ }
1890
+ cancel(reason) {
1891
+ if (this.timer !== null) {
1892
+ clearTimeout(this.timer);
1893
+ this.timer = null;
1894
+ }
1895
+ if (!this.controller.signal.aborted) {
1896
+ this._err = new CancelledError(reason ?? "context cancelled");
1897
+ this.controller.abort();
1898
+ }
1899
+ }
1900
+ };
1901
+ function withDeadline(parent, deadline) {
1902
+ const ctx = new DeadlineContext(parent, deadline);
1903
+ return [ctx, (reason) => ctx.cancel(reason)];
1904
+ }
1905
+ function withTimeout(parent, ms) {
1906
+ return withDeadline(parent, new Date(Date.now() + ms));
1907
+ }
1908
+ var ValueContext = class extends BaseContext {
1909
+ key;
1910
+ val;
1911
+ constructor(parent, key, val) {
1912
+ super(parent);
1913
+ this.key = key;
1914
+ this.val = val;
1915
+ }
1916
+ value(key) {
1917
+ if (key === this.key) return this.val;
1918
+ return this.parent?.value(key);
1919
+ }
1920
+ };
1921
+ function withValue(parent, key, value) {
1922
+ return new ValueContext(parent, key, value);
1923
+ }
1482
1924
  export {
1925
+ CancelledError,
1926
+ Cond,
1927
+ DeadlineExceededError,
1483
1928
  ErrGroup,
1484
1929
  Mutex,
1485
1930
  Once,
1931
+ RWMutex,
1486
1932
  Ticker,
1933
+ Timer,
1487
1934
  WaitGroup,
1488
1935
  after,
1936
+ background,
1489
1937
  chan,
1490
1938
  configure,
1491
1939
  detectCapability,
@@ -1496,6 +1944,10 @@ export {
1496
1944
  spawn,
1497
1945
  stats,
1498
1946
  task,
1499
- ticker
1947
+ ticker,
1948
+ withCancel,
1949
+ withDeadline,
1950
+ withTimeout,
1951
+ withValue
1500
1952
  };
1501
1953
  //# sourceMappingURL=index.js.map