@atolljs/core 0.1.0 → 0.1.1

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.
Files changed (51) hide show
  1. package/README.md +159 -59
  2. package/dist/contract/listSchema.d.ts +47 -0
  3. package/dist/contract/msgpackCodec.d.ts +7 -0
  4. package/dist/contract/msgpackrCodec.d.ts +2 -0
  5. package/dist/contract/mz.d.ts +43 -0
  6. package/dist/contract/sharedMemory.d.ts +350 -0
  7. package/dist/contract/types.d.ts +131 -0
  8. package/dist/contract/zod.d.ts +53 -0
  9. package/dist/index.d.ts +30 -0
  10. package/dist/index.js +18 -18
  11. package/dist/index10.js +14 -178
  12. package/dist/index11.js +188 -47
  13. package/dist/index12.js +45 -59
  14. package/dist/index13.js +59 -20
  15. package/dist/index14.js +24 -60
  16. package/dist/index15.js +56 -43
  17. package/dist/index16.js +45 -41
  18. package/dist/index17.js +42 -38
  19. package/dist/index18.js +36 -55
  20. package/dist/index19.js +52 -47
  21. package/dist/index20.js +48 -51
  22. package/dist/index21.js +59 -0
  23. package/dist/index3.js +24 -117
  24. package/dist/index4.js +116 -34
  25. package/dist/index5.js +32 -473
  26. package/dist/index6.js +489 -19
  27. package/dist/index7.js +19 -6
  28. package/dist/index8.js +6 -33
  29. package/dist/index9.js +29 -14
  30. package/dist/log.d.ts +35 -0
  31. package/dist/observable.d.ts +30 -0
  32. package/dist/pool/errors.d.ts +16 -0
  33. package/dist/pool/memory.d.ts +19 -0
  34. package/dist/pool/workerClient.d.ts +80 -0
  35. package/dist/pool/workerPool.d.ts +115 -0
  36. package/dist/reactive.d.ts +46 -0
  37. package/dist/service.d.ts +181 -0
  38. package/dist/shared/sharedWorkerClient.d.ts +35 -0
  39. package/dist/shared/sharedWorkerHost.d.ts +21 -0
  40. package/dist/task.d.ts +40 -0
  41. package/dist/worker/defineWorker.d.ts +63 -0
  42. package/dist/worker/registry.d.ts +8 -0
  43. package/dist/worker/workerBootstrap.d.ts +1 -0
  44. package/package.json +14 -2
  45. package/src/contract/listSchema.ts +13 -8
  46. package/src/contract/mz.ts +1 -1
  47. package/src/contract/sharedMemory.ts +28 -1
  48. package/src/contract/types.ts +21 -0
  49. package/src/contract/zod.ts +44 -0
  50. package/src/index.ts +2 -1
  51. package/src/pool/workerPool.ts +27 -1
package/dist/index10.js CHANGED
@@ -1,186 +1,22 @@
1
- import { fmtBytes as e, scoped as t } from "./index4.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
- constructor(t) {
35
- 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.");
36
- if (!t.workerUrl && !t.createWorker) throw Error("WorkerPool requires either `workerUrl` or `createWorker` in its config.");
37
- 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()));
38
- let r = t.poolSize ?? "auto", i = r === "auto" ? navigator.hardwareConcurrency ?? 4 : r;
39
- 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");
40
- for (let e = 0; e < i; e++) this.addWorker(this.spawn());
41
- let a = t.tasks ?? {};
42
- for (let [e, t] of Object.entries(a)) {
43
- if (e in this) {
44
- s.warn(`task "${e}" collides with a WorkerPool member — not installed`);
45
- continue;
46
- }
47
- this[e] = (...e) => this.runTask(t, ...e);
48
- }
49
- 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";
50
9
  }
51
- addWorker(e) {
52
- let t = {
53
- worker: e,
54
- inFlight: 0
55
- };
56
- return e.addEventListener("message", (e) => {
57
- this.handleReply(t, e.data);
58
- }), e.addEventListener("error", (e) => {
59
- this.handleWorkerError(t, e);
60
- }), this.memoryManager ? e.postMessage({
61
- type: "INIT_MEMORY",
62
- memory: this.memoryManager.memory
63
- }) : 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";
64
13
  }
