@offmain/workerkit 0.8.9 → 0.9.2

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/README.md CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  A lightweight TypeScript library for running functions in Web Workers with support for partitioning, retries, and concurrency control — all without the boilerplate.
4
4
 
5
- Instead of manually creating worker scripts and wiring up `postMessage` / `onmessage`, you write plain exported functions and hand them to `MainWorkerFactory`. The library serialises them into Blob workers, manages a pool per function, handles retries on failure, and merges results back on the main thread.
5
+ Instead of manually creating worker scripts and wiring up `postMessage` / `onmessage`, you write plain exported functions and hand them to `MainWorkerFactory`. The library serializes them into Blob workers, manages threads per function, handles retries on failure, and merges results back on the main thread.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Installation
10
10
 
11
11
  ```bash
12
- npm install workerkit
12
+ npm install @offmain/workerkit
13
13
  # or
14
- pnpm add workerkit
14
+ pnpm add @offmain/workerkit
15
15
  ```
16
16
 
17
17
  ---
@@ -32,10 +32,10 @@ export function sum({ data }: { data: number[] }): number {
32
32
  ### 2. Register and run it
33
33
 
34
34
  ```ts
35
- import { MainWorkerFactory } from 'workerkit';
35
+ import { MainWorkerFactory } from '@offmain/workerkit';
36
36
  import { sum } from './sum.worker.ts';
37
37
 
38
- const factory = new MainWorkerFactory(initiator, {
38
+ const factory = new MainWorkerFactory({
39
39
  workers: [
40
40
  {
41
41
  name: 'sum',
@@ -47,10 +47,10 @@ const factory = new MainWorkerFactory(initiator, {
47
47
  ],
48
48
  });
49
49
 
50
- const result = await factory.runWorker('sum', { srcData: [1, 2, 3, 4, 5] });
51
- const { data } = await factory.collectResults(result);
50
+ const settled = await factory.runWorker('sum', { srcData: [1, 2, 3, 4, 5] });
51
+ const { data } = await factory.collectResults(settled);
52
52
 
53
- console.log(data); // 15
53
+ console.log(data); // [15]
54
54
  ```
55
55
 
56
56
  ---
@@ -63,7 +63,7 @@ console.log(data); // 15
63
63
  | `role` | `string` | — | Logical grouping label |
64
64
  | `func` | `Function` | — | The exported worker function to run |
65
65
  | `maxConcurrency` | `number` | `navigator.hardwareConcurrency` | Max parallel worker instances — defaults to the number of logical CPU cores reported by the browser |
66
- | `retries` | `number` | `0` | How many times to retry a failed shard |
66
+ | `retries` | `number` | `0` | How many times to retry a failed shard before marking it as rejected |
67
67
  | `partition` | `boolean` | `false` | Split array input across multiple workers automatically |
68
68
 
69
69
  ---
@@ -73,21 +73,23 @@ console.log(data); // 15
73
73
  When `partition: true`, an array passed as `srcData` is automatically split across worker instances and results are merged back.
74
74
 
75
75
  ```ts
76
- const result = await factory.runWorker('sum', {
76
+ const settled = await factory.runWorker('sum', {
77
77
  srcData: largeArray, // split across workers
78
78
  });
79
79
 
80
- const { data, succeeded, failed } = await factory.collectResults(result);
80
+ const { data, succeeded, failed } = await factory.collectResults(settled);
81
81
  ```
82
82
 
83
83
  You can also provide a custom reducer to control how shard results are merged:
84
84
 
85
85
  ```ts
86
- const { data } = await factory.collectResults(result, {
86
+ const { data } = await factory.collectResults(settled, {
87
87
  reducer: (shards) => shards.flat().sort((a, b) => b.score - a.score),
88
88
  });
89
89
  ```
90
90
 
91
+ > **Note:** The reducer runs inside a worker thread and must be self-contained — it cannot reference variables from the outer scope.
92
+
91
93
  ---
92
94
 
93
95
  ## ESLint Plugin
@@ -98,7 +100,7 @@ The package ships with two ESLint rules to catch common worker mistakes at lint
98
100
 
99
101
  ```js
100
102
  // eslint.config.js
101
- import workerPlugin from 'workerkit/eslint-plugin';
103
+ import workerPlugin from '@offmain/workerkit/eslint-plugin';
102
104
 
103
105
  export default [...workerPlugin.configs.recommended];
104
106
  ```
@@ -113,16 +115,16 @@ Flags usage of browser main-thread-only APIs that are unavailable inside Web Wor
113
115
 
114
116
  ```ts
115
117
  // sum.worker.ts ❌ — will be flagged
116
- export function sum({ data }: MessageEvent) {
118
+ export function sum({ data }: { data: number[] }) {
117
119
  document.title = 'working...'; // Error: 'document' is not available inside Web Workers
118
- return data.reduce((a: number, b: number) => a + b, 0);
120
+ return data.reduce((a, b) => a + b, 0);
119
121
  }
120
122
  ```
121
123
 
122
124
  ```ts
123
125
  // sum.worker.ts ✅
124
- export function sum({ data }: MessageEvent) {
125
- return data.reduce((a: number, b: number) => a + b, 0);
126
+ export function sum({ data }: { data: number[] }) {
127
+ return data.reduce((a, b) => a + b, 0);
126
128
  }
127
129
  ```
128
130
 
@@ -150,8 +152,8 @@ You can also import rules individually if you don't want the full recommended co
150
152
 
151
153
  ```js
152
154
  // eslint.config.js
153
- import noDomInWorker from 'workerkit/eslint-rules/no-dom-in-worker';
154
- import workerExportable from 'workerkit/eslint-rules/worker-exportable';
155
+ import noDomInWorker from '@offmain/workerkit/eslint-rules/no-dom-in-worker';
156
+ import workerExportable from '@offmain/workerkit/eslint-rules/worker-exportable';
155
157
 
156
158
  export default [
157
159
  {
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var g=Object.defineProperty;var m=(t,e,r)=>e in t?g(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var h=(t,e,r)=>m(t,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=t=>`
1
+ "use strict";var y=Object.defineProperty;var m=(t,e,r)=>e in t?y(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var g=(t,e,r)=>m(t,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=t=>`
2
2
  const extractTransferables = (value, seen = new Set()) => {
3
3
  if (value === null || typeof value !== 'object') return [];
4
4
  if (seen.has(value)) return [];
@@ -14,11 +14,14 @@ const extractTransferables = (value, seen = new Set()) => {
14
14
  };
15
15
 
16
16
  self.addEventListener('message', async (event) => {
17
- const begin = performance.now();
17
+ try {
18
18
  const output = await ${t}(event.data);
19
- self.postMessage(output, extractTransferables(output));
20
- })
21
- `;class k{constructor(e){h(this,"_worker");const r=w(e.toString()),s=new Blob([r],{type:"application/javascript"});this._worker=new Worker(URL.createObjectURL(s))}get getWorker(){return this._worker}}function p(t,e=new Set){return t===null||typeof t!="object"?[]:e.has(t)?[]:(e.add(t),t instanceof ArrayBuffer||t instanceof MessagePort||typeof ImageBitmap<"u"&&t instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas?[t]:ArrayBuffer.isView(t)?[t.buffer]:Array.isArray(t)?t.flatMap(r=>p(r,e)):Object.values(t).flatMap(r=>p(r,e)))}class W{constructor(e,r){h(this,"_workers");h(this,"_threads");this._workers=r.workers,this._threads=navigator.hardwareConcurrency}initWorker(e){return new k(e)}partitionArray(e,r){if(!e.length)return[];if(r<=0)throw new Error("numChunks must be positive");const s=Math.min(r,e.length),a=Math.floor(e.length/s),c=e.length%s,o=[];let f=0;for(let n=0;n<s;n++){const i=a+(n<c?1:0);o.push(e.slice(f,f+i)),f+=i}return o}findWorkerByName(e){return this._workers.find(r=>r.name===e)}async runWorker(e,{srcData:r,...s}){const a=this.findWorkerByName(e);if(!a)return Promise.reject(new Error(`Worker "${e}" not found`));const c=a.maxConcurrency??this._threads,o=!!(Array.isArray(r)&&r.length>1&&a.partition),f=o?this.partitionArray(r,c):r,n=this.createWorkerPromises(a,e,{data:f,...s},c,o);return Promise.allSettled(n)}createWorkerPromises(e,r,s,a,c){const{data:o,...f}=s;return Array.from({length:a},(n,i)=>{const u=c&&Array.isArray(o)?o[i]:o;return this.runWorkerWithRetry({workerFunc:e.func,workerName:r,index:i,data:{data:u,...f}},e.retries)})}async runWorkerWithRetry(e,r=2){try{return await this.initiateWorker(e)}catch(s){if(r>0)return console.error(`Worker ${e.index} failed, retrying (${r} left):`,s),this.runWorkerWithRetry(e,r-1);throw console.error("Worker failed after all retries:",s),s}}initiateWorker({workerFunc:e,workerName:r,index:s,data:a}){return new Promise((c,o)=>{const n=this.initWorker(e).getWorker;n.onerror=u=>{n.terminate(),o({index:s,workerConfigs:{workerFunc:e,workerName:r,index:s,data:a},failedResult:u})},n.onmessage=u=>{c({index:s,workerConfigs:{workerFunc:e,workerName:r,index:s,data:a},successResult:u}),n.terminate()};const i={index:s,...Array.isArray(a)?{data:a}:a};n.postMessage(i,p(i))})}async collectResults(e,r={}){const s=e.filter(n=>n.status==="fulfilled"),a=e.filter(n=>n.status==="rejected"),c=s.map(n=>n.value.successResult.data),o=r.reducer?r.reducer.toString():"(shards) => shards.flat()";return{data:await new Promise((n,i)=>{const u=`
19
+ self.postMessage({ ok: true, data: output }, extractTransferables(output));
20
+ } catch (err) {
21
+ self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
22
+ }
23
+ })
24
+ `;class p{constructor(e){g(this,"_worker");const r=w(e.toString()),s=new Blob([r],{type:"application/javascript"});this._worker=new Worker(URL.createObjectURL(s))}get getWorker(){return this._worker}}class W{constructor(e){this.results=e}}function k(t,e=new Set){return t===null||typeof t!="object"?[]:e.has(t)?[]:(e.add(t),t instanceof ArrayBuffer||t instanceof MessagePort||typeof ImageBitmap<"u"&&t instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas?[t]:ArrayBuffer.isView(t)?[t.buffer]:Array.isArray(t)?t.flatMap(r=>k(r,e)):Object.values(t).flatMap(r=>k(r,e)))}class v{constructor(e){g(this,"_workers");g(this,"_threads");this._workers=e.workers,this._threads=navigator.hardwareConcurrency}initWorker(e){return new p(e)}partitionArray(e,r){if(!e.length)return[];if(r<=0)throw new Error("numChunks must be positive");const s=Math.min(r,e.length),n=Math.floor(e.length/s),c=e.length%s,o=[];let f=0;for(let a=0;a<s;a++){const i=n+(a<c?1:0);o.push(e.slice(f,f+i)),f+=i}return o}findWorkerByName(e){return this._workers.find(r=>r.name===e)}async runWorker(e,{srcData:r,...s}){const n=this.findWorkerByName(e);if(!n)return Promise.reject(new Error(`Worker "${e}" not found`));const c=n.maxConcurrency??this._threads,o=!!(Array.isArray(r)&&r.length>1&&n.partition),f=o?this.partitionArray(r,c):r,a=this.createWorkerPromises(n,e,{data:f,...s},c,o),i=await Promise.allSettled(a);return new W(i)}createWorkerPromises(e,r,s,n,c){const{data:o,...f}=s;return Array.from({length:n},(a,i)=>{const l=c&&Array.isArray(o)?o[i]:o;return this.runWorkerWithRetry({workerFunc:e.func,workerName:r,index:i,data:{data:l,...f}},e.retries)})}async runWorkerWithRetry(e,r=2){try{return await this.initiateWorker(e)}catch(s){if(r>0)return console.error(`Worker ${e.index} failed, retrying (${r} left):`,s),this.runWorkerWithRetry(e,r-1);throw console.error("Worker failed after all retries:",s),s}}initiateWorker({workerFunc:e,workerName:r,index:s,data:n}){return new Promise((c,o)=>{const a=this.initWorker(e).getWorker;a.onerror=l=>{a.terminate(),o({index:s,workerConfigs:{workerFunc:e,workerName:r,index:s,data:n},failedResult:l})},a.onmessage=l=>{var h,u;if(((h=l.data)==null?void 0:h.ok)===!1){a.terminate(),o({index:s,workerConfigs:{workerFunc:e,workerName:r,index:s,data:n},failedResult:new ErrorEvent("error",{message:l.data.error})});return}c({index:s,workerConfigs:{workerFunc:e,workerName:r,index:s,data:n},successResult:new MessageEvent("message",{data:(u=l.data)==null?void 0:u.data})}),a.terminate()};const i={index:s,...Array.isArray(n)?{data:n}:n};a.postMessage(i,k(i))})}async collectResults(e,r={}){const s=e.results.filter(a=>a.status==="fulfilled"),n=e.results.filter(a=>a.status==="rejected"),c=s.map(a=>a.value.successResult.data),o=r.reducer?r.reducer.toString():"(shards) => shards.flat()";return{data:await new Promise((a,i)=>{const l=`
22
25
  const reducer = ${o};
23
26
  self.addEventListener('message', (event) => {
24
27
  try {
@@ -28,4 +31,4 @@ self.addEventListener('message', async (event) => {
28
31
  self.postMessage({ ok: false, error: String(err) });
29
32
  }
30
33
  });
31
- `,y=new Blob([u],{type:"application/javascript"}),l=new Worker(URL.createObjectURL(y));l.onmessage=d=>{l.terminate(),d.data.ok?n(d.data.data):i(new Error(d.data.error))},l.onerror=d=>{l.terminate(),i(d)},l.postMessage(c)}),succeeded:s.length,failed:a.length,errors:a}}}exports.MainWorkerFactory=W;exports.WorkerFactory=k;
34
+ `,h=new Blob([l],{type:"application/javascript"}),u=new Worker(URL.createObjectURL(h));u.onmessage=d=>{u.terminate(),d.data.ok?a(d.data.data):i(new Error(d.data.error))},u.onerror=d=>{u.terminate(),i(d)},u.postMessage(c)}),succeeded:s.length,failed:n.length,errors:n}}}exports.MainWorkerFactory=v;exports.WorkerFactory=p;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- var y = Object.defineProperty;
2
- var g = (t, e, r) => e in t ? y(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
3
- var h = (t, e, r) => g(t, typeof e != "symbol" ? e + "" : e, r);
1
+ var k = Object.defineProperty;
2
+ var y = (t, e, r) => e in t ? k(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
3
+ var p = (t, e, r) => y(t, typeof e != "symbol" ? e + "" : e, r);
4
4
  const m = (t) => `
5
5
  const extractTransferables = (value, seen = new Set()) => {
6
6
  if (value === null || typeof value !== 'object') return [];
@@ -17,82 +17,191 @@ const extractTransferables = (value, seen = new Set()) => {
17
17
  };
18
18
 
19
19
  self.addEventListener('message', async (event) => {
20
- const begin = performance.now();
20
+ try {
21
21
  const output = await ${t}(event.data);
22
- self.postMessage(output, extractTransferables(output));
23
- })
22
+ self.postMessage({ ok: true, data: output }, extractTransferables(output));
23
+ } catch (err) {
24
+ self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
25
+ }
26
+ })
24
27
  `;
25
28
  class w {
29
+ /**
30
+ * Creates a new `Worker` from the given function.
31
+ *
32
+ * The function is stringified, embedded into a self-contained worker script,
33
+ * converted to a `Blob` URL, and passed to the `Worker` constructor.
34
+ *
35
+ * @param workerFunction - The function to run inside the worker thread.
36
+ * Must be self-contained — it cannot reference variables from the outer
37
+ * scope because it is serialised via `.toString()`.
38
+ */
26
39
  constructor(e) {
27
- h(this, "_worker");
40
+ p(this, "_worker");
28
41
  const r = m(e.toString()), s = new Blob([r], {
29
42
  type: "application/javascript"
30
43
  });
31
44
  this._worker = new Worker(URL.createObjectURL(s));
32
45
  }
46
+ /**
47
+ * Returns the underlying native `Worker` instance.
48
+ *
49
+ * Use this to attach `onmessage` / `onerror` handlers and call
50
+ * `postMessage` / `terminate` directly.
51
+ */
33
52
  get getWorker() {
34
53
  return this._worker;
35
54
  }
36
55
  }
37
- function p(t, e = /* @__PURE__ */ new Set()) {
38
- return t === null || typeof t != "object" ? [] : e.has(t) ? [] : (e.add(t), t instanceof ArrayBuffer || t instanceof MessagePort || typeof ImageBitmap < "u" && t instanceof ImageBitmap || typeof OffscreenCanvas < "u" && t instanceof OffscreenCanvas ? [t] : ArrayBuffer.isView(t) ? [t.buffer] : Array.isArray(t) ? t.flatMap((r) => p(r, e)) : Object.values(t).flatMap(
39
- (r) => p(r, e)
56
+ class W {
57
+ constructor(e) {
58
+ this.results = e;
59
+ }
60
+ }
61
+ function g(t, e = /* @__PURE__ */ new Set()) {
62
+ return t === null || typeof t != "object" ? [] : e.has(t) ? [] : (e.add(t), t instanceof ArrayBuffer || t instanceof MessagePort || typeof ImageBitmap < "u" && t instanceof ImageBitmap || typeof OffscreenCanvas < "u" && t instanceof OffscreenCanvas ? [t] : ArrayBuffer.isView(t) ? [t.buffer] : Array.isArray(t) ? t.flatMap((r) => g(r, e)) : Object.values(t).flatMap(
63
+ (r) => g(r, e)
40
64
  ));
41
65
  }
42
- class v {
43
- constructor(e, r) {
44
- h(this, "_workers");
45
- h(this, "_threads");
46
- this._workers = r.workers, this._threads = navigator.hardwareConcurrency;
66
+ class b {
67
+ /**
68
+ * Creates a new `MainWorkerFactory`.
69
+ *
70
+ * @param options - Configuration object containing the `workers` registry.
71
+ */
72
+ constructor(e) {
73
+ p(this, "_workers");
74
+ p(this, "_threads");
75
+ this._workers = e.workers, this._threads = navigator.hardwareConcurrency;
47
76
  }
77
+ /**
78
+ * Instantiates a {@link WorkerFactory} for the given worker function.
79
+ *
80
+ * @param workerFunction - The function to run inside the worker thread.
81
+ * @returns A new `WorkerFactory` wrapping the worker.
82
+ */
48
83
  initWorker(e) {
49
84
  return new w(e);
50
85
  }
51
86
  /**
52
- * Partitions an array into up to numChunks evenly-sized chunks.
87
+ * Splits an array into up to `numChunks` evenly-sized sub-arrays.
88
+ *
89
+ * When the array length is not evenly divisible, the first `remainder`
90
+ * chunks receive one extra element so no data is lost.
91
+ *
92
+ * @param array - The source array to partition.
93
+ * @param numChunks - Maximum number of chunks to produce.
94
+ * Clamped to `array.length` so you never get empty chunks.
95
+ * @returns An array of sub-arrays. Returns `[]` when `array` is empty.
96
+ * @throws {Error} When `numChunks` is not a positive integer.
97
+ *
98
+ * @example
99
+ * partitionArray([1, 2, 3, 4, 5], 3);
100
+ * // → [[1, 2], [3, 4], [5]]
53
101
  */
54
102
  partitionArray(e, r) {
55
103
  if (!e.length) return [];
56
104
  if (r <= 0) throw new Error("numChunks must be positive");
57
- const s = Math.min(r, e.length), a = Math.floor(e.length / s), c = e.length % s, o = [];
105
+ const s = Math.min(r, e.length), n = Math.floor(e.length / s), c = e.length % s, o = [];
58
106
  let f = 0;
59
- for (let n = 0; n < s; n++) {
60
- const i = a + (n < c ? 1 : 0);
107
+ for (let a = 0; a < s; a++) {
108
+ const i = n + (a < c ? 1 : 0);
61
109
  o.push(e.slice(f, f + i)), f += i;
62
110
  }
63
111
  return o;
64
112
  }
113
+ /**
114
+ * Looks up a registered worker configuration by name.
115
+ *
116
+ * @param name - The `name` field of the target {@link WorkerConfig}.
117
+ * @returns The matching config, or `undefined` if not found.
118
+ */
65
119
  findWorkerByName(e) {
66
120
  return this._workers.find((r) => r.name === e);
67
121
  }
68
- async runWorker(e, { srcData: r, ...s }) {
69
- const a = this.findWorkerByName(e);
70
- if (!a)
122
+ /**
123
+ * Runs a named worker against the provided data, distributing work across
124
+ * threads when the worker is configured for partitioning.
125
+ *
126
+ * When `config.partition` is `true` and `srcData` is an array with more
127
+ * than one element, the array is split into up to `maxConcurrency` (or
128
+ * `navigator.hardwareConcurrency`) shards and each shard is processed by
129
+ * a separate worker thread in parallel.
130
+ *
131
+ * All threads are awaited with `Promise.allSettled`, so a failure in one
132
+ * shard does not cancel the others. Use {@link collectResults} to merge
133
+ * the settled output.
134
+ *
135
+ * @typeParam TName - The literal name of the worker to run (inferred from
136
+ * the registered `workers` tuple).
137
+ *
138
+ * @param workerName - Name of the worker as declared in the `workers` config.
139
+ * @param params - Object containing `srcData` (the payload) plus any
140
+ * additional key/value pairs forwarded to the worker verbatim.
141
+ *
142
+ * @returns A {@link TypedSettledResults} wrapping the settled promises from
143
+ * all spawned worker threads.
144
+ *
145
+ * @example
146
+ * const settled = await foreman.runWorker('sum', { srcData: [1, 2, 3] });
147
+ */
148
+ async runWorker(e, {
149
+ srcData: r,
150
+ ...s
151
+ }) {
152
+ const n = this.findWorkerByName(e);
153
+ if (!n)
71
154
  return Promise.reject(new Error(`Worker "${e}" not found`));
72
- const c = a.maxConcurrency ?? this._threads, o = !!(Array.isArray(r) && r.length > 1 && a.partition), f = o ? this.partitionArray(r, c) : r, n = this.createWorkerPromises(
73
- a,
155
+ const c = n.maxConcurrency ?? this._threads, o = !!(Array.isArray(r) && r.length > 1 && n.partition), f = o ? this.partitionArray(r, c) : r, a = this.createWorkerPromises(
156
+ n,
74
157
  e,
75
158
  { data: f, ...s },
76
159
  c,
77
160
  o
78
- );
79
- return Promise.allSettled(n);
161
+ ), i = await Promise.allSettled(a);
162
+ return new W(i);
80
163
  }
81
- createWorkerPromises(e, r, s, a, c) {
164
+ /**
165
+ * Builds the array of per-thread worker promises for a single `runWorker`
166
+ * call.
167
+ *
168
+ * When `isPartitioned` is `true`, each promise receives its own slice of
169
+ * `srcData`; otherwise every thread receives the full payload.
170
+ *
171
+ * @param config - The resolved {@link WorkerConfig} for this run.
172
+ * @param workerName - Name used in error/retry logging.
173
+ * @param srcWorkerData - Combined `{ data, ...otherParams }` payload.
174
+ * @param threadCount - Number of parallel worker threads to spawn.
175
+ * @param isPartitioned - Whether `data` is a pre-split array of shards.
176
+ * @returns An array of promises, one per thread.
177
+ */
178
+ createWorkerPromises(e, r, s, n, c) {
82
179
  const { data: o, ...f } = s;
83
- return Array.from({ length: a }, (n, i) => {
84
- const u = c && Array.isArray(o) ? o[i] : o;
180
+ return Array.from({ length: n }, (a, i) => {
181
+ const l = c && Array.isArray(o) ? o[i] : o;
85
182
  return this.runWorkerWithRetry(
86
183
  {
87
184
  workerFunc: e.func,
88
185
  workerName: r,
89
186
  index: i,
90
- data: { data: u, ...f }
187
+ data: { data: l, ...f }
91
188
  },
92
189
  e.retries
93
190
  );
94
191
  });
95
192
  }
193
+ /**
194
+ * Runs a single worker instance, retrying on failure up to `retryCount`
195
+ * times before re-throwing the last error.
196
+ *
197
+ * Each retry is logged to `console.error` with the remaining attempt count
198
+ * so failures are visible during development.
199
+ *
200
+ * @param instanceConfig - Full configuration for the worker instance.
201
+ * @param retryCount - Remaining retry attempts (default `2`).
202
+ * @returns The successful {@link WorkerResult} once the worker resolves.
203
+ * @throws The last caught error when all retries are exhausted.
204
+ */
96
205
  async runWorkerWithRetry(e, r = 2) {
97
206
  try {
98
207
  return await this.initiateWorker(e);
@@ -105,39 +214,86 @@ class v {
105
214
  throw console.error("Worker failed after all retries:", s), s;
106
215
  }
107
216
  }
217
+ /**
218
+ * Spawns a single worker thread, posts the payload, and resolves or rejects
219
+ * based on the message the worker sends back.
220
+ *
221
+ * The worker is expected to respond with either:
222
+ * - `{ ok: true, data: T }` — success; resolves with a {@link WorkerResult}.
223
+ * - `{ ok: false, error: string }` — logical failure; rejects with a
224
+ * structured error object.
225
+ *
226
+ * Any transferable objects found in the payload are moved (not copied) to
227
+ * the worker via the `transfer` list of `postMessage`.
228
+ *
229
+ * The underlying `Worker` is always terminated after the first message,
230
+ * whether it succeeded or failed.
231
+ *
232
+ * @param instanceConfig - Worker function, name, shard index, and data.
233
+ * @returns A promise that resolves with the worker's result.
234
+ */
108
235
  initiateWorker({
109
236
  workerFunc: e,
110
237
  workerName: r,
111
238
  index: s,
112
- data: a
239
+ data: n
113
240
  }) {
114
241
  return new Promise((c, o) => {
115
- const n = this.initWorker(e).getWorker;
116
- n.onerror = (u) => {
117
- n.terminate(), o({
242
+ const a = this.initWorker(e).getWorker;
243
+ a.onerror = (l) => {
244
+ a.terminate(), o({
118
245
  index: s,
119
- workerConfigs: { workerFunc: e, workerName: r, index: s, data: a },
120
- failedResult: u
246
+ workerConfigs: { workerFunc: e, workerName: r, index: s, data: n },
247
+ failedResult: l
121
248
  });
122
- }, n.onmessage = (u) => {
249
+ }, a.onmessage = (l) => {
250
+ var h, u;
251
+ if (((h = l.data) == null ? void 0 : h.ok) === !1) {
252
+ a.terminate(), o({
253
+ index: s,
254
+ workerConfigs: { workerFunc: e, workerName: r, index: s, data: n },
255
+ failedResult: new ErrorEvent("error", {
256
+ message: l.data.error
257
+ })
258
+ });
259
+ return;
260
+ }
123
261
  c({
124
262
  index: s,
125
- workerConfigs: { workerFunc: e, workerName: r, index: s, data: a },
126
- successResult: u
127
- }), n.terminate();
263
+ workerConfigs: { workerFunc: e, workerName: r, index: s, data: n },
264
+ successResult: new MessageEvent("message", {
265
+ data: (u = l.data) == null ? void 0 : u.data
266
+ })
267
+ }), a.terminate();
128
268
  };
129
269
  const i = {
130
270
  index: s,
131
- ...Array.isArray(a) ? { data: a } : a
271
+ ...Array.isArray(n) ? { data: n } : n
132
272
  };
133
- n.postMessage(i, p(i));
273
+ a.postMessage(i, g(i));
134
274
  });
135
275
  }
136
276
  /**
137
- * Collects and merges the settled results from `runWorker` — off the main thread.
277
+ * Collects and merges the settled results from {@link runWorker} — off the
278
+ * main thread.
279
+ *
280
+ * Fulfilled shards are extracted and passed to the `reducer` function, which
281
+ * runs inside a dedicated inline worker so the merge itself never blocks the
282
+ * main thread. Failed shards are counted and their raw rejection reasons are
283
+ * preserved in `errors`.
284
+ *
285
+ * @typeParam T - The per-shard data type (inferred from `settled`).
286
+ * @typeParam R - The final merged output type (defaults to a flat array of
287
+ * `T` items when no custom reducer is provided).
288
+ *
289
+ * @param settled - The {@link TypedSettledResults} returned by `runWorker`.
290
+ * @param options - Optional {@link CollectOptions}. Supply a `reducer` to
291
+ * control how shards are merged. The reducer **must be self-contained**
292
+ * (no closures over external variables) because it is serialised and run
293
+ * inside a worker.
138
294
  *
139
- * @param settled The `PromiseSettledResult[]` returned by `runWorker`
140
- * @param options Optional `reducer` function (must be self-contained)
295
+ * @returns A {@link CollectedResult} with the merged `data`, counts of
296
+ * `succeeded`/`failed` shards, and the raw `errors` array.
141
297
  *
142
298
  * @example
143
299
  * // default: flat array of all shard data
@@ -150,14 +306,14 @@ class v {
150
306
  * });
151
307
  */
152
308
  async collectResults(e, r = {}) {
153
- const s = e.filter(
154
- (n) => n.status === "fulfilled"
155
- ), a = e.filter(
156
- (n) => n.status === "rejected"
157
- ), c = s.map((n) => n.value.successResult.data), o = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
309
+ const s = e.results.filter(
310
+ (a) => a.status === "fulfilled"
311
+ ), n = e.results.filter(
312
+ (a) => a.status === "rejected"
313
+ ), c = s.map((a) => a.value.successResult.data), o = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
158
314
  return {
159
- data: await new Promise((n, i) => {
160
- const u = `
315
+ data: await new Promise((a, i) => {
316
+ const l = `
161
317
  const reducer = ${o};
162
318
  self.addEventListener('message', (event) => {
163
319
  try {
@@ -167,20 +323,20 @@ class v {
167
323
  self.postMessage({ ok: false, error: String(err) });
168
324
  }
169
325
  });
170
- `, k = new Blob([u], { type: "application/javascript" }), l = new Worker(URL.createObjectURL(k));
171
- l.onmessage = (d) => {
172
- l.terminate(), d.data.ok ? n(d.data.data) : i(new Error(d.data.error));
173
- }, l.onerror = (d) => {
174
- l.terminate(), i(d);
175
- }, l.postMessage(c);
326
+ `, h = new Blob([l], { type: "application/javascript" }), u = new Worker(URL.createObjectURL(h));
327
+ u.onmessage = (d) => {
328
+ u.terminate(), d.data.ok ? a(d.data.data) : i(new Error(d.data.error));
329
+ }, u.onerror = (d) => {
330
+ u.terminate(), i(d);
331
+ }, u.postMessage(c);
176
332
  }),
177
333
  succeeded: s.length,
178
- failed: a.length,
179
- errors: a
334
+ failed: n.length,
335
+ errors: n
180
336
  };
181
337
  }
182
338
  }
183
339
  export {
184
- v as MainWorkerFactory,
340
+ b as MainWorkerFactory,
185
341
  w as WorkerFactory
186
342
  };
@@ -1,31 +1,169 @@
1
- import { CollectOptions, CollectedResult, MainWorkerFactoryOptions, WorkerFunction, WorkerResult } from './types.ts';
1
+ import { CollectOptions, CollectedResult, WorkerConfig, WorkerConfigMap, WorkerDataParam, WorkerFunction, WorkerReturnType, TypedSettledResults } from './types.ts';
2
2
  /**
3
3
  * Recursively collects all Transferable objects from a value.
4
4
  * Transferables (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas)
5
5
  * are zero-copy — they are moved to the worker instead of cloned.
6
6
  */
7
7
  export declare function extractTransferables(value: unknown, seen?: Set<object>): Transferable[];
8
- declare class MainWorkerFactory {
8
+ /**
9
+ * Central orchestrator for running typed Web Workers in parallel.
10
+ *
11
+ * `MainWorkerFactory` manages a registry of named worker configurations and
12
+ * handles the full lifecycle of each worker: spawning, partitioning input
13
+ * data across threads, retrying on failure, and collecting results.
14
+ *
15
+ * @typeParam TConfigs - A readonly tuple of {@link WorkerConfig} objects that
16
+ * defines the set of available workers and their typed signatures.
17
+ *
18
+ * @example
19
+ * const foreman = new MainWorkerFactory({
20
+ * workers: [
21
+ * { name: 'sum', role: 'compute', func: sumWorker, partition: true },
22
+ * ],
23
+ * });
24
+ *
25
+ * const settled = await foreman.runWorker('sum', { srcData: [1, 2, 3, 4] });
26
+ * const { data } = await foreman.collectResults(settled);
27
+ */
28
+ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFunction<any, any>>[]> {
9
29
  private readonly _workers;
10
30
  private readonly _threads;
11
- constructor(_initiator: WorkerFunction, options: MainWorkerFactoryOptions);
31
+ /**
32
+ * Creates a new `MainWorkerFactory`.
33
+ *
34
+ * @param options - Configuration object containing the `workers` registry.
35
+ */
36
+ constructor(options: {
37
+ workers: TConfigs;
38
+ });
39
+ /**
40
+ * Instantiates a {@link WorkerFactory} for the given worker function.
41
+ *
42
+ * @param workerFunction - The function to run inside the worker thread.
43
+ * @returns A new `WorkerFactory` wrapping the worker.
44
+ */
12
45
  private initWorker;
13
46
  /**
14
- * Partitions an array into up to numChunks evenly-sized chunks.
47
+ * Splits an array into up to `numChunks` evenly-sized sub-arrays.
48
+ *
49
+ * When the array length is not evenly divisible, the first `remainder`
50
+ * chunks receive one extra element so no data is lost.
51
+ *
52
+ * @param array - The source array to partition.
53
+ * @param numChunks - Maximum number of chunks to produce.
54
+ * Clamped to `array.length` so you never get empty chunks.
55
+ * @returns An array of sub-arrays. Returns `[]` when `array` is empty.
56
+ * @throws {Error} When `numChunks` is not a positive integer.
57
+ *
58
+ * @example
59
+ * partitionArray([1, 2, 3, 4, 5], 3);
60
+ * // → [[1, 2], [3, 4], [5]]
15
61
  */
16
62
  partitionArray<T>(array: T[], numChunks: number): T[][];
63
+ /**
64
+ * Looks up a registered worker configuration by name.
65
+ *
66
+ * @param name - The `name` field of the target {@link WorkerConfig}.
67
+ * @returns The matching config, or `undefined` if not found.
68
+ */
17
69
  private findWorkerByName;
18
- runWorker(workerName: string, { srcData, ...otherParams }: {
19
- srcData: unknown;
20
- } & Record<string, unknown>): Promise<PromiseSettledResult<WorkerResult>[]>;
70
+ /**
71
+ * Runs a named worker against the provided data, distributing work across
72
+ * threads when the worker is configured for partitioning.
73
+ *
74
+ * When `config.partition` is `true` and `srcData` is an array with more
75
+ * than one element, the array is split into up to `maxConcurrency` (or
76
+ * `navigator.hardwareConcurrency`) shards and each shard is processed by
77
+ * a separate worker thread in parallel.
78
+ *
79
+ * All threads are awaited with `Promise.allSettled`, so a failure in one
80
+ * shard does not cancel the others. Use {@link collectResults} to merge
81
+ * the settled output.
82
+ *
83
+ * @typeParam TName - The literal name of the worker to run (inferred from
84
+ * the registered `workers` tuple).
85
+ *
86
+ * @param workerName - Name of the worker as declared in the `workers` config.
87
+ * @param params - Object containing `srcData` (the payload) plus any
88
+ * additional key/value pairs forwarded to the worker verbatim.
89
+ *
90
+ * @returns A {@link TypedSettledResults} wrapping the settled promises from
91
+ * all spawned worker threads.
92
+ *
93
+ * @example
94
+ * const settled = await foreman.runWorker('sum', { srcData: [1, 2, 3] });
95
+ */
96
+ runWorker<TName extends keyof WorkerConfigMap<TConfigs> & string>(workerName: TName, { srcData, ...otherParams }: {
97
+ srcData: WorkerDataParam<WorkerConfigMap<TConfigs>[TName]>;
98
+ } & Record<string, unknown>): Promise<TypedSettledResults<WorkerReturnType<WorkerConfigMap<TConfigs>[TName]>>>;
99
+ /**
100
+ * Builds the array of per-thread worker promises for a single `runWorker`
101
+ * call.
102
+ *
103
+ * When `isPartitioned` is `true`, each promise receives its own slice of
104
+ * `srcData`; otherwise every thread receives the full payload.
105
+ *
106
+ * @param config - The resolved {@link WorkerConfig} for this run.
107
+ * @param workerName - Name used in error/retry logging.
108
+ * @param srcWorkerData - Combined `{ data, ...otherParams }` payload.
109
+ * @param threadCount - Number of parallel worker threads to spawn.
110
+ * @param isPartitioned - Whether `data` is a pre-split array of shards.
111
+ * @returns An array of promises, one per thread.
112
+ */
21
113
  private createWorkerPromises;
114
+ /**
115
+ * Runs a single worker instance, retrying on failure up to `retryCount`
116
+ * times before re-throwing the last error.
117
+ *
118
+ * Each retry is logged to `console.error` with the remaining attempt count
119
+ * so failures are visible during development.
120
+ *
121
+ * @param instanceConfig - Full configuration for the worker instance.
122
+ * @param retryCount - Remaining retry attempts (default `2`).
123
+ * @returns The successful {@link WorkerResult} once the worker resolves.
124
+ * @throws The last caught error when all retries are exhausted.
125
+ */
22
126
  private runWorkerWithRetry;
127
+ /**
128
+ * Spawns a single worker thread, posts the payload, and resolves or rejects
129
+ * based on the message the worker sends back.
130
+ *
131
+ * The worker is expected to respond with either:
132
+ * - `{ ok: true, data: T }` — success; resolves with a {@link WorkerResult}.
133
+ * - `{ ok: false, error: string }` — logical failure; rejects with a
134
+ * structured error object.
135
+ *
136
+ * Any transferable objects found in the payload are moved (not copied) to
137
+ * the worker via the `transfer` list of `postMessage`.
138
+ *
139
+ * The underlying `Worker` is always terminated after the first message,
140
+ * whether it succeeded or failed.
141
+ *
142
+ * @param instanceConfig - Worker function, name, shard index, and data.
143
+ * @returns A promise that resolves with the worker's result.
144
+ */
23
145
  private initiateWorker;
24
146
  /**
25
- * Collects and merges the settled results from `runWorker` — off the main thread.
147
+ * Collects and merges the settled results from {@link runWorker} — off the
148
+ * main thread.
149
+ *
150
+ * Fulfilled shards are extracted and passed to the `reducer` function, which
151
+ * runs inside a dedicated inline worker so the merge itself never blocks the
152
+ * main thread. Failed shards are counted and their raw rejection reasons are
153
+ * preserved in `errors`.
154
+ *
155
+ * @typeParam T - The per-shard data type (inferred from `settled`).
156
+ * @typeParam R - The final merged output type (defaults to a flat array of
157
+ * `T` items when no custom reducer is provided).
158
+ *
159
+ * @param settled - The {@link TypedSettledResults} returned by `runWorker`.
160
+ * @param options - Optional {@link CollectOptions}. Supply a `reducer` to
161
+ * control how shards are merged. The reducer **must be self-contained**
162
+ * (no closures over external variables) because it is serialised and run
163
+ * inside a worker.
26
164
  *
27
- * @param settled The `PromiseSettledResult[]` returned by `runWorker`
28
- * @param options Optional `reducer` function (must be self-contained)
165
+ * @returns A {@link CollectedResult} with the merged `data`, counts of
166
+ * `succeeded`/`failed` shards, and the raw `errors` array.
29
167
  *
30
168
  * @example
31
169
  * // default: flat array of all shard data
@@ -37,6 +175,6 @@ declare class MainWorkerFactory {
37
175
  * reducer: (shards) => shards.flat().reduce((a, b) => a + b, 0),
38
176
  * });
39
177
  */
40
- collectResults<T = unknown, R = T[]>(settled: PromiseSettledResult<WorkerResult>[], options?: CollectOptions<T, R>): Promise<CollectedResult<R>>;
178
+ collectResults<T = unknown, R = T extends (infer Item)[] ? Item[] : T[]>(settled: TypedSettledResults<T>, options?: CollectOptions<T, R>): Promise<CollectedResult<R>>;
41
179
  }
42
180
  export default MainWorkerFactory;
@@ -1,35 +1,128 @@
1
1
  import { WorkerFactory } from '../worker-factory';
2
+ /** A unique string identifier for a worker, matching its `name` field. */
2
3
  export type WorkerName = string;
4
+ /** A descriptive label for the worker's role (e.g. `'compute'`, `'io'`). */
3
5
  export type WorkerRole = string;
4
- export type WorkerFunction = (...params: unknown[]) => void;
5
- export interface WorkerConfig {
6
+ /**
7
+ * The shape of a function that runs inside a Web Worker.
8
+ *
9
+ * Workers receive a single `params` argument posted from the main thread and
10
+ * return (or resolve) a result that is posted back.
11
+ *
12
+ * @typeParam TParams - The type of the message payload sent to the worker.
13
+ * @typeParam TResult - The type of the value the worker posts back.
14
+ */
15
+ export type WorkerFunction<TParams = unknown, TResult = unknown> = (params: TParams) => TResult;
16
+ /**
17
+ * Configuration object that registers a named worker with the factory.
18
+ *
19
+ * @typeParam TFunc - The concrete {@link WorkerFunction} type for this worker.
20
+ */
21
+ export interface WorkerConfig<TFunc extends WorkerFunction = WorkerFunction> {
22
+ /** Unique name used to look up this worker via `runWorker`. */
6
23
  name: WorkerName;
24
+ /** Human-readable role label (e.g. `'compute'`, `'transform'`). */
7
25
  role: WorkerRole;
8
- func: WorkerFunction;
26
+ /** The worker function that will be serialised and run in a thread. */
27
+ func: TFunc;
28
+ /**
29
+ * Maximum number of parallel threads to spawn for this worker.
30
+ * Defaults to `navigator.hardwareConcurrency` when omitted.
31
+ */
9
32
  maxConcurrency?: number;
33
+ /**
34
+ * Number of times a failed worker thread is retried before the shard is
35
+ * marked as rejected. Defaults to `2`.
36
+ */
10
37
  retries?: number;
38
+ /**
39
+ * When `true`, an array `srcData` is split into per-thread shards before
40
+ * being dispatched. Each thread receives one shard instead of the full
41
+ * array.
42
+ */
11
43
  partition?: boolean;
12
44
  }
45
+ /**
46
+ * Derives a `name → function` map from a readonly tuple of
47
+ * {@link WorkerConfig} objects.
48
+ *
49
+ * Used internally to give `runWorker` a fully-typed `workerName` parameter
50
+ * and to infer the correct `srcData` type for each worker.
51
+ *
52
+ * @typeParam T - The readonly tuple of `WorkerConfig` values.
53
+ */
54
+ export type WorkerConfigMap<T extends readonly WorkerConfig<WorkerFunction<any, any>>[]> = {
55
+ [K in T[number]['name']]: Extract<T[number], {
56
+ name: K;
57
+ }>['func'];
58
+ };
59
+ /**
60
+ * Extracts the `params` type from a {@link WorkerFunction}.
61
+ *
62
+ * @typeParam TFunc - The worker function to inspect.
63
+ */
64
+ export type WorkerParams<TFunc extends WorkerFunction> = TFunc extends WorkerFunction<infer P, unknown> ? P : never;
65
+ /**
66
+ * Extracts the `data` field type from a worker function's params.
67
+ * Workers receive `{ data: T, index: number, ...otherParams }` — this
68
+ * pulls out just `T` so callers only need to supply the payload.
69
+ *
70
+ * Always allows `D | D[]` so partitioned workers can receive an array
71
+ * that the framework splits into per-shard items.
72
+ */
73
+ export type WorkerDataParam<TFunc extends WorkerFunction> = WorkerParams<TFunc> extends {
74
+ data: infer D;
75
+ } ? D extends (infer Item)[] ? Item[] : D | D[] : WorkerParams<TFunc>;
76
+ /** Extracts the return type from a {@link WorkerFunction}, unwrapping `Promise<T>` → `T`. */
77
+ export type WorkerReturnType<TFunc extends WorkerFunction> = TFunc extends WorkerFunction<any, infer R> ? R extends Promise<infer Resolved> ? Resolved : R : never;
78
+ /** Options passed to the `MainWorkerFactory` constructor. */
13
79
  export interface MainWorkerFactoryOptions {
14
80
  workers: WorkerConfig[];
15
81
  }
82
+ /** Internal representation of a worker config that has been instantiated. */
16
83
  export interface MainWorkerFactoryWorker extends WorkerConfig {
17
84
  worker: WorkerFactory;
18
85
  }
19
- export interface WorkerInstanceConfig {
86
+ /**
87
+ * Runtime configuration for a single worker thread instance.
88
+ *
89
+ * @typeParam TFunc - The worker function type for this instance.
90
+ */
91
+ export interface WorkerInstanceConfig<TFunc extends WorkerFunction = WorkerFunction> {
92
+ /** Name of the parent worker config, used in logs and error objects. */
20
93
  workerName: WorkerName;
21
- workerFunc: WorkerFunction;
94
+ /** The function serialised and executed inside the thread. */
95
+ workerFunc: TFunc;
96
+ /** Zero-based shard index assigned to this thread. */
22
97
  index: number;
98
+ /** The data payload (full or partitioned shard) sent to the thread. */
23
99
  data: unknown;
24
100
  }
101
+ /** The raw `MessageEvent` received when a worker thread succeeds. */
25
102
  export type WorkerSuccessResult = MessageEvent;
103
+ /** The raw `MessageEvent` (or `ErrorEvent`) received when a worker thread fails. */
26
104
  export type WorkerFailedResult = MessageEvent;
105
+ /** Structured result returned by a single worker thread, whether it succeeded or failed. */
27
106
  export interface WorkerResult {
107
+ /** Zero-based shard index of the thread that produced this result. */
28
108
  index: number;
109
+ /** The full instance config used to spawn this thread. */
29
110
  workerConfigs: WorkerInstanceConfig;
111
+ /** Present when the thread resolved successfully. */
30
112
  successResult?: WorkerSuccessResult;
113
+ /** Present when the thread rejected or posted `{ ok: false }`. */
31
114
  failedResult?: WorkerFailedResult;
32
115
  }
116
+ /**
117
+ * Typed wrapper around the settled results from `runWorker`.
118
+ * Carries `T` (the worker's return type) so `collectResults` can infer it.
119
+ */
120
+ export declare class TypedSettledResults<T> {
121
+ readonly results: PromiseSettledResult<WorkerResult>[];
122
+ constructor(results: PromiseSettledResult<WorkerResult>[]);
123
+ /** Never actually exists at runtime — used only for type inference. */
124
+ readonly __type: T;
125
+ }
33
126
  /** Options for collectResults */
34
127
  export interface CollectOptions<T, R = T[]> {
35
128
  /**
@@ -1,7 +1,34 @@
1
1
  import { WorkerFunction } from '../main-worker-factory/types';
2
+ /**
3
+ * Low-level factory that serialises a {@link WorkerFunction} into a Blob URL
4
+ * and spawns a native `Worker` from it.
5
+ *
6
+ * `WorkerFactory` is an internal building block used by `MainWorkerFactory`.
7
+ * It handles the mechanics of turning a plain TypeScript function into a
8
+ * runnable worker thread — you rarely need to use it directly.
9
+ *
10
+ * The worker script is generated by {@link workerTemplate}, which wraps the
11
+ * function with a message listener and transferable-extraction logic.
12
+ */
2
13
  declare class WorkerFactory {
3
14
  readonly _worker: Worker;
15
+ /**
16
+ * Creates a new `Worker` from the given function.
17
+ *
18
+ * The function is stringified, embedded into a self-contained worker script,
19
+ * converted to a `Blob` URL, and passed to the `Worker` constructor.
20
+ *
21
+ * @param workerFunction - The function to run inside the worker thread.
22
+ * Must be self-contained — it cannot reference variables from the outer
23
+ * scope because it is serialised via `.toString()`.
24
+ */
4
25
  constructor(workerFunction: WorkerFunction);
26
+ /**
27
+ * Returns the underlying native `Worker` instance.
28
+ *
29
+ * Use this to attach `onmessage` / `onerror` handlers and call
30
+ * `postMessage` / `terminate` directly.
31
+ */
5
32
  get getWorker(): Worker;
6
33
  }
7
34
  export default WorkerFactory;
@@ -1,2 +1,18 @@
1
+ /**o with
2
+ * Default initiator — a minimal echo worker used as a no-op placeholder.
3
+ *
4
+ * When no custom initiator is passed to `MainWorkerFactory`, this function
5
+ * is used. It simply reflects every incoming message back to the sender,
6
+ * which is useful for testing the messaging pipeline without any real
7
+ * computation.
8
+ *
9
+ * @example
10
+ * // Automatically used as the default:
11
+ * new MainWorkerFactory({ workers: [...] });
12
+ *
13
+ * // Equivalent explicit usage:
14
+ * import defaultInitiator from './initiator';
15
+ * new MainWorkerFactory({ workers: [...] }, defaultInitiator);
16
+ */
1
17
  declare const _default: () => void;
2
18
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@offmain/workerkit",
3
- "version": "0.8.9",
3
+ "version": "0.9.2",
4
4
  "description": "A lightweight manager for running functions in Web Workers with partitioning, retries, and concurrency control",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -28,11 +28,11 @@
28
28
  "lint": "eslint --fix .",
29
29
  "test": "vitest run",
30
30
  "test:watch": "vitest",
31
- "release:patch": "npm version patch && npm run release:publish",
32
- "release:minor": "npm version minor && npm run release:publish",
33
- "release:major": "npm version major && npm run release:publish",
34
- "release:publish": "npm run build:lib && npm publish",
35
- "prepublishOnly": "npm run build:lib",
31
+ "release": "release-it --ci",
32
+ "release:patch": "release-it --increment patch --ci",
33
+ "release:minor": "release-it --increment minor --ci",
34
+ "release:major": "release-it --increment major --ci",
35
+ "release:dry": "release-it --dry-run",
36
36
  "lint-staged": "lint-staged",
37
37
  "prepare": "husky"
38
38
  },
@@ -59,6 +59,7 @@
59
59
  ]
60
60
  },
61
61
  "devDependencies": {
62
+ "@release-it/conventional-changelog": "11.0.0",
62
63
  "@typescript-eslint/eslint-plugin": "^8.58.2",
63
64
  "@typescript-eslint/parser": "^8.58.2",
64
65
  "date-fns": "^4.1.0",
@@ -71,6 +72,7 @@
71
72
  "jsdom": "^24.0.0",
72
73
  "lint-staged": "^16.4.0",
73
74
  "prettier": "^3.4.2",
75
+ "release-it": "19.0.3",
74
76
  "typescript": "^5.7.2",
75
77
  "vite": "^6.3.3",
76
78
  "vite-plugin-dts": "^4.5.4",
@@ -1 +0,0 @@
1
- export {};