@atolljs/node 0.1.7 → 0.1.9
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/{chunk-SHWEILEH.js → chunk-VEJT6OVD.js} +344 -57
- package/dist/index.js +1 -1
- package/dist/redis.js +1 -1
- package/package.json +2 -2
|
@@ -1,6 +1,93 @@
|
|
|
1
|
+
// ../../src/devtools.ts
|
|
2
|
+
var THREAD = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope ? "worker" : "main";
|
|
3
|
+
var sink = null;
|
|
4
|
+
var emitDevtools = (event) => {
|
|
5
|
+
if (sink) sink({ ...event, at: performance.now(), thread: THREAD });
|
|
6
|
+
};
|
|
7
|
+
var forwardDevtools = (event) => {
|
|
8
|
+
if (sink) sink(event);
|
|
9
|
+
};
|
|
10
|
+
var devtoolsEnabled = () => sink !== null;
|
|
11
|
+
var previewValue = (value, max = 2048) => {
|
|
12
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
13
|
+
let out;
|
|
14
|
+
try {
|
|
15
|
+
out = JSON.stringify(value, (_k, v) => {
|
|
16
|
+
if (typeof v === "function") return "[fn]";
|
|
17
|
+
if (typeof v === "bigint") return `${v}n`;
|
|
18
|
+
if (v instanceof ArrayBuffer) return `[ArrayBuffer ${v.byteLength}B]`;
|
|
19
|
+
if (ArrayBuffer.isView(v)) return `[${v.constructor.name} ${v.byteLength}B]`;
|
|
20
|
+
if (v instanceof Map) return { "[Map]": [...v.entries()].slice(0, 50) };
|
|
21
|
+
if (v instanceof Set) return { "[Set]": [...v].slice(0, 50) };
|
|
22
|
+
if (v && typeof v === "object") {
|
|
23
|
+
if (seen.has(v)) return "[cycle]";
|
|
24
|
+
seen.add(v);
|
|
25
|
+
}
|
|
26
|
+
return v;
|
|
27
|
+
}) ?? String(value);
|
|
28
|
+
} catch {
|
|
29
|
+
out = String(value);
|
|
30
|
+
}
|
|
31
|
+
return out.length > max ? `${out.slice(0, max)}\u2026` : out;
|
|
32
|
+
};
|
|
33
|
+
var estimateCloneBytes = (value, budget = 5e3) => {
|
|
34
|
+
let nodes = 0;
|
|
35
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
36
|
+
const walk = (v) => {
|
|
37
|
+
if (++nodes > budget) return 0;
|
|
38
|
+
switch (typeof v) {
|
|
39
|
+
case "string":
|
|
40
|
+
return 4 + v.length * 2;
|
|
41
|
+
case "number":
|
|
42
|
+
return 8;
|
|
43
|
+
case "boolean":
|
|
44
|
+
return 4;
|
|
45
|
+
case "bigint":
|
|
46
|
+
return 16;
|
|
47
|
+
case "undefined":
|
|
48
|
+
return 1;
|
|
49
|
+
case "object": {
|
|
50
|
+
if (v === null) return 1;
|
|
51
|
+
if (v instanceof ArrayBuffer) return v.byteLength;
|
|
52
|
+
if (ArrayBuffer.isView(v)) return v.byteLength;
|
|
53
|
+
if (typeof SharedArrayBuffer !== "undefined" && v instanceof SharedArrayBuffer) return 8;
|
|
54
|
+
if (seen.has(v)) return 4;
|
|
55
|
+
seen.add(v);
|
|
56
|
+
let n = 8;
|
|
57
|
+
if (v instanceof Map) for (const [k, x] of v) n += walk(k) + walk(x);
|
|
58
|
+
else if (v instanceof Set) for (const x of v) n += walk(x);
|
|
59
|
+
else if (Array.isArray(v)) for (const x of v) n += walk(x);
|
|
60
|
+
else for (const k of Object.keys(v)) n += 4 + k.length * 2 + walk(v[k]);
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
default:
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
return walk(value);
|
|
68
|
+
};
|
|
69
|
+
var commands = /* @__PURE__ */ new Map();
|
|
70
|
+
var registerDevtoolsCommand = (name, handler) => {
|
|
71
|
+
commands.set(name, handler);
|
|
72
|
+
return () => {
|
|
73
|
+
if (commands.get(name) === handler) commands.delete(name);
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
var BODY_PREVIEW = 8 * 1024;
|
|
77
|
+
var seq = 0;
|
|
78
|
+
var named = /* @__PURE__ */ new Map();
|
|
79
|
+
var nextDevtoolsId = (prefix, name) => {
|
|
80
|
+
const slug = name?.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
81
|
+
if (!slug) return `${prefix}-${++seq}`;
|
|
82
|
+
const base = `${slug}-${prefix === "pool" ? "p" : prefix === "worker" ? "w" : prefix}`;
|
|
83
|
+
const n = (named.get(base) ?? 0) + 1;
|
|
84
|
+
named.set(base, n);
|
|
85
|
+
return n === 1 ? base : `${base}${n}`;
|
|
86
|
+
};
|
|
87
|
+
|
|
1
88
|
// ../../src/log.ts
|
|
2
89
|
var LEVELS = { trace: 0, debug: 1, info: 2, warn: 3, error: 4, off: 5 };
|
|
3
|
-
var
|
|
90
|
+
var THREAD2 = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope ? "worker" : "main";
|
|
4
91
|
var consoleSink = ({ level, scope, message, data, thread, at }) => {
|
|
5
92
|
const prefix = `[${(at / 1e3).toFixed(2)}s ${thread}:${scope}]`;
|
|
6
93
|
const fn = level === "error" ? console.error : level === "warn" ? console.warn : level === "info" ? console.info : console.debug;
|
|
@@ -8,10 +95,25 @@ var consoleSink = ({ level, scope, message, data, thread, at }) => {
|
|
|
8
95
|
else fn(`${prefix} ${message}`);
|
|
9
96
|
};
|
|
10
97
|
var minLevel = "info";
|
|
11
|
-
var
|
|
98
|
+
var sink2 = consoleSink;
|
|
99
|
+
var teeing = false;
|
|
12
100
|
function log(level, scope, message, data) {
|
|
13
101
|
if (LEVELS[level] < LEVELS[minLevel]) return;
|
|
14
|
-
|
|
102
|
+
sink2({ level, scope, message, data, thread: THREAD2, at: performance.now() });
|
|
103
|
+
if (!teeing && devtoolsEnabled()) {
|
|
104
|
+
teeing = true;
|
|
105
|
+
try {
|
|
106
|
+
emitDevtools({
|
|
107
|
+
type: "log",
|
|
108
|
+
level,
|
|
109
|
+
scope,
|
|
110
|
+
message,
|
|
111
|
+
...data !== void 0 ? { data: previewValue(data, 1024) } : {}
|
|
112
|
+
});
|
|
113
|
+
} finally {
|
|
114
|
+
teeing = false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
15
117
|
}
|
|
16
118
|
function scoped(scope) {
|
|
17
119
|
return {
|
|
@@ -24,20 +126,6 @@ function scoped(scope) {
|
|
|
24
126
|
}
|
|
25
127
|
var fmtBytes = (bytes) => bytes >= 1 << 20 ? `${(bytes / (1 << 20)).toFixed(1)}MB` : bytes >= 1024 ? `${(bytes / 1024).toFixed(1)}KB` : `${bytes}B`;
|
|
26
128
|
|
|
27
|
-
// ../../src/devtools.ts
|
|
28
|
-
var THREAD2 = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope ? "worker" : "main";
|
|
29
|
-
var sink2 = null;
|
|
30
|
-
var emitDevtools = (event) => {
|
|
31
|
-
if (sink2) sink2({ ...event, at: performance.now(), thread: THREAD2 });
|
|
32
|
-
};
|
|
33
|
-
var forwardDevtools = (event) => {
|
|
34
|
-
if (sink2) sink2(event);
|
|
35
|
-
};
|
|
36
|
-
var devtoolsEnabled = () => sink2 !== null;
|
|
37
|
-
var BODY_PREVIEW = 8 * 1024;
|
|
38
|
-
var seq = 0;
|
|
39
|
-
var nextDevtoolsId = (prefix) => `${prefix}-${++seq}`;
|
|
40
|
-
|
|
41
129
|
// ../../src/contract/sharedMemory.ts
|
|
42
130
|
var memLog = scoped("memory");
|
|
43
131
|
var codecTextEncoder = new TextEncoder();
|
|
@@ -366,6 +454,177 @@ var WorkerCrashedError = class extends Error {
|
|
|
366
454
|
}
|
|
367
455
|
};
|
|
368
456
|
|
|
457
|
+
// ../../src/userTiming.ts
|
|
458
|
+
var measureAtoll = (name, start, end, t) => {
|
|
459
|
+
const perf = globalThis.performance;
|
|
460
|
+
if (typeof perf?.measure !== "function") return;
|
|
461
|
+
try {
|
|
462
|
+
perf.measure(name, {
|
|
463
|
+
start,
|
|
464
|
+
end: Math.max(end, start),
|
|
465
|
+
detail: {
|
|
466
|
+
devtools: {
|
|
467
|
+
dataType: "track-entry",
|
|
468
|
+
track: t.track,
|
|
469
|
+
trackGroup: "atoll",
|
|
470
|
+
color: t.color ?? "primary",
|
|
471
|
+
properties: t.properties,
|
|
472
|
+
tooltipText: t.tooltipText
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
perf.clearMeasures?.(name);
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
var measureTaskCall = (runnerId, taskId, callId, slot, start, end, ok) => measureAtoll(`atoll task ${taskId}`, start, end, {
|
|
481
|
+
track: runnerId,
|
|
482
|
+
color: ok ? "primary" : "error",
|
|
483
|
+
properties: [["runner", runnerId], ["slot", slot], ["call", callId], ["outcome", ok ? "ok" : "error"]],
|
|
484
|
+
tooltipText: `${taskId} on ${runnerId}#${slot}: ${(end - start).toFixed(1)}ms`
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// ../../src/pool/workerChannel.ts
|
|
488
|
+
function setupChannelMemory(config, owner) {
|
|
489
|
+
if (config.sharedMemory || config.sharedBuffer !== void 0) {
|
|
490
|
+
if (typeof SharedArrayBuffer === "undefined" || typeof crossOriginIsolated !== "undefined" && !crossOriginIsolated) {
|
|
491
|
+
throw new Error(
|
|
492
|
+
`${owner} requires SharedArrayBuffer in a cross-origin isolated context \u2014 serve the page with COOP/COEP headers.`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
let injectedBuffer;
|
|
497
|
+
if (config.sharedBuffer !== void 0) {
|
|
498
|
+
injectedBuffer = typeof config.sharedBuffer === "function" ? config.sharedBuffer() : config.sharedBuffer;
|
|
499
|
+
if (!injectedBuffer) {
|
|
500
|
+
throw new Error(
|
|
501
|
+
`${owner}: \`sharedBuffer\` resolved to an empty buffer \u2014 the producing pool or contract has no bound memory yet.`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (!config.sharedMemory) return { sharedMemory: void 0, injectedBuffer, persistence: void 0 };
|
|
506
|
+
const sharedMemory = config.sharedMemory;
|
|
507
|
+
let memoryManager;
|
|
508
|
+
if (injectedBuffer) {
|
|
509
|
+
sharedMemory.bind(injectedBuffer);
|
|
510
|
+
} else {
|
|
511
|
+
memoryManager = new MemoryManager(config.memory);
|
|
512
|
+
memoryManager.ensureCapacity(sharedMemory.totalBytes);
|
|
513
|
+
sharedMemory.bind(memoryManager.getBuffer());
|
|
514
|
+
}
|
|
515
|
+
return { sharedMemory, memoryManager, injectedBuffer, persistence: config.persistence?.(sharedMemory) };
|
|
516
|
+
}
|
|
517
|
+
function sendInit(worker, mem) {
|
|
518
|
+
if (mem.memoryManager) {
|
|
519
|
+
worker.postMessage({ type: "INIT_MEMORY", memory: mem.memoryManager.memory, devtools: devtoolsEnabled() });
|
|
520
|
+
return "INIT_MEMORY";
|
|
521
|
+
}
|
|
522
|
+
if (mem.injectedBuffer) {
|
|
523
|
+
worker.postMessage({ type: "INIT_MEMORY", buffer: mem.injectedBuffer, devtools: devtoolsEnabled() });
|
|
524
|
+
return "INIT_MEMORY";
|
|
525
|
+
}
|
|
526
|
+
worker.postMessage({ type: "INIT", devtools: devtoolsEnabled() });
|
|
527
|
+
return "INIT";
|
|
528
|
+
}
|
|
529
|
+
function forwardWorkerDevtools(runnerId, slot, ev) {
|
|
530
|
+
const qualify = (id) => `${runnerId}#${slot}~${id}`;
|
|
531
|
+
forwardDevtools({
|
|
532
|
+
...ev,
|
|
533
|
+
...ev.poolId !== void 0 ? { poolId: qualify(ev.poolId) } : {},
|
|
534
|
+
worker: ev.worker === void 0 ? { poolId: runnerId, slot } : { poolId: qualify(ev.worker.poolId), slot: ev.worker.slot }
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ../../src/pool/devtoolsCommands.ts
|
|
539
|
+
var CHAOS_FAILURE = "chaos: injected failure";
|
|
540
|
+
var KILL_MESSAGE = "killed from devtools";
|
|
541
|
+
var runners = /* @__PURE__ */ new Map();
|
|
542
|
+
var uninstall = null;
|
|
543
|
+
var runnerOf = (args) => {
|
|
544
|
+
const id = args.poolId;
|
|
545
|
+
if (typeof id !== "string") throw new Error("poolId (string) is required");
|
|
546
|
+
const r = runners.get(id);
|
|
547
|
+
if (!r) {
|
|
548
|
+
throw new Error(
|
|
549
|
+
id.includes("~") ? `runner '${id}' lives inside a worker: nested pools are not controllable from the dashboard` : `no live runner '${id}' on this thread`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
return r;
|
|
553
|
+
};
|
|
554
|
+
var rate = (v, name) => {
|
|
555
|
+
if (v === void 0 || v === null) return void 0;
|
|
556
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 0 || v > 1) {
|
|
557
|
+
throw new Error(`${name} must be a number in 0..1`);
|
|
558
|
+
}
|
|
559
|
+
return v || void 0;
|
|
560
|
+
};
|
|
561
|
+
var parseChaos = (args) => {
|
|
562
|
+
const { delayMs } = args;
|
|
563
|
+
if (delayMs !== void 0 && delayMs !== null && (typeof delayMs !== "number" || !Number.isFinite(delayMs) || delayMs < 0)) {
|
|
564
|
+
throw new Error("delayMs must be a non-negative number");
|
|
565
|
+
}
|
|
566
|
+
const failRate = rate(args.failRate, "failRate");
|
|
567
|
+
const timeoutRate = rate(args.timeoutRate, "timeoutRate");
|
|
568
|
+
if ((failRate ?? 0) + (timeoutRate ?? 0) > 1) throw new Error("failRate + timeoutRate must not exceed 1");
|
|
569
|
+
const chaos = {};
|
|
570
|
+
if (delayMs) chaos.delayMs = delayMs;
|
|
571
|
+
if (failRate) chaos.failRate = failRate;
|
|
572
|
+
if (timeoutRate) chaos.timeoutRate = timeoutRate;
|
|
573
|
+
return Object.keys(chaos).length ? chaos : null;
|
|
574
|
+
};
|
|
575
|
+
var listRunners = () => [...runners.values()].map((r) => ({
|
|
576
|
+
poolId: r.poolId,
|
|
577
|
+
label: r.label ?? r.poolId,
|
|
578
|
+
size: r.size(),
|
|
579
|
+
dedicated: r.dedicated,
|
|
580
|
+
chaos: r.getChaos()
|
|
581
|
+
}));
|
|
582
|
+
var install = () => {
|
|
583
|
+
const offs = [
|
|
584
|
+
registerDevtoolsCommand("pool.list", () => listRunners()),
|
|
585
|
+
registerDevtoolsCommand("pool.stats", (args) => runnerOf(args).stats()),
|
|
586
|
+
registerDevtoolsCommand("worker.kill", (args) => {
|
|
587
|
+
const r = runnerOf(args);
|
|
588
|
+
const slot = args.slot ?? 0;
|
|
589
|
+
if (typeof slot !== "number" || !Number.isInteger(slot) || slot < 0) {
|
|
590
|
+
throw new Error("slot must be a non-negative integer");
|
|
591
|
+
}
|
|
592
|
+
r.kill(slot);
|
|
593
|
+
return { poolId: r.poolId, slot, killed: true };
|
|
594
|
+
}),
|
|
595
|
+
registerDevtoolsCommand("pool.chaos", (args) => {
|
|
596
|
+
const r = runnerOf(args);
|
|
597
|
+
r.setChaos(parseChaos(args));
|
|
598
|
+
return r.getChaos();
|
|
599
|
+
})
|
|
600
|
+
];
|
|
601
|
+
return () => offs.forEach((off) => off());
|
|
602
|
+
};
|
|
603
|
+
var registerDevtoolsRunner = (handle) => {
|
|
604
|
+
runners.set(handle.poolId, handle);
|
|
605
|
+
uninstall ??= install();
|
|
606
|
+
return () => {
|
|
607
|
+
if (runners.get(handle.poolId) !== handle) return;
|
|
608
|
+
runners.delete(handle.poolId);
|
|
609
|
+
if (runners.size === 0 && uninstall) {
|
|
610
|
+
uninstall();
|
|
611
|
+
uninstall = null;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
};
|
|
615
|
+
var applyChaos = (chaos, call) => {
|
|
616
|
+
const r = Math.random();
|
|
617
|
+
const fail = r < (chaos.failRate ?? 0);
|
|
618
|
+
const timeout = !fail && r < (chaos.failRate ?? 0) + (chaos.timeoutRate ?? 0);
|
|
619
|
+
if (!chaos.delayMs && !fail && !timeout) return call.post();
|
|
620
|
+
setTimeout(() => {
|
|
621
|
+
if (!call.live()) return;
|
|
622
|
+
if (fail) return call.fail();
|
|
623
|
+
if (timeout) call.timeout();
|
|
624
|
+
call.post();
|
|
625
|
+
}, chaos.delayMs ?? 0);
|
|
626
|
+
};
|
|
627
|
+
|
|
369
628
|
// ../../src/pool/workerPool.ts
|
|
370
629
|
var poolLog = scoped("pool");
|
|
371
630
|
var taskLog = scoped("task");
|
|
@@ -384,7 +643,10 @@ var WorkerPoolImpl = class {
|
|
|
384
643
|
pending = /* @__PURE__ */ new Map();
|
|
385
644
|
nextMessageId = 0;
|
|
386
645
|
queue = [];
|
|
646
|
+
mem;
|
|
387
647
|
memoryManager;
|
|
648
|
+
/** A caller-supplied buffer shipped via INIT_MEMORY (sub-worker / second-pool sharing). */
|
|
649
|
+
injectedBuffer;
|
|
388
650
|
workerUrl;
|
|
389
651
|
spawn;
|
|
390
652
|
concurrency;
|
|
@@ -392,6 +654,9 @@ var WorkerPoolImpl = class {
|
|
|
392
654
|
respawn;
|
|
393
655
|
taskTimeout;
|
|
394
656
|
closed = false;
|
|
657
|
+
/** Dashboard fault injection (`pool.chaos`); null unless devtools set it. */
|
|
658
|
+
chaos = null;
|
|
659
|
+
devtoolsOff;
|
|
395
660
|
drainWaiters = [];
|
|
396
661
|
statsAgg = {
|
|
397
662
|
completed: 0,
|
|
@@ -401,21 +666,18 @@ var WorkerPoolImpl = class {
|
|
|
401
666
|
waitMs: agg(),
|
|
402
667
|
runMs: agg()
|
|
403
668
|
};
|
|
404
|
-
/**
|
|
405
|
-
|
|
669
|
+
/**
|
|
670
|
+
* Devtools correlation id, stamped on every event this pool emits:
|
|
671
|
+
* '<name>-p' when the config has a `name`, else 'pool-N'.
|
|
672
|
+
*/
|
|
673
|
+
poolId;
|
|
406
674
|
label;
|
|
407
675
|
nextCallId = 0;
|
|
408
676
|
sharedMemory;
|
|
409
677
|
/** The attached memory-persistence adapter, if the config supplied one. */
|
|
410
678
|
persistence;
|
|
411
679
|
constructor(config) {
|
|
412
|
-
|
|
413
|
-
if (typeof SharedArrayBuffer === "undefined" || typeof crossOriginIsolated !== "undefined" && !crossOriginIsolated) {
|
|
414
|
-
throw new Error(
|
|
415
|
-
"WorkerPool requires SharedArrayBuffer in a cross-origin isolated context \u2014 serve the page with COOP/COEP headers."
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
680
|
+
const mem = setupChannelMemory(config, "WorkerPool");
|
|
419
681
|
if (!config.workerUrl && !config.createWorker) {
|
|
420
682
|
throw new Error("WorkerPool requires either `workerUrl` or `createWorker` in its config.");
|
|
421
683
|
}
|
|
@@ -426,17 +688,16 @@ var WorkerPoolImpl = class {
|
|
|
426
688
|
this.respawn = config.respawn ?? true;
|
|
427
689
|
this.taskTimeout = config.taskTimeout;
|
|
428
690
|
this.label = config.name;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
691
|
+
this.poolId = nextDevtoolsId("pool", config.name);
|
|
692
|
+
this.mem = mem;
|
|
693
|
+
this.sharedMemory = mem.sharedMemory;
|
|
694
|
+
this.memoryManager = mem.memoryManager;
|
|
695
|
+
this.injectedBuffer = mem.injectedBuffer;
|
|
696
|
+
this.persistence = mem.persistence;
|
|
436
697
|
const requestedSize = config.poolSize ?? "auto";
|
|
437
|
-
const poolSize = requestedSize === "auto" ? navigator.hardwareConcurrency ?? 4 : requestedSize;
|
|
698
|
+
const poolSize = requestedSize === "auto" ? typeof navigator !== "undefined" ? navigator.hardwareConcurrency ?? 4 : 4 : requestedSize;
|
|
438
699
|
poolLog.info(
|
|
439
|
-
this.sharedMemory ? `spawning ${poolSize} worker(s) with ${fmtBytes(this.sharedMemory.totalBytes)} shared buffer` : `spawning ${poolSize} worker(s), message-only`,
|
|
700
|
+
this.sharedMemory ? `spawning ${poolSize} worker(s) with ${fmtBytes(this.sharedMemory.totalBytes)} shared buffer` : this.injectedBuffer ? `spawning ${poolSize} worker(s) with ${fmtBytes(this.injectedBuffer.byteLength)} inherited buffer` : `spawning ${poolSize} worker(s), message-only`,
|
|
440
701
|
{ workerUrl: config.workerUrl ? String(config.workerUrl) : "createWorker()" }
|
|
441
702
|
);
|
|
442
703
|
poolLog.debug(`tip: setLogLevel('trace') for per-task detail`);
|
|
@@ -446,11 +707,29 @@ var WorkerPoolImpl = class {
|
|
|
446
707
|
label: this.label,
|
|
447
708
|
poolSize,
|
|
448
709
|
concurrency: this.concurrency,
|
|
449
|
-
memoryBytes: this.sharedMemory?.totalBytes
|
|
710
|
+
memoryBytes: this.sharedMemory?.totalBytes ?? this.injectedBuffer?.byteLength
|
|
450
711
|
});
|
|
451
712
|
for (let i = 0; i < poolSize; i++) {
|
|
452
713
|
this.addWorker(this.spawn());
|
|
453
714
|
}
|
|
715
|
+
if (devtoolsEnabled()) {
|
|
716
|
+
this.devtoolsOff = registerDevtoolsRunner({
|
|
717
|
+
poolId: this.poolId,
|
|
718
|
+
label: this.label,
|
|
719
|
+
dedicated: false,
|
|
720
|
+
size: () => this.slots.length,
|
|
721
|
+
stats: () => this.stats(),
|
|
722
|
+
kill: (slot) => {
|
|
723
|
+
const s = this.slots[slot];
|
|
724
|
+
if (!s) throw new Error(`${this.poolId} has no worker slot ${slot}`);
|
|
725
|
+
this.handleWorkerError(s, { message: KILL_MESSAGE });
|
|
726
|
+
},
|
|
727
|
+
getChaos: () => this.chaos,
|
|
728
|
+
setChaos: (chaos) => {
|
|
729
|
+
this.chaos = chaos;
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
}
|
|
454
733
|
const tasks = config.tasks ?? {};
|
|
455
734
|
for (const [name, contract] of Object.entries(tasks)) {
|
|
456
735
|
if (name in this) {
|
|
@@ -469,10 +748,7 @@ var WorkerPoolImpl = class {
|
|
|
469
748
|
worker.addEventListener("message", (event) => {
|
|
470
749
|
const data = event.data;
|
|
471
750
|
if (data.type === "ATOLL_DEVTOOLS" && data.event) {
|
|
472
|
-
|
|
473
|
-
...data.event,
|
|
474
|
-
worker: { poolId: this.poolId, slot: this.slots.indexOf(slot) }
|
|
475
|
-
});
|
|
751
|
+
forwardWorkerDevtools(this.poolId, this.slots.indexOf(slot), data.event);
|
|
476
752
|
return;
|
|
477
753
|
}
|
|
478
754
|
this.handleReply(slot, data);
|
|
@@ -480,13 +756,9 @@ var WorkerPoolImpl = class {
|
|
|
480
756
|
worker.addEventListener("error", (event) => {
|
|
481
757
|
this.handleWorkerError(slot, event);
|
|
482
758
|
});
|
|
483
|
-
|
|
484
|
-
worker.postMessage({ type: "INIT_MEMORY", memory: this.memoryManager.memory, devtools: devtoolsEnabled() });
|
|
485
|
-
} else {
|
|
486
|
-
worker.postMessage({ type: "INIT", devtools: devtoolsEnabled() });
|
|
487
|
-
}
|
|
759
|
+
const init = sendInit(worker, this.mem);
|
|
488
760
|
this.slots.push(slot);
|
|
489
|
-
poolLog.debug(`worker spawned, ${
|
|
761
|
+
poolLog.debug(`worker spawned, ${init} sent`);
|
|
490
762
|
emitDevtools({ type: "worker:spawn", poolId: this.poolId, slot: this.slots.indexOf(slot) });
|
|
491
763
|
return slot;
|
|
492
764
|
}
|
|
@@ -498,17 +770,24 @@ var WorkerPoolImpl = class {
|
|
|
498
770
|
this.detach(entry);
|
|
499
771
|
if (!entry.done) {
|
|
500
772
|
entry.done = true;
|
|
501
|
-
const
|
|
773
|
+
const start = entry.sentAt ?? entry.enqueuedAt;
|
|
774
|
+
const runMs = performance.now() - start;
|
|
502
775
|
this.statsAgg.runMs.add(runMs);
|
|
503
776
|
if (data.success) {
|
|
504
777
|
this.statsAgg.completed++;
|
|
505
778
|
taskLog.debug(`\u2190 ${entry.contract.taskId} ok`);
|
|
506
|
-
|
|
779
|
+
if (devtoolsEnabled()) {
|
|
780
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "ok", runMs, resultBytes: estimateCloneBytes(data.result) });
|
|
781
|
+
measureTaskCall(this.poolId, entry.contract.taskId, entry.callId, this.slots.indexOf(slot), start, start + runMs, true);
|
|
782
|
+
}
|
|
507
783
|
entry.resolve(data.result);
|
|
508
784
|
} else {
|
|
509
785
|
this.statsAgg.failed++;
|
|
510
786
|
taskLog.warn(`\u2190 ${entry.contract.taskId} failed: ${data.error}`);
|
|
511
|
-
|
|
787
|
+
if (devtoolsEnabled()) {
|
|
788
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "error", runMs, error: data.error });
|
|
789
|
+
measureTaskCall(this.poolId, entry.contract.taskId, entry.callId, this.slots.indexOf(slot), start, start + runMs, false);
|
|
790
|
+
}
|
|
512
791
|
entry.reject(new Error(data.error));
|
|
513
792
|
}
|
|
514
793
|
}
|
|
@@ -579,13 +858,20 @@ var WorkerPoolImpl = class {
|
|
|
579
858
|
this.pending.set(messageId, entry);
|
|
580
859
|
const slotIndex = this.slots.indexOf(slot);
|
|
581
860
|
taskLog.debug(`\u2192 ${entry.contract.taskId}`, { worker: slotIndex, args: entry.args });
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
861
|
+
if (devtoolsEnabled()) {
|
|
862
|
+
emitDevtools({ type: "task:dispatch", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, slot: slotIndex, waitMs, argBytes: estimateCloneBytes(entry.args) });
|
|
863
|
+
}
|
|
864
|
+
const msg = { type: "EXECUTE_TASK", messageId, taskId: entry.contract.taskId, args: entry.args };
|
|
865
|
+
if (this.chaos) {
|
|
866
|
+
applyChaos(this.chaos, {
|
|
867
|
+
live: () => this.pending.get(messageId) === entry,
|
|
868
|
+
fail: () => this.handleReply(slot, { messageId, taskId: msg.taskId, success: false, error: CHAOS_FAILURE }),
|
|
869
|
+
timeout: () => this.cancel(entry, new TaskTimeoutError()),
|
|
870
|
+
post: () => slot.worker.postMessage(msg)
|
|
871
|
+
});
|
|
872
|
+
} else {
|
|
873
|
+
slot.worker.postMessage(msg);
|
|
874
|
+
}
|
|
589
875
|
}
|
|
590
876
|
/** Detaches signal/timeout listeners from a finalized entry. */
|
|
591
877
|
detach(entry) {
|
|
@@ -675,7 +961,7 @@ var WorkerPoolImpl = class {
|
|
|
675
961
|
* (e.g. a second pool of HTTP workers via withSharedBuffer).
|
|
676
962
|
*/
|
|
677
963
|
get sharedBuffer() {
|
|
678
|
-
return this.memoryManager?.getBuffer();
|
|
964
|
+
return this.memoryManager?.getBuffer() ?? this.injectedBuffer;
|
|
679
965
|
}
|
|
680
966
|
/**
|
|
681
967
|
* Live snapshot of the pool's worker slots — for auxiliary messaging that
|
|
@@ -715,6 +1001,7 @@ var WorkerPoolImpl = class {
|
|
|
715
1001
|
}
|
|
716
1002
|
terminate() {
|
|
717
1003
|
this.closed = true;
|
|
1004
|
+
this.devtoolsOff?.();
|
|
718
1005
|
void this.persistence?.stop()?.catch?.(
|
|
719
1006
|
(e) => poolLog.warn("memory persistence stop failed", e)
|
|
720
1007
|
);
|
package/dist/index.js
CHANGED
package/dist/redis.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atolljs/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Node runtime adapter — node:worker_threads pools, no framework",
|
|
5
5
|
"homepage": "https://jwhenry3.github.io/atolljs/consumer/quickstart/",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"registry": "https://registry.npmjs.org"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@atolljs/core": "0.1.
|
|
37
|
+
"@atolljs/core": "0.1.9"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|