@offmain/workerkit 0.11.0 → 0.12.3

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,14 +57,68 @@ 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 |
65
- | `maxConcurrency` | `number` | `navigator.hardwareConcurrency` | Max parallel worker instances defaults to the number of logical CPU cores reported by the browser |
66
- | `retries` | `number` | `0` | How many times to retry a failed shard before marking it as rejected |
67
- | `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 `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 |
69
+
70
+ ---
71
+
72
+ ## Module Bundler Integration (`workerURL`)
73
+
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
+
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.
77
+
78
+ ### Dedicated Worker Script
79
+
80
+ ```ts
81
+ // transform-data.worker.ts
82
+ import { format } from 'date-fns';
83
+ import { t } from './i18n.ts';
84
+
85
+ self.addEventListener('message', (event) => {
86
+ try {
87
+ const { items, locale } = event.data.data;
88
+ const result = items.map((item: any) => ({
89
+ ...item,
90
+ formattedDate: format(new Date(item.timestamp), 'yyyy-MM-dd'),
91
+ label: t('transaction', locale),
92
+ }));
93
+
94
+ self.postMessage({ ok: true, data: result });
95
+ } catch (err) {
96
+ self.postMessage({ ok: false, error: (err as Error).message });
97
+ }
98
+ });
99
+ ```
100
+
101
+ ### Registering with `workerURL`
102
+
103
+ ```ts
104
+ import { MainWorkerFactory } from '@offmain/workerkit';
105
+
106
+ const factory = new MainWorkerFactory({
107
+ workers: [
108
+ {
109
+ name: 'transformData',
110
+ 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,
114
+ },
115
+ ] as const,
116
+ });
117
+
118
+ const settled = await factory.runWorker('transformData', {
119
+ srcData: { locale: 'es', items: [{ timestamp: Date.now() }] },
120
+ });
121
+ ```
68
122
 
69
123
  ---
