@atolljs/core 0.1.6 → 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/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 };
package/dist/index12.js CHANGED
@@ -1,64 +1,51 @@
1
- import { scoped as e } from "./index5.js";
2
- //#region src/shared/sharedWorkerClient.ts
3
- var t = e("shared-client"), n = e("task"), r = class {
4
- clientIndex;
5
- sharedMemory;
6
- port;
7
- constructor(e, n, r) {
8
- this.clientIndex = n, this.port = e, this.sharedMemory = r.sharedMemory;
9
- let i = r.tasks ?? {};
10
- for (let [e, n] of Object.entries(i)) {
11
- if (e in this) {
12
- t.warn(`task "${e}" collides with a SharedWorkerClient member — not installed`);
13
- continue;
14
- }
15
- this[e] = (...e) => this.runTask(n, ...e);
1
+ import { WorkerPool as e } from "./index11.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);
16
16
  }
17
- Object.keys(i).length > 0 && t.info(`task methods: ${Object.keys(i).join(", ")}`);
18
- }
19
- runTask(e, ...t) {
20
- return new Promise((r, i) => {
21
- let a = Math.random().toString(36).substring(2), o = performance.now();
22
- n.debug(`→ ${e.taskId}`, { args: t });
23
- let s = (t) => {
24
- let c = t.data;
25
- if (c.messageId === a) {
26
- this.port.removeEventListener("message", s);
27
- let t = (performance.now() - o).toFixed(1);
28
- c.success ? (n.debug(`← ${e.taskId} ok`, { ms: +t }), r(c.result)) : (n.warn(`← ${e.taskId} failed: ${c.error}`, { ms: +t }), i(Error(c.error)));
29
- }
30
- };
31
- this.port.addEventListener("message", s), this.port.postMessage({
32
- type: "EXECUTE_TASK",
33
- messageId: a,
34
- taskId: e.taskId,
35
- args: t
36
- });
37
- });
38
- }
39
- disconnect() {
40
- try {
41
- this.port.postMessage({ type: "SHARED_DISCONNECT" });
42
- } finally {
43
- this.port.close();
44
- }
45
- }
17
+ });
46
18
  };
47
- async function i(e) {
48
- if (typeof SharedArrayBuffer > "u" || typeof crossOriginIsolated < "u" && !crossOriginIsolated) throw Error("connectSharedWorker requires SharedArrayBuffer in a cross-origin isolated context — serve the page with COOP/COEP headers.");
49
- if (!e.port && !e.workerUrl && !e.createWorker) throw Error("connectSharedWorker requires `workerUrl`, `createWorker`, or `port` in its config.");
50
- let n = e.port ?? (e.createWorker ? e.createWorker().port : new SharedWorker(e.workerUrl, { type: "module" }).port);
51
- return n.start(), await new Promise((i, a) => {
52
- let o = setTimeout(() => a(/* @__PURE__ */ Error("connectSharedWorker: timed out waiting for the shared worker — is the host module calling sharedWorkerHost()?")), e.connectTimeoutMs ?? 1e4), s = (a) => {
53
- let c = a.data;
54
- c?.type === "SHARED_MEMORY" && (n.removeEventListener("message", s), clearTimeout(o), e.sharedMemory.bind(c.buffer), t.info(`connected as client #${c.clientIndex}`, { bytes: c.buffer.byteLength }), i(new r(n, c.clientIndex, e)));
55
- };
56
- n.addEventListener("message", s), n.postMessage({
57
- type: "SHARED_CONNECT",
58
- memoryBytes: e.sharedMemory.totalBytes,
59
- memory: e.memory
60
- });
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
+ name: n.name,
29
+ memory: n.memory,
30
+ concurrency: n.concurrency,
31
+ maxQueue: n.maxQueue,
32
+ respawn: n.respawn,
33
+ taskTimeout: n.taskTimeout
34
+ });
35
+ return n.lazy === !1 && i(), t([], i, {
36
+ start: () => {
37
+ i();
38
+ },
39
+ terminate: () => {
40
+ r?.terminate(), r = null;
41
+ },
42
+ get pool() {
43
+ return r;
44
+ },
45
+ get sharedMemory() {
46
+ return n.sharedMemory;
47
+ }
61
48
  });
62
49
  }
63
50
  //#endregion
