nestworker 2.0.4 → 2.1.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.
Files changed (40) hide show
  1. package/README.md +333 -134
  2. package/dist/core/worker.interfaces.d.ts +89 -10
  3. package/dist/core/worker.module.d.ts +21 -11
  4. package/dist/core/worker.module.js +38 -10
  5. package/dist/core/worker.module.js.map +1 -1
  6. package/dist/core/worker.pool.d.ts +26 -6
  7. package/dist/core/worker.pool.js +246 -101
  8. package/dist/core/worker.pool.js.map +1 -1
  9. package/dist/core/worker.service.d.ts +29 -76
  10. package/dist/core/worker.service.js +108 -80
  11. package/dist/core/worker.service.js.map +1 -1
  12. package/dist/decorators/worker-task.decorator.d.ts +7 -25
  13. package/dist/decorators/worker-task.decorator.js +5 -26
  14. package/dist/decorators/worker-task.decorator.js.map +1 -1
  15. package/dist/di/di-serializer.d.ts +9 -8
  16. package/dist/di/di-serializer.js +69 -46
  17. package/dist/di/di-serializer.js.map +1 -1
  18. package/dist/di/worker-container.d.ts +32 -30
  19. package/dist/di/worker-container.js +130 -70
  20. package/dist/di/worker-container.js.map +1 -1
  21. package/dist/discovery/discovery.service.d.ts +2 -9
  22. package/dist/discovery/discovery.service.js +86 -21
  23. package/dist/discovery/discovery.service.js.map +1 -1
  24. package/dist/example/image.service.d.ts +4 -0
  25. package/dist/example/image.service.js +16 -1
  26. package/dist/example/image.service.js.map +1 -1
  27. package/dist/example/main.js +15 -2
  28. package/dist/example/main.js.map +1 -1
  29. package/dist/health/worker.health.d.ts +46 -0
  30. package/dist/health/worker.health.js +77 -0
  31. package/dist/health/worker.health.js.map +1 -0
  32. package/dist/index.d.ts +6 -1
  33. package/dist/index.js +10 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/metrics/worker.metrics.d.ts +65 -0
  36. package/dist/metrics/worker.metrics.js +122 -0
  37. package/dist/metrics/worker.metrics.js.map +1 -0
  38. package/dist/worker/worker-runtime.js +124 -27
  39. package/dist/worker/worker-runtime.js.map +1 -1
  40. package/package.json +1 -1
@@ -17,7 +17,7 @@ let AppModule = class AppModule {
17
17
  };
18
18
  AppModule = __decorate([
19
19
  (0, common_1.Module)({
20
- imports: [worker_module_1.WorkerModule.forRoot({ poolSize: 4 })],
20
+ imports: [worker_module_1.WorkerModule.forRoot({ poolSize: 4, shutdownTimeout: 10000 })],
21
21
  providers: [config_service_1.ConfigService, image_service_1.ImageService],
22
22
  })
23
23
  ], AppModule);
@@ -26,6 +26,18 @@ async function bootstrap() {
26
26
  logger: false,
27
27
  });
28
28
  const workerService = app.get(worker_service_1.WorkerService);
29
+ workerService.onTaskStart((task) => {
30
+ console.log('Task started:', task.jobId, workerService.stats());
31
+ });
32
+ workerService.onTaskEnd(((task, durationMs) => {
33
+ console.log('Task ended:', task.jobId, durationMs);
34
+ }));
35
+ workerService.onTaskError(((task, error) => {
36
+ console.log('Task error:', task.jobId, error);
37
+ }));
38
+ workerService.onDead((task) => {
39
+ console.log('Task dead:', task.jobId);
40
+ });
29
41
  // ── Task 1: sequential ────────────────────────────────────────────────────
30
42
  console.log('▶ resizeImage [priority: HIGH]');
31
43
  console.time('resizeImage');
@@ -35,7 +47,7 @@ async function bootstrap() {
35
47
  // ── Task 2: sequential ────────────────────────────────────────────────────
36
48
  console.log('\n▶ generateThumbnail [priority: LOW override]');
37
49
  console.time('generateThumbnail');
38
- const thumb = await workerService.run('ImageService', 'generateThumbnail', [1920, 1080], { priority: 'LOW' });
50
+ const thumb = await workerService.run('ImageService', 'generateThumbnail', [1920, 1080], { priority: 'LOW', retry: 3, retryDelay: 1000 });
39
51
  console.timeEnd('generateThumbnail');
40
52
  console.log(' result:', thumb);
41
53
  // ── Task 3: concurrent — proves multiple workers run in parallel ───────────
@@ -47,6 +59,7 @@ async function bootstrap() {
47
59
  workerService.run('ImageService', 'generateThumbnail', [640, 480], { priority: 'HIGH' }),
48
60
  workerService.run('ImageService', 'moduleImport'),
49
61
  workerService.run('ImageService', 'moduleRequire'),
62
+ workerService.run('ImageService', 'outlineModule'),
50
63
  ]);
