@offmain/workerkit 0.12.2 → 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.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 h = (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,37 +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 k {
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
172
  h(this, "_worker");
177
- if (r != null && r.workerURL)
178
- this._worker = new Worker(r.workerURL, { type: "module" });
173
+ if (r != null && r.createWorker)
174
+ this._worker = r.createWorker();
179
175
  else if (e) {
180
- 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], {
181
177
  type: "application/javascript"
182
178
  });
183
179
  this._worker = new Worker(URL.createObjectURL(i));
184
180
  } else
185
181
  throw new Error(
186
- "Either workerFunction or options.workerURL must be provided to WorkerFactory."
182
+ "Either workerFunction or options.createWorker must be provided to WorkerFactory."
187
183
  );
188
184
  }
189
185
  /**
@@ -201,12 +197,12 @@ class b {
201
197
  this.results = e;
202
198
  }
203
199
  }
204
- function g(n, e = /* @__PURE__ */ new Set()) {
205
- 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(
206
- (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)
207
203
  ));
208
204
  }
209
- class A {
205
+ class E {
210
206
  /**
211
207
  * Creates a new `MainWorkerFactory`.
212
208
  *
@@ -216,18 +212,45 @@ class A {
216
212
  h(this, "_workers");
217
213
  h(this, "_threads");
218
214
  h(this, "_persistentWorkers", /* @__PURE__ */ new Map());
215
+ h(this, "_activeWorkers", /* @__PURE__ */ new Set());
216
+ h(this, "_isTerminated", !1);
219
217
  this._workers = e.workers, this._threads = navigator.hardwareConcurrency;
220
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
+ }
221
243
  /**
222
244
  * Instantiates a {@link WorkerFactory} for the given worker configuration.
223
245
  *
224
- * @param config - The worker configuration containing `func` or `workerURL`.
246
+ * @param config - The worker configuration containing `func` or `createWorker`.
225
247
  * @returns A new `WorkerFactory` wrapping the worker.
226
248
  */
