nestworker 1.1.20 → 2.0.4

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.
Files changed (63) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +334 -143
  3. package/dist/core/worker.interfaces.d.ts +36 -0
  4. package/dist/{models/task.js → core/worker.interfaces.js} +1 -1
  5. package/dist/core/worker.interfaces.js.map +1 -0
  6. package/dist/core/worker.module.d.ts +15 -0
  7. package/dist/core/worker.module.js +43 -0
  8. package/dist/core/worker.module.js.map +1 -0
  9. package/dist/core/worker.pool.d.ts +20 -0
  10. package/dist/core/worker.pool.js +210 -0
  11. package/dist/core/worker.pool.js.map +1 -0
  12. package/dist/core/worker.service.d.ts +87 -0
  13. package/dist/core/worker.service.js +133 -0
  14. package/dist/core/worker.service.js.map +1 -0
  15. package/dist/decorators/worker-task.decorator.d.ts +37 -0
  16. package/dist/decorators/worker-task.decorator.js +48 -0
  17. package/dist/decorators/worker-task.decorator.js.map +1 -0
  18. package/dist/di/di-serializer.d.ts +18 -0
  19. package/dist/di/di-serializer.js +110 -0
  20. package/dist/di/di-serializer.js.map +1 -0
  21. package/dist/di/worker-container.d.ts +58 -0
  22. package/dist/di/worker-container.js +110 -0
  23. package/dist/di/worker-container.js.map +1 -0
  24. package/dist/discovery/discovery.service.d.ts +20 -0
  25. package/dist/discovery/discovery.service.js +94 -0
  26. package/dist/discovery/discovery.service.js.map +1 -0
  27. package/dist/example/config.service.d.ts +13 -0
  28. package/dist/example/config.service.js +35 -0
  29. package/dist/example/config.service.js.map +1 -0
  30. package/dist/example/image.service.d.ts +9 -0
  31. package/dist/example/image.service.js +108 -0
  32. package/dist/example/image.service.js.map +1 -0
  33. package/dist/example/main.d.ts +1 -0
  34. package/dist/example/main.js +59 -0
  35. package/dist/example/main.js.map +1 -0
  36. package/dist/index.d.ts +5 -0
  37. package/dist/index.js +11 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/worker/worker-runtime.js +55 -0
  40. package/dist/worker/worker-runtime.js.map +1 -0
  41. package/package.json +58 -55
  42. package/CONTRIBUTING.md +0 -33
  43. package/dist/main.d.ts +0 -3
  44. package/dist/main.js +0 -29
  45. package/dist/main.js.map +0 -1
  46. package/dist/models/index.d.ts +0 -1
  47. package/dist/models/index.js +0 -18
  48. package/dist/models/index.js.map +0 -1
  49. package/dist/models/task.d.ts +0 -5
  50. package/dist/models/task.js.map +0 -1
  51. package/dist/tsconfig.build.tsbuildinfo +0 -1
  52. package/dist/worker/executor.d.ts +0 -5
  53. package/dist/worker/executor.js +0 -28
  54. package/dist/worker/executor.js.map +0 -1
  55. package/dist/worker/skeleton.d.ts +0 -6
  56. package/dist/worker/skeleton.js +0 -35
  57. package/dist/worker/skeleton.js.map +0 -1
  58. package/dist/worker/worker.js +0 -6
  59. package/dist/worker/worker.js.map +0 -1
  60. package/nest-cli.json +0 -8
  61. package/tsconfig.build.json +0 -4
  62. package/tsconfig.json +0 -25
  63. /package/dist/worker/{worker.d.ts → worker-runtime.d.ts} +0 -0
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Vahe Hakobyan
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,143 +1,334 @@
1
- ![](https://img.shields.io/badge/dependencies-none-brightgreen.svg)
2
- ![](https://img.shields.io/npm/dt/nestworker.svg)
3
- ![](https://img.shields.io/npm/v/nestworker.svg)
4
- ![](https://img.shields.io/npm/l/nestworker.svg)
5
- ![](https://img.shields.io/github/issues/VaheHak/nestworker.svg)
6
- ![](https://img.shields.io/github/contributors/VaheHak/nestworker.svg)
7
- ![](https://img.shields.io/github/last-commit/VaheHak/nestworker.svg)
8
- ![](https://img.shields.io/github/forks/VaheHak/nestworker.svg)
9
- ![](https://img.shields.io/github/stars/VaheHak/nestworker.svg)
10
- ![](https://img.shields.io/github/watchers/VaheHak/nestworker.svg)
11
-
12
-
13
- # nestworker
14
- A simple library that provides an abstraction for the Node.js `worker_threads` module. You can run your function in a dedicated thread by working with Promises.
15
-
16
- ### Example
17
- ```ts
18
- import { executeInThread } from 'nestworker';
19
-
20
- async function calculate(): Promise<void> {
21
- const values = await Promise.all([
22
- executeInThread(() => 2 ** 10), // this doesn't block the main thread
23
- executeInThread(() => 3 ** 10),
24
- ]);
25
-
26
- console.log(values); // [1024, 59049]
27
- }
28
-
29
- calculate();
30
- ```
31
-
32
- This example demonstrates the optimization of two resource-intensive calculations through parallel execution in distinct threads.
33
- By distributing the tasks across separate threads, significant time savings are achieved.
34
-
35
- Nestworker's takes a task function as its parameter, orchestrates its execution in a new thread, and subsequently delivers a Promise.
36
-
37
- **Surprisingly simple, isn't it?**
38
-
39
- ## Installation
40
-
41
- ```shell
42
- $ npm i nestworker
43
- ```
44
-
45
- ## All examples:
46
- - [Basic example](https://github.com/VaheHak/nestworker/tree/master/examples/basic.ts)
47
- - [Parameters for the thread task](https://github.com/VaheHak/nestworker/blob/master/examples/multi-params.ts)
48
- - [Async function inside the thread](https://github.com/VaheHak/nestworker/blob/master/examples/async-task.ts)
49
- - [Error handling](https://github.com/VaheHak/nestworker/blob/master/examples/error-handling.ts)
50
- - [Use modules inside the thread](https://github.com/VaheHak/nestworker/blob/master/examples/modules-in-thread.ts)
51
-
52
- ## API
53
-
54
- ### `executeInThread(task, { args: any[] }`
55
- Runs the specified function in a separate thread.
56
-
57
- #### Parameters
58
- - `Task (Function)`: The function to be executed in a thread.
59
- - This can also be a async function (promise).
60
- - `...params (Any)`: Additional arguments to be passed to the Task function.
61
- - Parameter cann't be a function.
62
-
63
- ```ts
64
- const task = function(a: number, b: object, c: boolean) { ... };
65
- executeInThread(task, { args: [1, {}, true] })
66
- ```
67
-
68
- The `executeInThread` function allows you to execute a given task function in a dedicated thread, similar to the behavior of `setTimeout` or `setInterval`. You provide the main function to be executed, along with any additional arguments (...args) that should be passed to the given function.
69
-
70
- #### Returns
71
- `Promise<any>`: A Promise that resolves with the return value of the callback.
72
-
73
- Inside the provided function, you have the flexibility to return any value, including a Promise. The returned value, whether it's a standard value or a Promise, will be passed back to you as the resolved result of the `Promise` returned by the `executeInThread` function.
74
-
75
- ```ts
76
- const number = await executeInThread(() => 123); // 123
77
- const name = await executeInThread(() => Promise.resolve('John')); // John
78
- ```
79
-
80
- #### Important (limitation)
81
-
82
- Access to data outside of the task function is restricted. If you require the use of a module, it should be required within the task function. The sole method for accessing data within a task function from external sources is through the utilization of the parameters. Closures do not function in this context.
83
-
84
- In this example, we're reading a file in a separate thread and returning the data in string format. We start by defining a task function that will run within the thread, and then we prepare the necessary parameters to be passed as inputs to that function.
85
-
86
- ```ts
87
- import { executeInThread } from 'nestworker';
88
-
89
- async function task(filename: string) {
90
- // Closure doesn't work here
91
- const { readFile } = await import('fs/promises');
92
- const content = await readFile(filename);
93
- return content.toString();
94
- }
95
-
96
- async function read() {
97
- const content = await executeInThread(task, { args: [filename] });
98
- console.log(content);
99
- }
100
-
101
- read();
102
- ```
103
-
104
- There is also another option if you don't want to use `import` inside the function.
105
-
106
- ```ts
107
- import { executeInThread } from 'nestworker';
108
-
109
- // this will be executed in a dedicated thread
110
- async function task(modules: { 'fs/promises': typeof import('fs/promises') }) {
111
- // Closure doesn't work here
112
- const { readFile } = modules['fs/promises'];
113
-
114
- const content = await readFile(__filename);
115
-
116
- return content.toString();
117
- }
118
-
119
- async function read() {
120
- const content = await executeInThread(task, {
121
- threadModules: ['fs/promises'],
122
- });
123
-
124
- console.log(content);
125
- }
126
-
127
- read();
128
- ```
129
-
130
- The `threadModules` parameter is an array of strings that represent the `modules` you want to use in the thread.
131
- The `modules` will be imported and passed to the task function `first argument` as an object.
132
-
133
- ## Contributing
134
-
135
- See the [contributing guide](https://github.com/VaheHak/nestworker/blob/master/CONTRIBUTING.md) for detailed instructions on how to get started with our project.
136
-
137
- ## Author
138
-
139
- Vahe Hakobyan: [Telegram](https://t.me/vahe_hak)
140
-
141
- ## License
142
-
143
- Licensed under [MIT](https://github.com/VaheHak/nestworker/blob/master/LICENSE).
1
+ ![](https://img.shields.io/badge/dependencies-none-brightgreen.svg)
2
+ ![](https://img.shields.io/npm/dt/nestworker.svg)
3
+ ![](https://img.shields.io/npm/v/nestworker.svg)
4
+ ![](https://img.shields.io/npm/l/nestworker.svg)
5
+ ![](https://img.shields.io/github/issues/VaheHak/nestworker.svg)
6
+ ![](https://img.shields.io/github/contributors/VaheHak/nestworker.svg)
7
+ ![](https://img.shields.io/github/last-commit/VaheHak/nestworker.svg)
8
+ ![](https://img.shields.io/github/forks/VaheHak/nestworker.svg)
9
+ ![](https://img.shields.io/github/stars/VaheHak/nestworker.svg)
10
+ ![](https://img.shields.io/github/watchers/VaheHak/nestworker.svg)
11
+
12
+ <p align="center">
13
+ <img src="icon.svg" width="120" alt="nestworker" />
14
+ </p>
15
+
16
+ # nestworker
17
+
18
+ Enterprise-grade worker thread module for NestJS. Offload CPU-bound work to a managed pool of Node.js worker threads without blocking the event loop — with decorator-driven auto-discovery, priority queuing, and transparent NestJS dependency injection inside workers.
19
+
20
+ ---
21
+
22
+ ## Features
23
+
24
+ - **Worker pool** — pre-spawned threads with backpressure queue; no jobs are ever dropped
25
+ - **Priority queue** — `HIGH / NORMAL / LOW`, binary-search sorted
26
+ - **Decorator discovery** — `@WorkerClass` + `@WorkerTask` replace all manual registration
27
+ - **DI in workers** — declared deps are snapshotted and reconstructed in each thread
28
+ - **Dynamic imports** — use `await import('node:os')` inside task methods
29
+ - **Per-task timeout** — configurable via decorator or overridden per call
30
+ - **Safe shutdown** — drains queue, terminates workers with a 2-second deadline
31
+
32
+ ---
33
+
34
+ ## Requirements
35
+
36
+ | Package | Version |
37
+ |---|---|
38
+ | Node.js | ≥ 16 (worker_threads) |
39
+ | `@nestjs/common` | `^10` or `^11` |
40
+ | `@nestjs/core` | `^10` or `^11` |
41
+ | `reflect-metadata` | `^0.1` or `^0.2` |
42
+ | TypeScript `target` | `ES2022` or higher |
43
+
44
+ `tsconfig.json` must have:
45
+
46
+ ```json
47
+ {
48
+ "compilerOptions": {
49
+ "target": "ES2022",
50
+ "experimentalDecorators": true,
51
+ "emitDecoratorMetadata": true
52
+ }
53
+ }
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ npm install nestworker
62
+ ```
63
+ ---
64
+
65
+ ## Quick Start
66
+
67
+ ### 1. Import `WorkerModule` in your root module
68
+
69
+ ```ts
70
+ // app.module.ts
71
+ import { Module } from '@nestjs/common';
72
+ import { WorkerModule } from 'nestworker';
73
+ import { ConfigService } from './config.service';
74
+ import { ImageService } from './image.service';
75
+
76
+ @Module({
77
+ imports: [WorkerModule.forRoot()],
78
+ providers: [ConfigService, ImageService], // register your @WorkerClass providers here
79
+ })
80
+ export class AppModule {}
81
+ ```
82
+
83
+ ### 2. Decorate your service
84
+
85
+ ```ts
86
+ // image.service.ts
87
+ import { Injectable } from '@nestjs/common';
88
+ import { WorkerClass, WorkerTask } from 'nestworker';
89
+ import { ConfigService } from './config.service';
90
+
91
+ @Injectable()
92
+ @WorkerClass({ deps: [ConfigService] }) // deps are injected into the worker
93
+ export class ImageService {
94
+ constructor(private readonly configService: ConfigService) {}
95
+
96
+ @WorkerTask({ priority: 'HIGH' })
97
+ resizeImage(value: number): number {
98
+ // runs in a worker thread — configService works normally here
99
+ const multiplier = this.configService.getNumber('MULTIPLIER');
100
+ let total = 0;
101
+ for (let i = 0; i < 10_000_000; i++) total += i * value * multiplier;
102
+ return total;
103
+ }
104
+
105
+ @WorkerTask({ priority: 'NORMAL', timeout: 5000 })
106
+ generateThumbnail(width: number, height: number): string {
107
+ let hash = 0;
108
+ for (let i = 0; i < 5_000_000; i++) hash ^= (i * width * height) | 0;
109
+ return `thumb_${hash.toString(16)}_${width}x${height}.webp`;
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### 3. Inject `WorkerService` and call `run()`
115
+
116
+ ```ts
117
+ // image.controller.ts
118
+ import { Controller, Get } from '@nestjs/common';
119
+ import { WorkerService } from 'nestworker';
120
+
121
+ @Controller('images')
122
+ export class ImageController {
123
+ constructor(private readonly workerService: WorkerService) {}
124
+
125
+ @Get('resize')
126
+ async resize() {
127
+ return this.workerService.run<number>('ImageService', 'resizeImage', [5]);
128
+ }
129
+
130
+ @Get('thumbnail')
131
+ async thumbnail() {
132
+ return this.workerService.run<string>(
133
+ 'ImageService', 'generateThumbnail', [1920, 1080]
134
+ );
135
+ }
136
+ }
137
+ ```
138
+ ---
139
+
140
+ ## Using Modules Inside Task Methods
141
+
142
+ Worker tasks are reconstructed from class source via `eval()`. Top-level `import` statements from your file are **not available** inside the worker. Use one of these two patterns instead.
143
+
144
+ ### Dynamic import — preferred
145
+
146
+ `import()` is a language keyword, not a variable. It works natively inside `eval()`'d code with no setup, and is compatible with both ESM and CommonJS projects.
147
+
148
+ ```ts
149
+ @WorkerTask()
150
+ async moduleImport(): Promise<string> {
151
+ const os = await import('node:os');
152
+ return `Import os size ${os.cpus().length}`
153
+ }
154
+ ```
155
+
156
+ ### Inline `require()` — CJS projects only
157
+
158
+ `require` is injected into the eval scope by `WorkerContainer`, so it works in CommonJS projects.
159
+
160
+ ```ts
161
+ @WorkerTask()
162
+ async moduleRequire(): Promise<string> {
163
+ const os = require('node:os');
164
+ return `Require os size ${os.cpus().length}`
165
+ }
166
+ ```
167
+
168
+ ### What is safe to import inside a worker
169
+
170
+ | ✅ Safe | ❌ Not safe |
171
+ |---|---|
172
+ | Node built-ins: `os`, `path`, `crypto`, `zlib`, `fs` | HTTP clients (`axios`, `fetch`) |
173
+ | Pure computation libraries | Database drivers |
174
+ | `Buffer`, `Math`, `Date` | `Socket`, `Stream` |
175
+
176
+ ---
177
+
178
+ ## API
179
+
180
+ ### `WorkerModule.forRoot(options?)`
181
+
182
+ Registers the module globally. Call once at the application root.
183
+
184
+ ```ts
185
+ WorkerModule.forRoot({
186
+ poolSize: 4, // default: os.cpus().length
187
+ })
188
+ ```
189
+
190
+ | Option | Type | Default | Description |
191
+ |---|---|---|---|
192
+ | `poolSize` | `number` | `os.cpus().length` | Number of worker threads to spawn |
193
+
194
+ ---
195
+
196
+ ### `@WorkerClass(options?)`
197
+
198
+ Class decorator. Marks a NestJS provider as a container of worker tasks.
199
+
200
+ ```ts
201
+ @WorkerClass({ deps: [ConfigService, LoggerService] })
202
+ export class MyService { ... }
203
+ ```
204
+
205
+ | Option | Type | Description |
206
+ |---|---|---|
207
+ | `deps` | `Type[]` | Injectable dependencies to reconstruct inside workers |
208
+
209
+ ---
210
+
211
+ ### `@WorkerTask(options?)`
212
+
213
+ Method decorator. Marks a method to be offloaded to a worker thread.
214
+
215
+ ```ts
216
+ @WorkerTask({ priority: 'HIGH', timeout: 3000 })
217
+ heavyComputation(input: number): number { ... }
218
+ ```
219
+
220
+ | Option | Type | Default | Description |
221
+ |---|---|---|---|
222
+ | `priority` | `'HIGH' \| 'NORMAL' \| 'LOW'` | `'NORMAL'` | Queue priority — `HIGH` jobs run first |
223
+ | `timeout` | `number` | `undefined` | Reject the job after this many ms |
224
+
225
+ ---
226
+
227
+ ### `WorkerService.run<T>(serviceName, methodName, args, overrides?)`
228
+
229
+ Executes a `@WorkerTask` method in a worker thread.
230
+
231
+ ```ts
232
+ // Uses priority/timeout from the @WorkerTask decorator
233
+ const result = await workerService.run<number>('ImageService', 'resizeImage', [5]);
234
+
235
+ // Override priority or timeout for a specific call
236
+ const result = await workerService.run<number>(
237
+ 'ImageService', 'resizeImage', [5],
238
+ { priority: 'LOW', timeout: 10_000 }
239
+ );
240
+ ```
241
+
242
+ | Parameter | Type | Description |
243
+ |---|---|---|
244
+ | `serviceName` | `string` | Class name of the `@WorkerClass` provider |
245
+ | `methodName` | `string` | Method name decorated with `@WorkerTask` |
246
+ | `args` | `unknown[]` | Arguments to pass — must be structuredClone-compatible |
247
+ | `overrides` | `object` | Optional `priority` / `timeout` override for this call |
248
+
249
+ Returns a `Promise<T>` that resolves with the method's return value.
250
+
251
+ ---
252
+
253
+ ## How DI in Workers Works
254
+
255
+ Worker threads run in an isolated V8 context — they share no heap with the main thread. Passing live NestJS services across the boundary is impossible.
256
+
257
+ This module solves it in three steps:
258
+
259
+ **1. Main thread — `serializeForWorker()`**
260
+
261
+ `Class.toString()` extracts each class as a plain JS source string (no imports, no decorators). Each dep's data properties are snapshotted via `structuredClone`. Both are sent to workers via `workerData`.
262
+
263
+ **2. Worker thread — `WorkerContainer`**
264
+
265
+ The class source strings are `eval()`'d back into constructors. Each dep is reconstructed as `Object.create(DepClass.prototype) + Object.assign(snapshot)` — restoring prototype methods AND runtime state. The service class is then `new ServiceClass(...depInstances)`.
266
+
267
+ **3. Result**
268
+
269
+ `this.configService.get('KEY')` inside a worker task works exactly as on the main thread — as long as the dep reads from plain data (no DB connections, no HTTP clients).
270
+
271
+ ```
272
+ MAIN THREAD WORKER THREAD
273
+ ──────────────────────────────── ────────────────────────────────────
274
+ WorkerService.run()
275
+ → discovery.scan()
276
+ → ConfigService live instance
277
+ → snapshot: { config: {...} } → Object.create(ConfigService.prototype)
278
+ → classSource: "class Cfg..." → eval("class ConfigService { get()... }")
279
+ Object.assign(inst, snapshot)
280
+ → ImageService classSource → eval("class ImageService {...}")
281
+ new ImageService(configInst)
282
+ this.configService.get() ✓
283
+ ```
284
+
285
+ ### What deps can be passed to workers
286
+
287
+ ✅ Services holding plain config data (`Record`, `Map`, arrays, primitives)
288
+ ✅ Services whose methods only read from their own properties
289
+ ❌ Services that hold DB connections, HTTP clients, or open streams
290
+ ❌ Services with `Socket`, `Stream`, or non-cloneable properties
291
+
292
+ ---
293
+
294
+ ## Priority Queue
295
+
296
+ Jobs queue when all threads are busy. The queue is sorted by priority — `HIGH` always runs before `NORMAL` which runs before `LOW`. Within the same priority, jobs are FIFO.
297
+
298
+ ```ts
299
+ // These four tasks are dispatched to the pool concurrently.
300
+ // HIGH tasks run first regardless of arrival order.
301
+ await Promise.all([
302
+ workerService.run('Svc', 'lowPriorityTask', [], { priority: 'LOW' }),
303
+ workerService.run('Svc', 'highPriorityTask', [], { priority: 'HIGH' }),
304
+ workerService.run('Svc', 'normalPriorityTask', [], { priority: 'NORMAL' }),
305
+ workerService.run('Svc', 'highPriorityTask2', [], { priority: 'HIGH' }),
306
+ ]);
307
+ ```
308
+ ---
309
+
310
+ ## Constraints
311
+
312
+ ### Arguments and Return Values
313
+
314
+ Task arguments and return values cross a thread boundary via `postMessage()` and must be [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) compatible.
315
+
316
+ | ✅ Supported | ❌ Not supported |
317
+ |---|---|
318
+ | Primitives, plain objects, arrays | Class instances |
319
+ | `Map`, `Set`, `ArrayBuffer` | Functions |
320
+ | `TypedArray`, `DataView` | `Promise`, `WeakMap` |
321
+
322
+ ### Circular deps
323
+
324
+ Circular dependencies between `@WorkerClass({ deps })` entries are not supported.
325
+
326
+ ---
327
+
328
+ ## Contributing
329
+
330
+ See the [contributing guide](https://github.com/VaheHak/nestworker/blob/master/CONTRIBUTING.md) for detailed instructions on how to get started with our project.
331
+
332
+ ## License
333
+
334
+ Licensed under [MIT](https://github.com/VaheHak/nestworker/blob/master/LICENSE).
@@ -0,0 +1,36 @@
1
+ export type TaskPriority = 'HIGH' | 'NORMAL' | 'LOW';
2
+ export interface WorkerJob {
3
+ serviceName: string;
4
+ methodName: string;
5
+ args: unknown[];
6
+ /** Sourced from @WorkerTask({ priority }) — used by WorkerPool to sort the queue */
7
+ priority: TaskPriority;
8
+ /** Sourced from @WorkerTask({ timeout }) — rejects the job after this many ms */
9
+ timeout?: number;
10
+ }
11
+ export interface WorkerResult<T = unknown> {
12
+ ok: boolean;
13
+ data?: T;
14
+ error?: {
15
+ message: string;
16
+ stack?: string;
17
+ };
18
+ }
19
+ export interface DiscoveredTask {
20
+ serviceName: string;
21
+ methodName: string;
22
+ priority: TaskPriority;
23
+ timeout?: number;
24
+ /** Bound method on the live main-thread instance */
25
+ fn: (...args: unknown[]) => unknown;
26
+ /** Class constructor — used to locate the compiled file and read metadata */
27
+ metatype: new (...args: unknown[]) => unknown;
28
+ /** Live main-thread service instance — used to locate dep property keys */
29
+ instance: unknown;
30
+ /** Resolved dep instances from the NestJS container, in declaration order */
31
+ deps: unknown[];
32
+ }
33
+ export interface WorkerModuleOptions {
34
+ /** Number of worker threads. Defaults to os.cpus().length */
35
+ poolSize?: number;
36
+ }
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=task.js.map
3
+ //# sourceMappingURL=worker.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.interfaces.js","sourceRoot":"","sources":["../../src/core/worker.interfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import type { WorkerModuleOptions } from './worker.interfaces';
3
+ /**
4
+ * WorkerModule – import once at the root of your application.
5
+ *
6
+ * @example
7
+ * @Module({
8
+ * imports: [WorkerModule.forRoot()],
9
+ * providers: [ConfigService, ImageService],
10
+ * })
11
+ * export class AppModule {}
12
+ */
13
+ export declare class WorkerModule {
14
+ static forRoot(options?: WorkerModuleOptions): DynamicModule;
15
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var WorkerModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.WorkerModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const core_1 = require("@nestjs/core");
13
+ const worker_service_1 = require("./worker.service");
14
+ const discovery_service_1 = require("../discovery/discovery.service");
15
+ /**
16
+ * WorkerModule – import once at the root of your application.
17
+ *
18
+ * @example
19
+ * @Module({
20
+ * imports: [WorkerModule.forRoot()],
21
+ * providers: [ConfigService, ImageService],
22
+ * })
23
+ * export class AppModule {}
24
+ */
25
+ let WorkerModule = WorkerModule_1 = class WorkerModule {
26
+ static forRoot(options = {}) {
27
+ return {
28
+ module: WorkerModule_1,
29
+ imports: [core_1.DiscoveryModule],
30
+ providers: [
31
+ { provide: 'WORKER_OPTIONS', useValue: options },
32
+ discovery_service_1.WorkerDiscoveryService,
33
+ worker_service_1.WorkerService,
34
+ ],
35
+ exports: [worker_service_1.WorkerService],
36
+ };
37
+ }
38
+ };
39
+ exports.WorkerModule = WorkerModule;
40
+ exports.WorkerModule = WorkerModule = WorkerModule_1 = __decorate([
41
+ (0, common_1.Module)({})
42
+ ], WorkerModule);
43
+ //# sourceMappingURL=worker.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.module.js","sourceRoot":"","sources":["../../src/core/worker.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,uCAA+C;AAC/C,qDAAiD;AACjD,sEAAwE;AAGxE;;;;;;;;;GASG;AAEI,IAAM,YAAY,oBAAlB,MAAM,YAAY;IACvB,MAAM,CAAC,OAAO,CAAC,UAA+B,EAAE;QAC9C,OAAO;YACL,MAAM,EAAE,cAAY;YACpB,OAAO,EAAE,CAAC,sBAAe,CAAC;YAC1B,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBAChD,0CAAsB;gBACtB,8BAAa;aACd;YACD,OAAO,EAAE,CAAC,8BAAa,CAAC;SACzB,CAAC;IACJ,CAAC;CACF,CAAA;AAbY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,YAAY,CAaxB"}
@@ -0,0 +1,20 @@
1
+ import type { WorkerJob } from './worker.interfaces';
2
+ import type { SerializedService } from '../di/worker-container';
3
+ export declare class WorkerPool {
4
+ private readonly services;
5
+ private readonly size;
6
+ private readonly workers;
7
+ private readonly idle;
8
+ private readonly queue;
9
+ private destroyed;
10
+ private readonly active;
11
+ constructor(services: SerializedService[], size?: number);
12
+ execute<T = unknown>(job: WorkerJob): Promise<T>;
13
+ private preemptIfNeeded;
14
+ private enqueue;
15
+ private schedule;
16
+ private dispatch;
17
+ private spawnWorker;
18
+ private replaceWorker;
19
+ destroy(): Promise<void>;
20
+ }