51
64
  console.timeEnd('concurrent');
52
65
  console.log(' results:', results);
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/example/main.ts"],"names":[],"mappings":";;;;;;;;AAAA,4BAA0B;AAC1B,uCAAyC;AACzC,2CAAsC;AACtC,yDAAmD;AACnD,2DAAqD;AACrD,qDAA+C;AAC/C,mDAA6C;AAM7C,IAAM,SAAS,GAAf,MAAM,SAAS;CACd,CAAA;AADK,SAAS;IAJd,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC,OAAO,CAAC,EAAC,QAAQ,EAAE,CAAC,EAAC,CAAC,CAAC;QAC9C,SAAS,EAAE,CAAC,8BAAa,EAAE,4BAAY,CAAC;KACzC,CAAC;GACI,SAAS,CACd;AAED,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,wBAAwB,CAAC,SAAS,EAAE;QAChE,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,8BAAa,CAAC,CAAC;IAE7C,6EAA6E;IAC7E,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,GAAG,CACrC,cAAc,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,CACnC,CAAC;IACF,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAElC,6EAA6E;IAC7E,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,CACnC,cAAc,EAAE,mBAAmB,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAC,CACrE,CAAC;IACF,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAEhC,8EAA8E;IAC9E,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACrE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;QACvC,aAAa,CAAC,GAAG,CAAS,cAAc,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAC,CAAC;QAChF,aAAa,CAAC,GAAG,CAAS,cAAc,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAC,CAAC;QAC9F,aAAa,CAAC,GAAG,CAAS,cAAc,EAAE,mBAAmB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC;QAC9F,aAAa,CAAC,GAAG,CAAS,cAAc,EAAE,cAAc,CAAC;QACzD,aAAa,CAAC,GAAG,CAAS,cAAc,EAAE,eAAe,CAAC;KAC3D,CAAC,CAAC;IACH,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAEnC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;AACpB,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/example/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { WorkerService } from '../core/worker.service';
2
+ export interface WorkerHealthResult {
3
+ status: 'up' | 'down';
4
+ details: {
5
+ poolSize: number;
6
+ idle: number;
7
+ busy: number;
8
+ queued: number;
9
+ warmingUp: number;
10
+ };
11
+ error?: string;
12
+ }
13
+ /**
14
+ * WorkerHealthIndicator — plugs into NestJS Terminus.
15
+ *
16
+ * @example
17
+ * // health.controller.ts
18
+ * import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
19
+ * import { WorkerHealthIndicator } from 'nestworker';
20
+ *
21
+ * @Controller('health')
22
+ * export class HealthController {
23
+ * constructor(
24
+ * private health: HealthCheckService,
25
+ * private workerHealth: WorkerHealthIndicator,
26
+ * ) {}
27
+ *
28
+ * @Get()
29
+ * @HealthCheck()
30
+ * check() {
31
+ * return this.health.check([
32
+ * () => this.workerHealth.check('workers'),
33
+ * ]);
34
+ * }
35
+ * }
36
+ */
37
+ export declare class WorkerHealthIndicator {
38
+ private readonly workerService;
39
+ constructor(workerService: WorkerService);
40
+ /**
41
+ * Returns a Terminus-compatible health indicator result.
42
+ * Reports 'down' when warmingUp > 0 (pool not fully ready) or when
43
+ * queued jobs exceed the pool size (backpressure signal).
44
+ */
45
+ check(key: string): Record<string, WorkerHealthResult>;
46
+ }
@@ -0,0 +1,77 @@
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 __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WorkerHealthIndicator = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const worker_service_1 = require("../core/worker.service");
15
+ /**
16
+ * WorkerHealthIndicator — plugs into NestJS Terminus.
17
+ *
18
+ * @example
19
+ * // health.controller.ts
20
+ * import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
21
+ * import { WorkerHealthIndicator } from 'nestworker';
22
+ *
23
+ * @Controller('health')
24
+ * export class HealthController {
25
+ * constructor(
26
+ * private health: HealthCheckService,
27
+ * private workerHealth: WorkerHealthIndicator,
28
+ * ) {}
29
+ *
30
+ * @Get()
31
+ * @HealthCheck()
32
+ * check() {
33
+ * return this.health.check([
34
+ * () => this.workerHealth.check('workers'),
35
+ * ]);
36
+ * }
37
+ * }
38
+ */
39
+ let WorkerHealthIndicator = class WorkerHealthIndicator {
40
+ workerService;
41
+ constructor(workerService) {
42
+ this.workerService = workerService;
43
+ }
44
+ /**
45
+ * Returns a Terminus-compatible health indicator result.
46
+ * Reports 'down' when warmingUp > 0 (pool not fully ready) or when
47
+ * queued jobs exceed the pool size (backpressure signal).
48
+ */
49
+ check(key) {
50
+ const stats = this.workerService.stats();
51
+ const isDown = stats.warmingUp > 0 ||
52
+ stats.queued > stats.poolSize;
53
+ const result = {
54
+ status: isDown ? 'down' : 'up',
55
+ details: stats,
56
+ ...(isDown && stats.warmingUp > 0
57
+ ? { error: `${stats.warmingUp} worker(s) still warming up` }
58
+ : {}),
59
+ ...(isDown && stats.queued > stats.poolSize
60
+ ? { error: `Queue depth (${stats.queued}) exceeds pool size (${stats.poolSize})` }
61
+ : {}),
62
+ };
63
+ if (isDown) {
64
+ throw Object.assign(new Error(`Worker pool unhealthy`), {
65
+ [key]: result,
66
+ causes: [result.error],
67
+ });
68
+ }
69
+ return { [key]: result };
70
+ }
71
+ };
72
+ exports.WorkerHealthIndicator = WorkerHealthIndicator;
73
+ exports.WorkerHealthIndicator = WorkerHealthIndicator = __decorate([
74
+ (0, common_1.Injectable)(),
75
+ __metadata("design:paramtypes", [worker_service_1.WorkerService])
76
+ ], WorkerHealthIndicator);
77
+ //# sourceMappingURL=worker.health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.health.js","sourceRoot":"","sources":["../../src/health/worker.health.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2DAAuD;AAcvD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAE7D;;;;OAIG;IACH,KAAK,CAAC,GAAW;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACzC,MAAM,MAAM,GACV,KAAK,CAAC,SAAS,GAAG,CAAC;YACnB,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC;QAEhC,MAAM,MAAM,GAAuB;YACjC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;YAC9B,OAAO,EAAE,KAAK;YACd,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC;gBAC/B,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,SAAS,6BAA6B,EAAE;gBAC5D,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,QAAQ;gBACzC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,KAAK,CAAC,MAAM,wBAAwB,KAAK,CAAC,QAAQ,GAAG,EAAE;gBAClF,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QAEF,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,EAAE;gBACtD,CAAC,GAAG,CAAC,EAAE,MAAM;gBACb,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC;aACvB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AAlCY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAEiC,8BAAa;GAD9C,qBAAqB,CAkCjC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export { WorkerModule } from './core/worker.module';
2
2
  export { WorkerService } from './core/worker.service';
