@atolljs/core 0.1.5 → 0.1.7

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
@@ -1,18 +1,20 @@
1
1
  import { msgpackrCodec as e } from "./index2.js";
2
- import { listSchema as t } from "./index4.js";
3
- import { getLogLevel as n, log as r, scoped as i, setLogLevel as a, setLogSink as o } from "./index5.js";
4
- import { bindSharedMemories as s, defineSharedMemory as c, field as l, getDefinedSharedMemoryCount as u, jsonCodec as d, registerConnectorFactory as f } from "./index6.js";
5
- import { reef as p } from "./index7.js";
6
- import { MemoryManager as m } from "./index8.js";
7
- import { PoolQueueFullError as h, TaskAbortedError as g, TaskTimeoutError as _, WorkerCrashedError as v } from "./index9.js";
8
- import { WorkerPool as y } from "./index10.js";
9
- import { connectWorker as b, workerClient as x } from "./index11.js";
10
- import { connectSharedWorker as S } from "./index12.js";
11
- import { TaskRegistry as C } from "./index13.js";
12
- import { sharedWorkerHost as w } from "./index14.js";
13
- import { createClient as T, defineService as E, implementService as D, rpc as O, serviceMethod as k } from "./index15.js";
14
- import { RESERVED_CLIENT_KEYS as A, defineWorker as j } from "./index17.js";
15
- import { reactive as M, shallowEqual as N, watch as P } from "./index18.js";
16
- import { observe as F } from "./index19.js";
17
- import { defineTask as I, isAsyncTask as L, toTask as R } from "./index20.js";
18
- export { m as MemoryManager, h as PoolQueueFullError, A as RESERVED_CLIENT_KEYS, g as TaskAbortedError, C as TaskRegistry, _ as TaskTimeoutError, v as WorkerCrashedError, y as WorkerPool, s as bindSharedMemories, S as connectSharedWorker, b as connectWorker, T as createClient, E as defineService, c as defineSharedMemory, I as defineTask, j as defineWorker, l as field, u as getDefinedSharedMemoryCount, n as getLogLevel, D as implementService, L as isAsyncTask, d as jsonCodec, t as listSchema, r as log, e as msgpackrCodec, F as observe, M as reactive, p as reef, f as registerConnectorFactory, O as rpc, i as scoped, k as serviceMethod, a as setLogLevel, o as setLogSink, N as shallowEqual, w as sharedWorkerHost, R as toTask, P as watch, x as workerClient };
2
+ import { SchemaError as t, z as n } from "./index3.js";
3
+ import { listSchema as r } from "./index4.js";
4
+ import { devtoolsEnabled as i, emitDevtools as a, installFetchProbe as o, installMemoryProbe as s, setDevtoolsSink as c } from "./index5.js";
5
+ import { getLogLevel as l, log as u, scoped as d, setLogLevel as f, setLogSink as p } from "./index6.js";
6
+ import { bindSharedMemories as m, defineSharedMemory as h, field as g, getDefinedSharedMemoryCount as _, jsonCodec as v, registerConnectorFactory as y } from "./index7.js";
7
+ import { reef as b } from "./index8.js";
8
+ import { MemoryManager as x } from "./index9.js";
9
+ import { PoolQueueFullError as S, TaskAbortedError as C, TaskTimeoutError as w, WorkerCrashedError as T } from "./index10.js";
10
+ import { WorkerPool as E } from "./index11.js";
11
+ import { connectWorker as D, workerClient as O } from "./index12.js";
12
+ import { connectSharedWorker as k } from "./index13.js";
13
+ import { TaskRegistry as A } from "./index14.js";
14
+ import { sharedWorkerHost as j } from "./index15.js";
15
+ import { createClient as M, defineService as N, implementService as P, rpc as F, serviceMethod as I } from "./index16.js";
16
+ import { RESERVED_CLIENT_KEYS as L, defineWorker as R } from "./index18.js";
17
+ import { reactive as z, shallowEqual as B, watch as V } from "./index19.js";
18
+ import { observe as H } from "./index20.js";
19
+ import { defineTask as U, isAsyncTask as W, toTask as G } from "./index21.js";
20
+ export { x as MemoryManager, S as PoolQueueFullError, L as RESERVED_CLIENT_KEYS, t as SchemaError, C as TaskAbortedError, A as TaskRegistry, w as TaskTimeoutError, T as WorkerCrashedError, E as WorkerPool, m as bindSharedMemories, k as connectSharedWorker, D as connectWorker, M as createClient, N as defineService, h as defineSharedMemory, U as defineTask, R as defineWorker, i as devtoolsEnabled, a as emitDevtools, g as field, _ as getDefinedSharedMemoryCount, l as getLogLevel, P as implementService, o as installFetchProbe, s as installMemoryProbe, W as isAsyncTask, v as jsonCodec, r as listSchema, u as log, e as msgpackrCodec, H as observe, z as reactive, b as reef, y as registerConnectorFactory, F as rpc, d as scoped, I as serviceMethod, c as setDevtoolsSink, f as setLogLevel, p as setLogSink, B as shallowEqual, j as sharedWorkerHost, G as toTask, V as watch, O as workerClient, n as z };
package/dist/index10.js CHANGED
@@ -1,191 +1,20 @@
1
- import { fmtBytes as e, scoped as t } from "./index5.js";
2
- import { MemoryManager as n } from "./index8.js";
3
- import { PoolQueueFullError as r, TaskAbortedError as i, TaskTimeoutError as a, WorkerCrashedError as o } from "./index9.js";
4
- //#region src/pool/workerPool.ts
5
- var s = t("pool"), c = t("task"), l = () => ({
6
- count: 0,
7
- mean: 0,
8
- max: 0,
9
- add(e) {
10
- this.count++, this.mean += (e - this.mean) / this.count, e > this.max && (this.max = e);
1
+ //#region src/pool/errors.ts
2
+ var e = class extends Error {
3
+ constructor(e = "WorkerPool task queue is full") {
4
+ super(e), this.name = "PoolQueueFullError";
11
5
  }
12
- }), u = class {
13
- slots = [];
14
- pending = /* @__PURE__ */ new Map();
15
- nextMessageId = 0;
16
- queue = [];
17
- memoryManager;
18
- workerUrl;
19
- spawn;
20
- concurrency;
21
- maxQueue;
22
- respawn;
23
- taskTimeout;
24
- closed = !1;
25
- drainWaiters = [];
26
- statsAgg = {
27
- completed: 0,
28
- failed: 0,
29
- aborted: 0,
30
- waitMs: l(),
31
- runMs: l()
32
- };
33
- sharedMemory;
34
- persistence;
35
- constructor(t) {
36
- if (t.sharedMemory && (typeof SharedArrayBuffer > "u" || typeof crossOriginIsolated < "u" && !crossOriginIsolated)) throw Error("WorkerPool requires SharedArrayBuffer in a cross-origin isolated context — serve the page with COOP/COEP headers.");
37
- if (!t.workerUrl && !t.createWorker) throw Error("WorkerPool requires either `workerUrl` or `createWorker` in its config.");
38
- this.workerUrl = t.workerUrl, this.spawn = t.createWorker ?? (() => new Worker(this.workerUrl, { type: "module" })), this.concurrency = t.concurrency ?? 1, this.maxQueue = t.maxQueue ?? Infinity, this.respawn = t.respawn ?? !0, this.taskTimeout = t.taskTimeout, t.sharedMemory && (this.sharedMemory = t.sharedMemory, this.memoryManager = new n(t.memory), this.memoryManager.ensureCapacity(this.sharedMemory.totalBytes), this.sharedMemory.bind(this.memoryManager.getBuffer()), this.persistence = t.persistence?.(t.sharedMemory));
39
- let r = t.poolSize ?? "auto", i = r === "auto" ? navigator.hardwareConcurrency ?? 4 : r;
40
- s.info(this.sharedMemory ? `spawning ${i} worker(s) with ${e(this.sharedMemory.totalBytes)} shared buffer` : `spawning ${i} worker(s), message-only`, { workerUrl: t.workerUrl ? String(t.workerUrl) : "createWorker()" }), s.debug("tip: setLogLevel('trace') for per-task detail");
41
- for (let e = 0; e < i; e++) this.addWorker(this.spawn());
42
- let a = t.tasks ?? {};
43
- for (let [e, t] of Object.entries(a)) {
44
- if (e in this) {
45
- s.warn(`task "${e}" collides with a WorkerPool member — not installed`);
46
- continue;
47
- }
48
- this[e] = (...e) => this.runTask(t, ...e);
49
- }
50
- Object.keys(a).length > 0 && s.info(`task methods: ${Object.keys(a).join(", ")}`);
6
+ }, t = class extends Error {
7
+ constructor(e = "task aborted") {
8
+ super(e), this.name = "AbortError";
51
9
  }
52
- addWorker(e) {
53
- let t = {
54
- worker: e,
55
- inFlight: 0
56
- };
57
- return e.addEventListener("message", (e) => {
58
- this.handleReply(t, e.data);
59
- }), e.addEventListener("error", (e) => {
60
- this.handleWorkerError(t, e);
61
- }), this.memoryManager ? e.postMessage({
62
- type: "INIT_MEMORY",
63
- memory: this.memoryManager.memory
64
- }) : e.postMessage({ type: "INIT" }), this.slots.push(t), s.debug(`worker spawned, ${this.memoryManager ? "INIT_MEMORY" : "INIT"} sent`), t;
10
+ }, n = class extends Error {
11
+ constructor(e = "task timed out") {
12
+ super(e), this.name = "TaskTimeoutError";
65
13
  }
66
- handleReply(e, t) {
67
- let n = t.messageId === void 0 ? void 0 : this.pending.get(t.messageId);
68
- n && (this.pending.delete(t.messageId), e.inFlight--, this.detach(n), n.done || (n.done = !0, this.statsAgg.runMs.add(performance.now() - (n.sentAt ?? n.enqueuedAt)), t.success ? (this.statsAgg.completed++, c.debug(`← ${n.contract.taskId} ok`), n.resolve(t.result)) : (this.statsAgg.failed++, c.warn(`← ${n.contract.taskId} failed: ${t.error}`), n.reject(Error(t.error)))), this.drainQueue(), this.checkDrained());
69
- }
70
- handleWorkerError(e, t) {
71
- if (this.closed) return;
72
- let n = t, r = n?.message ?? (n?.error ? String(n.error) : "worker error");
73
- s.warn(`worker error: ${r}`);
74
- for (let [t, n] of [...this.pending]) if (n.slot === e) {
75
- if (this.pending.delete(t), this.detach(n), n.done) continue;
76
- n.done = !0, this.statsAgg.failed++, n.reject(new o(r));
77
- }
78
- let i = this.slots.indexOf(e);
79
- if (i >= 0 && this.slots.splice(i, 1), e.worker.terminate(), this.respawn) s.info("respawning crashed worker"), this.addWorker(this.spawn());
80
- else if (this.slots.length === 0) for (let e of this.queue.splice(0)) e.done = !0, this.detach(e), this.statsAgg.failed++, e.reject(new o(r));
81
- this.drainQueue(), this.checkDrained();
82
- }
83
- pickWorker() {
84
- let e;
85
- for (let t of this.slots) t.inFlight >= this.concurrency || (!e || t.inFlight < e.inFlight) && (e = t);
86
- return e;
87
- }
88
- drainQueue() {
89
- let e;
90
- for (; this.queue.length > 0 && (e = this.pickWorker());) {
91
- let t = this.queue.shift();
92
- t.done || this.send(e, t);
93
- }
94
- }
95
- send(e, t) {
96
- let n = this.nextMessageId++;
97
- t.messageId = n, t.slot = e, t.sentAt = performance.now(), this.statsAgg.waitMs.add(performance.now() - t.enqueuedAt), e.inFlight++, this.pending.set(n, t), c.debug(`→ ${t.contract.taskId}`, {
98
- worker: this.slots.indexOf(e),
99
- args: t.args
100
- }), e.worker.postMessage({
101
- type: "EXECUTE_TASK",
102
- messageId: n,
103
- taskId: t.contract.taskId,
104
- args: t.args
105
- });
106
- }
107
- detach(e) {
108
- e.timer && clearTimeout(e.timer), e.signal && e.onSignalAbort && e.signal.removeEventListener("abort", e.onSignalAbort);
109
- }
110
- cancel(e, t) {
111
- if (e.done) return;
112
- let n = this.queue.indexOf(e);
113
- n >= 0 && this.queue.splice(n, 1), e.done = !0, this.detach(e), this.statsAgg.aborted++, e.reject(t), this.drainQueue(), this.checkDrained();
114
- }
115
- checkDrained() {
116
- if (this.queue.length === 0 && this.pending.size === 0) {
117
- let e = this.drainWaiters.splice(0);
118
- for (let t of e) t();
119
- }
120
- }
121
- dispatch(e, t, n) {
122
- return this.closed || this.slots.length === 0 ? Promise.reject(new o("WorkerPool has been terminated — no workers available.")) : n?.signal?.aborted ? Promise.reject(new i()) : new Promise((o, s) => {
123
- let c = {
124
- contract: e,
125
- args: t,
126
- enqueuedAt: performance.now(),
127
- resolve: o,
128
- reject: s,
129
- done: !1,
130
- signal: n?.signal
131
- };
132
- n?.signal && (c.onSignalAbort = () => this.cancel(c, new i()), n.signal.addEventListener("abort", c.onSignalAbort, { once: !0 }));
133
- let l = n?.timeout ?? this.taskTimeout;
134
- l !== void 0 && (c.timer = setTimeout(() => this.cancel(c, new a()), l));
135
- let u = this.pickWorker();
136
- if (u) this.send(u, c);
137
- else if (this.queue.length >= this.maxQueue) {
138
- c.done = !0, this.detach(c), s(new r());
139
- return;
140
- } else this.queue.push(c);
141
- });
142
- }
143
- runTask(e, ...t) {
144
- return this.dispatch(e, t);
145
- }
146
- get sharedBuffer() {
147
- return this.memoryManager?.getBuffer();
148
- }
149
- get workers() {
150
- return this.slots.map((e) => e.worker);
151
- }
152
- stats() {
153
- return {
154
- workers: this.slots.length,
155
- idle: this.slots.filter((e) => e.inFlight === 0).length,
156
- inFlight: this.pending.size,
157
- queued: this.queue.length,
158
- completed: this.statsAgg.completed,
159
- failed: this.statsAgg.failed,
160
- aborted: this.statsAgg.aborted,
161
- waitMs: {
162
- count: this.statsAgg.waitMs.count,
163
- mean: this.statsAgg.waitMs.mean,
164
- max: this.statsAgg.waitMs.max
165
- },
166
- runMs: {
167
- count: this.statsAgg.runMs.count,
168
- mean: this.statsAgg.runMs.mean,
169
- max: this.statsAgg.runMs.max
170
- }
171
- };
172
- }
173
- close() {
174
- return this.queue.length === 0 && this.pending.size === 0 ? (this.terminate(), Promise.resolve()) : new Promise((e) => {
175
- this.drainWaiters.push(() => {
176
- this.terminate(), e();
177
- });
178
- });
179
- }
180
- terminate() {
181
- this.closed = !0, this.persistence?.stop()?.catch?.((e) => s.warn("memory persistence stop failed", e)), s.info(`terminating ${this.slots.length} worker(s)`);
182
- let e = new o("pool terminated");
183
- for (let [, t] of this.pending) this.detach(t), !t.done && (t.done = !0, this.statsAgg.failed++, t.reject(e));
184
- this.pending.clear();
185
- for (let t of this.queue.splice(0)) t.done = !0, this.detach(t), this.statsAgg.failed++, t.reject(e);
186
- for (let e of this.slots) e.worker.terminate();
187
- this.slots = [], this.checkDrained();
14
+ }, r = class extends Error {
15
+ constructor(e = "worker crashed") {
16
+ super(e), this.name = "WorkerCrashedError";
188
17
  }
189
18
  };
190
19
  //#endregion
191
- export { u as WorkerPool };
20
+ export { e as PoolQueueFullError, t as TaskAbortedError, n as TaskTimeoutError, r as WorkerCrashedError };
package/dist/index11.js CHANGED
@@ -1,50 +1,313 @@
1
- import { WorkerPool as e } from "./index10.js";
2
- //#region src/pool/workerClient.ts
3
- var t = (e, n, r, i) => {
4
- let a = /* @__PURE__ */ new Map();
5
- return new Proxy(() => {}, {
6
- get(o, s) {
7
- if (s === "then" || typeof s == "symbol") return;
8
- if (s === "with") return (i) => t(e, n, r, i);
9
- if (e.length === 0 && r && s in r) return Reflect.get(r, s);
10
- let c = a.get(s);
11
- return c || (c = t([...e, s], n, r, i), a.set(s, c)), c;
12
- },
13
- apply(t, r, a) {
14
- let o = n(), s = { taskId: e.join(".") };
15
- return o.dispatch ? o.dispatch(s, a, i) : o.runTask(s, ...a);
1
+ import { devtoolsEnabled as e, emitDevtools as t, forwardDevtools as n, nextDevtoolsId as r } from "./index5.js";
2
+ import { fmtBytes as i, scoped as a } from "./index6.js";
3
+ import { MemoryManager as o } from "./index9.js";
4
+ import { PoolQueueFullError as s, TaskAbortedError as c, TaskTimeoutError as l, WorkerCrashedError as u } from "./index10.js";
5
+ //#region src/pool/workerPool.ts
6
+ var d = a("pool"), f = a("task"), p = () => ({
7
+ count: 0,
8
+ mean: 0,
9
+ max: 0,
10
+ add(e) {
11
+ this.count++, this.mean += (e - this.mean) / this.count, e > this.max && (this.max = e);
12
+ }
13
+ }), m = class {
14
+ slots = [];
15
+ pending = /* @__PURE__ */ new Map();
16
+ nextMessageId = 0;
17
+ queue = [];
18
+ memoryManager;
19
+ workerUrl;
20
+ spawn;
21
+ concurrency;
22
+ maxQueue;
23
+ respawn;
24
+ taskTimeout;
25
+ closed = !1;
26
+ drainWaiters = [];
27
+ statsAgg = {
28
+ completed: 0,
29
+ failed: 0,
30
+ aborted: 0,
31
+ waitMs: p(),
32
+ runMs: p()
33
+ };
34
+ poolId = r("pool");
35
+ label;
36
+ nextCallId = 0;
37
+ sharedMemory;
38
+ persistence;
39
+ constructor(e) {
40
+ if (e.sharedMemory && (typeof SharedArrayBuffer > "u" || typeof crossOriginIsolated < "u" && !crossOriginIsolated)) throw Error("WorkerPool requires SharedArrayBuffer in a cross-origin isolated context — serve the page with COOP/COEP headers.");
41
+ if (!e.workerUrl && !e.createWorker) throw Error("WorkerPool requires either `workerUrl` or `createWorker` in its config.");
42
+ this.workerUrl = e.workerUrl, this.spawn = e.createWorker ?? (() => new Worker(this.workerUrl, { type: "module" })), this.concurrency = e.concurrency ?? 1, this.maxQueue = e.maxQueue ?? Infinity, this.respawn = e.respawn ?? !0, this.taskTimeout = e.taskTimeout, this.label = e.name, e.sharedMemory && (this.sharedMemory = e.sharedMemory, this.memoryManager = new o(e.memory), this.memoryManager.ensureCapacity(this.sharedMemory.totalBytes), this.sharedMemory.bind(this.memoryManager.getBuffer()), this.persistence = e.persistence?.(e.sharedMemory));
43
+ let n = e.poolSize ?? "auto", r = n === "auto" ? navigator.hardwareConcurrency ?? 4 : n;
44
+ d.info(this.sharedMemory ? `spawning ${r} worker(s) with ${i(this.sharedMemory.totalBytes)} shared buffer` : `spawning ${r} worker(s), message-only`, { workerUrl: e.workerUrl ? String(e.workerUrl) : "createWorker()" }), d.debug("tip: setLogLevel('trace') for per-task detail"), t({
45
+ type: "pool:init",
46
+ poolId: this.poolId,
47
+ label: this.label,
48
+ poolSize: r,
49
+ concurrency: this.concurrency,
50
+ memoryBytes: this.sharedMemory?.totalBytes
51
+ });
52
+ for (let e = 0; e < r; e++) this.addWorker(this.spawn());
53
+ let a = e.tasks ?? {};
54
+ for (let [e, t] of Object.entries(a)) {
55
+ if (e in this) {
56
+ d.warn(`task "${e}" collides with a WorkerPool member — not installed`);
57
+ continue;
58
+ }
59
+ this[e] = (...e) => this.runTask(t, ...e);
16
60
  }
17
- });
18
- };
19
- function n(e) {
20
- let n;
21
- return t([], typeof e == "function" ? () => n ??= e() : () => e);
22
- }
23
- function r(n) {
24
- let r = null, i = () => r ??= new e({
25
- sharedMemory: n.sharedMemory,
26
- createWorker: typeof n.worker == "function" ? n.worker : () => new Worker(n.worker, { type: "module" }),
27
- poolSize: n.poolSize,
28
- memory: n.memory,
29
- concurrency: n.concurrency,
30
- maxQueue: n.maxQueue,
31
- respawn: n.respawn,
32
- taskTimeout: n.taskTimeout
33
- });
34
- return n.lazy === !1 && i(), t([], i, {
35
- start: () => {
36
- i();
37
- },
38
- terminate: () => {
39
- r?.terminate(), r = null;
40
- },
41
- get pool() {
42
- return r;
43
- },
44
- get sharedMemory() {
45
- return n.sharedMemory;
61
+ Object.keys(a).length > 0 && d.info(`task methods: ${Object.keys(a).join(", ")}`);
62
+ }
63
+ addWorker(r) {
64
+ let i = {
65
+ worker: r,
66
+ inFlight: 0
67
+ };
68
+ return r.addEventListener("message", (e) => {
69
+ let t = e.data;
70
+ if (t.type === "ATOLL_DEVTOOLS" && t.event) {
71
+ n({
72
+ ...t.event,
73
+ worker: {
74
+ poolId: this.poolId,
75
+ slot: this.slots.indexOf(i)
76
+ }
77
+ });
78
+ return;
79
+ }
80
+ this.handleReply(i, t);
81
+ }), r.addEventListener("error", (e) => {
82
+ this.handleWorkerError(i, e);
83
+ }), this.memoryManager ? r.postMessage({
84
+ type: "INIT_MEMORY",
85
+ memory: this.memoryManager.memory,
86
+ devtools: e()
87
+ }) : r.postMessage({
88
+ type: "INIT",
89
+ devtools: e()
90
+ }), this.slots.push(i), d.debug(`worker spawned, ${this.memoryManager ? "INIT_MEMORY" : "INIT"} sent`), t({
91
+ type: "worker:spawn",
92
+ poolId: this.poolId,
93
+ slot: this.slots.indexOf(i)
94
+ }), i;
95
+ }
96
+ handleReply(e, n) {
97
+ let r = n.messageId === void 0 ? void 0 : this.pending.get(n.messageId);
98
+ if (r) {
99
+ if (this.pending.delete(n.messageId), e.inFlight--, this.detach(r), !r.done) {
100
+ r.done = !0;
101
+ let e = performance.now() - (r.sentAt ?? r.enqueuedAt);
102
+ this.statsAgg.runMs.add(e), n.success ? (this.statsAgg.completed++, f.debug(`← ${r.contract.taskId} ok`), t({
103
+ type: "task:settle",
104
+ poolId: this.poolId,
105
+ callId: r.callId,
106
+ taskId: r.contract.taskId,
107
+ outcome: "ok",
108
+ runMs: e
109
+ }), r.resolve(n.result)) : (this.statsAgg.failed++, f.warn(`← ${r.contract.taskId} failed: ${n.error}`), t({
110
+ type: "task:settle",
111
+ poolId: this.poolId,
112
+ callId: r.callId,
113
+ taskId: r.contract.taskId,
114
+ outcome: "error",
115
+ runMs: e,
116
+ error: n.error
117
+ }), r.reject(Error(n.error)));
118
+ }
119
+ this.drainQueue(), this.checkDrained();
120
+ }
121
+ }
122
+ handleWorkerError(e, n) {
123
+ if (this.closed) return;
124
+ let r = n, i = r?.message ?? (r?.error ? String(r.error) : "worker error");
125
+ d.warn(`worker error: ${i}`);
126
+ let a = this.slots.indexOf(e);
127
+ t({
128
+ type: "worker:error",
129
+ poolId: this.poolId,
130
+ slot: a,
131
+ message: i
132
+ });
133
+ for (let [n, r] of [...this.pending]) if (r.slot === e) {
134
+ if (this.pending.delete(n), this.detach(r), r.done) continue;
135
+ r.done = !0, this.statsAgg.failed++, t({
136
+ type: "task:settle",
137
+ poolId: this.poolId,
138
+ callId: r.callId,
139
+ taskId: r.contract.taskId,
140
+ outcome: "crashed",
141
+ error: i
142
+ }), r.reject(new u(i));
143
+ }
144
+ if (a >= 0 && this.slots.splice(a, 1), e.worker.terminate(), this.respawn) d.info("respawning crashed worker"), this.addWorker(this.spawn()), t({
145
+ type: "worker:respawn",
146
+ poolId: this.poolId,
147
+ slot: this.slots.length - 1
148
+ });
149
+ else if (this.slots.length === 0) for (let e of this.queue.splice(0)) e.done = !0, this.detach(e), this.statsAgg.failed++, t({
150
+ type: "task:settle",
151
+ poolId: this.poolId,
152
+ callId: e.callId,
153
+ taskId: e.contract.taskId,
154
+ outcome: "crashed",
155
+ error: i
156
+ }), e.reject(new u(i));
157
+ this.drainQueue(), this.checkDrained();
158
+ }
159
+ pickWorker() {
160
+ let e;
161
+ for (let t of this.slots) t.inFlight >= this.concurrency || (!e || t.inFlight < e.inFlight) && (e = t);
162
+ return e;
163
+ }
164
+ drainQueue() {
165
+ let e;
166
+ for (; this.queue.length > 0 && (e = this.pickWorker());) {
167
+ let t = this.queue.shift();
168
+ t.done || this.send(e, t);
46
169
  }
47
- });
48
- }
170
+ }
171
+ send(e, n) {
172
+ let r = this.nextMessageId++;
173
+ n.messageId = r, n.slot = e, n.sentAt = performance.now();
174
+ let i = n.sentAt - n.enqueuedAt;
175
+ this.statsAgg.waitMs.add(i), e.inFlight++, this.pending.set(r, n);
176
+ let a = this.slots.indexOf(e);
177
+ f.debug(`→ ${n.contract.taskId}`, {
178
+ worker: a,
179
+ args: n.args
180
+ }), t({
181
+ type: "task:dispatch",
182
+ poolId: this.poolId,
183
+ callId: n.callId,
184
+ taskId: n.contract.taskId,
185
+ slot: a,
186
+ waitMs: i
187
+ }), e.worker.postMessage({
188
+ type: "EXECUTE_TASK",
189
+ messageId: r,
190
+ taskId: n.contract.taskId,
191
+ args: n.args
192
+ });
193
+ }
194
+ detach(e) {
195
+ e.timer && clearTimeout(e.timer), e.signal && e.onSignalAbort && e.signal.removeEventListener("abort", e.onSignalAbort);
196
+ }
197
+ cancel(e, n) {
198
+ if (e.done) return;
199
+ let r = this.queue.indexOf(e);
200
+ r >= 0 && this.queue.splice(r, 1), e.done = !0, this.detach(e), this.statsAgg.aborted++, t({
201
+ type: "task:settle",
202
+ poolId: this.poolId,
203
+ callId: e.callId,
204
+ taskId: e.contract.taskId,
205
+ outcome: n instanceof l ? "timeout" : "aborted"
206
+ }), e.reject(n), this.drainQueue(), this.checkDrained();
207
+ }
208
+ checkDrained() {
209
+ if (this.queue.length === 0 && this.pending.size === 0) {
210
+ let e = this.drainWaiters.splice(0);
211
+ for (let t of e) t();
212
+ }
213
+ }
214
+ dispatch(e, n, r) {
215
+ return this.closed || this.slots.length === 0 ? Promise.reject(new u("WorkerPool has been terminated — no workers available.")) : r?.signal?.aborted ? Promise.reject(new c()) : new Promise((i, a) => {
216
+ let o = {
217
+ contract: e,
218
+ args: n,
219
+ callId: ++this.nextCallId,
220
+ enqueuedAt: performance.now(),
221
+ resolve: i,
222
+ reject: a,
223
+ done: !1,
224
+ signal: r?.signal
225
+ };
226
+ r?.signal && (o.onSignalAbort = () => this.cancel(o, new c()), r.signal.addEventListener("abort", o.onSignalAbort, { once: !0 }));
227
+ let u = r?.timeout ?? this.taskTimeout;
228
+ u !== void 0 && (o.timer = setTimeout(() => this.cancel(o, new l()), u)), t({
229
+ type: "task:enqueue",
230
+ poolId: this.poolId,
231
+ callId: o.callId,
232
+ taskId: e.taskId
233
+ });
234
+ let d = this.pickWorker();
235
+ if (d) this.send(d, o);
236
+ else if (this.queue.length >= this.maxQueue) {
237
+ o.done = !0, this.detach(o), t({
238
+ type: "task:settle",
239
+ poolId: this.poolId,
240
+ callId: o.callId,
241
+ taskId: e.taskId,
242
+ outcome: "queue-full"
243
+ }), a(new s());
244
+ return;
245
+ } else this.queue.push(o);
246
+ });
247
+ }
248
+ runTask(e, ...t) {
249
+ return this.dispatch(e, t);
250
+ }
251
+ get sharedBuffer() {
252
+ return this.memoryManager?.getBuffer();
253
+ }
254
+ get workers() {
255
+ return this.slots.map((e) => e.worker);
256
+ }
257
+ stats() {
258
+ return {
259
+ workers: this.slots.length,
260
+ idle: this.slots.filter((e) => e.inFlight === 0).length,
261
+ inFlight: this.pending.size,
262
+ queued: this.queue.length,
263
+ completed: this.statsAgg.completed,
264
+ failed: this.statsAgg.failed,
265
+ aborted: this.statsAgg.aborted,
266
+ waitMs: {
267
+ count: this.statsAgg.waitMs.count,
268
+ mean: this.statsAgg.waitMs.mean,
269
+ max: this.statsAgg.waitMs.max
270
+ },
271
+ runMs: {
272
+ count: this.statsAgg.runMs.count,
273
+ mean: this.statsAgg.runMs.mean,
274
+ max: this.statsAgg.runMs.max
275
+ }
276
+ };
277
+ }
278
+ close() {
279
+ return this.queue.length === 0 && this.pending.size === 0 ? (this.terminate(), Promise.resolve()) : new Promise((e) => {
280
+ this.drainWaiters.push(() => {
281
+ this.terminate(), e();
282
+ });
283
+ });
284
+ }
285
+ terminate() {
286
+ this.closed = !0, this.persistence?.stop()?.catch?.((e) => d.warn("memory persistence stop failed", e)), d.info(`terminating ${this.slots.length} worker(s)`), t({
287
+ type: "pool:terminate",
288
+ poolId: this.poolId
289
+ });
290
+ let e = new u("pool terminated");
291
+ for (let [, n] of this.pending) this.detach(n), !n.done && (n.done = !0, this.statsAgg.failed++, t({
292
+ type: "task:settle",
293
+ poolId: this.poolId,
294
+ callId: n.callId,
295
+ taskId: n.contract.taskId,
296
+ outcome: "crashed",
297
+ error: "pool terminated"
298
+ }), n.reject(e));
299
+ this.pending.clear();
300
+ for (let n of this.queue.splice(0)) n.done = !0, this.detach(n), this.statsAgg.failed++, t({
301
+ type: "task:settle",
302
+ poolId: this.poolId,
303
+ callId: n.callId,
304
+ taskId: n.contract.taskId,
305
+ outcome: "crashed",
306
+ error: "pool terminated"
307
+ }), n.reject(e);
308
+ for (let e of this.slots) e.worker.terminate();
309
+ this.slots = [], this.checkDrained();
310
+ }
311
+ };
49
312
  //#endregion
50
- export { r as connectWorker, n as workerClient };
313
+ export { m as WorkerPool };