@offmain/workerkit 0.12.3 → 0.13.0

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
@@ -57,23 +57,23 @@ console.log(data); // [15]
57
57
 
58
58
  ## WorkerConfig Options
59
59
 
60
- | Option | Type | Default | Description |
61
- | ---------------- | --------------- | ------------------------------- | --------------------------------------------------------------------------------------------------- |
62
- | `name` | `string` | — | Unique identifier used to call the worker |
63
- | `role` | `string` | — | Logical grouping label |
64
- | `func` | `Function` | — | The exported worker function to run (optional if `workerURL` is provided) |
65
- | `workerURL` | `string \| URL` | — | Worker script URL or `new URL(..., import.meta.url)` for module bundlers |
66
- | `maxConcurrency` | `number` | `navigator.hardwareConcurrency` | Max parallel worker instances — defaults to the number of logical CPU cores reported by the browser |
67
- | `retries` | `number` | `0` | How many times to retry a failed shard before marking it as rejected |
68
- | `partition` | `boolean` | `false` | Split array input across multiple workers automatically |
60
+ | Option | Type | Default | Description |
61
+ | ---------------- | -------------- | ------------------------------- | --------------------------------------------------------------------------------------------------- |
62
+ | `name` | `string` | — | Unique identifier used to call the worker |
63
+ | `role` | `string` | — | Logical grouping label |
64
+ | `func` | `Function` | — | The exported worker function to run (optional if `createWorker` is provided) |
65
+ | `createWorker` | `() => Worker` | — | Worker factory function `() => new Worker(new URL(...))` for Webpack 5 / Vite static analysis |
66
+ | `maxConcurrency` | `number` | `navigator.hardwareConcurrency` | Max parallel worker instances — defaults to the number of logical CPU cores reported by the browser |
67
+ | `retries` | `number` | `0` | How many times to retry a failed shard before marking it as rejected |
68
+ | `partition` | `boolean` | `false` | Split array input across multiple workers automatically |
69
69
 
70
70
  ---
71
71
 
72
- ## Module Bundler Integration (`workerURL`)
72
+ ## Module Bundler Integration (`createWorker`)
73
73
 
74
74
  When worker logic relies on external npm packages (such as Luxon, `date-fns`, `i18next`, or custom data transformation modules), dynamic inline stringification (`.toString()`) cannot access those closed-over imports.
75
75
 
76
- Passing `workerURL` enables modern module bundlers (Webpack 5, Vite, Rollup, Parcel) to analyze and bundle the worker file along with all of its dependencies into a dedicated ES module worker chunk.
76
+ Passing `createWorker` enables modern module bundlers (Webpack 5, Vite, Rollup, Parcel) to analyze and bundle the worker file along with all of its dependencies into a dedicated ES module worker chunk.
77
77
 
78
78
  ### Dedicated Worker Script
79
79
 
@@ -98,7 +98,9 @@ self.addEventListener('message', (event) => {
98
98
  });
99
99
  ```
100
100
 
101
- ### Registering with `workerURL`
101
+ ### Webpack 5 & Vite Static Analysis (`createWorker`)
102
+
103
+ Webpack 5 and Vite look for literal `new Worker(new URL(..., import.meta.url))` calls inside consumer source files. By providing a `createWorker` factory function, bundlers statically detect and bundle the worker into a separate JS file, while allowing `MainWorkerFactory` to scale `maxConcurrency` across multiple threads:
102
104
 
103
105
  ```ts
104
106
  import { MainWorkerFactory } from '@offmain/workerkit';