3
+ export type { RunOptions } from './core/worker.service';
3
4
  export { WorkerClass, WorkerTask } from './decorators/worker-task.decorator';
4
- export type { WorkerModuleOptions, TaskPriority } from './core/worker.interfaces';
5
5
  export type { WorkerTaskOptions } from './decorators/worker-task.decorator';
6
+ export { WorkerHealthIndicator } from './health/worker.health';
7
+ export type { WorkerHealthResult } from './health/worker.health';
8
+ export { WorkerMetricsService } from './metrics/worker.metrics';
9
+ export type { WorkerMetricsSnapshot } from './metrics/worker.metrics';
10
+ export type { WorkerJob, WorkerModuleOptions, WorkerModuleAsyncOptions, TaskPriority, DeadLetterEvent, PoolStats, SerializedError, } from './core/worker.interfaces';
package/dist/index.js CHANGED
@@ -1,11 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WorkerTask = exports.WorkerClass = exports.WorkerService = exports.WorkerModule = void 0;
3
+ exports.WorkerMetricsService = exports.WorkerHealthIndicator = exports.WorkerTask = exports.WorkerClass = exports.WorkerService = exports.WorkerModule = void 0;
4
+ // Module
4
5
  var worker_module_1 = require("./core/worker.module");
5
6
  Object.defineProperty(exports, "WorkerModule", { enumerable: true, get: function () { return worker_module_1.WorkerModule; } });