227
249
  initWorker(e) {
228
- return new k(e.func, {
229
- workerURL: e.workerURL
250
+ const r = new g(e.func, {
251
+ createWorker: e.createWorker
230
252
  });
253
+ return this.trackWorker(r.getWorker), r;
231
254
  }
232
255
  /**
233
256
  * Splits an array into up to `numChunks` evenly-sized sub-arrays.
@@ -248,13 +271,13 @@ class A {
248
271
  partitionArray(e, r) {
249
272
  if (!e.length) return [];
250
273
  if (r <= 0) throw new Error("numChunks must be positive");
251
- const o = Math.min(r, e.length), a = Math.floor(e.length / o), i = e.length % o, f = [];
252
- 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;
253
276
  for (let t = 0; t < o; t++) {
254
277
  const s = a + (t < i ? 1 : 0);
255
- f.push(e.slice(l, l + s)), l += s;
278
+ c.push(e.slice(u, u + s)), u += s;
256
279
  }
257
- return f;
280
+ return c;
258
281
  }
259
282
  /**
260
283
  * Looks up a registered worker configuration by name.
@@ -295,15 +318,17 @@ class A {
295
318
  srcData: r,
296
319
  ...o
297
320
  }) {
321
+ if (this._isTerminated)
322
+ return Promise.reject(new Error("MainWorkerFactory has been terminated"));
298
323
  const a = this.findWorkerByName(e);
299
324
  if (!a)
300
325
  return Promise.reject(new Error(`Worker "${e}" not found`));
301
- 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(
302
327
  a,
303
328
  e,
304
- { data: l, ...o },
329
+ { data: u, ...o },
305
330
  i,
306
- f
331
+ c
307
332
  ), s = await Promise.allSettled(t);
308
333
  return new b(s);
309
334
  }
@@ -322,16 +347,16 @@ class A {
322
347
  * @returns An array of promises, one per thread.
323
348
  */
324
349
  createWorkerPromises(e, r, o, a, i) {
325
- const { data: f, ...l } = o;
350
+ const { data: c, ...u } = o;
326
351
  return Array.from({ length: a }, (t, s) => {
327
- const u = i && Array.isArray(f) ? f[s] : f;
352
+ const f = i && Array.isArray(c) ? c[s] : c;
328
353
  return this.runWorkerWithRetry(
329
354
  {
330
355
  workerFunc: e.func,
331
- workerURL: e.workerURL,
356
+ createWorker: e.createWorker,
332
357
  workerName: r,
333
358
  index: s,
334
- data: { data: u, ...l }
359
+ data: { data: f, ...u }
335
360
  },
336
361
  e.retries
337
362
  );
@@ -373,57 +398,74 @@ class A {
373
398
  * Any transferable objects found in the payload are moved (not copied) to
374
399
  * the worker via the `transfer` list of `postMessage`.
375
400
  *
376
- * The underlying `Worker` is always terminated after the first message,
377
- * whether it succeeded or failed.
401
+ * The underlying `Worker` is always terminated after the message completes.
378
402
  *
379
- * @param instanceConfig - Worker function, URL, name, shard index, and data.
403
+ * @param instanceConfig - Worker function, factory, name, shard index, and data.
380
404
  * @returns A promise that resolves with the worker's result.
381
405
  */
382
406
  initiateWorker({
383
407
  workerFunc: e,
384
- workerURL: r,
408
+ createWorker: r,
385
409
  workerName: o,
386
410
  index: a,
387
411
  data: i
388
412
  }) {
389
- return new Promise((f, l) => {
413
+ return new Promise((c, u) => {
390
414
  const s = this.initWorker({
391
415
  name: o,
392
416
  role: "",
393
417
  func: e,
394
- workerURL: r
418
+ createWorker: r
395
419
  }).getWorker;
396
- s.onerror = (c) => {
397
- s.terminate(), l({
420
+ s.onerror = (l) => {
421
+ this.terminateWorker(s), u({
398
422
  index: a,
399
- workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
400
- failedResult: c
423
+ workerConfigs: {
424
+ workerFunc: e,
425
+ createWorker: r,
426
+ workerName: o,
427
+ index: a,
428
+ data: i
429
+ },
430
+ failedResult: l
401
431
  });
402
- }, s.onmessage = (c) => {
432
+ }, s.onmessage = (l) => {
403
433
  var d, p;
404
- if (((d = c.data) == null ? void 0 : d.ok) === !1) {
405
- s.terminate(), l({
434
+ if (((d = l.data) == null ? void 0 : d.ok) === !1) {
435
+ this.terminateWorker(s), u({
406
436
  index: a,
407
- 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
+ },
408
444
  failedResult: new ErrorEvent("error", {
409
- message: c.data.error
445
+ message: l.data.error
410
446
  })
411
447
  });
412
448
  return;
413
449
  }
414
- f({
450
+ c({
415
451
  index: a,
416
- 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
+ },
417
459
  successResult: new MessageEvent("message", {
418
- data: (p = c.data) == null ? void 0 : p.data
460
+ data: (p = l.data) == null ? void 0 : p.data
419
461
  })
420
- }), s.terminate();
462
+ }), this.terminateWorker(s);
421
463
  };
422
- const u = {
464
+ const f = {
423
465
  index: a,
424
466
  ...Array.isArray(i) ? { data: i } : i
425
467
  };
426
- s.postMessage(u, g(u));
468
+ s.postMessage(f, k(f));
427
469
  });
428
470
  }
429
471
  /**
@@ -459,15 +501,17 @@ class A {
459
501
  * });
460
502
  */
461
503
  async collectResults(e, r = {}) {
504
+ if (this._isTerminated)
505
+ throw new Error("MainWorkerFactory has been terminated");
462
506
  const o = e.results.filter(
463
507
  (t) => t.status === "fulfilled"
464
508
  ), a = e.results.filter(
465
509
  (t) => t.status === "rejected"
466
- ), 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()";
467
511
  return {
468
512
  data: await new Promise((t, s) => {
469
- const u = `
470
- const reducer = ${f};
513
+ const f = `
514
+ const reducer = ${c};
471
515
  self.addEventListener('message', (event) => {
472
516
  try {
473
517
  const result = reducer(event.data);
@@ -476,11 +520,11 @@ class A {
476
520
  self.postMessage({ ok: false, error: String(err) });
477
521
  }
478
522
  });
479
- `, 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)));
480
524
  d.onmessage = (p) => {
481
- 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));
482
526
  }, d.onerror = (p) => {
483
- d.terminate(), s(p);
527
+ this.terminateWorker(d), s(p);
484
528
  }, d.postMessage(i);
485
529
  }),
486
530
  succeeded: o.length,
@@ -515,23 +559,25 @@ class A {
515
559
  * console.log(result); // final transformed + filtered data
516
560
  */
517
561
  async pipeline(e) {
562
+ if (this._isTerminated)
563
+ throw new Error("MainWorkerFactory has been terminated");
518
564
  if (e.length === 0)
519
565
  throw new Error("Pipeline requires at least one step");
520
566
  if (e.length === 1) {
521
567
  const r = e[0], o = this.findWorkerByName(r.worker);
522
568
  if (!o) throw new Error(`Worker "${r.worker}" not found`);
523
569
  const i = this.initWorker(o).getWorker;
524
- return new Promise((f, l) => {
570
+ return new Promise((c, u) => {
525
571
  i.onmessage = (s) => {
526
- var u, c;
527
- 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);
528
574
  }, i.onerror = (s) => {
529
- i.terminate(), l(s);
575
+ this.terminateWorker(i), u(s);
530
576
  };
531
577
  const t = r.srcData ?? {};
532
578
  i.postMessage(
533
579
  { data: t, index: 0 },
534
- g(t)
580
+ k(t)
535
581
  );
536
582
  });
537
583
  }
@@ -543,32 +589,32 @@ class A {
543
589
  o(new Error(`Worker "${t.worker}" not found`));
544
590
  return;
545
591
  }
546
- const u = new k(s.func, {
547
- mode: w.Pipeline,
548
- workerURL: s.workerURL
549
- });
550
- 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);
551
597
  }
552
598
  for (let t = 0; t < a.length - 1; t++)
553
599
  i.push(new MessageChannel());
554
600
  for (let t = 0; t < a.length; t++) {
555
- const s = [], u = {};
556
- 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(
557
- { __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 },
558
604
  s
559
605
  );
560
606
  }
561
- const f = a[a.length - 1];
562
- f.onmessage = (t) => {
563
- var s, u;
564
- 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);
565
- }, f.onerror = (t) => {
566
- 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);
567
613
  };
568
- const l = e[0].srcData ?? {};
614
+ const u = e[0].srcData ?? {};
569
615
  a[0].postMessage(
570
- { data: l, index: 0 },
571
- g(l)
616
+ { data: u, index: 0 },
617
+ k(u)
572
618
  );
573
619
  });
574
620
  }
@@ -607,24 +653,30 @@ class A {
607
653
  * factory.release('transform');
608
654
  */
609
655
  async runPersistent(e, r) {
656
+ if (this._isTerminated)
657
+ throw new Error("MainWorkerFactory has been terminated");
610
658
  const o = this.findWorkerByName(e);
611
659
  if (!o) throw new Error(`Worker "${e}" not found`);
612
660
  let a = this._persistentWorkers.get(e);
613
- return a || (a = new k(o.func, {
614
- mode: w.Persistent,
615
- workerURL: o.workerURL
616
- }).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) => {
617
669
  a.onmessage = (t) => {
618
- var s, u;
619
- ((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);
620
672
  }, a.onerror = (t) => {
621
- f(t);
673
+ c(t);
622
674
  };
623
- const l = {
675
+ const u = {
624
676
  type: "run",
625
677
  config: r.config
626
678
  };
627
- 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));
628
680
  });
629
681
  }
630
682
  /**
@@ -638,10 +690,55 @@ class A {
638
690
  */
639
691
  release(e) {
640
692
  const r = this._persistentWorkers.get(e);
641
- 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();
642
739
  }
643
740
  }
644
741
  export {
645
- A as MainWorkerFactory,
646
- k as WorkerFactory
742
+ E as MainWorkerFactory,
743
+ g as WorkerFactory
647
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,8 +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 URL to use instead of creating a worker from a function. */
11
- workerURL?: string | URL;
10
+ /** Factory function returning a native `Worker` instance. */
11
+ createWorker?: () => Worker;
12
12
  }
13
13
  /**
14
14
  * Low-level factory that serializes a {@link WorkerFunction} into a Blob URL
@@ -34,16 +34,12 @@ export interface WorkerFactoryOptions {
34
34
  declare class WorkerFactory {
35
35
  readonly _worker: Worker;
36
36
  /**
37
- * Creates a new `Worker` from the given function.
38
- *
39
- * The function is stringified, embedded into a self-contained worker script,
40
- * converted to a `Blob` URL, and passed to the `Worker` constructor.
37
+ * Creates a new `Worker` from the given function or factory option.
41
38
  *
42
39
  * @param workerFunction - The function to run inside the worker thread.
43
40
  * Must be self-contained — it cannot reference variables from the outer
44
41
  * scope because it is serialized via `.toString()`.
45
- * @param options - Optional configuration. Set `mode` to control the
46
- * worker execution mode (default, pipeline, or persistent).
42
+ * @param options - Optional configuration containing `createWorker` or `mode`.
47
43
  */
48
44
  constructor(workerFunction?: WorkerFunction, options?: WorkerFactoryOptions);
49
45
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@offmain/workerkit",
3
- "version": "0.12.2",
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",