@offmain/workerkit 0.14.0 → 1.0.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 +225 -46
- package/dist/define-worker.cjs +1 -0
- package/dist/define-worker.js +4 -0
- package/dist/index-2AONniOz.js +59 -0
- package/dist/index-CXKVsLvY.cjs +1 -0
- package/dist/index.cjs +195 -43
- package/dist/index.js +945 -487
- package/dist/types/tools/collect-results/collect-results.d.ts +26 -0
- package/dist/types/tools/collect-results/index.d.ts +2 -0
- package/dist/types/tools/collect-results/types.d.ts +10 -0
- package/dist/types/tools/define-worker/define-worker.d.ts +31 -0
- package/dist/types/tools/define-worker/define-worker.test.d.ts +1 -0
- package/dist/types/tools/define-worker/index.d.ts +1 -0
- package/dist/types/tools/define-worker-config/define-worker-config.d.ts +23 -0
- package/dist/types/tools/define-worker-config/index.d.ts +1 -0
- package/dist/types/tools/extract-transferable/extract-transferable.d.ts +15 -0
- package/dist/types/tools/extract-transferable/extract-transferable.test.d.ts +1 -0
- package/dist/types/tools/extract-transferable/index.d.ts +1 -0
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/logger/index.d.ts +2 -0
- package/dist/types/tools/logger/logger.d.ts +21 -0
- package/dist/types/tools/logger/logger.test.d.ts +1 -0
- package/dist/types/tools/logger/types.d.ts +6 -0
- package/dist/types/tools/main-worker-factory/main-worker-factory.d.ts +94 -228
- package/dist/types/tools/main-worker-factory/types.d.ts +71 -4
- package/dist/types/tools/memory-store/index.d.ts +3 -0
- package/dist/types/tools/memory-store/memory-store.d.ts +50 -0
- package/dist/types/tools/memory-store/memory-store.test.d.ts +1 -0
- package/dist/types/tools/memory-store/memory-worker-proxy.d.ts +75 -0
- package/dist/types/tools/memory-store/memory-worker.d.ts +11 -0
- package/dist/types/tools/orchestrator/index.d.ts +2 -0
- package/dist/types/tools/orchestrator/orchestrator.d.ts +21 -0
- package/dist/types/tools/orchestrator/orchestrator.test.d.ts +1 -0
- package/dist/types/tools/orchestrator/types.d.ts +10 -0
- package/dist/types/tools/partition-array/index.d.ts +1 -0
- package/dist/types/tools/partition-array/partition-array.d.ts +14 -0
- package/dist/types/tools/partition-array/partition-array.test.d.ts +1 -0
- package/dist/types/tools/persistent-manager/index.d.ts +2 -0
- package/dist/types/tools/persistent-manager/persistent-manager.d.ts +25 -0
- package/dist/types/tools/persistent-manager/persistent-manager.test.d.ts +1 -0
- package/dist/types/tools/persistent-manager/types.d.ts +9 -0
- package/dist/types/tools/pipeline/index.d.ts +2 -0
- package/dist/types/tools/pipeline/pipeline.d.ts +17 -0
- package/dist/types/tools/pipeline/pipeline.test.d.ts +1 -0
- package/dist/types/tools/pipeline/types.d.ts +13 -0
- package/dist/types/tools/run-worker/index.d.ts +2 -0
- package/dist/types/tools/run-worker/run-worker.d.ts +26 -0
- package/dist/types/tools/run-worker/run-worker.test.d.ts +1 -0
- package/dist/types/tools/run-worker/types.d.ts +14 -0
- package/dist/types/tools/worker-factory/index.d.ts +1 -1
- package/dist/types/tools/worker-factory/worker-factory.d.ts +7 -1
- package/dist/types/workers/initiator.d.ts +1 -1
- package/dist/types/workers/initiator.test.d.ts +1 -0
- package/package.json +11 -5
- package/dist/types/tools/define-worker.d.ts +0 -21
- /package/dist/types/tools/{define-worker.test.d.ts → collect-results/collect-results.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# workerkit
|
|
2
2
|
|
|
3
|
-
A lightweight TypeScript library for running functions in Web Workers with support for partitioning, retries, and concurrency control
|
|
3
|
+
A lightweight TypeScript library for running functions in Web Workers with support for partitioning, retries, and concurrency control all without the boilerplate.
|
|
4
4
|
|
|
5
5
|
Instead of manually creating worker scripts and wiring up `postMessage` / `onmessage`, you write plain exported functions and hand them to `MainWorkerFactory`. The library serializes them into Blob workers, manages threads per function, handles retries on failure, and merges results back on the main thread.
|
|
6
6
|
|
|
@@ -32,23 +32,29 @@ export function sum({ data }: { data: number[] }): number {
|
|
|
32
32
|
### 2. Register and run it
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
MainWorkerFactory,
|
|
37
|
+
defineWorkerConfig,
|
|
38
|
+
defineWorkerConfigs,
|
|
39
|
+
} from '@offmain/workerkit';
|
|
36
40
|
import { sum } from './sum.worker.ts';
|
|
37
41
|
|
|
38
42
|
const factory = new MainWorkerFactory({
|
|
39
|
-
workers:
|
|
40
|
-
{
|
|
43
|
+
workers: defineWorkerConfigs(
|
|
44
|
+
defineWorkerConfig({
|
|
41
45
|
name: 'sum',
|
|
42
46
|
role: 'computation',
|
|
43
47
|
func: sum,
|
|
44
48
|
maxConcurrency: 4,
|
|
45
49
|
retries: 2,
|
|
46
|
-
},
|
|
47
|
-
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
48
52
|
});
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
const { data } = await factory.
|
|
54
|
+
// Fully type-safe: 'sum' autocompletes, srcData is type-checked, and data is typed as number[]!
|
|
55
|
+
const { data, succeeded } = await factory.runWorker('sum', {
|
|
56
|
+
srcData: [1, 2, 3, 4, 5],
|
|
57
|
+
});
|
|
52
58
|
|
|
53
59
|
console.log(data); // [15]
|
|
54
60
|
```
|
|
@@ -57,15 +63,156 @@ console.log(data); // [15]
|
|
|
57
63
|
|
|
58
64
|
## WorkerConfig Options
|
|
59
65
|
|
|
60
|
-
| Option | Type | Default
|
|
61
|
-
| ---------------- | -------------- |
|
|
62
|
-
| `name` | `string` | —
|
|
63
|
-
| `role` | `string` | —
|
|
64
|
-
| `func` | `Function` | —
|
|
65
|
-
| `createWorker` | `() => Worker` | —
|
|
66
|
-
| `maxConcurrency` | `number` | `navigator
|
|
67
|
-
| `retries` | `number` | `0`
|
|
68
|
-
| `partition` | `boolean` | `false`
|
|
66
|
+
| Option | Type | Default | Description |
|
|
67
|
+
| ---------------- | -------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `name` | `string` | — | Unique identifier used to call the worker |
|
|
69
|
+
| `role` | `string` | — | Logical grouping label |
|
|
70
|
+
| `func` | `Function` | — | The exported worker function to run (optional if `createWorker` is provided) |
|
|
71
|
+
| `createWorker` | `() => Worker` | — | Worker factory function `() => new Worker(new URL(...))` for Webpack 5 / Vite static analysis |
|
|
72
|
+
| `maxConcurrency` | `number` | `navigator.`<br>`hardwareConcurrency` | Max parallel worker instances — defaults to the number of logical CPU cores reported by the browser |
|
|
73
|
+
| `retries` | `number` | `0` | How many times to retry a failed shard before marking it as rejected |
|
|
74
|
+
| `partition` | `boolean` | `false` | Split array input across multiple workers automatically |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🎯 Type-Friendly Configuration & Strict Inference
|
|
79
|
+
|
|
80
|
+
`workerkit` provides first-class, end-to-end TypeScript safety. You get full autocompletion for registered worker names, compile-time validation of input payloads (`srcData`), and strongly typed return data without manual type assertions.
|
|
81
|
+
|
|
82
|
+
### `defineWorkerConfig` & `defineWorkerConfigs`
|
|
83
|
+
|
|
84
|
+
Use `defineWorkerConfigs(...)` and `defineWorkerConfig(...)` to declare your worker suite. This eliminates the need for manual `as const` casts while preserving literal worker names:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import {
|
|
88
|
+
MainWorkerFactory,
|
|
89
|
+
defineWorkerConfig,
|
|
90
|
+
defineWorkerConfigs,
|
|
91
|
+
} from '@offmain/workerkit';
|
|
92
|
+
import { sum } from './sum.worker.ts';
|
|
93
|
+
import type { DataPayload, TransformedItem } from './transform.worker.ts';
|
|
94
|
+
|
|
95
|
+
const workerConfigs = defineWorkerConfigs(
|
|
96
|
+
// 1. Inlined function — input and output types are inferred automatically from `func`
|
|
97
|
+
defineWorkerConfig({
|
|
98
|
+
name: 'sum',
|
|
99
|
+
role: 'computation',
|
|
100
|
+
func: sum,
|
|
101
|
+
maxConcurrency: 4,
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
// 2. Bundled worker (createWorker) — explicit types via currying
|
|
105
|
+
defineWorkerConfig<(p: { data: DataPayload }) => TransformedItem[]>()({
|
|
106
|
+
name: 'transform',
|
|
107
|
+
role: 'transform',
|
|
108
|
+
createWorker: () =>
|
|
109
|
+
new Worker(new URL('./transform.worker.ts', import.meta.url), {
|
|
110
|
+
type: 'module',
|
|
111
|
+
}),
|
|
112
|
+
maxConcurrency: 2,
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const factory = new MainWorkerFactory({ workers: workerConfigs });
|
|
117
|
+
|
|
118
|
+
// ✅ 'sum' and 'transform' autocomplete in your IDE
|
|
119
|
+
// ✅ srcData is type-checked against DataPayload
|
|
120
|
+
// ✅ data is typed as TransformedItem[] (NOT unknown!)
|
|
121
|
+
const { data } = await factory.runWorker('transform', {
|
|
122
|
+
srcData: { items: [], locale: 'en' },
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 🔄 Migrating from v0.14.1 to v1.0.0 (Breaking Changes)
|
|
127
|
+
|
|
128
|
+
The upcoming **v1.0.0** release transforms `workerkit` into a modern, type-friendly library and streamlines the worker execution model. If you are upgrading from **v0.14.1** (published on npm), review the concrete breaking changes and migration steps below:
|
|
129
|
+
|
|
130
|
+
| Feature | In `v0.14.1` | In `v1.0.0` | Migration Action |
|
|
131
|
+
| :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
132
|
+
| **Worker Registration** | Pure raw object literals with mandatory `as const`: `[{ name: 'calc', func, ... }] as const`. Workers using `createWorker` had **no way to be typed**. | Replaced by `defineWorkerConfigs(...)` and `defineWorkerConfig(...)`. `createWorker` can now be strongly typed via currying. | Wrap configs with `defineWorkerConfigs(defineWorkerConfig({...}))`. Remove `as const`. |
|
|
133
|
+
| **`runWorker()` Execution** | **Two-step execution**: returned `TypedSettledResults` requiring an explicit `const { data } = await factory.collectResults(settled)`. | **Single-step execution**: auto-collects by default, directly returning `Promise<CollectedResult<R>>` with `{ data, succeeded, failed, errors }`. | Remove `collectResults(...)`. Destructure `{ data }` directly from `runWorker()`. Pass `autoCollect: false` only if you need raw settled results. |
|
|
134
|
+
| **`createWorker` Type Safety** | Untyped: lacked any type hint mechanism. Payloads and return values for bundled workers were always `unknown`. | **Curried Type Hint**: `defineWorkerConfig<WorkerFunctionType>()({ name: '...', createWorker: ... })` provides 100% type safety. | Use curried `defineWorkerConfig<T>()({...})` to define input and output types for bundled workers without runtime overhead. |
|
|
135
|
+
| **`pipeline()` Return Value** | Returned the unwrapped raw final value: `Promise<TResult>`. | Returns `Promise<CollectedResult<TResult>>` unified with `runWorker()`, including execution statistics (`succeeded`, `failed`, `errors`). | Access the final pipeline output via `result.data` instead of directly awaiting `result`. |
|
|
136
|
+
| **`runPersistent()` Typing** | `workerName` accepted any `string`, and the return type defaulted to untyped `unknown`. | Strictly typed: `workerName` autocompletes from registered configs, and return value is inferred from that worker's return type. | No casting needed — results are automatically type-safe. |
|
|
137
|
+
|
|
138
|
+
#### Concrete Code Comparison (v0.14.1 vs v1.0.0)
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// ❌ v0.14.1 (Pure configs + as const + 2-step execution + untyped createWorker)
|
|
143
|
+
// ============================================================================
|
|
144
|
+
import { MainWorkerFactory } from '@offmain/workerkit';
|
|
145
|
+
import { calcWorker } from './calc.worker';
|
|
146
|
+
|
|
147
|
+
const factory = new MainWorkerFactory({
|
|
148
|
+
workers: [
|
|
149
|
+
{
|
|
150
|
+
name: 'calc',
|
|
151
|
+
role: 'computation',
|
|
152
|
+
func: calcWorker,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'bundled',
|
|
156
|
+
role: 'computation',
|
|
157
|
+
// In v0.14.1, createWorker had NO way to define parameter or return types:
|
|
158
|
+
createWorker: () =>
|
|
159
|
+
new Worker(new URL('./bundled.worker.ts', import.meta.url), {
|
|
160
|
+
type: 'module',
|
|
161
|
+
}),
|
|
162
|
+
},
|
|
163
|
+
] as const, // Required 'as const' to preserve name literals
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Required two separate steps to get data:
|
|
167
|
+
const settled = await factory.runWorker('calc', { srcData: [1, 2, 3] });
|
|
168
|
+
const { data } = await factory.collectResults(settled);
|
|
169
|
+
|
|
170
|
+
// pipeline() returned raw unwrapped value:
|
|
171
|
+
const pipelineResult = await factory.pipeline([
|
|
172
|
+
{ worker: 'calc', srcData: [1, 2, 3] },
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// ✅ v1.0.0 (Type-friendly helpers + 1-step execution + fully-typed createWorker)
|
|
177
|
+
// ============================================================================
|
|
178
|
+
import {
|
|
179
|
+
MainWorkerFactory,
|
|
180
|
+
defineWorkerConfig,
|
|
181
|
+
defineWorkerConfigs,
|
|
182
|
+
} from '@offmain/workerkit';
|
|
183
|
+
import { calcWorker } from './calc.worker';
|
|
184
|
+
import type { BundledPayload, BundledResult } from './bundled.worker';
|
|
185
|
+
|
|
186
|
+
const factory = new MainWorkerFactory({
|
|
187
|
+
workers: defineWorkerConfigs(
|
|
188
|
+
// Inferred automatically from `func`:
|
|
189
|
+
defineWorkerConfig({
|
|
190
|
+
name: 'calc',
|
|
191
|
+
role: 'computation',
|
|
192
|
+
func: calcWorker,
|
|
193
|
+
}),
|
|
194
|
+
// Explicitly typed via curried signature (no 'unknown' returns!):
|
|
195
|
+
defineWorkerConfig<(p: { data: BundledPayload }) => BundledResult>()({
|
|
196
|
+
name: 'bundled',
|
|
197
|
+
role: 'computation',
|
|
198
|
+
createWorker: () =>
|
|
199
|
+
new Worker(new URL('./bundled.worker.ts', import.meta.url), {
|
|
200
|
+
type: 'module',
|
|
201
|
+
}),
|
|
202
|
+
}),
|
|
203
|
+
), // No 'as const' needed!
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// 1-step execution: autocompletes 'calc', validates srcData, and data is typed!
|
|
207
|
+
const { data, succeeded } = await factory.runWorker('calc', {
|
|
208
|
+
srcData: [1, 2, 3],
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// pipeline() returns CollectedResult with stats:
|
|
212
|
+
const { data: pipelineData } = await factory.pipeline([
|
|
213
|
+
{ worker: 'calc', srcData: [1, 2, 3] },
|
|
214
|
+
]);
|
|
215
|
+
```
|
|
69
216
|
|
|
70
217
|
---
|
|
71
218
|
|
|
@@ -108,11 +255,17 @@ export default defineWorker(
|
|
|
108
255
|
Webpack 5 and Vite look for literal `new Worker(new URL(..., import.meta.url))` calls inside consumer source files. By providing a `createWorker` factory function, bundlers statically detect and bundle the worker into a separate JS file, while allowing `MainWorkerFactory` to scale `maxConcurrency` across multiple threads:
|
|
109
256
|
|
|
110
257
|
```ts
|
|
111
|
-
import {
|
|
258
|
+
import {
|
|
259
|
+
MainWorkerFactory,
|
|
260
|
+
defineWorkerConfig,
|
|
261
|
+
defineWorkerConfigs,
|
|
262
|
+
} from '@offmain/workerkit';
|
|
112
263
|
|
|
113
264
|
const factory = new MainWorkerFactory({
|
|
114
|
-
workers:
|
|
115
|
-
|
|
265
|
+
workers: defineWorkerConfigs(
|
|
266
|
+
defineWorkerConfig<
|
|
267
|
+
(p: { data: { locale: string; items: { timestamp: number }[] } }) => any
|
|
268
|
+
>()({
|
|
116
269
|
name: 'transformData',
|
|
117
270
|
role: 'compute',
|
|
118
271
|
// Webpack 5 and Vite statically analyze new Worker(new URL(..., import.meta.url))
|
|
@@ -122,11 +275,11 @@ const factory = new MainWorkerFactory({
|
|
|
122
275
|
type: 'module',
|
|
123
276
|
}),
|
|
124
277
|
maxConcurrency: 4, // Spawns up to 4 parallel worker instances
|
|
125
|
-
},
|
|
126
|
-
|
|
278
|
+
}),
|
|
279
|
+
),
|
|
127
280
|
});
|
|
128
281
|
|
|
129
|
-
const
|
|
282
|
+
const { data } = await factory.runWorker('transformData', {
|
|
130
283
|
srcData: { locale: 'es', items: [{ timestamp: Date.now() }] },
|
|
131
284
|
});
|
|
132
285
|
```
|
|
@@ -138,23 +291,33 @@ const settled = await factory.runWorker('transformData', {
|
|
|
138
291
|
When `partition: true`, an array passed as `srcData` is automatically split across worker instances and results are merged back.
|
|
139
292
|
|
|
140
293
|
```ts
|
|
141
|
-
|
|
294
|
+
// Results are automatically merged and returned by default!
|
|
295
|
+
const { data, succeeded, failed } = await factory.runWorker('sum', {
|
|
142
296
|
srcData: largeArray, // split across workers
|
|
143
297
|
});
|
|
144
|
-
|
|
145
|
-
const { data, succeeded, failed } = await factory.collectResults(settled);
|
|
146
298
|
```
|
|
147
299
|
|
|
148
|
-
You can also provide a custom reducer to control how shard results are merged:
|
|
300
|
+
You can also provide a custom reducer directly to `runWorker` to control how shard results are merged:
|
|
149
301
|
|
|
150
302
|
```ts
|
|
151
|
-
const { data } = await factory.
|
|
303
|
+
const { data } = await factory.runWorker('sum', {
|
|
304
|
+
srcData: largeArray,
|
|
152
305
|
reducer: (shards) => shards.flat().sort((a, b) => b.score - a.score),
|
|
153
306
|
});
|
|
154
307
|
```
|
|
155
308
|
|
|
309
|
+
_(Optional escape hatch: pass `autoCollect: false` to `runWorker` to receive raw settled results and manually call `factory.collectResults(settled, options)`)._
|
|
310
|
+
|
|
156
311
|
> **Note:** The reducer runs inside a worker thread and must be self-contained — it cannot reference variables from the outer scope.
|
|
157
312
|
|
|
313
|
+
### Dynamic Thread Scaling & Partitioning Behavior
|
|
314
|
+
|
|
315
|
+
When `partition: true` is enabled on a worker:
|
|
316
|
+
|
|
317
|
+
- **Dynamic Thread Allocation:** The library calculates worker thread count as `Math.min(maxConcurrency, srcData.length)`. For instance, if an array has 2 items and `maxConcurrency` is 20, the factory will spawn **only 2 worker threads** (instead of 20), eliminating idle thread overhead and memory pressure.
|
|
318
|
+
- **No Data Duplication:** Each thread receives only its assigned chunk (e.g. Worker 0 gets `[item1]`, Worker 1 gets `[item2]`), ensuring results are processed once without duplication.
|
|
319
|
+
- **Non-Partitioned Workers (`partition: false` / omitted):** If `partition` is not enabled, the input payload is not split, and up to `maxConcurrency` threads will each execute the full payload independently in parallel.
|
|
320
|
+
|
|
158
321
|
---
|
|
159
322
|
|
|
160
323
|
## Pipeline
|
|
@@ -181,17 +344,22 @@ Only the final result crosses back to the main thread. If your pipeline generate
|
|
|
181
344
|
|
|
182
345
|
### Usage
|
|
183
346
|
|
|
184
|
-
|
|
185
|
-
import {
|
|
347
|
+
```ts
|
|
348
|
+
import {
|
|
349
|
+
MainWorkerFactory,
|
|
350
|
+
defineWorkerConfig,
|
|
351
|
+
defineWorkerConfigs,
|
|
352
|
+
} from '@offmain/workerkit';
|
|
186
353
|
import { fetchData, transform, aggregate } from './workers.ts';
|
|
187
354
|
|
|
188
355
|
const factory = new MainWorkerFactory({
|
|
189
|
-
workers:
|
|
190
|
-
{ name: 'fetchData', role: 'io', func: fetchData },
|
|
191
|
-
{ name: 'transform', role: 'compute', func: transform },
|
|
192
|
-
{ name: 'aggregate', role: 'compute', func: aggregate },
|
|
193
|
-
|
|
356
|
+
workers: defineWorkerConfigs(
|
|
357
|
+
defineWorkerConfig({ name: 'fetchData', role: 'io', func: fetchData }),
|
|
358
|
+
defineWorkerConfig({ name: 'transform', role: 'compute', func: transform }),
|
|
359
|
+
defineWorkerConfig({ name: 'aggregate', role: 'compute', func: aggregate }),
|
|
360
|
+
),
|
|
194
361
|
});
|
|
362
|
+
```
|
|
195
363
|
|
|
196
364
|
### Step-Specific Options and Configs in Pipeline
|
|
197
365
|
|
|
@@ -213,7 +381,7 @@ const result = await factory.pipeline<AggregateResult>([
|
|
|
213
381
|
options: { threshold: 10 },
|
|
214
382
|
},
|
|
215
383
|
]);
|
|
216
|
-
|
|
384
|
+
```
|
|
217
385
|
|
|
218
386
|
### How each step receives data and parameters
|
|
219
387
|
|
|
@@ -223,12 +391,15 @@ const result = await factory.pipeline<AggregateResult>([
|
|
|
223
391
|
|
|
224
392
|
### When to use pipeline vs runWorker
|
|
225
393
|
|
|
226
|
-
| Scenario
|
|
227
|
-
|
|
|
228
|
-
| Single step, or steps that need partitioning
|
|
229
|
-
| Multi-step chain where intermediate data is large
|
|
230
|
-
| Steps that are independent (not sequential)
|
|
231
|
-
| Steps where only the final result matters to the UI
|
|
394
|
+
| Scenario | Use |
|
|
395
|
+
| ------------------------------------------------------------- | ----------------------- |
|
|
396
|
+
| Single step, or steps that need multi-core array partitioning | `runWorker` |
|
|
397
|
+
| Multi-step chain where intermediate data is large | `pipeline` |
|
|
398
|
+
| Steps that are independent (not sequential) | `runWorker` in parallel |
|
|
399
|
+
| Steps where only the final result matters to the UI | `pipeline` |
|
|
400
|
+
|
|
401
|
+
> **Note on `partition: true` in Pipelines:**
|
|
402
|
+
> `pipeline()` creates **1 worker thread per step** in a linear 1:1 `MessageChannel` chain. If a worker in a pipeline step has `partition: true`, `pipeline()` processes it as a single streaming step without splitting it across parallel worker threads. For parallel multi-core array partitioning across CPU threads, use `runWorker()`.
|
|
232
403
|
|
|
233
404
|
---
|
|
234
405
|
|
|
@@ -259,13 +430,21 @@ Only the first call transfers the dataset. Subsequent calls send just the config
|
|
|
259
430
|
### Usage
|
|
260
431
|
|
|
261
432
|
```ts
|
|
262
|
-
import {
|
|
433
|
+
import {
|
|
434
|
+
MainWorkerFactory,
|
|
435
|
+
defineWorkerConfig,
|
|
436
|
+
defineWorkerConfigs,
|
|
437
|
+
} from '@offmain/workerkit';
|
|
263
438
|
import { transformArray } from './transform.worker.ts';
|
|
264
439
|
|
|
265
440
|
const factory = new MainWorkerFactory({
|
|
266
|
-
workers:
|
|
267
|
-
{
|
|
268
|
-
|
|
441
|
+
workers: defineWorkerConfigs(
|
|
442
|
+
defineWorkerConfig({
|
|
443
|
+
name: 'transform',
|
|
444
|
+
role: 'computation',
|
|
445
|
+
func: transformArray,
|
|
446
|
+
}),
|
|
447
|
+
),
|
|
269
448
|
});
|
|
270
449
|
|
|
271
450
|
// First call: send dataset + config (dataset gets cached in worker memory)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-CXKVsLvY.cjs");exports.defineWorker=e.defineWorker;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function m(e, s = /* @__PURE__ */ new Set()) {
|
|
2
|
+
return e === null || typeof e != "object" ? [] : s.has(e) ? [] : (s.add(e), e instanceof ArrayBuffer || e instanceof MessagePort || typeof ImageBitmap < "u" && e instanceof ImageBitmap || typeof OffscreenCanvas < "u" && e instanceof OffscreenCanvas ? [e] : ArrayBuffer.isView(e) ? [e.buffer] : Array.isArray(e) ? e.flatMap((o) => m(o, s)) : Object.values(e).flatMap(
|
|
3
|
+
(o) => m(o, s)
|
|
4
|
+
));
|
|
5
|
+
}
|
|
6
|
+
function k(e) {
|
|
7
|
+
if (typeof self > "u") return;
|
|
8
|
+
let s = null, o = null, i = null, p = {}, n = null, y = null;
|
|
9
|
+
const l = (a, t) => {
|
|
10
|
+
self.postMessage(a, t);
|
|
11
|
+
};
|
|
12
|
+
async function d(a) {
|
|
13
|
+
try {
|
|
14
|
+
const t = typeof a == "object" && a !== null && "data" in a ? { ...p, ...a } : { data: a, ...p, index: 0 }, r = await e(t), f = m(r);
|
|
15
|
+
if (s)
|
|
16
|
+
s.postMessage({ ok: !0, data: r }, f);
|
|
17
|
+
else if (n) {
|
|
18
|
+
const u = "mem_" + crypto.randomUUID();
|
|
19
|
+
await new Promise((g, P) => {
|
|
20
|
+
n.onmessage = (c) => {
|
|
21
|
+
var _;
|
|
22
|
+
((_ = c.data) == null ? void 0 : _.ref) === u && (c.data.ok ? g() : P(new Error(c.data.error ?? "MemoryWorker SET failed")));
|
|
23
|
+
}, n.postMessage({
|
|
24
|
+
action: "SET",
|
|
25
|
+
factoryToken: y,
|
|
26
|
+
ref: u,
|
|
27
|
+
data: r
|
|
28
|
+
});
|
|
29
|
+
}), l({ ok: !0, __memory_ref__: u });
|
|
30
|
+
} else
|
|
31
|
+
l({ ok: !0, data: r }, f);
|
|
32
|
+
} catch (t) {
|
|
33
|
+
const r = {
|
|
34
|
+
ok: !1,
|
|
35
|
+
error: t instanceof Error ? t.message : String(t)
|
|
36
|
+
};
|
|
37
|
+
s ? s.postMessage(r) : l(r);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
self.addEventListener("message", (a) => {
|
|
41
|
+
const t = a.data;
|
|
42
|
+
if (t && t.__init_memory_port__) {
|
|
43
|
+
n = a.ports[0] ?? t.memPort, y = t.factoryToken, n && n.start();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (t && t.__pipeline_ports__) {
|
|
47
|
+
t.stepParams && (p = t.stepParams), t.outputPort && (s = t.outputPort), t.inputPort && (o = t.inputPort, o.onmessage = (r) => {
|
|
48
|
+
var f;
|
|
49
|
+
r.data && r.data.ok === !1 ? s ? s.postMessage(r.data) : l(r.data) : d({ data: (f = r.data) == null ? void 0 : f.data, ...p, index: 0 });
|
|
50
|
+
}), i !== null && (d(i), i = null);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
o ? i = t : d(t);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
k as d,
|
|
58
|
+
m as e
|
|
59
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function c(e,s=new Set){return e===null||typeof e!="object"?[]:s.has(e)?[]:(s.add(e),e instanceof ArrayBuffer||e instanceof MessagePort||typeof ImageBitmap<"u"&&e instanceof ImageBitmap||typeof OffscreenCanvas<"u"&&e instanceof OffscreenCanvas?[e]:ArrayBuffer.isView(e)?[e.buffer]:Array.isArray(e)?e.flatMap(n=>c(n,s)):Object.values(e).flatMap(n=>c(n,s)))}function k(e){if(typeof self>"u")return;let s=null,n=null,i=null,p={},o=null,y=null;const l=(a,t)=>{self.postMessage(a,t)};async function u(a){try{const t=typeof a=="object"&&a!==null&&"data"in a?{...p,...a}:{data:a,...p,index:0},r=await e(t),f=c(r);if(s)s.postMessage({ok:!0,data:r},f);else if(o){const d="mem_"+crypto.randomUUID();await new Promise((g,P)=>{o.onmessage=m=>{var _;((_=m.data)==null?void 0:_.ref)===d&&(m.data.ok?g():P(new Error(m.data.error??"MemoryWorker SET failed")))},o.postMessage({action:"SET",factoryToken:y,ref:d,data:r})}),l({ok:!0,__memory_ref__:d})}else l({ok:!0,data:r},f)}catch(t){const r={ok:!1,error:t instanceof Error?t.message:String(t)};s?s.postMessage(r):l(r)}}self.addEventListener("message",a=>{const t=a.data;if(t&&t.__init_memory_port__){o=a.ports[0]??t.memPort,y=t.factoryToken,o&&o.start();return}if(t&&t.__pipeline_ports__){t.stepParams&&(p=t.stepParams),t.outputPort&&(s=t.outputPort),t.inputPort&&(n=t.inputPort,n.onmessage=r=>{var f;r.data&&r.data.ok===!1?s?s.postMessage(r.data):l(r.data):u({data:(f=r.data)==null?void 0:f.data,...p,index:0})}),i!==null&&(u(i),i=null);return}n?i=t:u(t)})}exports.defineWorker=k;exports.extractTransferable=c;
|