7
+ // Services
6
8
  var worker_service_1 = require("./core/worker.service");
7
9
  Object.defineProperty(exports, "WorkerService", { enumerable: true, get: function () { return worker_service_1.WorkerService; } });
10
+ // Decorators
8
11
  var worker_task_decorator_1 = require("./decorators/worker-task.decorator");
9
12
  Object.defineProperty(exports, "WorkerClass", { enumerable: true, get: function () { return worker_task_decorator_1.WorkerClass; } });
10
13
  Object.defineProperty(exports, "WorkerTask", { enumerable: true, get: function () { return worker_task_decorator_1.WorkerTask; } });
14
+ // Health
15
+ var worker_health_1 = require("./health/worker.health");
16
+ Object.defineProperty(exports, "WorkerHealthIndicator", { enumerable: true, get: function () { return worker_health_1.WorkerHealthIndicator; } });
17
+ // Metrics
18
+ var worker_metrics_1 = require("./metrics/worker.metrics");
19
+ Object.defineProperty(exports, "WorkerMetricsService", { enumerable: true, get: function () { return worker_metrics_1.WorkerMetricsService; } });
11
20
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,sDAAkD;AAA1C,6GAAA,YAAY,OAAA;AACpB,wDAAoD;AAA5C,+GAAA,aAAa,OAAA;AACrB,4EAA2E;AAAnE,oHAAA,WAAW,OAAA;AAAE,mHAAA,UAAU,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,sDAAsD;AAA7C,6GAAA,YAAY,OAAA;AAErB,WAAW;AACX,wDAAuD;AAA9C,+GAAA,aAAa,OAAA;AAGtB,aAAa;AACb,4EAA6E;AAApE,oHAAA,WAAW,OAAA;AAAE,mHAAA,UAAU,OAAA;AAGhC,SAAS;AACT,wDAA+D;AAAtD,sHAAA,qBAAqB,OAAA;AAG9B,UAAU;AACV,2DAAiE;AAAxD,sHAAA,oBAAoB,OAAA"}
@@ -0,0 +1,65 @@
1
+ import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2
+ import { WorkerService } from '../core/worker.service';
3
+ export interface WorkerMetricsSnapshot {
4
+ /** Total jobs dispatched since startup */
5
+ jobsTotal: number;
6
+ /** Jobs that completed successfully */
7
+ jobsSuccess: number;
8
+ /** Jobs that failed (after all retries) */
9
+ jobsFailed: number;
10
+ /** Jobs that timed out */
11
+ jobsTimeout: number;
12
+ /** Jobs sent to dead letter queue */
13
+ jobsDead: number;
14
+ /** Current queue depth */
15
+ queueDepth: number;
16
+ /** Current idle worker count */
17
+ idleWorkers: number;
18
+ /** Current busy worker count */
19
+ busyWorkers: number;
20
+ /** Per-task duration histogram (p50, p95, p99) in ms */
21
+ durations: Record<string, {
22
+ p50: number;
23
+ p95: number;
24
+ p99: number;
25
+ count: number;
26
+ }>;
27
+ }
28
+ /**
29
+ * WorkerMetricsService — collects runtime metrics from the worker pool.
30
+ *
31
+ * Designed to be framework-agnostic: read the snapshot and push to
32
+ * Prometheus, Datadog, CloudWatch, or any other metrics provider.
33
+ *
34
+ * @example
35
+ * // Prometheus integration
36
+ * @Get('metrics')
37
+ * async metrics() {
38
+ * const snap = this.workerMetrics.snapshot();
39
+ * return [
40
+ * `nestworker_jobs_total ${snap.jobsTotal}`,
41
+ * `nestworker_jobs_success ${snap.jobsSuccess}`,
42
+ * `nestworker_queue_depth ${snap.queueDepth}`,
43
+ * ].join('\n');
44
+ * }
45
+ */
46
+ export declare class WorkerMetricsService implements OnModuleInit, OnModuleDestroy {
47
+ private readonly workerService;
48
+ private jobsTotal;
49
+ private jobsSuccess;
50
+ private jobsFailed;
51
+ private jobsTimeout;
52
+ private jobsDead;
53
+ /** Raw duration samples per task key — capped at 1000 samples */
54
+ private readonly durationSamples;
55
+ /** Interval handle for periodic pool-stats polling */
56
+ private statsInterval?;
57
+ constructor(workerService: WorkerService);
58
+ onModuleInit(): void;
59
+ onModuleDestroy(): void;
60
+ /** Returns a point-in-time snapshot of all metrics */
61
+ snapshot(): WorkerMetricsSnapshot;
62
+ /** Reset all counters (useful in tests) */
63
+ reset(): void;
64
+ private pushDuration;
65
+ }
@@ -0,0 +1,122 @@
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 __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.WorkerMetricsService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const worker_service_1 = require("../core/worker.service");
15
+ /**
16
+ * WorkerMetricsService — collects runtime metrics from the worker pool.
17
+ *
18
+ * Designed to be framework-agnostic: read the snapshot and push to
19
+ * Prometheus, Datadog, CloudWatch, or any other metrics provider.
20
+ *
21
+ * @example
22
+ * // Prometheus integration
23
+ * @Get('metrics')
24
+ * async metrics() {
25
+ * const snap = this.workerMetrics.snapshot();
26
+ * return [
27
+ * `nestworker_jobs_total ${snap.jobsTotal}`,
28
+ * `nestworker_jobs_success ${snap.jobsSuccess}`,
29
+ * `nestworker_queue_depth ${snap.queueDepth}`,
30
+ * ].join('\n');
31
+ * }
32
+ */
33
+ let WorkerMetricsService = class WorkerMetricsService {
34
+ workerService;
35
+ jobsTotal = 0;
36
+ jobsSuccess = 0;
37
+ jobsFailed = 0;
38
+ jobsTimeout = 0;
39
+ jobsDead = 0;
40
+ /** Raw duration samples per task key — capped at 1000 samples */
41
+ durationSamples = new Map();
42
+ /** Interval handle for periodic pool-stats polling */
43
+ statsInterval;
44
+ constructor(workerService) {
45
+ this.workerService = workerService;
46
+ }
47
+ onModuleInit() {
48
+ // Task start
49
+ this.workerService.onTaskStart((job) => {
50
+ this.jobsTotal++;
51
+ });
52
+ // Task end: record duration + success
53
+ this.workerService.onTaskEnd((job, durationMs) => {
54
+ const key = `${job.serviceName}.${job.methodName}`;
55
+ this.jobsSuccess++;
56
+ this.pushDuration(key, durationMs);
57
+ });
58
+ // Task error: record failure
59
+ this.workerService.onTaskError((job, error) => {
60
+ this.jobsFailed++;
61
+ if (error.name === 'TimeoutError')
62
+ this.jobsTimeout++;
63
+ });
64
+ // Dead letter: separate counter
65
+ this.workerService.onDead(() => {
66
+ this.jobsDead++;
67
+ });
68
+ }
69
+ onModuleDestroy() {
70
+ if (this.statsInterval)
71
+ clearInterval(this.statsInterval);
72
+ }
73
+ /** Returns a point-in-time snapshot of all metrics */
74
+ snapshot() {
75
+ const stats = this.workerService.stats();
76
+ const durations = {};
77
+ for (const [key, samples] of this.durationSamples) {
78
+ durations[key] = computePercentiles(samples);
79
+ }
80
+ return {
81
+ jobsTotal: this.jobsTotal,
82
+ jobsSuccess: this.jobsSuccess,
83
+ jobsFailed: this.jobsFailed,
84
+ jobsTimeout: this.jobsTimeout,
85
+ jobsDead: this.jobsDead,
86
+ queueDepth: stats.queued,
87
+ idleWorkers: stats.idle,
88
+ busyWorkers: stats.busy,
89
+ durations,
90
+ };
91
+ }
92
+ /** Reset all counters (useful in tests) */
93
+ reset() {
94
+ this.jobsTotal = this.jobsSuccess = this.jobsFailed =
95
+ this.jobsTimeout = this.jobsDead = 0;
96
+ this.durationSamples.clear();
97
+ }
98
+ pushDuration(key, ms) {
99
+ if (!this.durationSamples.has(key))
100
+ this.durationSamples.set(key, []);
101
+ const samples = this.durationSamples.get(key);
102
+ samples.push(ms);
103
+ // Keep memory bounded — reservoir sampling after 1000 entries
104
+ if (samples.length > 1000) {
105
+ const idx = Math.floor(Math.random() * samples.length);
106
+ samples.splice(idx, 1);
107
+ }
108
+ }
109
+ };
110
+ exports.WorkerMetricsService = WorkerMetricsService;
111
+ exports.WorkerMetricsService = WorkerMetricsService = __decorate([
112
+ (0, common_1.Injectable)(),
113
+ __metadata("design:paramtypes", [worker_service_1.WorkerService])
114
+ ], WorkerMetricsService);
115
+ function computePercentiles(samples) {
116
+ if (samples.length === 0)
117
+ return { p50: 0, p95: 0, p99: 0, count: 0 };
118
+ const sorted = [...samples].sort((a, b) => a - b);
119
+ const p = (pct) => sorted[Math.ceil((pct / 100) * sorted.length) - 1] ?? 0;
120
+ return { p50: p(50), p95: p(95), p99: p(99), count: samples.length };
121
+ }
122
+ //# sourceMappingURL=worker.metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.metrics.js","sourceRoot":"","sources":["../../src/metrics/worker.metrics.ts"],"names":[],"mappings":"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"}
@@ -1,28 +1,73 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const node_worker_threads_1 = require("node:worker_threads");
7
+ const node_async_hooks_1 = require("node:async_hooks");
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
4
9
  const worker_container_1 = require("../di/worker-container");
