@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.js
CHANGED
|
@@ -56,21 +56,20 @@ function getConfig() {
|
|
|
56
56
|
|
|
57
57
|
// src/runtime.ts
|
|
58
58
|
function detectRuntime() {
|
|
59
|
-
if (
|
|
60
|
-
if (
|
|
61
|
-
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node)
|
|
62
|
-
return "node";
|
|
59
|
+
if ("Bun" in globalThis) return "bun";
|
|
60
|
+
if ("Deno" in globalThis) return "deno";
|
|
61
|
+
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node) return "node";
|
|
63
62
|
return "browser";
|
|
64
63
|
}
|
|
65
64
|
function detectCapability() {
|
|
66
65
|
const runtime = detectRuntime();
|
|
67
66
|
if (runtime === "node" || runtime === "bun") return "full-threads";
|
|
68
|
-
if (
|
|
67
|
+
if ("Worker" in globalThis) return "full-threads";
|
|
69
68
|
return "single-thread";
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
// src/adapters/node.ts
|
|
73
|
-
import { Worker
|
|
72
|
+
import { Worker } from "worker_threads";
|
|
74
73
|
|
|
75
74
|
// src/bootstrap.ts
|
|
76
75
|
var CHANNEL_PROXY_CODE = `
|
|
@@ -286,7 +285,7 @@ self.postMessage({ type: 'ready' });
|
|
|
286
285
|
var NodeManagedWorker = class {
|
|
287
286
|
worker;
|
|
288
287
|
constructor() {
|
|
289
|
-
this.worker = new
|
|
288
|
+
this.worker = new Worker(NODE_BOOTSTRAP_CODE, { eval: true });
|
|
290
289
|
}
|
|
291
290
|
get id() {
|
|
292
291
|
return this.worker.threadId;
|
|
@@ -332,7 +331,11 @@ var BunManagedWorker = class {
|
|
|
332
331
|
id;
|
|
333
332
|
constructor() {
|
|
334
333
|
this.id = ++workerIdCounter;
|
|
335
|
-
|
|
334
|
+
const WorkerConstructor = globalThis.Worker;
|
|
335
|
+
if (!WorkerConstructor) {
|
|
336
|
+
throw new Error("Bun Worker constructor is not available in this runtime");
|
|
337
|
+
}
|
|
338
|
+
this.worker = new WorkerConstructor(getBootstrapFile());
|
|
336
339
|
}
|
|
337
340
|
postMessage(data) {
|
|
338
341
|
this.worker.postMessage(data);
|
|
@@ -344,27 +347,28 @@ var BunManagedWorker = class {
|
|
|
344
347
|
on(event, handler) {
|
|
345
348
|
if (event === "message") {
|
|
346
349
|
this.worker.addEventListener("message", (e) => {
|
|
350
|
+
;
|
|
347
351
|
handler(e.data);
|
|
348
352
|
});
|
|
349
353
|
} else if (event === "error") {
|
|
350
354
|
this.worker.addEventListener("error", (e) => {
|
|
355
|
+
;
|
|
351
356
|
handler(e.error ?? new Error(e.message));
|
|
352
357
|
});
|
|
353
358
|
} else if (event === "exit") {
|
|
354
359
|
this.worker.addEventListener("close", (e) => {
|
|
360
|
+
;
|
|
355
361
|
handler(e.code ?? 0);
|
|
356
362
|
});
|
|
357
363
|
}
|
|
358
364
|
}
|
|
359
365
|
unref() {
|
|
360
366
|
if ("unref" in this.worker && typeof this.worker.unref === "function") {
|
|
361
|
-
;
|
|
362
367
|
this.worker.unref();
|
|
363
368
|
}
|
|
364
369
|
}
|
|
365
370
|
ref() {
|
|
366
371
|
if ("ref" in this.worker && typeof this.worker.ref === "function") {
|
|
367
|
-
;
|
|
368
372
|
this.worker.ref();
|
|
369
373
|
}
|
|
370
374
|
}
|
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
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", (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1237
|
-
|
|
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
|
-
|
|
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
|