@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/AGENTS.md +148 -37
- package/README.md +123 -443
- package/dist/index.cjs +510 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +290 -17
- package/dist/index.d.ts +290 -17
- package/dist/index.js +501 -49
- package/dist/index.js.map +1 -1
- package/llms-full.txt +175 -8
- package/llms.txt +9 -5
- package/package.json +34 -4
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
CancelledError: () => CancelledError,
|
|
24
|
+
Cond: () => Cond,
|
|
25
|
+
DeadlineExceededError: () => DeadlineExceededError,
|
|
23
26
|
ErrGroup: () => ErrGroup,
|
|
24
27
|
Mutex: () => Mutex,
|
|
25
28
|
Once: () => Once,
|
|
29
|
+
RWMutex: () => RWMutex,
|
|
26
30
|
Ticker: () => Ticker,
|
|
31
|
+
Timer: () => Timer,
|
|
27
32
|
WaitGroup: () => WaitGroup,
|
|
28
33
|
after: () => after,
|
|
34
|
+
background: () => background,
|
|
29
35
|
chan: () => chan,
|
|
30
36
|
configure: () => configure,
|
|
31
37
|
detectCapability: () => detectCapability,
|
|
@@ -36,7 +42,11 @@ __export(index_exports, {
|
|
|
36
42
|
spawn: () => spawn,
|
|
37
43
|
stats: () => stats,
|
|
38
44
|
task: () => task,
|
|
39
|
-
ticker: () => ticker
|
|
45
|
+
ticker: () => ticker,
|
|
46
|
+
withCancel: () => withCancel,
|
|
47
|
+
withDeadline: () => withDeadline,
|
|
48
|
+
withTimeout: () => withTimeout,
|
|
49
|
+
withValue: () => withValue
|
|
40
50
|
});
|
|
41
51
|
module.exports = __toCommonJS(index_exports);
|
|
42
52
|
|
|
@@ -98,16 +108,15 @@ function getConfig() {
|
|
|
98
108
|
|
|
99
109
|
// src/runtime.ts
|
|
100
110
|
function detectRuntime() {
|
|
101
|
-
if (
|
|
102
|
-
if (
|
|
103
|
-
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node)
|
|
104
|
-
return "node";
|
|
111
|
+
if ("Bun" in globalThis) return "bun";
|
|
112
|
+
if ("Deno" in globalThis) return "deno";
|
|
113
|
+
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node) return "node";
|
|
105
114
|
return "browser";
|
|
106
115
|
}
|
|
107
116
|
function detectCapability() {
|
|
108
117
|
const runtime = detectRuntime();
|
|
109
118
|
if (runtime === "node" || runtime === "bun") return "full-threads";
|
|
110
|
-
if (
|
|
119
|
+
if ("Worker" in globalThis) return "full-threads";
|
|
111
120
|
return "single-thread";
|
|
112
121
|
}
|
|
113
122
|
|
|
@@ -374,7 +383,11 @@ var BunManagedWorker = class {
|
|
|
374
383
|
id;
|
|
375
384
|
constructor() {
|
|
376
385
|
this.id = ++workerIdCounter;
|
|
377
|
-
|
|
386
|
+
const WorkerConstructor = globalThis.Worker;
|
|
387
|
+
if (!WorkerConstructor) {
|
|
388
|
+
throw new Error("Bun Worker constructor is not available in this runtime");
|
|
389
|
+
}
|
|
390
|
+
this.worker = new WorkerConstructor(getBootstrapFile());
|
|
378
391
|
}
|
|
379
392
|
postMessage(data) {
|
|
380
393
|
this.worker.postMessage(data);
|
|
@@ -386,27 +399,28 @@ var BunManagedWorker = class {
|
|
|
386
399
|
on(event, handler) {
|
|
387
400
|
if (event === "message") {
|
|
388
401
|
this.worker.addEventListener("message", (e) => {
|
|
402
|
+
;
|
|
389
403
|
handler(e.data);
|
|
390
404
|
});
|
|
391
405
|
} else if (event === "error") {
|
|
392
406
|
this.worker.addEventListener("error", (e) => {
|
|
407
|
+
;
|
|
393
408
|
handler(e.error ?? new Error(e.message));
|
|
394
409
|
});
|
|
395
410
|
} else if (event === "exit") {
|
|
396
411
|
this.worker.addEventListener("close", (e) => {
|
|
412
|
+
;
|
|
397
413
|
handler(e.code ?? 0);
|
|
398
414
|
});
|
|
399
415
|
}
|
|
400
416
|
}
|
|
401
417
|
unref() {
|
|
402
418
|
if ("unref" in this.worker && typeof this.worker.unref === "function") {
|
|
403
|
-
;
|
|
404
419
|
this.worker.unref();
|
|
405
420
|
}
|
|
406
421
|
}
|
|
407
422
|
ref() {
|
|
408
423
|
if ("ref" in this.worker && typeof this.worker.ref === "function") {
|
|
409
|
-
;
|
|
410
424
|
this.worker.ref();
|
|
411
425
|
}
|
|
412
426
|
}
|
|
@@ -435,6 +449,12 @@ var ChannelImpl = class {
|
|
|
435
449
|
this.capacity = capacity;
|
|
436
450
|
channelRegistry.set(this._id, this);
|
|
437
451
|
}
|
|
452
|
+
get len() {
|
|
453
|
+
return this.buffer.length;
|
|
454
|
+
}
|
|
455
|
+
get cap() {
|
|
456
|
+
return this.capacity;
|
|
457
|
+
}
|
|
438
458
|
send(value) {
|
|
439
459
|
if (this.closed) {
|
|
440
460
|
return Promise.reject(new Error("send on closed channel"));
|
|
@@ -488,6 +508,40 @@ var ChannelImpl = class {
|
|
|
488
508
|
}
|
|
489
509
|
this.sendQueue = [];
|
|
490
510
|
}
|
|
511
|
+
sendOnly() {
|
|
512
|
+
const send = (value) => this.send(value);
|
|
513
|
+
const close = () => this.close();
|
|
514
|
+
const getLen = () => this.len;
|
|
515
|
+
const getCap = () => this.cap;
|
|
516
|
+
return {
|
|
517
|
+
send,
|
|
518
|
+
close,
|
|
519
|
+
get len() {
|
|
520
|
+
return getLen();
|
|
521
|
+
},
|
|
522
|
+
get cap() {
|
|
523
|
+
return getCap();
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
recvOnly() {
|
|
528
|
+
const recv = () => this.recv();
|
|
529
|
+
const getLen = () => this.len;
|
|
530
|
+
const getCap = () => this.cap;
|
|
531
|
+
const getIter = () => this[Symbol.asyncIterator]();
|
|
532
|
+
return {
|
|
533
|
+
recv,
|
|
534
|
+
get len() {
|
|
535
|
+
return getLen();
|
|
536
|
+
},
|
|
537
|
+
get cap() {
|
|
538
|
+
return getCap();
|
|
539
|
+
},
|
|
540
|
+
[Symbol.asyncIterator]() {
|
|
541
|
+
return getIter();
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
491
545
|
async *[Symbol.asyncIterator]() {
|
|
492
546
|
while (true) {
|
|
493
547
|
const value = await this.recv();
|
|
@@ -505,6 +559,9 @@ function chan(capacity = 0) {
|
|
|
505
559
|
function getChannelById(id) {
|
|
506
560
|
return channelRegistry.get(id);
|
|
507
561
|
}
|
|
562
|
+
function getChannelId(channel) {
|
|
563
|
+
return channel._id;
|
|
564
|
+
}
|
|
508
565
|
|
|
509
566
|
// src/adapters/inline.ts
|
|
510
567
|
var inlineIdCounter = 0;
|
|
@@ -521,15 +578,14 @@ var InlineManagedWorker = class {
|
|
|
521
578
|
this.emit("message", { type: "ready" });
|
|
522
579
|
});
|
|
523
580
|
}
|
|
524
|
-
postMessage(
|
|
581
|
+
postMessage(msg) {
|
|
525
582
|
if (this.terminated) return;
|
|
526
|
-
const msg = data;
|
|
527
583
|
if (msg.type === "execute") {
|
|
528
|
-
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent
|
|
584
|
+
this.executeTask(msg.taskId, msg.fnStr, msg.concurrent, msg.channels);
|
|
529
585
|
} else if (msg.type === "cancel") {
|
|
530
586
|
this.cancelledTasks.add(msg.taskId);
|
|
531
587
|
} else if (msg.type === "channel-result") {
|
|
532
|
-
|
|
588
|
+
return;
|
|
533
589
|
} else if (msg.type === "shutdown") {
|
|
534
590
|
this.terminated = true;
|
|
535
591
|
this.emit("exit", 0);
|
|
@@ -559,7 +615,6 @@ var InlineManagedWorker = class {
|
|
|
559
615
|
}
|
|
560
616
|
}
|
|
561
617
|
buildChannelProxies(channels) {
|
|
562
|
-
const self = this;
|
|
563
618
|
const proxies = {};
|
|
564
619
|
for (const [name, channelId] of Object.entries(channels)) {
|
|
565
620
|
proxies[name] = {
|
|
@@ -581,13 +636,7 @@ var InlineManagedWorker = class {
|
|
|
581
636
|
[Symbol.asyncIterator]() {
|
|
582
637
|
const ch = getChannelById(channelId);
|
|
583
638
|
if (!ch) throw new Error(`Channel ${channelId} not found`);
|
|
584
|
-
return
|
|
585
|
-
async next() {
|
|
586
|
-
const value = await ch.recv();
|
|
587
|
-
if (value === null) return { done: true, value: void 0 };
|
|
588
|
-
return { done: false, value };
|
|
589
|
-
}
|
|
590
|
-
};
|
|
639
|
+
return ch[Symbol.asyncIterator]();
|
|
591
640
|
}
|
|
592
641
|
};
|
|
593
642
|
}
|
|
@@ -935,6 +984,15 @@ var WorkerPool = class {
|
|
|
935
984
|
task2.reject(reason);
|
|
936
985
|
}
|
|
937
986
|
}
|
|
987
|
+
rejectExclusiveTaskForWorker(worker, reason) {
|
|
988
|
+
for (const [taskId, assignedWorker] of this.exclusiveWorkers) {
|
|
989
|
+
if (assignedWorker === worker) {
|
|
990
|
+
this.exclusiveWorkers.delete(taskId);
|
|
991
|
+
this.rejectTask(taskId, reason);
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
938
996
|
// --- Cancellation ---
|
|
939
997
|
cancelTask(taskId) {
|
|
940
998
|
const removed = this.removeFromQueue(taskId);
|
|
@@ -983,6 +1041,10 @@ var WorkerPool = class {
|
|
|
983
1041
|
}
|
|
984
1042
|
this.concurrentQueues[priority] = [];
|
|
985
1043
|
}
|
|
1044
|
+
for (const [taskId] of this.exclusiveWorkers) {
|
|
1045
|
+
this.taskMap.delete(taskId);
|
|
1046
|
+
}
|
|
1047
|
+
this.exclusiveWorkers.clear();
|
|
986
1048
|
for (const [, taskSet] of this.sharedWorkers) {
|
|
987
1049
|
for (const taskId of taskSet) {
|
|
988
1050
|
this.taskMap.delete(taskId);
|
|
@@ -1040,8 +1102,7 @@ var WorkerPool = class {
|
|
|
1040
1102
|
this.makeIdle(worker);
|
|
1041
1103
|
}
|
|
1042
1104
|
};
|
|
1043
|
-
worker.on("message", (
|
|
1044
|
-
const response = msg;
|
|
1105
|
+
worker.on("message", (response) => {
|
|
1045
1106
|
if (response.type === "ready") {
|
|
1046
1107
|
onReady();
|
|
1047
1108
|
return;
|
|
@@ -1049,12 +1110,7 @@ var WorkerPool = class {
|
|
|
1049
1110
|
this.handleWorkerMessage(worker, response);
|
|
1050
1111
|
});
|
|
1051
1112
|
worker.on("error", (err) => {
|
|
1052
|
-
|
|
1053
|
-
if (w === worker) {
|
|
1054
|
-
this.exclusiveWorkers.delete(taskId);
|
|
1055
|
-
break;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1113
|
+
this.rejectExclusiveTaskForWorker(worker, err);
|
|
1058
1114
|
const taskSet = this.sharedWorkers.get(worker);
|
|
1059
1115
|
if (taskSet) {
|
|
1060
1116
|
for (const taskId of taskSet) {
|
|
@@ -1074,12 +1130,7 @@ var WorkerPool = class {
|
|
|
1074
1130
|
if (idleIdx !== -1) {
|
|
1075
1131
|
this.idleWorkers.splice(idleIdx, 1);
|
|
1076
1132
|
}
|
|
1077
|
-
|
|
1078
|
-
if (w === worker) {
|
|
1079
|
-
this.exclusiveWorkers.delete(taskId);
|
|
1080
|
-
break;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1133
|
+
this.rejectExclusiveTaskForWorker(worker, new Error("Worker exited unexpectedly"));
|
|
1083
1134
|
const taskSet = this.sharedWorkers.get(worker);
|
|
1084
1135
|
if (taskSet) {
|
|
1085
1136
|
for (const taskId of taskSet) {
|
|
@@ -1173,11 +1224,7 @@ function spawn(fn, opts) {
|
|
|
1173
1224
|
if (opts?.channels) {
|
|
1174
1225
|
channelMap = {};
|
|
1175
1226
|
for (const [name, ch] of Object.entries(opts.channels)) {
|
|
1176
|
-
|
|
1177
|
-
if (!impl._id) {
|
|
1178
|
-
throw new Error(`Channel "${name}" is not a valid puru channel`);
|
|
1179
|
-
}
|
|
1180
|
-
channelMap[name] = impl._id;
|
|
1227
|
+
channelMap[name] = getChannelId(ch);
|
|
1181
1228
|
}
|
|
1182
1229
|
}
|
|
1183
1230
|
const task2 = {
|
|
@@ -1203,6 +1250,18 @@ function spawn(fn, opts) {
|
|
|
1203
1250
|
}
|
|
1204
1251
|
}
|
|
1205
1252
|
};
|
|
1253
|
+
const ctx = opts?.ctx;
|
|
1254
|
+
if (ctx) {
|
|
1255
|
+
if (ctx.signal.aborted) {
|
|
1256
|
+
settled = true;
|
|
1257
|
+
rejectFn(ctx.err ?? new DOMException("Task was cancelled", "AbortError"));
|
|
1258
|
+
return {
|
|
1259
|
+
result,
|
|
1260
|
+
cancel: () => {
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1206
1265
|
getPool().submit(task2);
|
|
1207
1266
|
const cancel = () => {
|
|
1208
1267
|
if (settled) return;
|
|
@@ -1210,6 +1269,14 @@ function spawn(fn, opts) {
|
|
|
1210
1269
|
getPool().cancelTask(taskId);
|
|
1211
1270
|
rejectFn(new DOMException("Task was cancelled", "AbortError"));
|
|
1212
1271
|
};
|
|
1272
|
+
if (ctx) {
|
|
1273
|
+
const onAbort = () => cancel();
|
|
1274
|
+
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
1275
|
+
result.then(
|
|
1276
|
+
() => ctx.signal.removeEventListener("abort", onAbort),
|
|
1277
|
+
() => ctx.signal.removeEventListener("abort", onAbort)
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1213
1280
|
return { result, cancel };
|
|
1214
1281
|
}
|
|
1215
1282
|
|
|
@@ -1217,6 +1284,17 @@ function spawn(fn, opts) {
|
|
|
1217
1284
|
var WaitGroup = class {
|
|
1218
1285
|
tasks = [];
|
|
1219
1286
|
controller = new AbortController();
|
|
1287
|
+
ctx;
|
|
1288
|
+
constructor(ctx) {
|
|
1289
|
+
this.ctx = ctx;
|
|
1290
|
+
if (ctx) {
|
|
1291
|
+
if (ctx.signal.aborted) {
|
|
1292
|
+
this.controller.abort();
|
|
1293
|
+
} else {
|
|
1294
|
+
ctx.signal.addEventListener("abort", () => this.cancel(), { once: true });
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1220
1298
|
/**
|
|
1221
1299
|
* An `AbortSignal` shared across all tasks in this group.
|
|
1222
1300
|
* Pass it into spawned functions so they can stop early when `cancel()` is called.
|
|
@@ -1233,7 +1311,7 @@ var WaitGroup = class {
|
|
|
1233
1311
|
if (this.controller.signal.aborted) {
|
|
1234
1312
|
throw new Error("WaitGroup has been cancelled");
|
|
1235
1313
|
}
|
|
1236
|
-
const handle = spawn(fn, opts);
|
|
1314
|
+
const handle = spawn(fn, { ...opts, ctx: this.ctx });
|
|
1237
1315
|
this.tasks.push(handle);
|
|
1238
1316
|
}
|
|
1239
1317
|
/**
|
|
@@ -1266,28 +1344,77 @@ var WaitGroup = class {
|
|
|
1266
1344
|
var ErrGroup = class {
|
|
1267
1345
|
tasks = [];
|
|
1268
1346
|
controller = new AbortController();
|
|
1269
|
-
firstError =
|
|
1347
|
+
firstError = null;
|
|
1270
1348
|
hasError = false;
|
|
1349
|
+
ctx;
|
|
1350
|
+
limit = 0;
|
|
1351
|
+
// 0 = unlimited
|
|
1352
|
+
inFlight = 0;
|
|
1353
|
+
waiting = [];
|
|
1354
|
+
constructor(ctx) {
|
|
1355
|
+
this.ctx = ctx;
|
|
1356
|
+
if (ctx) {
|
|
1357
|
+
if (ctx.signal.aborted) {
|
|
1358
|
+
this.controller.abort();
|
|
1359
|
+
} else {
|
|
1360
|
+
ctx.signal.addEventListener("abort", () => this.cancel(), { once: true });
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1271
1364
|
get signal() {
|
|
1272
1365
|
return this.controller.signal;
|
|
1273
1366
|
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Set the maximum number of tasks that can run concurrently.
|
|
1369
|
+
* Like Go's `errgroup.SetLimit()`. Must be called before any `spawn()`.
|
|
1370
|
+
* A value of 0 (default) means unlimited.
|
|
1371
|
+
*/
|
|
1372
|
+
setLimit(n) {
|
|
1373
|
+
if (this.tasks.length > 0) {
|
|
1374
|
+
throw new Error("SetLimit must be called before any spawn()");
|
|
1375
|
+
}
|
|
1376
|
+
if (n < 0 || !Number.isInteger(n)) {
|
|
1377
|
+
throw new RangeError("Limit must be a non-negative integer");
|
|
1378
|
+
}
|
|
1379
|
+
this.limit = n;
|
|
1380
|
+
}
|
|
1274
1381
|
spawn(fn, opts) {
|
|
1275
1382
|
if (this.controller.signal.aborted) {
|
|
1276
1383
|
throw new Error("ErrGroup has been cancelled");
|
|
1277
1384
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1385
|
+
if (this.limit > 0 && this.inFlight >= this.limit) {
|
|
1386
|
+
const result2 = new Promise((resolve) => {
|
|
1387
|
+
this.waiting.push(resolve);
|
|
1388
|
+
}).then(() => this.doSpawn(fn, opts));
|
|
1389
|
+
this.tasks.push({ result: result2, cancel: () => {
|
|
1390
|
+
} });
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
const result = this.doSpawn(fn, opts);
|
|
1394
|
+
this.tasks.push({ result, cancel: () => {
|
|
1395
|
+
} });
|
|
1396
|
+
}
|
|
1397
|
+
doSpawn(fn, opts) {
|
|
1398
|
+
this.inFlight++;
|
|
1399
|
+
const handle = spawn(fn, { ...opts, ctx: this.ctx });
|
|
1400
|
+
const onSettle = () => {
|
|
1401
|
+
this.inFlight--;
|
|
1402
|
+
const next = this.waiting.shift();
|
|
1403
|
+
if (next) next();
|
|
1404
|
+
};
|
|
1405
|
+
handle.result.then(onSettle, (err) => {
|
|
1406
|
+
onSettle();
|
|
1280
1407
|
if (!this.hasError) {
|
|
1281
1408
|
this.hasError = true;
|
|
1282
1409
|
this.firstError = err;
|
|
1283
1410
|
this.cancel();
|
|
1284
1411
|
}
|
|
1285
1412
|
});
|
|
1286
|
-
|
|
1413
|
+
return handle.result;
|
|
1287
1414
|
}
|
|
1288
1415
|
async wait() {
|
|
1289
1416
|
const settled = await Promise.allSettled(this.tasks.map((t) => t.result));
|
|
1290
|
-
if (this.hasError) {
|
|
1417
|
+
if (this.hasError && this.firstError) {
|
|
1291
1418
|
throw this.firstError;
|
|
1292
1419
|
}
|
|
1293
1420
|
return settled.map((r) => {
|
|
@@ -1339,6 +1466,121 @@ var Mutex = class {
|
|
|
1339
1466
|
return this.locked;
|
|
1340
1467
|
}
|
|
1341
1468
|
};
|
|
1469
|
+
var RWMutex = class {
|
|
1470
|
+
readers = 0;
|
|
1471
|
+
writing = false;
|
|
1472
|
+
readQueue = [];
|
|
1473
|
+
writeQueue = [];
|
|
1474
|
+
async rLock() {
|
|
1475
|
+
if (!this.writing && this.writeQueue.length === 0) {
|
|
1476
|
+
this.readers++;
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
return new Promise((resolve) => {
|
|
1480
|
+
this.readQueue.push(() => {
|
|
1481
|
+
this.readers++;
|
|
1482
|
+
resolve();
|
|
1483
|
+
});
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
rUnlock() {
|
|
1487
|
+
if (this.readers <= 0) {
|
|
1488
|
+
throw new Error("Cannot rUnlock a RWMutex that is not read-locked");
|
|
1489
|
+
}
|
|
1490
|
+
this.readers--;
|
|
1491
|
+
if (this.readers === 0) {
|
|
1492
|
+
this.wakeWriter();
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
async lock() {
|
|
1496
|
+
if (!this.writing && this.readers === 0) {
|
|
1497
|
+
this.writing = true;
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
return new Promise((resolve) => {
|
|
1501
|
+
this.writeQueue.push(() => {
|
|
1502
|
+
this.writing = true;
|
|
1503
|
+
resolve();
|
|
1504
|
+
});
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
unlock() {
|
|
1508
|
+
if (!this.writing) {
|
|
1509
|
+
throw new Error("Cannot unlock a RWMutex that is not write-locked");
|
|
1510
|
+
}
|
|
1511
|
+
this.writing = false;
|
|
1512
|
+
if (this.readQueue.length > 0) {
|
|
1513
|
+
this.wakeReaders();
|
|
1514
|
+
} else {
|
|
1515
|
+
this.wakeWriter();
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
async withRLock(fn) {
|
|
1519
|
+
await this.rLock();
|
|
1520
|
+
try {
|
|
1521
|
+
return await fn();
|
|
1522
|
+
} finally {
|
|
1523
|
+
this.rUnlock();
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
async withLock(fn) {
|
|
1527
|
+
await this.lock();
|
|
1528
|
+
try {
|
|
1529
|
+
return await fn();
|
|
1530
|
+
} finally {
|
|
1531
|
+
this.unlock();
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
get isLocked() {
|
|
1535
|
+
return this.writing || this.readers > 0;
|
|
1536
|
+
}
|
|
1537
|
+
wakeReaders() {
|
|
1538
|
+
const queue = this.readQueue;
|
|
1539
|
+
this.readQueue = [];
|
|
1540
|
+
for (const wake of queue) {
|
|
1541
|
+
wake();
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
wakeWriter() {
|
|
1545
|
+
const next = this.writeQueue.shift();
|
|
1546
|
+
if (next) next();
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
// src/cond.ts
|
|
1551
|
+
var Cond = class {
|
|
1552
|
+
mu;
|
|
1553
|
+
waiters = [];
|
|
1554
|
+
constructor(mu) {
|
|
1555
|
+
this.mu = mu;
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Atomically releases the mutex, suspends the caller until `signal()` or `broadcast()`
|
|
1559
|
+
* is called, then re-acquires the mutex before returning.
|
|
1560
|
+
*
|
|
1561
|
+
* Must be called while holding the mutex.
|
|
1562
|
+
*/
|
|
1563
|
+
async wait() {
|
|
1564
|
+
this.mu.unlock();
|
|
1565
|
+
await new Promise((resolve) => {
|
|
1566
|
+
this.waiters.push(resolve);
|
|
1567
|
+
});
|
|
1568
|
+
await this.mu.lock();
|
|
1569
|
+
}
|
|
1570
|
+
/** Wake one waiting task (if any). */
|
|
1571
|
+
signal() {
|
|
1572
|
+
const next = this.waiters.shift();
|
|
1573
|
+
if (next) next();
|
|
1574
|
+
}
|
|
1575
|
+
/** Wake all waiting tasks. */
|
|
1576
|
+
broadcast() {
|
|
1577
|
+
const queue = this.waiters;
|
|
1578
|
+
this.waiters = [];
|
|
1579
|
+
for (const wake of queue) {
|
|
1580
|
+
wake();
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1342
1584
|
|
|
1343
1585
|
// src/once.ts
|
|
1344
1586
|
var Once = class {
|
|
@@ -1377,6 +1619,7 @@ function select(cases, opts) {
|
|
|
1377
1619
|
if (settled) return;
|
|
1378
1620
|
settled = true;
|
|
1379
1621
|
try {
|
|
1622
|
+
;
|
|
1380
1623
|
handler(value);
|
|
1381
1624
|
resolve();
|
|
1382
1625
|
} catch (err) {
|
|
@@ -1410,6 +1653,7 @@ function select(cases, opts) {
|
|
|
1410
1653
|
if (settled) return;
|
|
1411
1654
|
settled = true;
|
|
1412
1655
|
try {
|
|
1656
|
+
;
|
|
1413
1657
|
handler(value);
|
|
1414
1658
|
resolve();
|
|
1415
1659
|
} catch (err) {
|
|
@@ -1480,11 +1724,60 @@ function ticker(ms) {
|
|
|
1480
1724
|
return new Ticker(ms);
|
|
1481
1725
|
}
|
|
1482
1726
|
|
|
1727
|
+
// src/timer.ts
|
|
1728
|
+
var Timer = class {
|
|
1729
|
+
timer = null;
|
|
1730
|
+
_stopped = false;
|
|
1731
|
+
/** Promise that resolves when the timer fires. Replaced on `reset()`. */
|
|
1732
|
+
channel;
|
|
1733
|
+
constructor(ms) {
|
|
1734
|
+
this.channel = this.schedule(ms);
|
|
1735
|
+
}
|
|
1736
|
+
schedule(ms) {
|
|
1737
|
+
return new Promise((resolve) => {
|
|
1738
|
+
this.timer = setTimeout(() => {
|
|
1739
|
+
this._stopped = true;
|
|
1740
|
+
this.timer = null;
|
|
1741
|
+
resolve();
|
|
1742
|
+
}, ms);
|
|
1743
|
+
if (typeof this.timer === "object" && "unref" in this.timer) {
|
|
1744
|
+
this.timer.unref();
|
|
1745
|
+
}
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Stop the timer. Returns `true` if the timer was pending (stopped before firing),
|
|
1750
|
+
* `false` if it had already fired or was already stopped.
|
|
1751
|
+
*
|
|
1752
|
+
* After stopping, the current `channel` promise will never resolve.
|
|
1753
|
+
*/
|
|
1754
|
+
stop() {
|
|
1755
|
+
if (this.timer === null) return false;
|
|
1756
|
+
clearTimeout(this.timer);
|
|
1757
|
+
this.timer = null;
|
|
1758
|
+
this._stopped = true;
|
|
1759
|
+
return true;
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Reset the timer to fire after `ms` milliseconds.
|
|
1763
|
+
* If the timer was pending, it is stopped first. Creates a new `channel` promise.
|
|
1764
|
+
*/
|
|
1765
|
+
reset(ms) {
|
|
1766
|
+
this.stop();
|
|
1767
|
+
this._stopped = false;
|
|
1768
|
+
this.channel = this.schedule(ms);
|
|
1769
|
+
}
|
|
1770
|
+
/** Whether the timer has fired or been stopped. */
|
|
1771
|
+
get stopped() {
|
|
1772
|
+
return this._stopped;
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
|
|
1483
1776
|
// src/registry.ts
|
|
1484
1777
|
var taskCounter2 = 0;
|
|
1485
1778
|
function task(fn) {
|
|
1779
|
+
const fnStr = serializeFunction(fn);
|
|
1486
1780
|
return (...args) => {
|
|
1487
|
-
const fnStr = serializeFunction(fn);
|
|
1488
1781
|
const serializedArgs = args.map((a) => {
|
|
1489
1782
|
const json = JSON.stringify(a);
|
|
1490
1783
|
if (json === void 0) {
|
|
@@ -1521,14 +1814,179 @@ function task(fn) {
|
|
|
1521
1814
|
return result;
|
|
1522
1815
|
};
|
|
1523
1816
|
}
|
|
1817
|
+
|
|
1818
|
+
// src/context.ts
|
|
1819
|
+
var CancelledError = class extends Error {
|
|
1820
|
+
constructor(message = "context cancelled") {
|
|
1821
|
+
super(message);
|
|
1822
|
+
this.name = "CancelledError";
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
var DeadlineExceededError = class extends Error {
|
|
1826
|
+
constructor() {
|
|
1827
|
+
super("context deadline exceeded");
|
|
1828
|
+
this.name = "DeadlineExceededError";
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
var BaseContext = class {
|
|
1832
|
+
_err = null;
|
|
1833
|
+
controller;
|
|
1834
|
+
parent;
|
|
1835
|
+
constructor(parent) {
|
|
1836
|
+
this.parent = parent;
|
|
1837
|
+
this.controller = new AbortController();
|
|
1838
|
+
if (parent) {
|
|
1839
|
+
if (parent.signal.aborted) {
|
|
1840
|
+
this._err = parent.err ?? new CancelledError();
|
|
1841
|
+
this.controller.abort();
|
|
1842
|
+
} else {
|
|
1843
|
+
parent.signal.addEventListener(
|
|
1844
|
+
"abort",
|
|
1845
|
+
() => {
|
|
1846
|
+
if (!this.controller.signal.aborted) {
|
|
1847
|
+
this._err = parent.err ?? new CancelledError();
|
|
1848
|
+
this.controller.abort();
|
|
1849
|
+
}
|
|
1850
|
+
},
|
|
1851
|
+
{ once: true }
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
get signal() {
|
|
1857
|
+
return this.controller.signal;
|
|
1858
|
+
}
|
|
1859
|
+
get deadline() {
|
|
1860
|
+
return this.parent?.deadline ?? null;
|
|
1861
|
+
}
|
|
1862
|
+
get err() {
|
|
1863
|
+
return this._err;
|
|
1864
|
+
}
|
|
1865
|
+
value(_key) {
|
|
1866
|
+
return this.parent?.value(_key);
|
|
1867
|
+
}
|
|
1868
|
+
done() {
|
|
1869
|
+
if (this.controller.signal.aborted) return Promise.resolve();
|
|
1870
|
+
return new Promise((resolve) => {
|
|
1871
|
+
this.controller.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
var BackgroundContext = class {
|
|
1876
|
+
_signal = new AbortController().signal;
|
|
1877
|
+
get signal() {
|
|
1878
|
+
return this._signal;
|
|
1879
|
+
}
|
|
1880
|
+
get deadline() {
|
|
1881
|
+
return null;
|
|
1882
|
+
}
|
|
1883
|
+
get err() {
|
|
1884
|
+
return null;
|
|
1885
|
+
}
|
|
1886
|
+
value(_key) {
|
|
1887
|
+
return void 0;
|
|
1888
|
+
}
|
|
1889
|
+
done() {
|
|
1890
|
+
return new Promise(() => {
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
var bg = null;
|
|
1895
|
+
function background() {
|
|
1896
|
+
if (!bg) bg = new BackgroundContext();
|
|
1897
|
+
return bg;
|
|
1898
|
+
}
|
|
1899
|
+
var CancelContext = class extends BaseContext {
|
|
1900
|
+
cancel(reason) {
|
|
1901
|
+
if (!this.controller.signal.aborted) {
|
|
1902
|
+
this._err = new CancelledError(reason ?? "context cancelled");
|
|
1903
|
+
this.controller.abort();
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
function withCancel(parent) {
|
|
1908
|
+
const ctx = new CancelContext(parent);
|
|
1909
|
+
return [ctx, (reason) => ctx.cancel(reason)];
|
|
1910
|
+
}
|
|
1911
|
+
var DeadlineContext = class extends BaseContext {
|
|
1912
|
+
_deadline;
|
|
1913
|
+
timer = null;
|
|
1914
|
+
constructor(parent, deadline) {
|
|
1915
|
+
super(parent);
|
|
1916
|
+
this._deadline = deadline;
|
|
1917
|
+
if (parent.deadline && parent.deadline < deadline) {
|
|
1918
|
+
this._deadline = parent.deadline;
|
|
1919
|
+
}
|
|
1920
|
+
if (this.controller.signal.aborted) {
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
const ms = this._deadline.getTime() - Date.now();
|
|
1924
|
+
if (ms <= 0) {
|
|
1925
|
+
this._err = new DeadlineExceededError();
|
|
1926
|
+
this.controller.abort();
|
|
1927
|
+
} else {
|
|
1928
|
+
this.timer = setTimeout(() => {
|
|
1929
|
+
if (!this.controller.signal.aborted) {
|
|
1930
|
+
this._err = new DeadlineExceededError();
|
|
1931
|
+
this.controller.abort();
|
|
1932
|
+
}
|
|
1933
|
+
}, ms);
|
|
1934
|
+
if (typeof this.timer === "object" && "unref" in this.timer) {
|
|
1935
|
+
this.timer.unref();
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
get deadline() {
|
|
1940
|
+
return this._deadline;
|
|
1941
|
+
}
|
|
1942
|
+
cancel(reason) {
|
|
1943
|
+
if (this.timer !== null) {
|
|
1944
|
+
clearTimeout(this.timer);
|
|
1945
|
+
this.timer = null;
|
|
1946
|
+
}
|
|
1947
|
+
if (!this.controller.signal.aborted) {
|
|
1948
|
+
this._err = new CancelledError(reason ?? "context cancelled");
|
|
1949
|
+
this.controller.abort();
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1953
|
+
function withDeadline(parent, deadline) {
|
|
1954
|
+
const ctx = new DeadlineContext(parent, deadline);
|
|
1955
|
+
return [ctx, (reason) => ctx.cancel(reason)];
|
|
1956
|
+
}
|
|
1957
|
+
function withTimeout(parent, ms) {
|
|
1958
|
+
return withDeadline(parent, new Date(Date.now() + ms));
|
|
1959
|
+
}
|
|
1960
|
+
var ValueContext = class extends BaseContext {
|
|
1961
|
+
key;
|
|
1962
|
+
val;
|
|
1963
|
+
constructor(parent, key, val) {
|
|
1964
|
+
super(parent);
|
|
1965
|
+
this.key = key;
|
|
1966
|
+
this.val = val;
|
|
1967
|
+
}
|
|
1968
|
+
value(key) {
|
|
1969
|
+
if (key === this.key) return this.val;
|
|
1970
|
+
return this.parent?.value(key);
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1973
|
+
function withValue(parent, key, value) {
|
|
1974
|
+
return new ValueContext(parent, key, value);
|
|
1975
|
+
}
|
|
1524
1976
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1525
1977
|
0 && (module.exports = {
|
|
1978
|
+
CancelledError,
|
|
1979
|
+
Cond,
|
|
1980
|
+
DeadlineExceededError,
|
|
1526
1981
|
ErrGroup,
|
|
1527
1982
|
Mutex,
|
|
1528
1983
|
Once,
|
|
1984
|
+
RWMutex,
|
|
1529
1985
|
Ticker,
|
|
1986
|
+
Timer,
|
|
1530
1987
|
WaitGroup,
|
|
1531
1988
|
after,
|
|
1989
|
+
background,
|
|
1532
1990
|
chan,
|
|
1533
1991
|
configure,
|
|
1534
1992
|
detectCapability,
|
|
@@ -1539,6 +1997,10 @@ function task(fn) {
|
|
|
1539
1997
|
spawn,
|
|
1540
1998
|
stats,
|
|
1541
1999
|
task,
|
|
1542
|
-
ticker
|
|
2000
|
+
ticker,
|
|
2001
|
+
withCancel,
|
|
2002
|
+
withDeadline,
|
|
2003
|
+
withTimeout,
|
|
2004
|
+
withValue
|
|
1543
2005
|
});
|
|
1544
2006
|
//# sourceMappingURL=index.cjs.map
|