5
- /**
6
- * Worker Runtime – entry point for every worker thread.
7
- *
8
- * Receives SerializedService[] via workerData and boots a WorkerContainer
9
- * which eval()s the stripped class sources and reconstructs the full
10
- * service graph (deps injected via constructor) without any require() calls
11
- * into NestJS-annotated files.
12
- *
13
- * Jobs are processed serially via a queue — one postMessage() reply per job,
14
- * which is exactly what the pool's worker.once('message') expects.
15
- */
16
10
  const services = node_worker_threads_1.workerData?.services ?? [];
17
11
  const container = new worker_container_1.WorkerContainer();
18
12
  container.load(services);
19
- // Build flat dispatch map: "ServiceName.methodName" → bound callable
20
- const tasks = {};
13
+ const instances = new Map();
21
14
  for (const svc of services) {
22
- const inst = container.get(svc.name);
23
- for (const { methodName } of svc.methods) {
24
- tasks[`${svc.name}.${methodName}`] = (...args) => inst[methodName](...args);
15
+ instances.set(svc.name, container.get(svc.name));
16
+ }
17
+ node_worker_threads_1.parentPort?.postMessage({ type: 'worker:ready' });
18
+ // ── Pending IPC calls ─────────────────────────────────────────────────────
19
+ const pendingIpc = new Map();
20
+ // ── Pending AbortControllers (keyed by abortSignalId) ────────────────────
21
+ const pendingAborts = new Map();
22
+ // ── Internal ALS for context propagation ─────────────────────────────────
23
+ // One ALS instance per context key — we use a single generic ALS that holds
24
+ // the full context map, then the user's ALS instances read from it via
25
+ // the context restoration path in WorkerService.
26
+ const workerAls = new node_async_hooks_1.AsyncLocalStorage();
27
+ node_worker_threads_1.parentPort?.on('message', (msg) => {
28
+ const message = msg;
29
+ // IPC result from main thread
30
+ if (message.type === 'ipc:result') {
31
+ const res = message;
32
+ const pending = pendingIpc.get(res.callId);
33
+ if (!pending)
34
+ return;
35
+ pendingIpc.delete(res.callId);
36
+ res.ok ? pending.resolve(res.data) : pending.reject(new Error(res.error ?? 'IPC failed'));
37
+ return;
38
+ }
39
+ // Abort signal from main thread
40
+ if (message.type === 'abort') {
41
+ const abort = message;
42
+ pendingAborts.get(abort.abortSignalId)?.abort();
43
+ return;
44
+ }
45
+ // Normal job
46
+ queue.push(message);
47
+ runNext();
48
+ });
49
+ function buildProxy(descriptor) {
50
+ const proxy = {};
51
+ for (const methodName of descriptor.methodNames) {
52
+ proxy[methodName] = (...args) => new Promise((resolve, reject) => {
53
+ const callId = node_crypto_1.default.randomUUID();
54
+ pendingIpc.set(callId, { resolve, reject });
55
+ const request = {
56
+ type: 'ipc:invoke', callId,
57
+ propertyKey: descriptor.propertyKey,
58
+ methodName, args,
59
+ };
60
+ try {
61
+ node_worker_threads_1.parentPort?.postMessage(request);
62
+ }
63
+ catch (err) {
64
+ pendingIpc.delete(callId);
65
+ reject(new Error(`IPC proxy "${descriptor.propertyKey}.${methodName}" could not serialise args: ` +
66
+ `${err.message}`));
67
+ }
68
+ });
25
69
  }
70
+ return proxy;
26
71
  }
27
72
  const queue = [];
28
73
  let busy = false;
@@ -30,26 +75,78 @@ async function runNext() {
30
75
  if (busy || queue.length === 0)
31
76
  return;
32
77
  busy = true;
33
- const payload = queue.shift();
34
- const key = `${payload.serviceName}.${payload.methodName}`;
78
+ const job = queue.shift();
79
+ // Set up AbortController for this job
80
+ const abortController = new AbortController();
81
+ if (job.abortSignalId) {
82
+ pendingAborts.set(job.abortSignalId, abortController);
83
+ }
84
+ // Inject proxy stubs
85
+ if (job.proxyServices?.length) {
86
+ const inst = instances.get(job.serviceName);
87
+ if (inst) {
88
+ for (const descriptor of job.proxyServices) {
89
+ inst[descriptor.propertyKey] = buildProxy(descriptor);
90
+ }
91
+ }
92
+ }
93
+ // Restore ALS context and run the task inside it
94
+ const alsContext = job.alsContext ?? {};
95
+ const run = async () => {
96
+ const inst = instances.get(job.serviceName);
97
+ if (!inst)
98
+ throw new Error(`Service "${job.serviceName}" is not registered`);
99
+ const fn = inst[job.methodName];
100
+ if (typeof fn !== 'function') {
101
+ throw new Error(`Task "${job.serviceName}.${job.methodName}" is not registered`);
102
+ }
103
+ // Inject AbortSignal as a last argument if the job has an abortSignalId.
104
+ // Call via inst.method() — not as a detached fn() — so `this` is preserved.
105
+ const args = job.abortSignalId
106
+ ? [...job.args, abortController.signal]
107
+ : job.args;
108
+ return inst[job.methodName](...args);
109
+ };
35
110
  try {
36
- const fn = tasks[key];
37
- if (!fn)
38
- throw new Error(`Task "${key}" is not registered`);
39
- const data = await fn(...payload.args);
111
+ // Run the task inside the ALS context so any nested ALS.getStore() calls
112
+ // inside the task body see the propagated main-thread context.
113
+ const data = await workerAls.run(alsContext, run);
40
114
  node_worker_threads_1.parentPort?.postMessage({ ok: true, data });
41
115
  }
42
116
  catch (error) {
43
117
  const e = error;
44
- node_worker_threads_1.parentPort?.postMessage({ ok: false, error: { message: e.message, stack: e.stack } });
118
+ const serialized = {
119
+ name: e.name ?? 'Error',
120
+ message: e.message,
121
+ stack: e.stack,
122
+ code: e.code,
123
+ // Capture any extra own enumerable properties (e.g. HttpException.status)
124
+ extra: serializeExtraProps(e),
125
+ };
126
+ node_worker_threads_1.parentPort?.postMessage({ ok: false, error: serialized });
45
127
  }
46
128
  finally {
129
+ if (job.abortSignalId) {
130
+ pendingAborts.delete(job.abortSignalId);
131
+ }
47
132
  busy = false;
48
133
  runNext();
49
134
  }
50
135
  }
51
- node_worker_threads_1.parentPort?.on('message', (payload) => {
52
- queue.push(payload);
53
- runNext();
54
- });
136
+ function serializeExtraProps(err) {
137
+ const skip = new Set(['name', 'message', 'stack', 'code']);
138
+ const extra = {};
139
+ let hasExtra = false;
140
+ for (const key of Object.keys(err)) {
141
+ if (skip.has(key))
142
+ continue;
143
+ try {
144
+ structuredClone(err[key]);
145
+ extra[key] = err[key];
146
+ hasExtra = true;
147
+ }
148
+ catch { /* skip non-cloneable */ }
149
+ }
150
+ return hasExtra ? extra : undefined;
151
+ }
55
152
  //# sourceMappingURL=worker-runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-runtime.js","sourceRoot":"","sources":["../../src/worker/worker-runtime.ts"],"names":[],"mappings":";;AAAA,6DAA6D;AAC7D,6DAAyD;AAGzD;;;;;;;;;;GAUG;AAEH,MAAM,QAAQ,GAAwB,gCAAU,EAAE,QAAQ,IAAI,EAAE,CAAC;AACjE,MAAM,SAAS,GAAG,IAAI,kCAAe,EAAE,CAAC;AACxC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAEzB,qEAAqE;AACrE,MAAM,KAAK,GAAoD,EAAE,CAAC;AAClE,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAkD,GAAG,CAAC,IAAI,CAAC,CAAC;IACtF,KAAK,MAAM,EAAE,UAAU,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACzC,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAID,MAAM,KAAK,GAAU,EAAE,CAAC;AACxB,IAAI,IAAI,GAAG,KAAK,CAAC;AAEjB,KAAK,UAAU,OAAO;IACpB,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACvC,IAAI,GAAG,IAAI,CAAC;IACZ,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;IAC/B,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,qBAAqB,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,gCAAU,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,KAAc,CAAC;QACzB,gCAAU,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;YAAS,CAAC;QACT,IAAI,GAAG,KAAK,CAAC;QACb,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,gCAAU,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,OAAY,EAAE,EAAE;IACzC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"worker-runtime.js","sourceRoot":"","sources":["../../src/worker/worker-runtime.ts"],"names":[],"mappings":";;;;;AAAA,6DAA6D;AAC7D,uDAAqD;AACrD,8DAAiC;AACjC,6DAAyD;AAYzD,MAAM,QAAQ,GAAwB,gCAAU,EAAE,QAAQ,IAAI,EAAE,CAAC;AACjE,MAAM,SAAS,GAAG,IAAI,kCAAe,EAAE,CAAC;AACxC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAIzB,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;AACrD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC3B,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAkB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,gCAAU,EAAE,WAAW,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;AAElD,6EAA6E;AAC7E,MAAM,UAAU,GAAG,IAAI,GAAG,EAGvB,CAAC;AAEJ,4EAA4E;AAC5E,MAAM,aAAa,GAAG,IAAI,GAAG,EAA2B,CAAC;AAEzD,4EAA4E;AAC5E,4EAA4E;AAC5E,uEAAuE;AACvE,iDAAiD;AACjD,MAAM,SAAS,GAAG,IAAI,oCAAiB,EAA2B,CAAC;AAEnE,gCAAU,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,GAAY,EAAE,EAAE;IACzC,MAAM,OAAO,GAAG,GAA2B,CAAC;IAE5C,8BAA8B;IAC9B,IAAK,OAA6B,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACzD,MAAM,GAAG,GAAG,OAA4B,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9B,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC,CAAC;QAC1F,OAAO;IACT,CAAC;IAED,gCAAgC;IAChC,IAAK,OAA6B,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,OAA6B,CAAC;QAC5C,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;QAChD,OAAO;IACT,CAAC;IAED,aAAa;IACb,KAAK,CAAC,IAAI,CAAC,OAAoB,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,UAAkC;IACpD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,UAAU,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;QAChD,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,IAAe,EAAoB,EAAE,CAC3D,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC9B,MAAM,MAAM,GAAG,qBAAM,CAAC,UAAU,EAAE,CAAC;YACnC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAqB;gBAChC,IAAI,EAAE,YAAY,EAAE,MAAM;gBAC1B,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,UAAU,EAAE,IAAI;aACjB,CAAC;YACF,IAAI,CAAC;gBACH,gCAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC1B,MAAM,CAAC,IAAI,KAAK,CACd,cAAc,UAAU,CAAC,WAAW,IAAI,UAAU,8BAA8B;oBAChF,GAAI,GAAa,CAAC,OAAO,EAAE,CAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACP,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,KAAK,GAAgB,EAAE,CAAC;AAC9B,IAAI,IAAI,GAAG,KAAK,CAAC;AAEjB,KAAK,UAAU,OAAO;IACpB,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACvC,IAAI,GAAG,IAAI,CAAC;IACZ,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;IAE3B,sCAAsC;IACtC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;IAC9C,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;QACtB,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACxD,CAAC;IAED,qBAAqB;IACrB,IAAI,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,MAAM,UAAU,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;gBAC3C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC,UAAU,CAA+C,CAAC;YACtG,CAAC;QACH,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;IAExC,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;QACrB,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,WAAW,qBAAqB,CAAC,CAAC;QAE7E,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAChC,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,UAAU,qBAAqB,CAAC,CAAC;QACnF,CAAC;QAED,yEAAyE;QACzE,4EAA4E;QAC5E,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa;YAC5B,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC;YACvC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QAEb,OAAO,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAClD,gCAAU,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,KAAmE,CAAC;QAC9E,MAAM,UAAU,GAAoB;YAClC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,OAAO;YACvB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,0EAA0E;YAC1E,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC;SAC9B,CAAC;QACF,gCAAU,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;YAAS,CAAC;QACT,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;YACtB,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,GAAG,KAAK,CAAC;QACb,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAoC;IAEpC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC;YACH,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC,CAAC,wBAAwB,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestworker",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "description": "Enterprise-grade worker thread module for NestJS — priority pool, auto-discovery, DI in workers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",