64
- export { i as connectSharedWorker };
51
+ export { r as connectWorker, n as workerClient };
package/dist/index13.js CHANGED
@@ -1,25 +1,64 @@
1
- import { scoped as e } from "./index5.js";
2
- //#region src/worker/registry.ts
3
- var t = e("registry"), n = class {
4
- static tasks = /* @__PURE__ */ new Map();
5
- static register(e, n) {
6
- this.tasks.set(e.taskId, {
7
- contract: e,
8
- handler: n
9
- }), t.debug(`registered "${e.taskId}"`, {
10
- args: e.argsSchema ? "validated" : "unvalidated",
11
- result: e.resultSchema ? "validated" : "unvalidated"
12
- });
1
+ import { scoped as e } from "./index6.js";
2
+ //#region src/shared/sharedWorkerClient.ts
3
+ var t = e("shared-client"), n = e("task"), r = class {
4
+ clientIndex;
5
+ sharedMemory;
6
+ port;
7
+ constructor(e, n, r) {
8
+ this.clientIndex = n, this.port = e, this.sharedMemory = r.sharedMemory;
9
+ let i = r.tasks ?? {};
10
+ for (let [e, n] of Object.entries(i)) {
11
+ if (e in this) {
12
+ t.warn(`task "${e}" collides with a SharedWorkerClient member — not installed`);
13
+ continue;
14
+ }
15
+ this[e] = (...e) => this.runTask(n, ...e);
16
+ }
17
+ Object.keys(i).length > 0 && t.info(`task methods: ${Object.keys(i).join(", ")}`);
13
18
  }
14
- static getContract(e) {
15
- return this.tasks.get(e)?.contract;
19
+ runTask(e, ...t) {
20
+ return new Promise((r, i) => {
21
+ let a = Math.random().toString(36).substring(2), o = performance.now();
22
+ n.debug(`→ ${e.taskId}`, { args: t });
23
+ let s = (t) => {
24
+ let c = t.data;
25
+ if (c.messageId === a) {
26
+ this.port.removeEventListener("message", s);
27
+ let t = (performance.now() - o).toFixed(1);
28
+ c.success ? (n.debug(`← ${e.taskId} ok`, { ms: +t }), r(c.result)) : (n.warn(`← ${e.taskId} failed: ${c.error}`, { ms: +t }), i(Error(c.error)));
29
+ }
30
+ };
31
+ this.port.addEventListener("message", s), this.port.postMessage({
32
+ type: "EXECUTE_TASK",
33
+ messageId: a,
34
+ taskId: e.taskId,
35
+ args: t
36
+ });
37
+ });
16
38
  }
17
- static async execute(e, ...t) {
18
- let n = this.tasks.get(e);
19
- if (!n) throw Error(`Task handler not found for id: ${e}`);
20
- let { contract: r, handler: i } = n, a = await i(...r.argsSchema ? r.argsSchema.parse(t) : t);
21
- return r.resultSchema ? r.resultSchema.parse(a) : a;
39
+ disconnect() {
40
+ try {
41
+ this.port.postMessage({ type: "SHARED_DISCONNECT" });
42
+ } finally {
43
+ this.port.close();
44
+ }
22
45
  }
23
46
  };
47
+ async function i(e) {
48
+ if (typeof SharedArrayBuffer > "u" || typeof crossOriginIsolated < "u" && !crossOriginIsolated) throw Error("connectSharedWorker requires SharedArrayBuffer in a cross-origin isolated context — serve the page with COOP/COEP headers.");
49
+ if (!e.port && !e.workerUrl && !e.createWorker) throw Error("connectSharedWorker requires `workerUrl`, `createWorker`, or `port` in its config.");
50
+ let n = e.port ?? (e.createWorker ? e.createWorker().port : new SharedWorker(e.workerUrl, { type: "module" }).port);
51
+ return n.start(), await new Promise((i, a) => {
52
+ let o = setTimeout(() => a(/* @__PURE__ */ Error("connectSharedWorker: timed out waiting for the shared worker — is the host module calling sharedWorkerHost()?")), e.connectTimeoutMs ?? 1e4), s = (a) => {
53
+ let c = a.data;
54
+ c?.type === "SHARED_MEMORY" && (n.removeEventListener("message", s), clearTimeout(o), e.sharedMemory.bind(c.buffer), t.info(`connected as client #${c.clientIndex}`, { bytes: c.buffer.byteLength }), i(new r(n, c.clientIndex, e)));
55
+ };
56
+ n.addEventListener("message", s), n.postMessage({
57
+ type: "SHARED_CONNECT",
58
+ memoryBytes: e.sharedMemory.totalBytes,
59
+ memory: e.memory
60
+ });
61
+ });
62
+ }
24
63
  //#endregion
25
- export { n as TaskRegistry };
64
+ export { i as connectSharedWorker };
package/dist/index14.js CHANGED
@@ -1,61 +1,25 @@
1
- import { fmtBytes as e, scoped as t } from "./index5.js";
2
- import { bindSharedMemories as n } from "./index6.js";
3
- import { MemoryManager as r } from "./index8.js";
4
- import { TaskRegistry as i } from "./index13.js";
5
- //#region src/shared/sharedWorkerHost.ts
6
- var a = t("shared-worker"), o = null, s = 0;
7
- function c(t, i) {
8
- if (o) return o.buffer.byteLength < t && a.warn(`client requested ${e(t)} but buffer is ${e(o.buffer.byteLength)} — first client's capacity wins`), o;
9
- let s = new r(i);
10
- return s.ensureCapacity(t), o = s.memory, n(s.getBuffer()), a.info(`shared buffer allocated (${e(o.buffer.byteLength)})`), o;
11
- }
12
- function l(e) {
13
- let t = ++s;
14
- a.info(`client #${t} connected`), e.onmessage = async (n) => {
15
- let r = n.data;
16
- if (r?.type === "SHARED_CONNECT") {
17
- let n = c(r.memoryBytes ?? 0, r.memory);
18
- e.postMessage({
19
- type: "SHARED_MEMORY",
20
- buffer: n.buffer,
21
- clientIndex: t
22
- });
23
- return;
24
- }
25
- if (r?.type === "EXECUTE_TASK") {
26
- let { messageId: n, taskId: s, args: c } = r;
27
- if (!o) {
28
- e.postMessage({
29
- messageId: n,
30
- success: !1,
31
- error: "Shared memory not initialized in worker."
32
- });
33
- return;
34
- }
35
- let l = performance.now();
36
- try {
37
- let r = await i.execute(s, ...c || []);
38
- a.debug(`task ${s} → ${(performance.now() - l).toFixed(1)}ms (client #${t})`), e.postMessage({
39
- messageId: n,
40
- success: !0,
41
- result: r
42
- });
43
- } catch (t) {
44
- a.warn(`task ${s} failed: ${t.message || String(t)}`), e.postMessage({
45
- messageId: n,
46
- success: !1,
47
- error: t.message || String(t)
48
- });
49
- }
50
- return;
51
- }
52
- r?.type === "SHARED_DISCONNECT" && (e.close(), a.info(`client #${t} disconnected`));
53
- }, e.start();
54
- }
55
- function u() {
56
- self.onconnect = (e) => {
57
- l(e.ports[0]);
58
- };
59
- }
1
+ import { scoped as e } from "./index6.js";
2
+ //#region src/worker/registry.ts
3
+ var t = e("registry"), n = class {
4
+ static tasks = /* @__PURE__ */ new Map();
5
+ static register(e, n) {
6
+ this.tasks.set(e.taskId, {
7
+ contract: e,
8
+ handler: n
9
+ }), t.debug(`registered "${e.taskId}"`, {
10
+ args: e.argsSchema ? "validated" : "unvalidated",
11
+ result: e.resultSchema ? "validated" : "unvalidated"
12
+ });
13
+ }
14
+ static getContract(e) {
15
+ return this.tasks.get(e)?.contract;
16
+ }
17
+ static async execute(e, ...t) {
18
+ let n = this.tasks.get(e);
19
+ if (!n) throw Error(`Task handler not found for id: ${e}`);
20
+ let { contract: r, handler: i } = n, a = await i(...r.argsSchema ? r.argsSchema.parse(t) : t);
21
+ return r.resultSchema ? r.resultSchema.parse(a) : a;
22
+ }
23
+ };
60
24
  //#endregion
61
- export { l as attachSharedPort, u as sharedWorkerHost };
25
+ export { n as TaskRegistry };