@@ -108,9 +110,13 @@ const factory = new MainWorkerFactory({
108
110
  {
109
111
  name: 'transformData',
110
112
  role: 'compute',
111
- // Webpack 5 / Vite statically analyzes new URL(..., import.meta.url)
112
- workerURL: new URL('./transform-data.worker.ts', import.meta.url),
113
- maxConcurrency: 4,
113
+ // Webpack 5 and Vite statically analyze new Worker(new URL(..., import.meta.url))
114
+ // written inside this factory function and emit an individual bundled JS chunk.
115
+ createWorker: () =>
116
+ new Worker(new URL('./transform-data.worker.ts', import.meta.url), {
117
+ type: 'module',
118
+ }),
119
+ maxConcurrency: 4, // Spawns up to 4 parallel worker instances
114
120
  },
115
121
  ] as const,
116
122
  });
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var v=Object.defineProperty;var y=(n,e,r)=>e in n?v(n,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):n[e]=r;var k=(n,e,r)=>y(n,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=n=>`
1
+ "use strict";var y=Object.defineProperty;var w=(n,e,r)=>e in n?y(n,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):n[e]=r;var h=(n,e,r)=>w(n,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=n=>`
2
2
  const extractTransferables = (value, seen = new Set()) => {
3
3
  if (value === null || typeof value !== 'object') return [];
4
4
  if (seen.has(value)) return [];
@@ -21,7 +21,7 @@ self.addEventListener('message', async (event) => {
21
21
  self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
22
22
  }
23
23
  })
24
- `,P=n=>`
24
+ `,W=n=>`
25
25
  const extractTransferables = (value, seen = new Set()) => {
26
26
  if (value === null || typeof value !== 'object') return [];
27
27
  if (seen.has(value)) return [];
@@ -93,7 +93,7 @@ self.addEventListener('message', (event) => {
93
93
  pendingData = event.data;
94
94
  }
95
95
  });
96
- `,W=n=>`
96
+ `,M=n=>`
97
97
  const extractTransferables = (value, seen = new Set()) => {
98
98
  if (value === null || typeof value !== 'object') return [];
99
99
  if (seen.has(value)) return [];
@@ -149,8 +149,8 @@ self.addEventListener('message', async (event) => {
149
149
  self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
150
150
  }
151
151
  });
152
- `;var w=(n=>(n.Default="default",n.Pipeline="pipeline",n.Persistent="persistent",n))(w||{});const M=Object.freeze({persistent:W,pipeline:P,default:m});class h{constructor(e,r){k(this,"_worker");if(r!=null&&r.workerInstance)this._worker=r.workerInstance;else if(r!=null&&r.workerURL)this._worker=new Worker(r.workerURL,{type:"module"});else if(e){const o=(r==null?void 0:r.mode)??"default",a=M[o](e.toString()),i=new Blob([a],{type:"application/javascript"});this._worker=new Worker(URL.createObjectURL(i))}else throw new Error("Either workerFunction or options.workerURL must be provided to WorkerFactory.")}get getWorker(){return this._worker}}class b{constructor(e){this.results=e}}function g(n,e=new Set){return n===null||typeof n!="object"?[]:e.has(n)?[]:(e.add(n),n instanceof ArrayBuffer||n instanceof MessagePort||typeof ImageBitmap<"u"&&n instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&n instanceof OffscreenCanvas?[n]:ArrayBuffer.isView(n)?[n.buffer]:Array.isArray(n)?n.flatMap(r=>g(r,e)):Object.values(n).flatMap(r=>g(r,e)))}class E{constructor(e){k(this,"_workers");k(this,"_threads");k(this,"_persistentWorkers",new Map);this._workers=e.workers,this._threads=navigator.hardwareConcurrency}initWorker(e){return new h(e.func,{workerURL:e.workerURL})}partitionArray(e,r){if(!e.length)return[];if(r<=0)throw new Error("numChunks must be positive");const o=Math.min(r,e.length),a=Math.floor(e.length/o),i=e.length%o,l=[];let f=0;for(let t=0;t<o;t++){const s=a+(t<i?1:0);l.push(e.slice(f,f+s)),f+=s}return l}findWorkerByName(e){return this._workers.find(r=>r.name===e)}async runWorker(e,{srcData:r,...o}){const a=this.findWorkerByName(e);if(!a)return Promise.reject(new Error(`Worker "${e}" not found`));const i=a.maxConcurrency??this._threads,l=!!(Array.isArray(r)&&r.length>1&&a.partition),f=l?this.partitionArray(r,i):r,t=this.createWorkerPromises(a,e,{data:f,...o},i,l),s=await Promise.allSettled(t);return new b(s)}createWorkerPromises(e,r,o,a,i){const{data:l,...f}=o;return Array.from({length:a},(t,s)=>{const u=i&&Array.isArray(l)?l[s]:l;return this.runWorkerWithRetry({workerFunc:e.func,workerURL:e.workerURL,workerName:r,index:s,data:{data:u,...f}},e.retries)})}async runWorkerWithRetry(e,r=2){try{return await this.initiateWorker(e)}catch(o){if(r>0)return console.error(`Worker ${e.index} failed, retrying (${r} left):`,o),this.runWorkerWithRetry(e,r-1);throw console.error("Worker failed after all retries:",o),o}}initiateWorker({workerFunc:e,workerURL:r,workerName:o,index:a,data:i}){return new Promise((l,f)=>{const s=this.initWorker({name:o,role:"",func:e,workerURL:r}).getWorker;s.onerror=c=>{s.terminate(),f({index:a,workerConfigs:{workerFunc:e,workerName:o,index:a,data:i},failedResult:c})},s.onmessage=c=>{var d,p;if(((d=c.data)==null?void 0:d.ok)===!1){s.terminate(),f({index:a,workerConfigs:{workerFunc:e,workerName:o,index:a,data:i},failedResult:new ErrorEvent("error",{message:c.data.error})});return}l({index:a,workerConfigs:{workerFunc:e,workerName:o,index:a,data:i},successResult:new MessageEvent("message",{data:(p=c.data)==null?void 0:p.data})}),s.terminate()};const u={index:a,...Array.isArray(i)?{data:i}:i};s.postMessage(u,g(u))})}async collectResults(e,r={}){const o=e.results.filter(t=>t.status==="fulfilled"),a=e.results.filter(t=>t.status==="rejected"),i=o.map(t=>t.value.successResult.data),l=r.reducer?r.reducer.toString():"(shards) => shards.flat()";return{data:await new Promise((t,s)=>{const u=`
153
- const reducer = ${l};
152
+ `;var m=(n=>(n.Default="default",n.Pipeline="pipeline",n.Persistent="persistent",n))(m||{});const P=Object.freeze({persistent:M,pipeline:W,default:v});class g{constructor(e,r){h(this,"_worker");if(r!=null&&r.createWorker)this._worker=r.createWorker();else if(e){const o=(r==null?void 0:r.mode)??"default",a=P[o](e.toString()),i=new Blob([a],{type:"application/javascript"});this._worker=new Worker(URL.createObjectURL(i))}else throw new Error("Either workerFunction or options.createWorker must be provided to WorkerFactory.")}get getWorker(){return this._worker}}class b{constructor(e){this.results=e}}function k(n,e=new Set){return n===null||typeof n!="object"?[]:e.has(n)?[]:(e.add(n),n instanceof ArrayBuffer||n instanceof MessagePort||typeof ImageBitmap<"u"&&n instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&n instanceof OffscreenCanvas?[n]:ArrayBuffer.isView(n)?[n.buffer]:Array.isArray(n)?n.flatMap(r=>k(r,e)):Object.values(n).flatMap(r=>k(r,e)))}class _{constructor(e){h(this,"_workers");h(this,"_threads");h(this,"_persistentWorkers",new Map);h(this,"_activeWorkers",new Set);h(this,"_isTerminated",!1);this._workers=e.workers,this._threads=navigator.hardwareConcurrency}get isTerminated(){return this._isTerminated}trackWorker(e){if(this._isTerminated)throw e.terminate(),new Error("MainWorkerFactory has been terminated");return this._activeWorkers.add(e),e}terminateWorker(e){this._activeWorkers.delete(e);try{e.terminate()}catch{}}initWorker(e){const r=new g(e.func,{createWorker:e.createWorker});return this.trackWorker(r.getWorker),r}partitionArray(e,r){if(!e.length)return[];if(r<=0)throw new Error("numChunks must be positive");const o=Math.min(r,e.length),a=Math.floor(e.length/o),i=e.length%o,c=[];let u=0;for(let t=0;t<o;t++){const s=a+(t<i?1:0);c.push(e.slice(u,u+s)),u+=s}return c}findWorkerByName(e){return this._workers.find(r=>r.name===e)}async runWorker(e,{srcData:r,...o}){if(this._isTerminated)return Promise.reject(new Error("MainWorkerFactory has been terminated"));const a=this.findWorkerByName(e);if(!a)return Promise.reject(new Error(`Worker "${e}" not found`));const i=a.maxConcurrency??this._threads,c=!!(Array.isArray(r)&&r.length>1&&a.partition),u=c?this.partitionArray(r,i):r,t=this.createWorkerPromises(a,e,{data:u,...o},i,c),s=await Promise.allSettled(t);return new b(s)}createWorkerPromises(e,r,o,a,i){const{data:c,...u}=o;return Array.from({length:a},(t,s)=>{const f=i&&Array.isArray(c)?c[s]:c;return this.runWorkerWithRetry({workerFunc:e.func,createWorker:e.createWorker,workerName:r,index:s,data:{data:f,...u}},e.retries)})}async runWorkerWithRetry(e,r=2){try{return await this.initiateWorker(e)}catch(o){if(r>0)return console.error(`Worker ${e.index} failed, retrying (${r} left):`,o),this.runWorkerWithRetry(e,r-1);throw console.error("Worker failed after all retries:",o),o}}initiateWorker({workerFunc:e,createWorker:r,workerName:o,index:a,data:i}){return new Promise((c,u)=>{const s=this.initWorker({name:o,role:"",func:e,createWorker:r}).getWorker;s.onerror=l=>{this.terminateWorker(s),u({index:a,workerConfigs:{workerFunc:e,createWorker:r,workerName:o,index:a,data:i},failedResult:l})},s.onmessage=l=>{var d,p;if(((d=l.data)==null?void 0:d.ok)===!1){this.terminateWorker(s),u({index:a,workerConfigs:{workerFunc:e,createWorker:r,workerName:o,index:a,data:i},failedResult:new ErrorEvent("error",{message:l.data.error})});return}c({index:a,workerConfigs:{workerFunc:e,createWorker:r,workerName:o,index:a,data:i},successResult:new MessageEvent("message",{data:(p=l.data)==null?void 0:p.data})}),this.terminateWorker(s)};const f={index:a,...Array.isArray(i)?{data:i}:i};s.postMessage(f,k(f))})}async collectResults(e,r={}){if(this._isTerminated)throw new Error("MainWorkerFactory has been terminated");const o=e.results.filter(t=>t.status==="fulfilled"),a=e.results.filter(t=>t.status==="rejected"),i=o.map(t=>t.value.successResult.data),c=r.reducer?r.reducer.toString():"(shards) => shards.flat()";return{data:await new Promise((t,s)=>{const f=`
153
+ const reducer = ${c};
154
154
  self.addEventListener('message', (event) => {
155
155
  try {
156
156
  const result = reducer(event.data);
@@ -159,4 +159,4 @@ self.addEventListener('message', async (event) => {
159
159
  self.postMessage({ ok: false, error: String(err) });
160
160
  }
161
161
  });
162
- `,c=new Blob([u],{type:"application/javascript"}),d=new Worker(URL.createObjectURL(c));d.onmessage=p=>{d.terminate(),p.data.ok?t(p.data.data):s(new Error(p.data.error))},d.onerror=p=>{d.terminate(),s(p)},d.postMessage(i)}),succeeded:o.length,failed:a.length,errors:a}}async pipeline(e){if(e.length===0)throw new Error("Pipeline requires at least one step");if(e.length===1){const r=e[0],o=this.findWorkerByName(r.worker);if(!o)throw new Error(`Worker "${r.worker}" not found`);const i=this.initWorker(o).getWorker;return new Promise((l,f)=>{i.onmessage=s=>{var u,c;i.terminate(),((u=s.data)==null?void 0:u.ok)===!1?f(new Error(s.data.error)):l((c=s.data)==null?void 0:c.data)},i.onerror=s=>{i.terminate(),f(s)};const t=r.srcData??{};i.postMessage({data:t,index:0},g(t))})}return new Promise((r,o)=>{const a=[],i=[];for(const t of e){const s=this.findWorkerByName(t.worker);if(!s){o(new Error(`Worker "${t.worker}" not found`));return}const u=new h(s.func,{mode:w.Pipeline,workerURL:s.workerURL});a.push(u.getWorker)}for(let t=0;t<a.length-1;t++)i.push(new MessageChannel);for(let t=0;t<a.length;t++){const s=[],u={};t>0&&(u.inputPort=i[t-1].port1,s.push(u.inputPort)),t<a.length-1&&(u.outputPort=i[t].port2,s.push(u.outputPort)),a[t].postMessage({__pipeline_ports__:!0,...u},s)}const l=a[a.length-1];l.onmessage=t=>{var s,u;a.forEach(c=>c.terminate()),((s=t.data)==null?void 0:s.ok)===!1?o(new Error(t.data.error)):r((u=t.data)==null?void 0:u.data)},l.onerror=t=>{a.forEach(s=>s.terminate()),o(t)};const f=e[0].srcData??{};a[0].postMessage({data:f,index:0},g(f))})}async runPersistent(e,r){const o=this.findWorkerByName(e);if(!o)throw new Error(`Worker "${e}" not found`);let a=this._persistentWorkers.get(e);return a||(a=new h(o.func,{mode:w.Persistent,workerURL:o.workerURL}).getWorker,this._persistentWorkers.set(e,a)),new Promise((i,l)=>{a.onmessage=t=>{var s,u;((s=t.data)==null?void 0:s.ok)===!1?l(new Error(t.data.error)):i((u=t.data)==null?void 0:u.data)},a.onerror=t=>{l(t)};const f={type:"run",config:r.config};r.dataset!==void 0&&(f.dataset=r.dataset),a.postMessage(f,g(f))})}release(e){const r=this._persistentWorkers.get(e);r&&(r.postMessage({type:"release"}),r.terminate(),this._persistentWorkers.delete(e))}}exports.MainWorkerFactory=E;exports.WorkerFactory=h;
162
+ `,l=new Blob([f],{type:"application/javascript"}),d=this.trackWorker(new Worker(URL.createObjectURL(l)));d.onmessage=p=>{this.terminateWorker(d),p.data.ok?t(p.data.data):s(new Error(p.data.error))},d.onerror=p=>{this.terminateWorker(d),s(p)},d.postMessage(i)}),succeeded:o.length,failed:a.length,errors:a}}async pipeline(e){if(this._isTerminated)throw new Error("MainWorkerFactory has been terminated");if(e.length===0)throw new Error("Pipeline requires at least one step");if(e.length===1){const r=e[0],o=this.findWorkerByName(r.worker);if(!o)throw new Error(`Worker "${r.worker}" not found`);const i=this.initWorker(o).getWorker;return new Promise((c,u)=>{i.onmessage=s=>{var f,l;this.terminateWorker(i),((f=s.data)==null?void 0:f.ok)===!1?u(new Error(s.data.error)):c((l=s.data)==null?void 0:l.data)},i.onerror=s=>{this.terminateWorker(i),u(s)};const t=r.srcData??{};i.postMessage({data:t,index:0},k(t))})}return new Promise((r,o)=>{const a=[],i=[];for(const t of e){const s=this.findWorkerByName(t.worker);if(!s){o(new Error(`Worker "${t.worker}" not found`));return}const f=new g(s.func,{mode:m.Pipeline,createWorker:s.createWorker}),l=this.trackWorker(f.getWorker);a.push(l)}for(let t=0;t<a.length-1;t++)i.push(new MessageChannel);for(let t=0;t<a.length;t++){const s=[],f={};t>0&&(f.inputPort=i[t-1].port1,s.push(f.inputPort)),t<a.length-1&&(f.outputPort=i[t].port2,s.push(f.outputPort)),a[t].postMessage({__pipeline_ports__:!0,...f},s)}const c=a[a.length-1];c.onmessage=t=>{var s,f;a.forEach(l=>this.terminateWorker(l)),((s=t.data)==null?void 0:s.ok)===!1?o(new Error(t.data.error)):r((f=t.data)==null?void 0:f.data)},c.onerror=t=>{a.forEach(s=>this.terminateWorker(s)),o(t)};const u=e[0].srcData??{};a[0].postMessage({data:u,index:0},k(u))})}async runPersistent(e,r){if(this._isTerminated)throw new Error("MainWorkerFactory has been terminated");const o=this.findWorkerByName(e);if(!o)throw new Error(`Worker "${e}" not found`);let a=this._persistentWorkers.get(e);if(!a){const i=new g(o.func,{mode:m.Persistent,createWorker:o.createWorker});a=this.trackWorker(i.getWorker),this._persistentWorkers.set(e,a)}return new Promise((i,c)=>{a.onmessage=t=>{var s,f;((s=t.data)==null?void 0:s.ok)===!1?c(new Error(t.data.error)):i((f=t.data)==null?void 0:f.data)},a.onerror=t=>{c(t)};const u={type:"run",config:r.config};r.dataset!==void 0&&(u.dataset=r.dataset),a.postMessage(u,k(u))})}release(e){const r=this._persistentWorkers.get(e);if(r){try{r.postMessage({type:"release"})}catch{}this.terminateWorker(r),this._persistentWorkers.delete(e)}}terminate(){this._isTerminated=!0;for(const e of this._persistentWorkers.values()){try{e.postMessage({type:"release"})}catch{}this.terminateWorker(e)}this._persistentWorkers.clear();for(const e of Array.from(this._activeWorkers))this.terminateWorker(e);this._activeWorkers.clear()}destroy(){this.terminate()}reset(){this.terminate(),this._isTerminated=!1}restart(){this.reset()}}exports.MainWorkerFactory=_;exports.WorkerFactory=g;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- var v = Object.defineProperty;
2
- var m = (n, e, r) => e in n ? v(n, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[e] = r;
3
- var k = (n, e, r) => m(n, typeof e != "symbol" ? e + "" : e, r);
1
+ var w = Object.defineProperty;
2
+ var v = (n, e, r) => e in n ? w(n, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[e] = r;
3
+ var h = (n, e, r) => v(n, typeof e != "symbol" ? e + "" : e, r);
4
4
  const y = (n) => `
5
5
  const extractTransferables = (value, seen = new Set()) => {
6
6
  if (value === null || typeof value !== 'object') return [];
@@ -24,7 +24,7 @@ self.addEventListener('message', async (event) => {
24
24
  self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
25
25
  }
26
26
  })
27
- `, P = (n) => `
27
+ `, W = (n) => `
28
28
  const extractTransferables = (value, seen = new Set()) => {
29
29
  if (value === null || typeof value !== 'object') return [];
30
30
  if (seen.has(value)) return [];
@@ -96,7 +96,7 @@ self.addEventListener('message', (event) => {
96
96
  pendingData = event.data;
97
97
  }
98
98
  });
99
- `, W = (n) => `
99
+ `, M = (n) => `
100
100
  const extractTransferables = (value, seen = new Set()) => {
101
101
  if (value === null || typeof value !== 'object') return [];
102
102
  if (seen.has(value)) return [];
@@ -153,39 +153,33 @@ self.addEventListener('message', async (event) => {
153
153
  }
154
154
  });
155
155
  `;
156
- var w = /* @__PURE__ */ ((n) => (n.Default = "default", n.Pipeline = "pipeline", n.Persistent = "persistent", n))(w || {});
157
- const M = Object.freeze({
158
- persistent: W,
159
- pipeline: P,
156
+ var m = /* @__PURE__ */ ((n) => (n.Default = "default", n.Pipeline = "pipeline", n.Persistent = "persistent", n))(m || {});
157
+ const P = Object.freeze({
158
+ persistent: M,
159
+ pipeline: W,
160
160
  default: y
161
161
  });
162
- class h {
162
+ class g {
163
163
  /**
164
- * Creates a new `Worker` from the given function.
165
- *
166
- * The function is stringified, embedded into a self-contained worker script,
167
- * converted to a `Blob` URL, and passed to the `Worker` constructor.
164
+ * Creates a new `Worker` from the given function or factory option.
168
165
  *
169
166
  * @param workerFunction - The function to run inside the worker thread.
170
167
  * Must be self-contained — it cannot reference variables from the outer
171
168
  * scope because it is serialized via `.toString()`.
172
- * @param options - Optional configuration. Set `mode` to control the
173
- * worker execution mode (default, pipeline, or persistent).
169
+ * @param options - Optional configuration containing `createWorker` or `mode`.
174
170
  */
175
171
  constructor(e, r) {
176
- k(this, "_worker");
177
- if (r != null && r.workerInstance)
178
- this._worker = r.workerInstance;
179
- else if (r != null && r.workerURL)
180
- this._worker = new Worker(r.workerURL, { type: "module" });
172
+ h(this, "_worker");
173
+ if (r != null && r.createWorker)
174
+ this._worker = r.createWorker();
181
175
  else if (e) {
182
- const o = (r == null ? void 0 : r.mode) ?? "default", a = M[o](e.toString()), i = new Blob([a], {
176
+ const o = (r == null ? void 0 : r.mode) ?? "default", a = P[o](e.toString()), i = new Blob([a], {
183
177
  type: "application/javascript"
184
178
  });
185
179
  this._worker = new Worker(URL.createObjectURL(i));
186
180
  } else
187
181
  throw new Error(
188
- "Either workerFunction or options.workerURL must be provided to WorkerFactory."
182
+ "Either workerFunction or options.createWorker must be provided to WorkerFactory."
189
183
  );
190
184
  }
191
185
  /**
@@ -203,33 +197,60 @@ class b {
203
197
  this.results = e;
204
198
  }
205
199
  }
206
- function g(n, e = /* @__PURE__ */ new Set()) {
207
- return n === null || typeof n != "object" ? [] : e.has(n) ? [] : (e.add(n), n instanceof ArrayBuffer || n instanceof MessagePort || typeof ImageBitmap < "u" && n instanceof ImageBitmap || typeof OffscreenCanvas < "u" && n instanceof OffscreenCanvas ? [n] : ArrayBuffer.isView(n) ? [n.buffer] : Array.isArray(n) ? n.flatMap((r) => g(r, e)) : Object.values(n).flatMap(
208
- (r) => g(r, e)
200
+ function k(n, e = /* @__PURE__ */ new Set()) {
201
+ return n === null || typeof n != "object" ? [] : e.has(n) ? [] : (e.add(n), n instanceof ArrayBuffer || n instanceof MessagePort || typeof ImageBitmap < "u" && n instanceof ImageBitmap || typeof OffscreenCanvas < "u" && n instanceof OffscreenCanvas ? [n] : ArrayBuffer.isView(n) ? [n.buffer] : Array.isArray(n) ? n.flatMap((r) => k(r, e)) : Object.values(n).flatMap(
202
+ (r) => k(r, e)
209
203
  ));
210
204
  }
211
- class _ {
205
+ class E {
212
206
  /**
213
207
  * Creates a new `MainWorkerFactory`.
214
208
  *
215
209
  * @param options - Configuration object containing the `workers` registry.
216
210
  */
217
211
  constructor(e) {
218
- k(this, "_workers");
219
- k(this, "_threads");
220
- k(this, "_persistentWorkers", /* @__PURE__ */ new Map());
212
+ h(this, "_workers");
213
+ h(this, "_threads");
214
+ h(this, "_persistentWorkers", /* @__PURE__ */ new Map());
215
+ h(this, "_activeWorkers", /* @__PURE__ */ new Set());
216
+ h(this, "_isTerminated", !1);
221
217
  this._workers = e.workers, this._threads = navigator.hardwareConcurrency;
222
218
  }
219
+ /**
220
+ * Returns `true` if the factory has been terminated.
221
+ */
222
+ get isTerminated() {
223
+ return this._isTerminated;
224
+ }
225
+ /**
226
+ * Registers an active worker instance for lifecycle tracking.
227
+ */
228
+ trackWorker(e) {
229
+ if (this._isTerminated)
230
+ throw e.terminate(), new Error("MainWorkerFactory has been terminated");
231
+ return this._activeWorkers.add(e), e;
232
+ }
233
+ /**
234
+ * Terminates a worker instance and removes it from tracking.
235
+ */
236
+ terminateWorker(e) {
237
+ this._activeWorkers.delete(e);
238
+ try {
239
+ e.terminate();
240
+ } catch {
241
+ }
242
+ }
223
243
  /**
224
244
  * Instantiates a {@link WorkerFactory} for the given worker configuration.
225
245
  *
226
- * @param config - The worker configuration containing `func` or `workerURL`.
246
+ * @param config - The worker configuration containing `func` or `createWorker`.
227
247
  * @returns A new `WorkerFactory` wrapping the worker.
228
248
  */
229
249
  initWorker(e) {
230
- return new h(e.func, {
231
- workerURL: e.workerURL
250
+ const r = new g(e.func, {
251
+ createWorker: e.createWorker
232
252
  });
253
+ return this.trackWorker(r.getWorker), r;
233
254
  }
234
255
  /**
235
256
  * Splits an array into up to `numChunks` evenly-sized sub-arrays.
@@ -250,13 +271,13 @@ class _ {
250
271
  partitionArray(e, r) {
251
272
  if (!e.length) return [];
252
273
  if (r <= 0) throw new Error("numChunks must be positive");
253
- const o = Math.min(r, e.length), a = Math.floor(e.length / o), i = e.length % o, f = [];
254
- let l = 0;
274
+ const o = Math.min(r, e.length), a = Math.floor(e.length / o), i = e.length % o, c = [];
275
+ let u = 0;
255
276
  for (let t = 0; t < o; t++) {
256
277
  const s = a + (t < i ? 1 : 0);
257
- f.push(e.slice(l, l + s)), l += s;
278
+ c.push(e.slice(u, u + s)), u += s;
258
279
  }
259
- return f;
280
+ return c;
260
281
  }
261
282
  /**
262
283
  * Looks up a registered worker configuration by name.
@@ -297,15 +318,17 @@ class _ {
297
318
  srcData: r,
298
319
  ...o
299
320
  }) {
321
+ if (this._isTerminated)
322
+ return Promise.reject(new Error("MainWorkerFactory has been terminated"));
300
323
  const a = this.findWorkerByName(e);
301
324
  if (!a)
302
325
  return Promise.reject(new Error(`Worker "${e}" not found`));
303
- const i = a.maxConcurrency ?? this._threads, f = !!(Array.isArray(r) && r.length > 1 && a.partition), l = f ? this.partitionArray(r, i) : r, t = this.createWorkerPromises(
326
+ const i = a.maxConcurrency ?? this._threads, c = !!(Array.isArray(r) && r.length > 1 && a.partition), u = c ? this.partitionArray(r, i) : r, t = this.createWorkerPromises(
304
327
  a,
305
328
  e,
306
- { data: l, ...o },
329
+ { data: u, ...o },
307
330
  i,
308
- f
331
+ c
309
332
  ), s = await Promise.allSettled(t);
310
333
  return new b(s);
311
334
  }
@@ -324,16 +347,16 @@ class _ {
324
347
  * @returns An array of promises, one per thread.
325
348
  */
326
349
  createWorkerPromises(e, r, o, a, i) {
327
- const { data: f, ...l } = o;
350
+ const { data: c, ...u } = o;
328
351
  return Array.from({ length: a }, (t, s) => {
329
- const u = i && Array.isArray(f) ? f[s] : f;
352
+ const f = i && Array.isArray(c) ? c[s] : c;
330
353
  return this.runWorkerWithRetry(
331
354
  {
332
355
  workerFunc: e.func,
333
- workerURL: e.workerURL,
356
+ createWorker: e.createWorker,
334
357
  workerName: r,
335
358
  index: s,
336
- data: { data: u, ...l }
359
+ data: { data: f, ...u }
337
360
  },
338
361
  e.retries
339
362
  );
@@ -375,57 +398,74 @@ class _ {
375
398
  * Any transferable objects found in the payload are moved (not copied) to
376
399
  * the worker via the `transfer` list of `postMessage`.
377
400
  *
378
- * The underlying `Worker` is always terminated after the first message,
379
- * whether it succeeded or failed.
401
+ * The underlying `Worker` is always terminated after the message completes.
380
402
  *
381
- * @param instanceConfig - Worker function, URL, name, shard index, and data.
403
+ * @param instanceConfig - Worker function, factory, name, shard index, and data.
382
404
  * @returns A promise that resolves with the worker's result.
383
405
  */
384
406
  initiateWorker({
385
407
  workerFunc: e,
386
- workerURL: r,
408
+ createWorker: r,
387
409
  workerName: o,
388
410
  index: a,
389
411
  data: i
390
412
  }) {
391
- return new Promise((f, l) => {
413
+ return new Promise((c, u) => {
392
414
  const s = this.initWorker({
393
415
  name: o,
394
416
  role: "",
395
417
  func: e,
396
- workerURL: r
418
+ createWorker: r
397
419
  }).getWorker;
398
- s.onerror = (c) => {
399
- s.terminate(), l({
420
+ s.onerror = (l) => {
421
+ this.terminateWorker(s), u({
400
422
  index: a,
401
- workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
402
- failedResult: c
423
+ workerConfigs: {
424
+ workerFunc: e,
425
+ createWorker: r,
426
+ workerName: o,
427
+ index: a,
428
+ data: i
429
+ },
430
+ failedResult: l
403
431
  });
404
- }, s.onmessage = (c) => {
432
+ }, s.onmessage = (l) => {
405
433
  var d, p;
406
- if (((d = c.data) == null ? void 0 : d.ok) === !1) {
407
- s.terminate(), l({
434
+ if (((d = l.data) == null ? void 0 : d.ok) === !1) {
435
+ this.terminateWorker(s), u({
408
436
  index: a,
409
- workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
437
+ workerConfigs: {
438
+ workerFunc: e,
439
+ createWorker: r,
440
+ workerName: o,
441
+ index: a,
442
+ data: i
443
+ },
410
444
  failedResult: new ErrorEvent("error", {
411
- message: c.data.error
445
+ message: l.data.error
412
446
  })
413
447
  });
414
448
  return;
415
449
  }
416
- f({
450
+ c({
417
451
  index: a,
418
- workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
452
+ workerConfigs: {
453
+ workerFunc: e,
454
+ createWorker: r,
455
+ workerName: o,
456
+ index: a,
457
+ data: i
458
+ },
419
459
  successResult: new MessageEvent("message", {
420
- data: (p = c.data) == null ? void 0 : p.data
460
+ data: (p = l.data) == null ? void 0 : p.data
421
461
  })
422
- }), s.terminate();
462
+ }), this.terminateWorker(s);
423
463
  };
424
- const u = {
464
+ const f = {
425
465
  index: a,
426
466
  ...Array.isArray(i) ? { data: i } : i
427
467
  };
428
- s.postMessage(u, g(u));
468
+ s.postMessage(f, k(f));
429
469
  });
430
470
  }
431
471
  /**
@@ -461,15 +501,17 @@ class _ {
461
501
  * });
462
502
  */
463
503
  async collectResults(e, r = {}) {
504
+ if (this._isTerminated)
505
+ throw new Error("MainWorkerFactory has been terminated");
464
506
  const o = e.results.filter(
465
507
  (t) => t.status === "fulfilled"
466
508
  ), a = e.results.filter(
467
509
  (t) => t.status === "rejected"
468
- ), i = o.map((t) => t.value.successResult.data), f = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
510
+ ), i = o.map((t) => t.value.successResult.data), c = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
469
511
  return {
470
512
  data: await new Promise((t, s) => {
471
- const u = `
472
- const reducer = ${f};
513
+ const f = `
514
+ const reducer = ${c};
473
515
  self.addEventListener('message', (event) => {
474
516
  try {
475
517
  const result = reducer(event.data);
@@ -478,11 +520,11 @@ class _ {
478
520
  self.postMessage({ ok: false, error: String(err) });
479
521
  }
480
522
  });
481
- `, c = new Blob([u], { type: "application/javascript" }), d = new Worker(URL.createObjectURL(c));
523
+ `, l = new Blob([f], { type: "application/javascript" }), d = this.trackWorker(new Worker(URL.createObjectURL(l)));
482
524
  d.onmessage = (p) => {
483
- d.terminate(), p.data.ok ? t(p.data.data) : s(new Error(p.data.error));
525
+ this.terminateWorker(d), p.data.ok ? t(p.data.data) : s(new Error(p.data.error));
484
526
  }, d.onerror = (p) => {
485
- d.terminate(), s(p);
527
+ this.terminateWorker(d), s(p);
486
528
  }, d.postMessage(i);
487
529
  }),
488
530
  succeeded: o.length,
@@ -517,23 +559,25 @@ class _ {
517
559
  * console.log(result); // final transformed + filtered data
518
560
  */
519
561
  async pipeline(e) {
562
+ if (this._isTerminated)
563
+ throw new Error("MainWorkerFactory has been terminated");
520
564
  if (e.length === 0)
521
565
  throw new Error("Pipeline requires at least one step");
522
566
  if (e.length === 1) {
523
567
  const r = e[0], o = this.findWorkerByName(r.worker);
524
568
  if (!o) throw new Error(`Worker "${r.worker}" not found`);
525
569
  const i = this.initWorker(o).getWorker;
526
- return new Promise((f, l) => {
570
+ return new Promise((c, u) => {
527
571
  i.onmessage = (s) => {
528
- var u, c;
529
- i.terminate(), ((u = s.data) == null ? void 0 : u.ok) === !1 ? l(new Error(s.data.error)) : f((c = s.data) == null ? void 0 : c.data);
572
+ var f, l;
573
+ this.terminateWorker(i), ((f = s.data) == null ? void 0 : f.ok) === !1 ? u(new Error(s.data.error)) : c((l = s.data) == null ? void 0 : l.data);
530
574
  }, i.onerror = (s) => {
531
- i.terminate(), l(s);
575
+ this.terminateWorker(i), u(s);
532
576
  };
533
577
  const t = r.srcData ?? {};
534
578
  i.postMessage(
535
579
  { data: t, index: 0 },
536
- g(t)
580
+ k(t)
537
581
  );
538
582
  });
539
583
  }
@@ -545,32 +589,32 @@ class _ {
545
589
  o(new Error(`Worker "${t.worker}" not found`));
546
590
  return;
547
591
  }
548
- const u = new h(s.func, {
549
- mode: w.Pipeline,
550
- workerURL: s.workerURL
551
- });
552
- a.push(u.getWorker);
592
+ const f = new g(s.func, {
593
+ mode: m.Pipeline,
594
+ createWorker: s.createWorker
595
+ }), l = this.trackWorker(f.getWorker);
596
+ a.push(l);
553
597
  }
554
598
  for (let t = 0; t < a.length - 1; t++)
555
599
  i.push(new MessageChannel());
556
600
  for (let t = 0; t < a.length; t++) {
557
- const s = [], u = {};
558
- t > 0 && (u.inputPort = i[t - 1].port1, s.push(u.inputPort)), t < a.length - 1 && (u.outputPort = i[t].port2, s.push(u.outputPort)), a[t].postMessage(
559
- { __pipeline_ports__: !0, ...u },
601
+ const s = [], f = {};
602
+ t > 0 && (f.inputPort = i[t - 1].port1, s.push(f.inputPort)), t < a.length - 1 && (f.outputPort = i[t].port2, s.push(f.outputPort)), a[t].postMessage(
603
+ { __pipeline_ports__: !0, ...f },
560
604
  s
561
605
  );
562
606
  }
563
- const f = a[a.length - 1];
564
- f.onmessage = (t) => {
565
- var s, u;
566
- a.forEach((c) => c.terminate()), ((s = t.data) == null ? void 0 : s.ok) === !1 ? o(new Error(t.data.error)) : r((u = t.data) == null ? void 0 : u.data);
567
- }, f.onerror = (t) => {
568
- a.forEach((s) => s.terminate()), o(t);
607
+ const c = a[a.length - 1];
608
+ c.onmessage = (t) => {
609
+ var s, f;
610
+ a.forEach((l) => this.terminateWorker(l)), ((s = t.data) == null ? void 0 : s.ok) === !1 ? o(new Error(t.data.error)) : r((f = t.data) == null ? void 0 : f.data);
611
+ }, c.onerror = (t) => {
612
+ a.forEach((s) => this.terminateWorker(s)), o(t);
569
613
  };
570
- const l = e[0].srcData ?? {};
614
+ const u = e[0].srcData ?? {};
571
615
  a[0].postMessage(
572
- { data: l, index: 0 },
573
- g(l)
616
+ { data: u, index: 0 },
617
+ k(u)
574
618
  );
575
619
  });
576
620
  }
@@ -609,24 +653,30 @@ class _ {
609
653
  * factory.release('transform');
610
654
  */
611
655
  async runPersistent(e, r) {
656
+ if (this._isTerminated)
657
+ throw new Error("MainWorkerFactory has been terminated");
612
658
  const o = this.findWorkerByName(e);
613
659
  if (!o) throw new Error(`Worker "${e}" not found`);
614
660
  let a = this._persistentWorkers.get(e);
615
- return a || (a = new h(o.func, {
616
- mode: w.Persistent,
617
- workerURL: o.workerURL
618
- }).getWorker, this._persistentWorkers.set(e, a)), new Promise((i, f) => {
661
+ if (!a) {
662
+ const i = new g(o.func, {
663
+ mode: m.Persistent,
664
+ createWorker: o.createWorker
665
+ });
666
+ a = this.trackWorker(i.getWorker), this._persistentWorkers.set(e, a);
667
+ }
668
+ return new Promise((i, c) => {
619
669
  a.onmessage = (t) => {
620
- var s, u;
621
- ((s = t.data) == null ? void 0 : s.ok) === !1 ? f(new Error(t.data.error)) : i((u = t.data) == null ? void 0 : u.data);
670
+ var s, f;
671
+ ((s = t.data) == null ? void 0 : s.ok) === !1 ? c(new Error(t.data.error)) : i((f = t.data) == null ? void 0 : f.data);
622
672
  }, a.onerror = (t) => {
623
- f(t);
673
+ c(t);
624
674
  };
625
- const l = {
675
+ const u = {
626
676
  type: "run",
627
677
  config: r.config
628
678
  };
629
- r.dataset !== void 0 && (l.dataset = r.dataset), a.postMessage(l, g(l));
679
+ r.dataset !== void 0 && (u.dataset = r.dataset), a.postMessage(u, k(u));
630
680
  });
631
681
  }
632
682
  /**
@@ -640,10 +690,55 @@ class _ {
640
690
  */
641
691
  release(e) {
642
692
  const r = this._persistentWorkers.get(e);
643
- r && (r.postMessage({ type: "release" }), r.terminate(), this._persistentWorkers.delete(e));
693
+ if (r) {
694
+ try {
695
+ r.postMessage({ type: "release" });
696
+ } catch {
697
+ }
698
+ this.terminateWorker(r), this._persistentWorkers.delete(e);
699
+ }
700
+ }
701
+ /**
702
+ * Terminates the factory and all active and persistent worker instances.
703
+ *
704
+ * Calling `terminate()` immediately stops all running worker threads, releases
705
+ * cached persistent workers, and clears all internal worker state.
706
+ */
707
+ terminate() {
708
+ this._isTerminated = !0;
709
+ for (const e of this._persistentWorkers.values()) {
710
+ try {
711
+ e.postMessage({ type: "release" });
712
+ } catch {
713
+ }
714
+ this.terminateWorker(e);
715
+ }
716
+ this._persistentWorkers.clear();
717
+ for (const e of Array.from(this._activeWorkers))
718
+ this.terminateWorker(e);
719
+ this._activeWorkers.clear();
720
+ }
721
+ /**
722
+ * Alias for {@link terminate}. Terminates the factory and all worker instances.
723
+ */
724
+ destroy() {
725
+ this.terminate();
726
+ }
727
+ /**
728
+ * Resets the factory by terminating all active and persistent workers
729
+ * and resetting the factory state, allowing new worker instances to be initiated.
730
+ */
731
+ reset() {
732
+ this.terminate(), this._isTerminated = !1;
733
+ }
734
+ /**
735
+ * Alias for {@link reset}. Resets the factory state to initiate new worker instances.
736
+ */
737
+ restart() {
738
+ this.reset();
644
739
  }
645
740
  }
646
741
  export {
647
- _ as MainWorkerFactory,
648
- h as WorkerFactory
742
+ E as MainWorkerFactory,
743
+ g as WorkerFactory
649
744
  };
@@ -1,10 +1,10 @@
1
1
  import { CollectOptions, CollectedResult, PipelineStep, WorkerConfig, WorkerConfigMap, WorkerDataParam, WorkerFunction, WorkerReturnType, TypedSettledResults } from './types.ts';
2
2
  /**
3
3
  * Recursively collects all Transferable objects from a value.
4
- * Transferables (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas)
4
+ * Transferable (ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas)
5
5
  * are zero-copy — they are moved to the worker instead of cloned.
6
6
  */
7
- export declare function extractTransferables(value: unknown, seen?: Set<object>): Transferable[];
7
+ export declare function extractTransferable(value: unknown, seen?: Set<object>): Transferable[];
8
8
  /**
9
9
  * Central orchestrator for running typed Web Workers in parallel.
10
10
  *
@@ -36,6 +36,8 @@ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFun
36
36
  private readonly _workers;
37
37
  private readonly _threads;
38
38
  private readonly _persistentWorkers;
39
+ private readonly _activeWorkers;
40
+ private _isTerminated;
39
41
  /**
40
42
  * Creates a new `MainWorkerFactory`.
41
43
  *
@@ -44,10 +46,22 @@ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFun
44
46
  constructor(options: {
45
47
  workers: TConfigs;
46
48
  });
49
+ /**
50
+ * Returns `true` if the factory has been terminated.
51
+ */
52
+ get isTerminated(): boolean;
53
+ /**
54
+ * Registers an active worker instance for lifecycle tracking.
55
+ */
56
+ private trackWorker;
57
+ /**
58
+ * Terminates a worker instance and removes it from tracking.
59
+ */
60
+ private terminateWorker;
47
61
  /**
48
62
  * Instantiates a {@link WorkerFactory} for the given worker configuration.
49
63
  *
50
- * @param config - The worker configuration containing `func` or `workerURL`.
64
+ * @param config - The worker configuration containing `func` or `createWorker`.
51
65
  * @returns A new `WorkerFactory` wrapping the worker.
52
66
  */
53
67
  private initWorker;
@@ -144,10 +158,9 @@ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFun
144
158
  * Any transferable objects found in the payload are moved (not copied) to
145
159
  * the worker via the `transfer` list of `postMessage`.
146
160
  *
147
- * The underlying `Worker` is always terminated after the first message,
148
- * whether it succeeded or failed.
161
+ * The underlying `Worker` is always terminated after the message completes.
149
162
  *
150
- * @param instanceConfig - Worker function, URL, name, shard index, and data.
163
+ * @param instanceConfig - Worker function, factory, name, shard index, and data.
151
164
  * @returns A promise that resolves with the worker's result.
152
165
  */
153
166
  private initiateWorker;
@@ -259,5 +272,25 @@ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFun
259
272
  * @param workerName - Name of the persistent worker to release.
260
273
  */
261
274
  release(workerName: string): void;
275
+ /**
276
+ * Terminates the factory and all active and persistent worker instances.
277
+ *
278
+ * Calling `terminate()` immediately stops all running worker threads, releases
279
+ * cached persistent workers, and clears all internal worker state.
280
+ */
281
+ terminate(): void;
282
+ /**
283
+ * Alias for {@link terminate}. Terminates the factory and all worker instances.
284
+ */
285
+ destroy(): void;
286
+ /**
287
+ * Resets the factory by terminating all active and persistent workers
288
+ * and resetting the factory state, allowing new worker instances to be initiated.
289
+ */
290
+ reset(): void;
291
+ /**
292
+ * Alias for {@link reset}. Resets the factory state to initiate new worker instances.
293
+ */
294
+ restart(): void;
262
295
  }
263
296
  export default MainWorkerFactory;
@@ -23,13 +23,14 @@ export interface WorkerConfig<TFunc extends WorkerFunction = WorkerFunction> {
23
23
  name: WorkerName;
24
24
  /** Human-readable role label (e.g. `'compute'`, `'transform'`). */
25
25
  role: WorkerRole;
26
- /** The worker function that will be serialised and run in a thread. Optional if `workerURL` is provided. */
26
+ /** The worker function that will be serialised and run in a thread. Optional if `createWorker` is provided. */
27
27
  func?: TFunc;
28
28
  /**
29
- * The worker script URL (or `URL` object created via `new URL(..., import.meta.url)`).
30
- * Enables module bundlers like Webpack 5, Vite, Rollup, and Parcel to bundle worker code and its dependencies.
29
+ * Factory function returning a native `Worker` instance (e.g. `() => new Worker(new URL('./worker.ts', import.meta.url))`).
30
+ * Enables bundlers like Webpack 5, Vite, Rollup, and Parcel to statically analyze and bundle worker code into individual chunks,
31
+ * while allowing `MainWorkerFactory` to scale concurrency and manage worker thread lifecycles.
31
32
  */
32
- workerURL?: string | URL;
33
+ createWorker?: () => Worker;
33
34
  /**
34
35
  * Maximum number of parallel threads to spawn for this worker.
35
36
  * Defaults to `navigator.hardwareConcurrency` when omitted.
@@ -103,10 +104,10 @@ export interface MainWorkerFactoryWorker extends WorkerConfig {
103
104
  export interface WorkerInstanceConfig<TFunc extends WorkerFunction = WorkerFunction> {
104
105
  /** Name of the parent worker config, used in logs and error objects. */
105
106
  workerName: WorkerName;
106
- /** The function serialised and executed inside the thread (optional if `workerURL` is set). */
107
+ /** The function serialised and executed inside the thread (optional if `createWorker` is set). */
107
108
  workerFunc?: TFunc;
108
- /** Worker script URL (string or URL object). */
109
- workerURL?: string | URL;
109
+ /** Factory function returning a Worker instance. */
110
+ createWorker?: () => Worker;
110
111
  /** Zero-based shard index assigned to this thread. */
111
112
  index: number;
112
113
  /** The data payload (full or partitioned shard) sent to the thread. */
@@ -7,10 +7,8 @@ export declare enum WorkerMode {
7
7
  export interface WorkerFactoryOptions {
8
8
  /** The worker execution mode. Defaults to `WorkerMode.Default`. */
9
9
  mode?: WorkerMode;
10
- /** The worker instance to use instead of creating a worker from a function. */
11
- workerInstance?: Worker;
12
- /** The worker URL to use instead of creating a worker from a function. */
13
- workerURL?: string | URL;
10
+ /** Factory function returning a native `Worker` instance. */
11
+ createWorker?: () => Worker;
14
12
  }
15
13
  /**
16
14
  * Low-level factory that serializes a {@link WorkerFunction} into a Blob URL
@@ -36,16 +34,12 @@ export interface WorkerFactoryOptions {
36
34
  declare class WorkerFactory {
37
35
  readonly _worker: Worker;
38
36
  /**
39
- * Creates a new `Worker` from the given function.
40
- *
41
- * The function is stringified, embedded into a self-contained worker script,
42
- * converted to a `Blob` URL, and passed to the `Worker` constructor.
37
+ * Creates a new `Worker` from the given function or factory option.
43
38
  *
44
39
  * @param workerFunction - The function to run inside the worker thread.
45
40
  * Must be self-contained — it cannot reference variables from the outer
46
41
  * scope because it is serialized via `.toString()`.
47
- * @param options - Optional configuration. Set `mode` to control the
48
- * worker execution mode (default, pipeline, or persistent).
42
+ * @param options - Optional configuration containing `createWorker` or `mode`.
49
43
  */
50
44
  constructor(workerFunction?: WorkerFunction, options?: WorkerFactoryOptions);
51
45
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@offmain/workerkit",
3
- "version": "0.12.3",
3
+ "version": "0.13.0",
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",