@offmain/workerkit 0.13.0 → 0.14.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
@@ -75,27 +75,32 @@ When worker logic relies on external npm packages (such as Luxon, `date-fns`, `i
75
75
 
76
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
- ### Dedicated Worker Script
78
+ ### Dedicated Worker Script with `defineWorker`
79
+
80
+ When writing standalone worker files, wrap your export in `defineWorker`. This automatically handles standard runs, worker-to-worker pipelines (`pipeline()`), and persistent dataset caching without manual `postMessage` / `onmessage` boilerplate:
79
81
 
80
82
  ```ts
81
83
  // transform-data.worker.ts
84
+ import { defineWorker } from '@offmain/workerkit';
82
85
  import { format } from 'date-fns';
83
86
  import { t } from './i18n.ts';
84
87
 
85
- self.addEventListener('message', (event) => {
86
- try {
87
- const { items, locale } = event.data.data;
88
- const result = items.map((item: any) => ({
88
+ export default defineWorker(
89
+ async ({
90
+ data,
91
+ options,
92
+ }: {
93
+ data: { items: any[]; locale: string };
94
+ options?: { prefix?: string };
95
+ }) => {
96
+ const { items, locale } = data;
97
+ return items.map((item: any) => ({
89
98
  ...item,
90
99
  formattedDate: format(new Date(item.timestamp), 'yyyy-MM-dd'),
91
- label: t('transaction', locale),
100
+ label: (options?.prefix ?? '') + t('transaction', locale),
92
101
  }));
93
-
94
- self.postMessage({ ok: true, data: result });
95
- } catch (err) {
96
- self.postMessage({ ok: false, error: (err as Error).message });
97
- }
98
- });
102
+ },
103
+ );
99
104
  ```
100
105
 
101
106
  ### Webpack 5 & Vite Static Analysis (`createWorker`)
@@ -176,7 +181,7 @@ Only the final result crosses back to the main thread. If your pipeline generate
176
181
 
177
182
  ### Usage
178
183
 
179
- ```ts
184
+ ````ts
180
185
  import { MainWorkerFactory } from '@offmain/workerkit';
181
186
  import { fetchData, transform, aggregate } from './workers.ts';
182
187
 
@@ -188,20 +193,33 @@ const factory = new MainWorkerFactory({
188
193
  ] as const,
189
194
  });
190
195
 
196
+ ### Step-Specific Options and Configs in Pipeline
197
+
198
+ You can pass step-specific parameters (such as `options`, `configs`, etc.) directly to each pipeline step:
199
+
200
+ ```ts
191
201
  const result = await factory.pipeline<AggregateResult>([
192
- { worker: 'fetchData', srcData: { url: '/api/records' } },
193
- { worker: 'transform' }, // receives fetchData output directly
194
- { worker: 'aggregate' }, // receives transform output directly
202
+ {
203
+ worker: 'fetchData',
204
+ srcData: { url: '/api/records' },
205
+ options: { timeout: 5000 },
206
+ },
207
+ {
208
+ worker: 'transform',
209
+ configs: { multiplier: 2 },
210
+ },
211
+ {
212
+ worker: 'aggregate',
213
+ options: { threshold: 10 },
214
+ },
195
215
  ]);
216
+ ````
196
217
 
197
- console.log(result); // only this small result crossed to main thread
198
- ```
199
-
200
- ### How each step receives data
218
+ ### How each step receives data and parameters
201
219
 
202
- - The first step receives `srcData` as `{ data: srcData, index: 0 }` same as `runWorker`.
203
- - Each subsequent step receives the previous step's output as `{ data: previousOutput, index: 0 }`.
204
- - Worker functions don't need any special handling they use the same `{ data }` parameter signature as regular workers.
220
+ - The first step receives `srcData` merged with its step parameters as `{ data: srcData, options: { timeout: 5000 }, index: 0 }`.
221
+ - Each subsequent step receives the previous step's output merged with its step parameters as `{ data: previousOutput, configs: { multiplier: 2 }, index: 0 }`.
222
+ - Worker functions (both inline functions and native scripts using `defineWorker`) receive all step parameters in their first argument.
205
223
 
206
224
  ### When to use pipeline vs runWorker
207
225
 
@@ -310,11 +328,42 @@ The framework handles the caching transparently — your function always receive
310
328
 
311
329
  The cached dataset lives in worker memory until `release()` is called. For large datasets, always call `release()` when you're done to free the memory:
312
330
 
331
+ After releasing, the next `runPersistent` call will create a fresh worker instance (requiring a new dataset).
332
+
333
+ ---
334
+
335
+ ## Lifecycle Management (`terminate`, `destroy`, `reset`, `restart`)
336
+
337
+ `MainWorkerFactory` provides built-in lifecycle management to terminate running workers and clean up browser resources:
338
+
339
+ ### `terminate()` / `destroy()`
340
+
341
+ Immediately stops all active workers (one-shot tasks, pipelines, reducers) and releases all cached persistent workers:
342
+
313
343
  ```ts
314
- factory.release('transform');
344
+ // Stop all active threads and release persistent workers
345
+ factory.terminate();
346
+ // or
347
+ factory.destroy(); // Alias for terminate()
348
+
349
+ console.log(factory.isTerminated); // true
315
350
  ```
316
351
 
317
- After releasing, the next `runPersistent` call will create a fresh worker instance (requiring a new dataset).
352
+ After calling `terminate()`, any attempt to run workers on the factory instance will immediately reject.
353
+
354
+ ### `reset()` / `restart()`
355
+
356
+ Terminates all active/persistent worker instances and restores the factory to an active state (`isTerminated = false`), allowing new worker instances to be initiated cleanly:
357
+
358
+ ```ts
359
+ // Stop existing workers and reset factory state
360
+ factory.reset(); // or factory.restart()
361
+
362
+ console.log(factory.isTerminated); // false
363
+
364
+ // Factory is ready to initiate fresh worker instances again
365
+ const settled = await factory.runWorker('sum', { srcData: [1, 2, 3] });
366
+ ```
318
367
 
319
368
  ---
320
369
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
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=>`
1
+ "use strict";var S=Object.defineProperty;var x=(n,e,r)=>e in n?S(n,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):n[e]=r;var y=(n,e,r)=>x(n,typeof e!="symbol"?e+"":e,r);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const O=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
- `,W=n=>`
24
+ `,F=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 [];
@@ -40,10 +40,15 @@ const workerFn = ${n};
40
40
  let outputPort = null;
41
41
  let inputPort = null;
42
42
  let pendingData = null;
43
+ let stepParams = {};
43
44
 
44
45
  async function processData(data) {
45
46
  try {
46
- const output = await workerFn(data);
47
+ const payload =
48
+ typeof data === 'object' && data !== null && 'data' in data
49
+ ? { ...stepParams, ...data }
50
+ : { data, ...stepParams, index: 0 };
51
+ const output = await workerFn(payload);
47
52
  const result = { ok: true, data: output };
48
53
  const transfers = extractTransferables(output);
49
54
  if (outputPort) {
@@ -63,6 +68,9 @@ async function processData(data) {
63
68
 
64
69
  self.addEventListener('message', (event) => {
65
70
  if (event.data && event.data.__pipeline_ports__) {
71
+ if (event.data.stepParams) {
72
+ stepParams = event.data.stepParams;
73
+ }
66
74
  if (event.data.outputPort) {
67
75
  outputPort = event.data.outputPort;
68
76
  }
@@ -74,7 +82,7 @@ self.addEventListener('message', (event) => {
74
82
  if (outputPort) outputPort.postMessage(e.data);
75
83
  else self.postMessage(e.data);
76
84
  } else {
77
- processData({ data: e.data.data, index: 0 });
85
+ processData({ data: e.data.data, ...stepParams, index: 0 });
78
86
  }
79
87
  };
80
88
  }
@@ -93,7 +101,7 @@ self.addEventListener('message', (event) => {
93
101
  pendingData = event.data;
94
102
  }
95
103
  });
96
- `,M=n=>`
104
+ `,R=n=>`
97
105
  const extractTransferables = (value, seen = new Set()) => {
98
106
  if (value === null || typeof value !== 'object') return [];
99
107
  if (seen.has(value)) return [];
@@ -149,7 +157,7 @@ self.addEventListener('message', async (event) => {
149
157
  self.postMessage({ ok: false, error: err instanceof Error ? err.message : String(err) });
150
158
  }
151
159
  });
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=`
160
+ `;var P=(n=>(n.Default="default",n.Pipeline="pipeline",n.Persistent="persistent",n))(P||{});const C=Object.freeze({persistent:R,pipeline:F,default:O});class v{constructor(e,r){y(this,"_worker");if(r!=null&&r.createWorker)this._worker=r.createWorker();else if(e){const s=(r==null?void 0:r.mode)??"default",t=C[s](e.toString()),l=new Blob([t],{type:"application/javascript"});this._worker=new Worker(URL.createObjectURL(l))}else throw new Error("Either workerFunction or options.createWorker must be provided to WorkerFactory.")}get getWorker(){return this._worker}}class j{constructor(e){this.results=e}}function m(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=>m(r,e)):Object.values(n).flatMap(r=>m(r,e)))}class L{constructor(e){y(this,"_workers");y(this,"_threads");y(this,"_persistentWorkers",new Map);y(this,"_activeWorkers",new Set);y(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 v(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 s=Math.min(r,e.length),t=Math.floor(e.length/s),l=e.length%s,c=[];let f=0;for(let a=0;a<s;a++){const o=t+(a<l?1:0);c.push(e.slice(f,f+o)),f+=o}return c}findWorkerByName(e){return this._workers.find(r=>r.name===e)}async runWorker(e,{srcData:r,...s}){if(this._isTerminated)return Promise.reject(new Error("MainWorkerFactory has been terminated"));const t=this.findWorkerByName(e);if(!t)return Promise.reject(new Error(`Worker "${e}" not found`));const l=t.maxConcurrency??this._threads,c=!!(Array.isArray(r)&&r.length>1&&t.partition),f=c?this.partitionArray(r,l):r,a=this.createWorkerPromises(t,e,{data:f,...s},l,c),o=await Promise.allSettled(a);return new j(o)}createWorkerPromises(e,r,s,t,l){const{data:c,...f}=s;return Array.from({length:t},(a,o)=>{const d=l&&Array.isArray(c)?c[o]:c;return this.runWorkerWithRetry({workerFunc:e.func,createWorker:e.createWorker,workerName:r,index:o,data:{data:d,...f}},e.retries)})}async runWorkerWithRetry(e,r=2){try{return await this.initiateWorker(e)}catch(s){if(r>0)return console.error(`Worker ${e.index} failed, retrying (${r} left):`,s),this.runWorkerWithRetry(e,r-1);throw console.error("Worker failed after all retries:",s),s}}initiateWorker({workerFunc:e,createWorker:r,workerName:s,index:t,data:l}){return new Promise((c,f)=>{const o=this.initWorker({name:s,role:"",func:e,createWorker:r}).getWorker;o.onerror=p=>{this.terminateWorker(o),f({index:t,workerConfigs:{workerFunc:e,createWorker:r,workerName:s,index:t,data:l},failedResult:p})},o.onmessage=p=>{var i,u;if(((i=p.data)==null?void 0:i.ok)===!1){this.terminateWorker(o),f({index:t,workerConfigs:{workerFunc:e,createWorker:r,workerName:s,index:t,data:l},failedResult:new ErrorEvent("error",{message:p.data.error})});return}c({index:t,workerConfigs:{workerFunc:e,createWorker:r,workerName:s,index:t,data:l},successResult:new MessageEvent("message",{data:(u=p.data)==null?void 0:u.data})}),this.terminateWorker(o)};const d={index:t,...Array.isArray(l)?{data:l}:l};o.postMessage(d,m(d))})}async collectResults(e,r={}){if(this._isTerminated)throw new Error("MainWorkerFactory has been terminated");const s=e.results.filter(a=>a.status==="fulfilled"),t=e.results.filter(a=>a.status==="rejected"),l=s.map(a=>a.value.successResult.data),c=r.reducer?r.reducer.toString():"(shards) => shards.flat()";return{data:await new Promise((a,o)=>{const d=`
153
161
  const reducer = ${c};
154
162
  self.addEventListener('message', (event) => {
155
163
  try {
@@ -159,4 +167,4 @@ self.addEventListener('message', async (event) => {
159
167
  self.postMessage({ ok: false, error: String(err) });
160
168
  }
161
169
  });
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;
170
+ `,p=new Blob([d],{type:"application/javascript"}),i=this.trackWorker(new Worker(URL.createObjectURL(p)));i.onmessage=u=>{this.terminateWorker(i),u.data.ok?a(u.data.data):o(new Error(u.data.error))},i.onerror=u=>{this.terminateWorker(i),o(u)},i.postMessage(l)}),succeeded:s.length,failed:t.length,errors:t}}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],{worker:s,srcData:t,...l}=r,c=this.findWorkerByName(r.worker);if(!c)throw new Error(`Worker "${r.worker}" not found`);const a=this.initWorker(c).getWorker;return new Promise((o,d)=>{a.onmessage=u=>{var k,g;this.terminateWorker(a),((k=u.data)==null?void 0:k.ok)===!1?d(new Error(u.data.error)):o((g=u.data)==null?void 0:g.data)},a.onerror=u=>{this.terminateWorker(a),d(u)};const i={data:t??{},...l,index:0};a.postMessage(i,m(i))})}return new Promise((r,s)=>{const t=[],l=[];for(const i of e){const u=this.findWorkerByName(i.worker);if(!u){s(new Error(`Worker "${i.worker}" not found`));return}const k=new v(u.func,{mode:P.Pipeline,createWorker:u.createWorker}),g=this.trackWorker(k.getWorker);t.push(g)}for(let i=0;i<t.length-1;i++)l.push(new MessageChannel);for(let i=0;i<t.length;i++){const{worker:u,srcData:k,...g}=e[i],W=[],w={};if(i>0&&(w.inputPort=l[i-1].port1,W.push(w.inputPort)),i<t.length-1&&(w.outputPort=l[i].port2,W.push(w.outputPort)),t[i].postMessage({__pipeline_ports__:!0,stepParams:g,...w},W),i<t.length-1){const _=t[i],A=t[i+1],{worker:I,srcData:U,...D}=e[i+1];_.onmessage=h=>{var E,T;if(h.data&&h.data.__pipeline_ports__)return;if(((E=h.data)==null?void 0:E.ok)===!1){t.forEach(B=>this.terminateWorker(B)),s(new Error(h.data.error));return}const b={data:((T=h.data)==null?void 0:T.ok)!==void 0?h.data.data:h.data,...D,index:0};A.postMessage(b,m(b))},_.onerror=h=>{t.forEach(M=>this.terminateWorker(M)),s(h)}}}const c=t[t.length-1];c.onmessage=i=>{var u,k;t.forEach(g=>this.terminateWorker(g)),((u=i.data)==null?void 0:u.ok)===!1?s(new Error(i.data.error)):r((k=i.data)==null?void 0:k.data)},c.onerror=i=>{t.forEach(u=>this.terminateWorker(u)),s(i)};const{worker:f,srcData:a,...o}=e[0],p={data:a??{},...o,index:0};t[0].postMessage(p,m(p))})}async runPersistent(e,r){if(this._isTerminated)throw new Error("MainWorkerFactory has been terminated");const s=this.findWorkerByName(e);if(!s)throw new Error(`Worker "${e}" not found`);let t=this._persistentWorkers.get(e);if(!t){const l=new v(s.func,{mode:P.Persistent,createWorker:s.createWorker});t=this.trackWorker(l.getWorker),this._persistentWorkers.set(e,t)}return new Promise((l,c)=>{t.onmessage=a=>{var o,d;((o=a.data)==null?void 0:o.ok)===!1?c(new Error(a.data.error)):l((d=a.data)==null?void 0:d.data)},t.onerror=a=>{c(a)};const f={type:"run",config:r.config};r.dataset!==void 0&&(f.dataset=r.dataset),t.postMessage(f,m(f))})}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()}}function $(n){if(typeof self>"u")return;let e=null,r=null,s=null,t={};const l=(f,a)=>{self.postMessage(f,a)};async function c(f){try{const a=typeof f=="object"&&f!==null&&"data"in f?{...t,...f}:{data:f,...t,index:0},o=await n(a),d={ok:!0,data:o},p=m(o);e?e.postMessage(d,p):l(d,p)}catch(a){const o={ok:!1,error:a instanceof Error?a.message:String(a)};e?e.postMessage(o):l(o)}}self.addEventListener("message",f=>{const a=f.data;if(a&&a.__pipeline_ports__){a.stepParams&&(t=a.stepParams),a.outputPort&&(e=a.outputPort),a.inputPort&&(r=a.inputPort,r.onmessage=o=>{var d;o.data&&o.data.ok===!1?e?e.postMessage(o.data):l(o.data):c({data:(d=o.data)==null?void 0:d.data,...t,index:0})}),s!==null&&(c(s),s=null);return}r?s=a:c(a)})}exports.MainWorkerFactory=L;exports.WorkerFactory=v;exports.defineWorker=$;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
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
- const y = (n) => `
1
+ var S = Object.defineProperty;
2
+ var x = (n, e, r) => e in n ? S(n, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : n[e] = r;
3
+ var y = (n, e, r) => x(n, typeof e != "symbol" ? e + "" : e, r);
4
+ const O = (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 [];
@@ -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
- `, W = (n) => `
27
+ `, R = (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 [];
@@ -43,10 +43,15 @@ const workerFn = ${n};
43
43
  let outputPort = null;
44
44
  let inputPort = null;
45
45
  let pendingData = null;
46
+ let stepParams = {};
46
47
 
47
48
  async function processData(data) {
48
49
  try {
49
- const output = await workerFn(data);
50
+ const payload =
51
+ typeof data === 'object' && data !== null && 'data' in data
52
+ ? { ...stepParams, ...data }
53
+ : { data, ...stepParams, index: 0 };
54
+ const output = await workerFn(payload);
50
55
  const result = { ok: true, data: output };
51
56
  const transfers = extractTransferables(output);
52
57
  if (outputPort) {
@@ -66,6 +71,9 @@ async function processData(data) {
66
71
 
67
72
  self.addEventListener('message', (event) => {
68
73
  if (event.data && event.data.__pipeline_ports__) {
74
+ if (event.data.stepParams) {
75
+ stepParams = event.data.stepParams;
76
+ }
69
77
  if (event.data.outputPort) {
70
78
  outputPort = event.data.outputPort;
71
79
  }
@@ -77,7 +85,7 @@ self.addEventListener('message', (event) => {
77
85
  if (outputPort) outputPort.postMessage(e.data);
78
86
  else self.postMessage(e.data);
79
87
  } else {
80
- processData({ data: e.data.data, index: 0 });
88
+ processData({ data: e.data.data, ...stepParams, index: 0 });
81
89
  }
82
90
  };
83
91
  }
@@ -96,7 +104,7 @@ self.addEventListener('message', (event) => {
96
104
  pendingData = event.data;
97
105
  }
98
106
  });
99
- `, M = (n) => `
107
+ `, F = (n) => `
100
108
  const extractTransferables = (value, seen = new Set()) => {
101
109
  if (value === null || typeof value !== 'object') return [];
102
110
  if (seen.has(value)) return [];
@@ -153,13 +161,13 @@ self.addEventListener('message', async (event) => {
153
161
  }
154
162
  });
155
163
  `;
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
- default: y
164
+ var P = /* @__PURE__ */ ((n) => (n.Default = "default", n.Pipeline = "pipeline", n.Persistent = "persistent", n))(P || {});
165
+ const C = Object.freeze({
166
+ persistent: F,
167
+ pipeline: R,
168
+ default: O
161
169
  });
162
- class g {
170
+ class W {
163
171
  /**
164
172
  * Creates a new `Worker` from the given function or factory option.
165
173
  *
@@ -169,14 +177,14 @@ class g {
169
177
  * @param options - Optional configuration containing `createWorker` or `mode`.
170
178
  */
171
179
  constructor(e, r) {
172
- h(this, "_worker");
180
+ y(this, "_worker");
173
181
  if (r != null && r.createWorker)
174
182
  this._worker = r.createWorker();
175
183
  else if (e) {
176
- const o = (r == null ? void 0 : r.mode) ?? "default", a = P[o](e.toString()), i = new Blob([a], {
184
+ const s = (r == null ? void 0 : r.mode) ?? "default", t = C[s](e.toString()), l = new Blob([t], {
177
185
  type: "application/javascript"
178
186
  });
179
- this._worker = new Worker(URL.createObjectURL(i));
187
+ this._worker = new Worker(URL.createObjectURL(l));
180
188
  } else
181
189
  throw new Error(
182
190
  "Either workerFunction or options.createWorker must be provided to WorkerFactory."
@@ -192,28 +200,28 @@ class g {
192
200
  return this._worker;
193
201
  }
194
202
  }
195
- class b {
203
+ class L {
196
204
  constructor(e) {
197
205
  this.results = e;
198
206
  }
199
207
  }
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)
208
+ function m(n, e = /* @__PURE__ */ new Set()) {
209
+ 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) => m(r, e)) : Object.values(n).flatMap(
210
+ (r) => m(r, e)
203
211
  ));
204
212
  }
205
- class E {
213
+ class U {
206
214
  /**
207
215
  * Creates a new `MainWorkerFactory`.
208
216
  *
209
217
  * @param options - Configuration object containing the `workers` registry.
210
218
  */
211
219
  constructor(e) {
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);
220
+ y(this, "_workers");
221
+ y(this, "_threads");
222
+ y(this, "_persistentWorkers", /* @__PURE__ */ new Map());
223
+ y(this, "_activeWorkers", /* @__PURE__ */ new Set());
224
+ y(this, "_isTerminated", !1);
217
225
  this._workers = e.workers, this._threads = navigator.hardwareConcurrency;
218
226
  }
219
227
  /**
@@ -247,7 +255,7 @@ class E {
247
255
  * @returns A new `WorkerFactory` wrapping the worker.
248
256
  */
249
257
  initWorker(e) {
250
- const r = new g(e.func, {
258
+ const r = new W(e.func, {
251
259
  createWorker: e.createWorker
252
260
  });
253
261
  return this.trackWorker(r.getWorker), r;
@@ -271,11 +279,11 @@ class E {
271
279
  partitionArray(e, r) {
272
280
  if (!e.length) return [];
273
281
  if (r <= 0) throw new Error("numChunks must be positive");
274
- const o = Math.min(r, e.length), a = Math.floor(e.length / o), i = e.length % o, c = [];
275
- let u = 0;
276
- for (let t = 0; t < o; t++) {
277
- const s = a + (t < i ? 1 : 0);
278
- c.push(e.slice(u, u + s)), u += s;
282
+ const s = Math.min(r, e.length), t = Math.floor(e.length / s), l = e.length % s, c = [];
283
+ let f = 0;
284
+ for (let a = 0; a < s; a++) {
285
+ const o = t + (a < l ? 1 : 0);
286
+ c.push(e.slice(f, f + o)), f += o;
279
287
  }
280
288
  return c;
281
289
  }
@@ -316,21 +324,21 @@ class E {
316
324
  */
317
325
  async runWorker(e, {
318
326
  srcData: r,
319
- ...o
327
+ ...s
320
328
  }) {
321
329
  if (this._isTerminated)
322
330
  return Promise.reject(new Error("MainWorkerFactory has been terminated"));
323
- const a = this.findWorkerByName(e);
324
- if (!a)
331
+ const t = this.findWorkerByName(e);
332
+ if (!t)
325
333
  return Promise.reject(new Error(`Worker "${e}" not found`));
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(
327
- a,
334
+ const l = t.maxConcurrency ?? this._threads, c = !!(Array.isArray(r) && r.length > 1 && t.partition), f = c ? this.partitionArray(r, l) : r, a = this.createWorkerPromises(
335
+ t,
328
336
  e,
329
- { data: u, ...o },
330
- i,
337
+ { data: f, ...s },
338
+ l,
331
339
  c
332
- ), s = await Promise.allSettled(t);
333
- return new b(s);
340
+ ), o = await Promise.allSettled(a);
341
+ return new L(o);
334
342
  }
335
343
  /**
336
344
  * Builds the array of per-thread worker promises for a single `runWorker`
@@ -346,17 +354,17 @@ class E {
346
354
  * @param isPartitioned - Whether `data` is a pre-split array of shards.
347
355
  * @returns An array of promises, one per thread.
348
356
  */
349
- createWorkerPromises(e, r, o, a, i) {
350
- const { data: c, ...u } = o;
351
- return Array.from({ length: a }, (t, s) => {
352
- const f = i && Array.isArray(c) ? c[s] : c;
357
+ createWorkerPromises(e, r, s, t, l) {
358
+ const { data: c, ...f } = s;
359
+ return Array.from({ length: t }, (a, o) => {
360
+ const d = l && Array.isArray(c) ? c[o] : c;
353
361
  return this.runWorkerWithRetry(
354
362
  {
355
363
  workerFunc: e.func,
356
364
  createWorker: e.createWorker,
357
365
  workerName: r,
358
- index: s,
359
- data: { data: f, ...u }
366
+ index: o,
367
+ data: { data: d, ...f }
360
368
  },
361
369
  e.retries
362
370
  );
@@ -377,13 +385,13 @@ class E {
377
385
  async runWorkerWithRetry(e, r = 2) {
378
386
  try {
379
387
  return await this.initiateWorker(e);
380
- } catch (o) {
388
+ } catch (s) {
381
389
  if (r > 0)
382
390
  return console.error(
383
391
  `Worker ${e.index} failed, retrying (${r} left):`,
384
- o
392
+ s
385
393
  ), this.runWorkerWithRetry(e, r - 1);
386
- throw console.error("Worker failed after all retries:", o), o;
394
+ throw console.error("Worker failed after all retries:", s), s;
387
395
  }
388
396
  }
389
397
  /**
@@ -406,66 +414,66 @@ class E {
406
414
  initiateWorker({
407
415
  workerFunc: e,
408
416
  createWorker: r,
409
- workerName: o,
410
- index: a,
411
- data: i
417
+ workerName: s,
418
+ index: t,
419
+ data: l
412
420
  }) {
413
- return new Promise((c, u) => {
414
- const s = this.initWorker({
415
- name: o,
421
+ return new Promise((c, f) => {
422
+ const o = this.initWorker({
423
+ name: s,
416
424
  role: "",
417
425
  func: e,
418
426
  createWorker: r
419
427
  }).getWorker;
420
- s.onerror = (l) => {
421
- this.terminateWorker(s), u({
422
- index: a,
428
+ o.onerror = (p) => {
429
+ this.terminateWorker(o), f({
430
+ index: t,
423
431
  workerConfigs: {
424
432
  workerFunc: e,
425
433
  createWorker: r,
426
- workerName: o,
427
- index: a,
428
- data: i
434
+ workerName: s,
435
+ index: t,
436
+ data: l
429
437
  },
430
- failedResult: l
438
+ failedResult: p
431
439
  });
432
- }, s.onmessage = (l) => {
433
- var d, p;
434
- if (((d = l.data) == null ? void 0 : d.ok) === !1) {
435
- this.terminateWorker(s), u({
436
- index: a,
440
+ }, o.onmessage = (p) => {
441
+ var i, u;
442
+ if (((i = p.data) == null ? void 0 : i.ok) === !1) {
443
+ this.terminateWorker(o), f({
444
+ index: t,
437
445
  workerConfigs: {
438
446
  workerFunc: e,
439
447
  createWorker: r,
440
- workerName: o,
441
- index: a,
442
- data: i
448
+ workerName: s,
449
+ index: t,
450
+ data: l
443
451
  },
444
452
  failedResult: new ErrorEvent("error", {
445
- message: l.data.error
453
+ message: p.data.error
446
454
  })
447
455
  });
448
456
  return;
449
457
  }
450
458
  c({
451
- index: a,
459
+ index: t,
452
460
  workerConfigs: {
453
461
  workerFunc: e,
454
462
  createWorker: r,
455
- workerName: o,
456
- index: a,
457
- data: i
463
+ workerName: s,
464
+ index: t,
465
+ data: l
458
466
  },
459
467
  successResult: new MessageEvent("message", {
460
- data: (p = l.data) == null ? void 0 : p.data
468
+ data: (u = p.data) == null ? void 0 : u.data
461
469
  })
462
- }), this.terminateWorker(s);
470
+ }), this.terminateWorker(o);
463
471
  };
464
- const f = {
465
- index: a,
466
- ...Array.isArray(i) ? { data: i } : i
472
+ const d = {
473
+ index: t,
474
+ ...Array.isArray(l) ? { data: l } : l
467
475
  };
468
- s.postMessage(f, k(f));
476
+ o.postMessage(d, m(d));
469
477
  });
470
478
  }
471
479
  /**
@@ -503,14 +511,14 @@ class E {
503
511
  async collectResults(e, r = {}) {
504
512
  if (this._isTerminated)
505
513
  throw new Error("MainWorkerFactory has been terminated");
506
- const o = e.results.filter(
507
- (t) => t.status === "fulfilled"
508
- ), a = e.results.filter(
509
- (t) => t.status === "rejected"
510
- ), i = o.map((t) => t.value.successResult.data), c = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
514
+ const s = e.results.filter(
515
+ (a) => a.status === "fulfilled"
516
+ ), t = e.results.filter(
517
+ (a) => a.status === "rejected"
518
+ ), l = s.map((a) => a.value.successResult.data), c = r.reducer ? r.reducer.toString() : "(shards) => shards.flat()";
511
519
  return {
512
- data: await new Promise((t, s) => {
513
- const f = `
520
+ data: await new Promise((a, o) => {
521
+ const d = `
514
522
  const reducer = ${c};
515
523
  self.addEventListener('message', (event) => {
516
524
  try {
@@ -520,16 +528,16 @@ class E {
520
528
  self.postMessage({ ok: false, error: String(err) });
521
529
  }
522
530
  });
523
- `, l = new Blob([f], { type: "application/javascript" }), d = this.trackWorker(new Worker(URL.createObjectURL(l)));
524
- d.onmessage = (p) => {
525
- this.terminateWorker(d), p.data.ok ? t(p.data.data) : s(new Error(p.data.error));
526
- }, d.onerror = (p) => {
527
- this.terminateWorker(d), s(p);
528
- }, d.postMessage(i);
531
+ `, p = new Blob([d], { type: "application/javascript" }), i = this.trackWorker(new Worker(URL.createObjectURL(p)));
532
+ i.onmessage = (u) => {
533
+ this.terminateWorker(i), u.data.ok ? a(u.data.data) : o(new Error(u.data.error));
534
+ }, i.onerror = (u) => {
535
+ this.terminateWorker(i), o(u);
536
+ }, i.postMessage(l);
529
537
  }),
530
- succeeded: o.length,
531
- failed: a.length,
532
- errors: a
538
+ succeeded: s.length,
539
+ failed: t.length,
540
+ errors: t
533
541
  };
534
542
  }
535
543
  /**
@@ -564,58 +572,66 @@ class E {
564
572
  if (e.length === 0)
565
573
  throw new Error("Pipeline requires at least one step");
566
574
  if (e.length === 1) {
567
- const r = e[0], o = this.findWorkerByName(r.worker);
568
- if (!o) throw new Error(`Worker "${r.worker}" not found`);
569
- const i = this.initWorker(o).getWorker;
570
- return new Promise((c, u) => {
571
- i.onmessage = (s) => {
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);
574
- }, i.onerror = (s) => {
575
- this.terminateWorker(i), u(s);
575
+ const r = e[0], { worker: s, srcData: t, ...l } = r, c = this.findWorkerByName(r.worker);
576
+ if (!c) throw new Error(`Worker "${r.worker}" not found`);
577
+ const a = this.initWorker(c).getWorker;
578
+ return new Promise((o, d) => {
579
+ a.onmessage = (u) => {
580
+ var k, g;
581
+ this.terminateWorker(a), ((k = u.data) == null ? void 0 : k.ok) === !1 ? d(new Error(u.data.error)) : o((g = u.data) == null ? void 0 : g.data);
582
+ }, a.onerror = (u) => {
583
+ this.terminateWorker(a), d(u);
576
584
  };
577
- const t = r.srcData ?? {};
578
- i.postMessage(
579
- { data: t, index: 0 },
580
- k(t)
581
- );
585
+ const i = { data: t ?? {}, ...l, index: 0 };
586
+ a.postMessage(i, m(i));
582
587
  });
583
588
  }
584
- return new Promise((r, o) => {
585
- const a = [], i = [];
586
- for (const t of e) {
587
- const s = this.findWorkerByName(t.worker);
588
- if (!s) {
589
- o(new Error(`Worker "${t.worker}" not found`));
589
+ return new Promise((r, s) => {
590
+ const t = [], l = [];
591
+ for (const i of e) {
592
+ const u = this.findWorkerByName(i.worker);
593
+ if (!u) {
594
+ s(new Error(`Worker "${i.worker}" not found`));
590
595
  return;
591
596
  }
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);
597
+ const k = new W(u.func, {
598
+ mode: P.Pipeline,
599
+ createWorker: u.createWorker
600
+ }), g = this.trackWorker(k.getWorker);
601
+ t.push(g);
597
602
  }
598
- for (let t = 0; t < a.length - 1; t++)
599
- i.push(new MessageChannel());
600
- for (let t = 0; t < a.length; t++) {
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 },
604
- s
605
- );
603
+ for (let i = 0; i < t.length - 1; i++)
604
+ l.push(new MessageChannel());
605
+ for (let i = 0; i < t.length; i++) {
606
+ const { worker: u, srcData: k, ...g } = e[i], v = [], w = {};
607
+ if (i > 0 && (w.inputPort = l[i - 1].port1, v.push(w.inputPort)), i < t.length - 1 && (w.outputPort = l[i].port2, v.push(w.outputPort)), t[i].postMessage(
608
+ { __pipeline_ports__: !0, stepParams: g, ...w },
609
+ v
610
+ ), i < t.length - 1) {
611
+ const _ = t[i], A = t[i + 1], { worker: j, srcData: $, ...D } = e[i + 1];
612
+ _.onmessage = (h) => {
613
+ var E, T;
614
+ if (h.data && h.data.__pipeline_ports__) return;
615
+ if (((E = h.data) == null ? void 0 : E.ok) === !1) {
616
+ t.forEach((B) => this.terminateWorker(B)), s(new Error(h.data.error));
617
+ return;
618
+ }
619
+ const b = { data: ((T = h.data) == null ? void 0 : T.ok) !== void 0 ? h.data.data : h.data, ...D, index: 0 };
620
+ A.postMessage(b, m(b));
621
+ }, _.onerror = (h) => {
622
+ t.forEach((M) => this.terminateWorker(M)), s(h);
623
+ };
624
+ }
606
625
  }
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);
626
+ const c = t[t.length - 1];
627
+ c.onmessage = (i) => {
628
+ var u, k;
629
+ t.forEach((g) => this.terminateWorker(g)), ((u = i.data) == null ? void 0 : u.ok) === !1 ? s(new Error(i.data.error)) : r((k = i.data) == null ? void 0 : k.data);
630
+ }, c.onerror = (i) => {
631
+ t.forEach((u) => this.terminateWorker(u)), s(i);
613
632
  };
614
- const u = e[0].srcData ?? {};
615
- a[0].postMessage(
616
- { data: u, index: 0 },
617
- k(u)
618
- );
633
+ const { worker: f, srcData: a, ...o } = e[0], p = { data: a ?? {}, ...o, index: 0 };
634
+ t[0].postMessage(p, m(p));
619
635
  });
620
636
  }
621
637
  /**
@@ -655,28 +671,28 @@ class E {
655
671
  async runPersistent(e, r) {
656
672
  if (this._isTerminated)
657
673
  throw new Error("MainWorkerFactory has been terminated");
658
- const o = this.findWorkerByName(e);
659
- if (!o) throw new Error(`Worker "${e}" not found`);
660
- let a = this._persistentWorkers.get(e);
661
- if (!a) {
662
- const i = new g(o.func, {
663
- mode: m.Persistent,
664
- createWorker: o.createWorker
674
+ const s = this.findWorkerByName(e);
675
+ if (!s) throw new Error(`Worker "${e}" not found`);
676
+ let t = this._persistentWorkers.get(e);
677
+ if (!t) {
678
+ const l = new W(s.func, {
679
+ mode: P.Persistent,
680
+ createWorker: s.createWorker
665
681
  });
666
- a = this.trackWorker(i.getWorker), this._persistentWorkers.set(e, a);
682
+ t = this.trackWorker(l.getWorker), this._persistentWorkers.set(e, t);
667
683
  }
668
- return new Promise((i, c) => {
669
- a.onmessage = (t) => {
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);
672
- }, a.onerror = (t) => {
673
- c(t);
684
+ return new Promise((l, c) => {
685
+ t.onmessage = (a) => {
686
+ var o, d;
687
+ ((o = a.data) == null ? void 0 : o.ok) === !1 ? c(new Error(a.data.error)) : l((d = a.data) == null ? void 0 : d.data);
688
+ }, t.onerror = (a) => {
689
+ c(a);
674
690
  };
675
- const u = {
691
+ const f = {
676
692
  type: "run",
677
693
  config: r.config
678
694
  };
679
- r.dataset !== void 0 && (u.dataset = r.dataset), a.postMessage(u, k(u));
695
+ r.dataset !== void 0 && (f.dataset = r.dataset), t.postMessage(f, m(f));
680
696
  });
681
697
  }
682
698
  /**
@@ -738,7 +754,38 @@ class E {
738
754
  this.reset();
739
755
  }
740
756
  }
757
+ function V(n) {
758
+ if (typeof self > "u") return;
759
+ let e = null, r = null, s = null, t = {};
760
+ const l = (f, a) => {
761
+ self.postMessage(f, a);
762
+ };
763
+ async function c(f) {
764
+ try {
765
+ const a = typeof f == "object" && f !== null && "data" in f ? { ...t, ...f } : { data: f, ...t, index: 0 }, o = await n(a), d = { ok: !0, data: o }, p = m(o);
766
+ e ? e.postMessage(d, p) : l(d, p);
767
+ } catch (a) {
768
+ const o = {
769
+ ok: !1,
770
+ error: a instanceof Error ? a.message : String(a)
771
+ };
772
+ e ? e.postMessage(o) : l(o);
773
+ }
774
+ }
775
+ self.addEventListener("message", (f) => {
776
+ const a = f.data;
777
+ if (a && a.__pipeline_ports__) {
778
+ a.stepParams && (t = a.stepParams), a.outputPort && (e = a.outputPort), a.inputPort && (r = a.inputPort, r.onmessage = (o) => {
779
+ var d;
780
+ o.data && o.data.ok === !1 ? e ? e.postMessage(o.data) : l(o.data) : c({ data: (d = o.data) == null ? void 0 : d.data, ...t, index: 0 });
781
+ }), s !== null && (c(s), s = null);
782
+ return;
783
+ }
784
+ r ? s = a : c(a);
785
+ });
786
+ }
741
787
  export {
742
- E as MainWorkerFactory,
743
- g as WorkerFactory
788
+ U as MainWorkerFactory,
789
+ W as WorkerFactory,
790
+ V as defineWorker
744
791
  };
@@ -0,0 +1,21 @@
1
+ import { WorkerFunction } from './main-worker-factory/types';
2
+ /**
3
+ * Defines and exports a function to run inside a native Web Worker script,
4
+ * providing full compatibility with `MainWorkerFactory` features including
5
+ * standard runs, worker-to-worker pipelines (`foreman.pipeline()`), and
6
+ * dataset caching (`foreman.runPersistent()`).
7
+ *
8
+ * @typeParam TParams - The type of the payload sent to the worker.
9
+ * @typeParam TResult - The return type of the worker function.
10
+ *
11
+ * @param workerFn - The worker execution function.
12
+ *
13
+ * @example
14
+ * // my-native-worker.ts
15
+ * import { defineWorker } from '@offmain/workerkit';
16
+ *
17
+ * export default defineWorker(async ({ data }: { data: number[] }) => {
18
+ * return data.map((x) => x * 2);
19
+ * });
20
+ */
21
+ export declare function defineWorker<TParams = unknown, TResult = unknown>(workerFn: WorkerFunction<TParams, TResult | Promise<TResult>>): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,2 +1,3 @@
1
1
  export * from './worker-factory';
2
2
  export * from './main-worker-factory';
3
+ export * from './define-worker';
@@ -168,4 +168,6 @@ export interface PipelineStep {
168
168
  worker: string;
169
169
  /** Input data for the first step (subsequent steps receive previous output) */
170
170
  srcData?: unknown;
171
+ /** Any additional step parameters, configs, or options forwarded to the worker payload. */
172
+ [key: string]: unknown;
171
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@offmain/workerkit",
3
- "version": "0.13.0",
3
+ "version": "0.14.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",