70
124
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var y=Object.defineProperty;var m=(s,e,t)=>e in s?y(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var h=(s,e,t)=>m(s,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=s=>`
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=>`
2
2
  const extractTransferables = (value, seen = new Set()) => {
3
3
  if (value === null || typeof value !== 'object') return [];
4
4
  if (seen.has(value)) return [];
@@ -15,13 +15,13 @@ const extractTransferables = (value, seen = new Set()) => {
15
15
 
16
16
  self.addEventListener('message', async (event) => {
17
17
  try {
18
- const output = await ${s}(event.data);
18
+ const output = await ${n}(event.data);
19
19
  self.postMessage({ ok: true, data: output }, extractTransferables(output));
20
20
  } catch (err) {
21
21
  self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
22
22
  }
23
23
  })
24
- `,P=s=>`
24
+ `,P=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 [];
@@ -36,7 +36,7 @@ const extractTransferables = (value, seen = new Set()) => {
36
36
  return Object.values(value).flatMap(v => extractTransferables(v, seen));
37
37
  };
38
38
 
39
- const workerFn = ${s};
39
+ const workerFn = ${n};
40
40
  let outputPort = null;
41
41
  let inputPort = null;
42
42
  let pendingData = null;
@@ -93,7 +93,7 @@ self.addEventListener('message', (event) => {
93
93
  pendingData = event.data;
94
94
  }
95
95
  });
96
- `,M=s=>`
96
+ `,W=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 [];
@@ -108,7 +108,7 @@ const extractTransferables = (value, seen = new Set()) => {
108
108
  return Object.values(value).flatMap(v => extractTransferables(v, seen));
109
109
  };
110
110
 
111
- const workerFn = ${s};
111
+ const workerFn = ${n};
112
112
  let cachedDataset = null;
113
113
 
114
114
  self.addEventListener('message', async (event) => {
@@ -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 v=(s=>(s.Default="default",s.Pipeline="pipeline",s.Persistent="persistent",s))(v||{});const W=Object.freeze({persistent:M,pipeline:P,default:w});class k{constructor(e,t){h(this,"_worker");const n=(t==null?void 0:t.mode)??"default",a=W[n](e.toString()),u=new Blob([a],{type:"application/javascript"});this._worker=new Worker(URL.createObjectURL(u))}get getWorker(){return this._worker}}class b{constructor(e){this.results=e}}function p(s,e=new Set){return s===null||typeof s!="object"?[]:e.has(s)?[]:(e.add(s),s instanceof ArrayBuffer||s instanceof MessagePort||typeof ImageBitmap<"u"&&s instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&s instanceof OffscreenCanvas?[s]:ArrayBuffer.isView(s)?[s.buffer]:Array.isArray(s)?s.flatMap(t=>p(t,e)):Object.values(s).flatMap(t=>p(t,e)))}class A{constructor(e){h(this,"_workers");h(this,"_threads");h(this,"_persistentWorkers",new Map);this._workers=e.workers,this._threads=navigator.hardwareConcurrency}initWorker(e){return new k(e)}partitionArray(e,t){if(!e.length)return[];if(t<=0)throw new Error("numChunks must be positive");const n=Math.min(t,e.length),a=Math.floor(e.length/n),u=e.length%n,f=[];let l=0;for(let r=0;r<n;r++){const o=a+(r<u?1:0);f.push(e.slice(l,l+o)),l+=o}return f}findWorkerByName(e){return this._workers.find(t=>t.name===e)}async runWorker(e,{srcData:t,...n}){const a=this.findWorkerByName(e);if(!a)return Promise.reject(new Error(`Worker "${e}" not found`));const u=a.maxConcurrency??this._threads,f=!!(Array.isArray(t)&&t.length>1&&a.partition),l=f?this.partitionArray(t,u):t,r=this.createWorkerPromises(a,e,{data:l,...n},u,f),o=await Promise.allSettled(r);return new b(o)}createWorkerPromises(e,t,n,a,u){const{data:f,...l}=n;return Array.from({length:a},(r,o)=>{const i=u&&Array.isArray(f)?f[o]:f;return this.runWorkerWithRetry({workerFunc:e.func,workerName:t,index:o,data:{data:i,...l}},e.retries)})}async runWorkerWithRetry(e,t=2){try{return await this.initiateWorker(e)}catch(n){if(t>0)return console.error(`Worker ${e.index} failed, retrying (${t} left):`,n),this.runWorkerWithRetry(e,t-1);throw console.error("Worker failed after all retries:",n),n}}initiateWorker({workerFunc:e,workerName:t,index:n,data:a}){return new Promise((u,f)=>{const r=this.initWorker(e).getWorker;r.onerror=i=>{r.terminate(),f({index:n,workerConfigs:{workerFunc:e,workerName:t,index:n,data:a},failedResult:i})},r.onmessage=i=>{var c,d;if(((c=i.data)==null?void 0:c.ok)===!1){r.terminate(),f({index:n,workerConfigs:{workerFunc:e,workerName:t,index:n,data:a},failedResult:new ErrorEvent("error",{message:i.data.error})});return}u({index:n,workerConfigs:{workerFunc:e,workerName:t,index:n,data:a},successResult:new MessageEvent("message",{data:(d=i.data)==null?void 0:d.data})}),r.terminate()};const o={index:n,...Array.isArray(a)?{data:a}:a};r.postMessage(o,p(o))})}async collectResults(e,t={}){const n=e.results.filter(r=>r.status==="fulfilled"),a=e.results.filter(r=>r.status==="rejected"),u=n.map(r=>r.value.successResult.data),f=t.reducer?t.reducer.toString():"(shards) => shards.flat()";return{data:await new Promise((r,o)=>{const i=`
153
- const reducer = ${f};
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};
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([i],{type:"application/javascript"}),d=new Worker(URL.createObjectURL(c));d.onmessage=g=>{d.terminate(),g.data.ok?r(g.data.data):o(new Error(g.data.error))},d.onerror=g=>{d.terminate(),o(g)},d.postMessage(u)}),succeeded:n.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 t=e[0],n=this.findWorkerByName(t.worker);if(!n)throw new Error(`Worker "${t.worker}" not found`);const u=this.initWorker(n.func).getWorker;return new Promise((f,l)=>{u.onmessage=o=>{var i,c;u.terminate(),((i=o.data)==null?void 0:i.ok)===!1?l(new Error(o.data.error)):f((c=o.data)==null?void 0:c.data)},u.onerror=o=>{u.terminate(),l(o)};const r=t.srcData??{};u.postMessage({data:r,index:0},p(r))})}return new Promise((t,n)=>{const a=[],u=[];for(const r of e){const o=this.findWorkerByName(r.worker);if(!o){n(new Error(`Worker "${r.worker}" not found`));return}const i=new k(o.func,{mode:v.Pipeline});a.push(i.getWorker)}for(let r=0;r<a.length-1;r++)u.push(new MessageChannel);for(let r=0;r<a.length;r++){const o=[],i={};r>0&&(i.inputPort=u[r-1].port1,o.push(i.inputPort)),r<a.length-1&&(i.outputPort=u[r].port2,o.push(i.outputPort)),a[r].postMessage({__pipeline_ports__:!0,...i},o)}const f=a[a.length-1];f.onmessage=r=>{var o,i;a.forEach(c=>c.terminate()),((o=r.data)==null?void 0:o.ok)===!1?n(new Error(r.data.error)):t((i=r.data)==null?void 0:i.data)},f.onerror=r=>{a.forEach(o=>o.terminate()),n(r)};const l=e[0].srcData??{};a[0].postMessage({data:l,index:0},p(l))})}async runPersistent(e,t){const n=this.findWorkerByName(e);if(!n)throw new Error(`Worker "${e}" not found`);let a=this._persistentWorkers.get(e);return a||(a=new k(n.func,{mode:v.Persistent}).getWorker,this._persistentWorkers.set(e,a)),new Promise((u,f)=>{a.onmessage=r=>{var o,i;((o=r.data)==null?void 0:o.ok)===!1?f(new Error(r.data.error)):u((i=r.data)==null?void 0:i.data)},a.onerror=r=>{f(r)};const l={type:"run",config:t.config};t.dataset!==void 0&&(l.dataset=t.dataset),a.postMessage(l,p(l))})}release(e){const t=this._persistentWorkers.get(e);t&&(t.postMessage({type:"release"}),t.terminate(),this._persistentWorkers.delete(e))}}exports.MainWorkerFactory=A;exports.WorkerFactory=k;
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;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- var w = Object.defineProperty;
2
- var m = (s, e, t) => e in s ? w(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
3
- var h = (s, e, t) => m(s, typeof e != "symbol" ? e + "" : e, t);
4
- const y = (s) => `
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);
4
+ const y = (n) => `
5
5
  const extractTransferables = (value, seen = new Set()) => {
6
6
  if (value === null || typeof value !== 'object') return [];
7
7
  if (seen.has(value)) return [];
@@ -18,13 +18,13 @@ const extractTransferables = (value, seen = new Set()) => {
18
18
 
19
19
  self.addEventListener('message', async (event) => {
20
20
  try {
21
- const output = await ${s}(event.data);
21
+ const output = await ${n}(event.data);
22
22
  self.postMessage({ ok: true, data: output }, extractTransferables(output));
23
23
  } catch (err) {
24
24
  self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
25
25
  }
26
26
  })
27
- `, P = (s) => `
27
+ `, P = (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 [];
@@ -39,7 +39,7 @@ const extractTransferables = (value, seen = new Set()) => {
39
39
  return Object.values(value).flatMap(v => extractTransferables(v, seen));
40
40
  };
41
41
 
42
- const workerFn = ${s};
42
+ const workerFn = ${n};
43
43
  let outputPort = null;
44
44
  let inputPort = null;
45
45
  let pendingData = null;
@@ -96,7 +96,7 @@ self.addEventListener('message', (event) => {
96
96
  pendingData = event.data;
97
97
  }
98
98
  });
99
- `, M = (s) => `
99
+ `, W = (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 [];
@@ -111,7 +111,7 @@ const extractTransferables = (value, seen = new Set()) => {
111
111
  return Object.values(value).flatMap(v => extractTransferables(v, seen));
112
112
  };
113
113
 
114
- const workerFn = ${s};
114
+ const workerFn = ${n};
115
115
  let cachedDataset = null;
116
116
 
117
117
  self.addEventListener('message', async (event) => {
@@ -153,13 +153,13 @@ self.addEventListener('message', async (event) => {
153
153
  }
154
154
  });
155
155
  `;
156
- var v = /* @__PURE__ */ ((s) => (s.Default = "default", s.Pipeline = "pipeline", s.Persistent = "persistent", s))(v || {});
157
- const W = Object.freeze({
158
- persistent: M,
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
159
  pipeline: P,
160
160
  default: y
161
161
  });
162
- class k {
162
+ class h {
163
163
  /**
164
164
  * Creates a new `Worker` from the given function.
165
165
  *
@@ -168,16 +168,25 @@ class k {
168
168
  *
169
169
  * @param workerFunction - The function to run inside the worker thread.
170
170
  * Must be self-contained — it cannot reference variables from the outer
171
- * scope because it is serialised via `.toString()`.
171
+ * scope because it is serialized via `.toString()`.
172
172
  * @param options - Optional configuration. Set `mode` to control the
173
173
  * worker execution mode (default, pipeline, or persistent).
174
174
  */
175
- constructor(e, t) {
176
- h(this, "_worker");
177
- const n = (t == null ? void 0 : t.mode) ?? "default", a = W[n](e.toString()), f = new Blob([a], {
178
- type: "application/javascript"
179
- });
180
- this._worker = new Worker(URL.createObjectURL(f));
175
+ 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" });
181
+ else if (e) {
182
+ const o = (r == null ? void 0 : r.mode) ?? "default", a = M[o](e.toString()), i = new Blob([a], {
183
+ type: "application/javascript"
184
+ });
185
+ this._worker = new Worker(URL.createObjectURL(i));
186
+ } else
187
+ throw new Error(
188
+ "Either workerFunction or options.workerURL must be provided to WorkerFactory."
189
+ );
181
190
  }
182
191
  /**
183
192
  * Returns the underlying native `Worker` instance.
@@ -194,31 +203,33 @@ class b {
194
203
  this.results = e;
195
204
  }
196
205
  }
197
- function p(s, e = /* @__PURE__ */ new Set()) {
198
- return s === null || typeof s != "object" ? [] : e.has(s) ? [] : (e.add(s), s instanceof ArrayBuffer || s instanceof MessagePort || typeof ImageBitmap < "u" && s instanceof ImageBitmap || typeof OffscreenCanvas < "u" && s instanceof OffscreenCanvas ? [s] : ArrayBuffer.isView(s) ? [s.buffer] : Array.isArray(s) ? s.flatMap((t) => p(t, e)) : Object.values(s).flatMap(
199
- (t) => p(t, e)
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
209
  ));
201
210
  }
202
- class B {
211
+ class _ {
203
212
  /**
204
213
  * Creates a new `MainWorkerFactory`.
205
214
  *
206
215
  * @param options - Configuration object containing the `workers` registry.
207
216
  */
208
217
  constructor(e) {
209
- h(this, "_workers");
210
- h(this, "_threads");
211
- h(this, "_persistentWorkers", /* @__PURE__ */ new Map());
218
+ k(this, "_workers");
219
+ k(this, "_threads");
220
+ k(this, "_persistentWorkers", /* @__PURE__ */ new Map());
212
221
  this._workers = e.workers, this._threads = navigator.hardwareConcurrency;
213
222
  }
214
223
  /**
215
- * Instantiates a {@link WorkerFactory} for the given worker function.
224
+ * Instantiates a {@link WorkerFactory} for the given worker configuration.
216
225
  *
217
- * @param workerFunction - The function to run inside the worker thread.
226
+ * @param config - The worker configuration containing `func` or `workerURL`.
218
227
  * @returns A new `WorkerFactory` wrapping the worker.
219
228
  */
220
229
  initWorker(e) {
221
- return new k(e);
230
+ return new h(e.func, {
231
+ workerURL: e.workerURL
232
+ });
222
233
  }
223
234
  /**
224
235
  * Splits an array into up to `numChunks` evenly-sized sub-arrays.
@@ -236,16 +247,16 @@ class B {
236
247
  * partitionArray([1, 2, 3, 4, 5], 3);
237
248
  * // → [[1, 2], [3, 4], [5]]
238
249
  */
239
- partitionArray(e, t) {
250
+ partitionArray(e, r) {
240
251
  if (!e.length) return [];
241
- if (t <= 0) throw new Error("numChunks must be positive");
242
- const n = Math.min(t, e.length), a = Math.floor(e.length / n), f = e.length % n, u = [];
252
+ 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 = [];
243
254
  let l = 0;
244
- for (let r = 0; r < n; r++) {
245
- const o = a + (r < f ? 1 : 0);
246
- u.push(e.slice(l, l + o)), l += o;
255
+ for (let t = 0; t < o; t++) {
256
+ const s = a + (t < i ? 1 : 0);
257
+ f.push(e.slice(l, l + s)), l += s;
247
258
  }
248
- return u;
259
+ return f;
249
260
  }
250
261
  /**
251
262
  * Looks up a registered worker configuration by name.
@@ -254,7 +265,7 @@ class B {
254
265
  * @returns The matching config, or `undefined` if not found.
255
266
  */
256
267
  findWorkerByName(e) {
257
- return this._workers.find((t) => t.name === e);
268
+ return this._workers.find((r) => r.name === e);
258
269
  }
259
270
  /**
260
271
  * Runs a named worker against the provided data, distributing work across
@@ -283,20 +294,20 @@ class B {
283
294
  * const settled = await foreman.runWorker('sum', { srcData: [1, 2, 3] });
284
295
  */
285
296
  async runWorker(e, {
286
- srcData: t,
287
- ...n
297
+ srcData: r,
298
+ ...o
288
299
  }) {
289
300
  const a = this.findWorkerByName(e);
290
301
  if (!a)
291
302
  return Promise.reject(new Error(`Worker "${e}" not found`));
292
- const f = a.maxConcurrency ?? this._threads, u = !!(Array.isArray(t) && t.length > 1 && a.partition), l = u ? this.partitionArray(t, f) : t, r = this.createWorkerPromises(
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(
293
304
  a,
294
305
  e,
295
- { data: l, ...n },
296
- f,
297
- u
298
- ), o = await Promise.allSettled(r);
299
- return new b(o);
306
+ { data: l, ...o },
307
+ i,
308
+ f
309
+ ), s = await Promise.allSettled(t);
310
+ return new b(s);
300
311
  }
301
312
  /**
302
313
  * Builds the array of per-thread worker promises for a single `runWorker`
@@ -312,16 +323,17 @@ class B {
312
323
  * @param isPartitioned - Whether `data` is a pre-split array of shards.
313
324
  * @returns An array of promises, one per thread.
314
325
  */
315
- createWorkerPromises(e, t, n, a, f) {
316
- const { data: u, ...l } = n;
317
- return Array.from({ length: a }, (r, o) => {
318
- const i = f && Array.isArray(u) ? u[o] : u;
326
+ createWorkerPromises(e, r, o, a, i) {
327
+ const { data: f, ...l } = o;
328
+ return Array.from({ length: a }, (t, s) => {
329
+ const u = i && Array.isArray(f) ? f[s] : f;
319
330
  return this.runWorkerWithRetry(
320
331
  {
321
332
  workerFunc: e.func,
322
- workerName: t,
323
- index: o,
324
- data: { data: i, ...l }
333
+ workerURL: e.workerURL,
334
+ workerName: r,
335
+ index: s,
336
+ data: { data: u, ...l }
325
337
  },
326
338
  e.retries
327
339
  );
@@ -339,16 +351,16 @@ class B {
339
351
  * @returns The successful {@link WorkerResult} once the worker resolves.
340
352
  * @throws The last caught error when all retries are exhausted.
341
353
  */
342
- async runWorkerWithRetry(e, t = 2) {
354
+ async runWorkerWithRetry(e, r = 2) {
343
355
  try {
344
356
  return await this.initiateWorker(e);
345
- } catch (n) {
346
- if (t > 0)
357
+ } catch (o) {
358
+ if (r > 0)
347
359
  return console.error(
348
- `Worker ${e.index} failed, retrying (${t} left):`,
349
- n
350
- ), this.runWorkerWithRetry(e, t - 1);
351
- throw console.error("Worker failed after all retries:", n), n;
360
+ `Worker ${e.index} failed, retrying (${r} left):`,
361
+ o
362
+ ), this.runWorkerWithRetry(e, r - 1);
363
+ throw console.error("Worker failed after all retries:", o), o;
352
364
  }
353
365
  }
354
366
  /**
@@ -366,48 +378,54 @@ class B {
366
378
  * The underlying `Worker` is always terminated after the first message,
367
379
  * whether it succeeded or failed.
368
380
  *
369
- * @param instanceConfig - Worker function, name, shard index, and data.
381
+ * @param instanceConfig - Worker function, URL, name, shard index, and data.
370
382
  * @returns A promise that resolves with the worker's result.
371
383
  */
372
384
  initiateWorker({
373
385
  workerFunc: e,
374
- workerName: t,
375
- index: n,
376
- data: a
386
+ workerURL: r,
387
+ workerName: o,
388
+ index: a,
389
+ data: i
377
390
  }) {
378
- return new Promise((f, u) => {
379
- const r = this.initWorker(e).getWorker;
380
- r.onerror = (i) => {
381
- r.terminate(), u({
382
- index: n,
383
- workerConfigs: { workerFunc: e, workerName: t, index: n, data: a },
384
- failedResult: i
391
+ return new Promise((f, l) => {
392
+ const s = this.initWorker({
393
+ name: o,
394
+ role: "",
395
+ func: e,
396
+ workerURL: r
397
+ }).getWorker;
398
+ s.onerror = (c) => {
399
+ s.terminate(), l({
400
+ index: a,
401
+ workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
402
+ failedResult: c
385
403
  });
386
- }, r.onmessage = (i) => {
387
- var c, d;
388
- if (((c = i.data) == null ? void 0 : c.ok) === !1) {
389
- r.terminate(), u({
390
- index: n,
391
- workerConfigs: { workerFunc: e, workerName: t, index: n, data: a },
404
+ }, s.onmessage = (c) => {
405
+ var d, p;
406
+ if (((d = c.data) == null ? void 0 : d.ok) === !1) {
407
+ s.terminate(), l({
408
+ index: a,
409
+ workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
392
410
  failedResult: new ErrorEvent("error", {
393
- message: i.data.error
411
+ message: c.data.error
394
412
  })
395
413
  });
396
414
  return;
397
415
  }
398
416
  f({
399
- index: n,
400
- workerConfigs: { workerFunc: e, workerName: t, index: n, data: a },
417
+ index: a,
418
+ workerConfigs: { workerFunc: e, workerName: o, index: a, data: i },
401
419
  successResult: new MessageEvent("message", {
402
- data: (d = i.data) == null ? void 0 : d.data
420
+ data: (p = c.data) == null ? void 0 : p.data
403
421
  })
404
- }), r.terminate();
422
+ }), s.terminate();
405
423
  };
406
- const o = {
407
- index: n,
408
- ...Array.isArray(a) ? { data: a } : a
424
+ const u = {
425
+ index: a,
426
+ ...Array.isArray(i) ? { data: i } : i
409
427
  };
410
- r.postMessage(o, p(o));
428
+ s.postMessage(u, g(u));
411
429
  });
412
430
  }
413
431
  /**
@@ -442,16 +460,16 @@ class B {
442
460
  * reducer: (shards) => shards.flat().reduce((a, b) => a + b, 0),
443
461
  * });
444
462
  */
445
- async collectResults(e, t = {}) {
446
- const n = e.results.filter(
447
- (r) => r.status === "fulfilled"
463
+ async collectResults(e, r = {}) {
464
+ const o = e.results.filter(
465
+ (t) => t.status === "fulfilled"
448
466
  ), a = e.results.filter(
449
- (r) => r.status === "rejected"
450
- ), f = n.map((r) => r.value.successResult.data), u = t.reducer ? t.reducer.toString() : "(shards) => shards.flat()";
467
+ (t) => t.status === "rejected"
468
+ ), i = o.map((t) => t.value.successResult.data), f = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
451
469
  return {
452
- data: await new Promise((r, o) => {
453
- const i = `
454
- const reducer = ${u};
470
+ data: await new Promise((t, s) => {
471
+ const u = `
472
+ const reducer = ${f};
455
473
  self.addEventListener('message', (event) => {
456
474
  try {
457
475
  const result = reducer(event.data);
@@ -460,14 +478,14 @@ class B {
460
478
  self.postMessage({ ok: false, error: String(err) });
461
479
  }
462
480
  });
463
- `, c = new Blob([i], { type: "application/javascript" }), d = new Worker(URL.createObjectURL(c));
464
- d.onmessage = (g) => {
465
- d.terminate(), g.data.ok ? r(g.data.data) : o(new Error(g.data.error));
466
- }, d.onerror = (g) => {
467
- d.terminate(), o(g);
468
- }, d.postMessage(f);
481
+ `, c = new Blob([u], { type: "application/javascript" }), d = new Worker(URL.createObjectURL(c));
482
+ d.onmessage = (p) => {
483
+ d.terminate(), p.data.ok ? t(p.data.data) : s(new Error(p.data.error));
484
+ }, d.onerror = (p) => {
485
+ d.terminate(), s(p);
486
+ }, d.postMessage(i);
469
487
  }),
470
- succeeded: n.length,
488
+ succeeded: o.length,
471
489
  failed: a.length,
472
490
  errors: a
473
491
  };
@@ -502,56 +520,57 @@ class B {
502
520
  if (e.length === 0)
503
521
  throw new Error("Pipeline requires at least one step");
504
522
  if (e.length === 1) {
505
- const t = e[0], n = this.findWorkerByName(t.worker);
506
- if (!n) throw new Error(`Worker "${t.worker}" not found`);
507
- const f = this.initWorker(n.func).getWorker;
508
- return new Promise((u, l) => {
509
- f.onmessage = (o) => {
510
- var i, c;
511
- f.terminate(), ((i = o.data) == null ? void 0 : i.ok) === !1 ? l(new Error(o.data.error)) : u((c = o.data) == null ? void 0 : c.data);
512
- }, f.onerror = (o) => {
513
- f.terminate(), l(o);
523
+ const r = e[0], o = this.findWorkerByName(r.worker);
524
+ if (!o) throw new Error(`Worker "${r.worker}" not found`);
525
+ const i = this.initWorker(o).getWorker;
526
+ return new Promise((f, l) => {
527
+ 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);
530
+ }, i.onerror = (s) => {
531
+ i.terminate(), l(s);
514
532
  };
515
- const r = t.srcData ?? {};
516
- f.postMessage(
517
- { data: r, index: 0 },
518
- p(r)
533
+ const t = r.srcData ?? {};
534
+ i.postMessage(
535
+ { data: t, index: 0 },
536
+ g(t)
519
537
  );
520
538
  });
521
539
  }
522
- return new Promise((t, n) => {
523
- const a = [], f = [];
524
- for (const r of e) {
525
- const o = this.findWorkerByName(r.worker);
526
- if (!o) {
527
- n(new Error(`Worker "${r.worker}" not found`));
540
+ return new Promise((r, o) => {
541
+ const a = [], i = [];
542
+ for (const t of e) {
543
+ const s = this.findWorkerByName(t.worker);
544
+ if (!s) {
545
+ o(new Error(`Worker "${t.worker}" not found`));
528
546
  return;
529
547
  }
530
- const i = new k(o.func, {
531
- mode: v.Pipeline
548
+ const u = new h(s.func, {
549
+ mode: w.Pipeline,
550
+ workerURL: s.workerURL
532
551
  });
533
- a.push(i.getWorker);
552
+ a.push(u.getWorker);
534
553
  }
535
- for (let r = 0; r < a.length - 1; r++)
536
- f.push(new MessageChannel());
537
- for (let r = 0; r < a.length; r++) {
538
- const o = [], i = {};
539
- r > 0 && (i.inputPort = f[r - 1].port1, o.push(i.inputPort)), r < a.length - 1 && (i.outputPort = f[r].port2, o.push(i.outputPort)), a[r].postMessage(
540
- { __pipeline_ports__: !0, ...i },
541
- o
554
+ for (let t = 0; t < a.length - 1; t++)
555
+ i.push(new MessageChannel());
556
+ 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 },
560
+ s
542
561
  );
543
562
  }
544
- const u = a[a.length - 1];
545
- u.onmessage = (r) => {
546
- var o, i;
547
- a.forEach((c) => c.terminate()), ((o = r.data) == null ? void 0 : o.ok) === !1 ? n(new Error(r.data.error)) : t((i = r.data) == null ? void 0 : i.data);
548
- }, u.onerror = (r) => {
549
- a.forEach((o) => o.terminate()), n(r);
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);
550
569
  };
551
570
  const l = e[0].srcData ?? {};
552
571
  a[0].postMessage(
553
572
  { data: l, index: 0 },
554
- p(l)
573
+ g(l)
555
574
  );
556
575
  });
557
576
  }
@@ -589,24 +608,25 @@ class B {
589
608
  * // Release when done
590
609
  * factory.release('transform');
591
610
  */
592
- async runPersistent(e, t) {
593
- const n = this.findWorkerByName(e);
594
- if (!n) throw new Error(`Worker "${e}" not found`);
611
+ async runPersistent(e, r) {
612
+ const o = this.findWorkerByName(e);
613
+ if (!o) throw new Error(`Worker "${e}" not found`);
595
614
  let a = this._persistentWorkers.get(e);
596
- return a || (a = new k(n.func, {
597
- mode: v.Persistent
598
- }).getWorker, this._persistentWorkers.set(e, a)), new Promise((f, u) => {
599
- a.onmessage = (r) => {
600
- var o, i;
601
- ((o = r.data) == null ? void 0 : o.ok) === !1 ? u(new Error(r.data.error)) : f((i = r.data) == null ? void 0 : i.data);
602
- }, a.onerror = (r) => {
603
- u(r);
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) => {
619
+ 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);
622
+ }, a.onerror = (t) => {
623
+ f(t);
604
624
  };
605
625
  const l = {
606
626
  type: "run",
607
- config: t.config
627
+ config: r.config
608
628
  };
609
- t.dataset !== void 0 && (l.dataset = t.dataset), a.postMessage(l, p(l));
629
+ r.dataset !== void 0 && (l.dataset = r.dataset), a.postMessage(l, g(l));
610
630
  });
611
631
  }
612
632
  /**
@@ -619,11 +639,11 @@ class B {
619
639
  * @param workerName - Name of the persistent worker to release.
620
640
  */
621
641
  release(e) {
622
- const t = this._persistentWorkers.get(e);
623
- t && (t.postMessage({ type: "release" }), t.terminate(), this._persistentWorkers.delete(e));
642
+ const r = this._persistentWorkers.get(e);
643
+ r && (r.postMessage({ type: "release" }), r.terminate(), this._persistentWorkers.delete(e));
624
644
  }
625
645
  }
626
646
  export {
627
- B as MainWorkerFactory,
628
- k as WorkerFactory
647
+ _ as MainWorkerFactory,
648
+ h as WorkerFactory
629
649
  };
@@ -45,9 +45,9 @@ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFun
45
45
  workers: TConfigs;
46
46
  });
47
47
  /**
48
- * Instantiates a {@link WorkerFactory} for the given worker function.
48
+ * Instantiates a {@link WorkerFactory} for the given worker configuration.
49
49
  *
50
- * @param workerFunction - The function to run inside the worker thread.
50
+ * @param config - The worker configuration containing `func` or `workerURL`.
51
51
  * @returns A new `WorkerFactory` wrapping the worker.
52
52
  */
53
53
  private initWorker;
@@ -147,7 +147,7 @@ declare class MainWorkerFactory<TConfigs extends readonly WorkerConfig<WorkerFun
147
147
  * The underlying `Worker` is always terminated after the first message,
148
148
  * whether it succeeded or failed.
149
149
  *
150
- * @param instanceConfig - Worker function, name, shard index, and data.
150
+ * @param instanceConfig - Worker function, URL, name, shard index, and data.
151
151
  * @returns A promise that resolves with the worker's result.
152
152
  */
153
153
  private initiateWorker;
@@ -23,8 +23,13 @@ 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. */
27
- func: TFunc;
26
+ /** The worker function that will be serialised and run in a thread. Optional if `workerURL` is provided. */
27
+ func?: TFunc;
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.
31
+ */
32
+ workerURL?: string | URL;
28
33
  /**
29
34
  * Maximum number of parallel threads to spawn for this worker.
30
35
  * Defaults to `navigator.hardwareConcurrency` when omitted.
@@ -41,6 +46,11 @@ export interface WorkerConfig<TFunc extends WorkerFunction = WorkerFunction> {
41
46
  * array.
42
47
  */
43
48
  partition?: boolean;
49
+ /**
50
+ * A list of worker functions that must complete before this worker can start.
51
+ * If any dependency fails, this worker will not run.
52
+ */
53
+ dependencies?: Array<() => void>;
44
54
  }
45
55
  /**
46
56
  * Derives a `name → function` map from a readonly tuple of
@@ -52,9 +62,11 @@ export interface WorkerConfig<TFunc extends WorkerFunction = WorkerFunction> {
52
62
  * @typeParam T - The readonly tuple of `WorkerConfig` values.
53
63
  */
54
64
  export type WorkerConfigMap<T extends readonly WorkerConfig<WorkerFunction<any, any>>[]> = {
55
- [K in T[number]['name']]: Extract<T[number], {
65
+ [K in T[number]['name']]: NonNullable<Extract<T[number], {
66
+ name: K;
67
+ }>['func']> extends WorkerFunction ? NonNullable<Extract<T[number], {
56
68
  name: K;
57
- }>['func'];
69
+ }>['func']> : WorkerFunction;
58
70
  };
59
71
  /**
60
72
  * Extracts the `params` type from a {@link WorkerFunction}.
@@ -91,8 +103,10 @@ export interface MainWorkerFactoryWorker extends WorkerConfig {
91
103
  export interface WorkerInstanceConfig<TFunc extends WorkerFunction = WorkerFunction> {
92
104
  /** Name of the parent worker config, used in logs and error objects. */
93
105
  workerName: WorkerName;
94
- /** The function serialised and executed inside the thread. */
95
- workerFunc: TFunc;
106
+ /** The function serialised and executed inside the thread (optional if `workerURL` is set). */
107
+ workerFunc?: TFunc;
108
+ /** Worker script URL (string or URL object). */
109
+ workerURL?: string | URL;
96
110
  /** Zero-based shard index assigned to this thread. */
97
111
  index: number;
98
112
  /** The data payload (full or partitioned shard) sent to the thread. */
@@ -7,9 +7,13 @@ 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
14
  }
11
15
  /**
12
- * Low-level factory that serialises a {@link WorkerFunction} into a Blob URL
16
+ * Low-level factory that serializes a {@link WorkerFunction} into a Blob URL
13
17
  * and spawns a native `Worker` from it.
14
18
  *
15
19
  * `WorkerFactory` is an internal building block used by `MainWorkerFactory`.
@@ -39,11 +43,11 @@ declare class WorkerFactory {
39
43
  *
40
44
  * @param workerFunction - The function to run inside the worker thread.
41
45
  * Must be self-contained — it cannot reference variables from the outer
42
- * scope because it is serialised via `.toString()`.
46
+ * scope because it is serialized via `.toString()`.
43
47
  * @param options - Optional configuration. Set `mode` to control the
44
48
  * worker execution mode (default, pipeline, or persistent).
45
49
  */
46
- constructor(workerFunction: WorkerFunction, options?: WorkerFactoryOptions);
50
+ constructor(workerFunction?: WorkerFunction, options?: WorkerFactoryOptions);
47
51
  /**
48
52
  * Returns the underlying native `Worker` instance.
49
53
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@offmain/workerkit",
3
- "version": "0.11.0",
3
+ "version": "0.12.3",
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",
@@ -76,6 +76,9 @@
76
76
  "typescript": "^5.7.2",
77
77
  "vite": "^6.3.3",
78
78
  "vite-plugin-dts": "^4.5.4",
79
- "vitest": "^1.0.0"
79
+ "vitest": "^1.0.0",
80
+ "luxon": "^3.7.2",
81
+ "i18next": "^25.5.1",
82
+ "@types/luxon": "^3.7.2"
80
83
  }
81
84
  }