65
- handleReply(e, t) {
66
- let n = t.messageId === void 0 ? void 0 : this.pending.get(t.messageId);
67
- 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());
68
- }
69
- handleWorkerError(e, t) {
70
- if (this.closed) return;
71
- let n = t, r = n?.message ?? (n?.error ? String(n.error) : "worker error");
72
- s.warn(`worker error: ${r}`);
73
- for (let [t, n] of [...this.pending]) if (n.slot === e) {
74
- if (this.pending.delete(t), this.detach(n), n.done) continue;
75
- n.done = !0, this.statsAgg.failed++, n.reject(new o(r));
76
- }
77
- let i = this.slots.indexOf(e);
78
- if (i >= 0 && this.slots.splice(i, 1), e.worker.terminate(), this.respawn) s.info("respawning crashed worker"), this.addWorker(this.spawn());
79
- 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));
80
- this.drainQueue(), this.checkDrained();
81
- }
82
- pickWorker() {
83
- let e;
84
- for (let t of this.slots) t.inFlight >= this.concurrency || (!e || t.inFlight < e.inFlight) && (e = t);
85
- return e;
86
- }
87
- drainQueue() {
88
- let e;
89
- for (; this.queue.length > 0 && (e = this.pickWorker());) {
90
- let t = this.queue.shift();
91
- t.done || this.send(e, t);
92
- }
93
- }
94
- send(e, t) {
95
- let n = this.nextMessageId++;
96
- 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}`, {
97
- worker: this.slots.indexOf(e),
98
- args: t.args
99
- }), e.worker.postMessage({
100
- type: "EXECUTE_TASK",
101
- messageId: n,
102
- taskId: t.contract.taskId,
103
- args: t.args
104
- });
105
- }
106
- detach(e) {
107
- e.timer && clearTimeout(e.timer), e.signal && e.onSignalAbort && e.signal.removeEventListener("abort", e.onSignalAbort);
108
- }
109
- cancel(e, t) {
110
- if (e.done) return;
111
- let n = this.queue.indexOf(e);
112
- n >= 0 && this.queue.splice(n, 1), e.done = !0, this.detach(e), this.statsAgg.aborted++, e.reject(t), this.drainQueue(), this.checkDrained();
113
- }
114
- checkDrained() {
115
- if (this.queue.length === 0 && this.pending.size === 0) {
116
- let e = this.drainWaiters.splice(0);
117
- for (let t of e) t();
118
- }
119
- }
120
- dispatch(e, t, n) {
121
- 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) => {
122
- let c = {
123
- contract: e,
124
- args: t,
125
- enqueuedAt: performance.now(),
126
- resolve: o,
127
- reject: s,
128
- done: !1,
129
- signal: n?.signal
130
- };
131
- n?.signal && (c.onSignalAbort = () => this.cancel(c, new i()), n.signal.addEventListener("abort", c.onSignalAbort, { once: !0 }));
132
- let l = n?.timeout ?? this.taskTimeout;
133
- l !== void 0 && (c.timer = setTimeout(() => this.cancel(c, new a()), l));
134
- let u = this.pickWorker();
135
- if (u) this.send(u, c);
136
- else if (this.queue.length >= this.maxQueue) {
137
- c.done = !0, this.detach(c), s(new r());
138
- return;
139
- } else this.queue.push(c);
140
- });
141
- }
142
- runTask(e, ...t) {
143
- return this.dispatch(e, t);
144
- }
145
- stats() {
146
- return {
147
- workers: this.slots.length,
148
- idle: this.slots.filter((e) => e.inFlight === 0).length,
149
- inFlight: this.pending.size,
150
- queued: this.queue.length,
151
- completed: this.statsAgg.completed,
152
- failed: this.statsAgg.failed,
153
- aborted: this.statsAgg.aborted,
154
- waitMs: {
155
- count: this.statsAgg.waitMs.count,
156
- mean: this.statsAgg.waitMs.mean,
157
- max: this.statsAgg.waitMs.max
158
- },
159
- runMs: {
160
- count: this.statsAgg.runMs.count,
161
- mean: this.statsAgg.runMs.mean,
162
- max: this.statsAgg.runMs.max
163
- }
164
- };
165
- }
166
- close() {
167
- return this.queue.length === 0 && this.pending.size === 0 ? (this.terminate(), Promise.resolve()) : new Promise((e) => {
168
- this.drainWaiters.push(() => {
169
- this.terminate(), e();
170
- });
171
- });
172
- }
173
- terminate() {
174
- this.closed = !0, s.info(`terminating ${this.slots.length} worker(s)`);
175
- let e = new o("pool terminated");
176
- for (let [, t] of this.pending) this.detach(t), !t.done && (t.done = !0, this.statsAgg.failed++, t.reject(e));
177
- this.pending.clear();
178
- for (let t of this.queue.splice(0)) t.done = !0, this.detach(t), this.statsAgg.failed++, t.reject(e);
179
- for (let e of this.slots) e.worker.terminate();
180
- this.slots = [], this.checkDrained();
14
+ }, r = class extends Error {
15
+ constructor(e = "worker crashed") {
16
+ super(e), this.name = "WorkerCrashedError";
181
17
  }
182
18
  };
183
19
  //#endregion
184
- export { u as WorkerPool };
20
+ export { e as PoolQueueFullError, t as TaskAbortedError, n as TaskTimeoutError, r as WorkerCrashedError };
185
21
 
186
22
  //# sourceMappingURL=index10.js.map
package/dist/index11.js CHANGED
@@ -1,52 +1,193 @@
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 { fmtBytes as e, scoped as t } from "./index5.js";
2
+ import { MemoryManager as n } from "./index9.js";
3
+ import { PoolQueueFullError as r, TaskAbortedError as i, TaskTimeoutError as a, WorkerCrashedError as o } from "./index10.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);
11
+ }
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);
16
49
  }
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;
50
+ Object.keys(a).length > 0 && s.info(`task methods: ${Object.keys(a).join(", ")}`);
51
+ }
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;
65
+ }
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);
46
93
  }
47
- });
48
- }
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();
188
+ }
189
+ };
49
190
  //#endregion
50
- export { r as connectWorker, n as workerClient };
191
+ export { u as WorkerPool };
51
192
 
52
193
  //# sourceMappingURL=index11.js.map
package/dist/index12.js CHANGED
@@ -1,66 +1,52 @@
1
- import { scoped as e } from "./index4.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
+ 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;
46
+ }
61
47
  });
62
48
  }
63
49
  //#endregion
64
- export { i as connectSharedWorker };
50
+ export { r as connectWorker, n as workerClient };
65
51
 
66
52
  //# sourceMappingURL=index12.js.map
package/dist/index13.js CHANGED
@@ -1,27 +1,66 @@
1
- import { scoped as e } from "./index4.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 "./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);
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 };
26
65
 
27
66
  //# sourceMappingURL=index13.js.map