@fluojs/queue 1.0.0 → 1.0.2

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.ko.md CHANGED
@@ -92,7 +92,7 @@ QueueModule.forRoot({ clientName: 'jobs' })
92
92
 
93
93
  ### 부트스트랩 및 종료 수명 주기
94
94
 
95
- Queue는 애플리케이션 부트스트랩 중 worker를 탐색하고 Queue가 소유하는 BullMQ 리소스를 만들지만, BullMQ worker processor는 runtime이 전체 애플리케이션 bootstrap/readiness sequence 완료를 표시한 뒤에만 시작합니다. 다른 `onApplicationBootstrap()` hook에서 enqueue한 job은 Queue 서비스가 초기화된 뒤에는 받을 수 있으며, processor는 뒤에 실행되는 async bootstrap hook이나 애플리케이션 readiness보다 앞서 실행되지 않고 bootstrap-ready handoff 이후 실행됩니다.
95
+ Queue는 애플리케이션 부트스트랩 중 worker를 탐색하고 Queue가 소유하는 BullMQ 리소스를 만들지만, BullMQ worker processor는 runtime이 전체 애플리케이션 bootstrap/readiness sequence 완료를 표시한 뒤에만 시작합니다. 다른 `onApplicationBootstrap()` hook에서 enqueue한 job은 Queue 서비스가 초기화된 뒤에는 받을 수 있으며, processor는 뒤에 실행되는 async bootstrap hook이나 애플리케이션 readiness보다 앞서 실행되지 않고 bootstrap-ready handoff 이후 실행됩니다. Queue status는 해당 BullMQ processor가 실제로 시작될 때까지 degraded readiness를 보고합니다. Processor 시작에 실패하면 lifecycle이 `failed`로 이동하고, status snapshot은 worker를 ready로 숨기지 않고 실패를 노출합니다.
96
96
 
97
97
  애플리케이션 종료가 시작되면 Queue는 상태를 `stopping`으로 바꾸고 새 enqueue를 거부한 다음 Queue 소유 worker/queue/connection을 닫고 pending dead-letter write를 drain합니다. Worker 종료는 `workerShutdownTimeoutMs`로 bounded wait를 적용하므로 끝나지 않는 active processor가 애플리케이션 종료를 무기한 막을 수 없습니다. Timeout이 지나면 Queue는 로그를 남기고 BullMQ worker에 force-close를 요청한 뒤 나머지 리소스 정리를 계속합니다.
98
98
 
@@ -122,25 +122,33 @@ Job은 JSON으로 직렬화 가능한 plain object여야 합니다. Queue는 enq
122
122
  ### 핵심 구성 요소
123
123
  - `QueueModule`: 큐 기능을 위한 기본 모듈입니다.
124
124
  - `QueueModule.forRoot(options)`: 애플리케이션 수준 큐 등록을 구성합니다.
125
- - `QueueLifecycleService`: 작업을 큐에 추가(`enqueue(job)`)하기 위한 기본 서비스입니다.
125
+ - `QueueLifecycleService`: 작업을 큐에 추가하고 lifecycle/status snapshot을 생성(`enqueue(job)`, `createPlatformStatusSnapshot()`)하기 위한 기본 서비스입니다.
126
126
  - `@QueueWorker(JobClass, options?)`: 특정 작업을 처리할 핸들러를 지정하는 데코레이터입니다.
127
127
  - `QUEUE`: queue facade를 위한 호환성 주입 토큰입니다.
128
128
  - `createQueuePlatformStatusSnapshot(...)`: lifecycle/readiness diagnostics를 위한 status snapshot helper입니다.
129
129
 
130
130
 
131
131
  ### 타입
132
- - `QueueModuleOptions`: 전역 설정(clientName, 기본 시도 횟수, `defaultBackoff`, 동시성, 전송률 제한, dead-letter retention 등) 위한 타입입니다.
132
+ - `Queue`: 애플리케이션 코드와 `QUEUE` 토큰에서 사용하는 `enqueue(job)` 호환성 facade입니다.
133
+ - `QueueJobType`: job payload class를 식별하고 rehydrate하는 데 사용하는 constructor 타입입니다.
134
+ - `QueueModuleOptions`: 전역 큐 설정(`global`, clientName, 기본 시도 횟수, `defaultBackoff`, 동시성, 전송률 제한, dead-letter retention 등)을 위한 타입입니다.
133
135
  - `QueueWorkerOptions`: 개별 작업 설정(시도 횟수, 백오프, 동시성, jobName, 전송률 제한 등)을 위한 타입입니다.
136
+ - `QueueBackoffType`: 지원되는 retry backoff strategy 이름(`fixed`, `exponential`)입니다.
134
137
  - `QueueBackoffOptions`: 재시도 백오프 설정(`type`, `delayMs`)을 위한 타입입니다.
138
+ - `QueueRateLimiterOptions`: worker 수준 distributed rate limiter 설정(`max`, `duration`)을 위한 타입입니다.
139
+ - `QueueLifecycleState`: Queue status adapter가 보고하는 lifecycle state(`idle`, `starting`, `started`, `stopping`, `stopped`, `failed`)입니다.
140
+ - `QueueStatusAdapterInput`: `createQueuePlatformStatusSnapshot(...)`에 전달하는 normalized queue metrics와 worker-start diagnostics 타입입니다.
141
+ - `QueuePlatformStatusSnapshot`: status helper와 `QueueLifecycleService.createPlatformStatusSnapshot()`이 반환하는 Queue 전용 readiness, health, ownership, detail snapshot 타입입니다.
135
142
 
136
143
  `QueueModuleOptions`에는 `workerShutdownTimeoutMs`, `defaultDeadLetterMaxEntries` 같은 lifecycle 및 dead-letter retention 설정도 포함됩니다.
137
144
 
138
145
  `QueueModuleOptions` 수명 주기/status 설정:
139
146
 
147
+ - `global`: queue module 등록을 global로 만들지 여부입니다. 기본값은 `true`이며, queue provider를 importing module graph 안에만 scope하고 싶으면 `false`를 지정합니다.
140
148
  - `workerShutdownTimeoutMs`: 종료 중 active worker processor를 기다리는 최대 시간입니다. 시간이 지나면 BullMQ worker를 force-close합니다. 기본값은 `30_000`입니다.
141
149
  - `defaultDeadLetterMaxEntries`: job별로 유지할 dead-letter record의 최대 개수이며, trimming을 끄려면 `false`를 지정합니다. 기본값은 `1_000`입니다.
142
150
 
143
- `createQueuePlatformStatusSnapshot(...)`은 Queue가 `started`에 도달한 뒤에만 readiness를 `ready`로 보고합니다. `starting`은 degraded readiness, `stopping`/`stopped`는 not-ready로 보고합니다. Snapshot details에는 Redis dependency id, lifecycle state, ready/discovered worker 수, pending dead-letter write 수, dead-letter drain timeout, `workerShutdownTimeoutMs`가 포함됩니다.
151
+ `QueueLifecycleService.createPlatformStatusSnapshot()`은 `createQueuePlatformStatusSnapshot(...)`과 같은 공개 snapshot 계약을 사용합니다. Queue가 `started`에 도달하고 탐색된 모든 BullMQ worker processor가 시작된 뒤에만 readiness를 `ready`로 보고합니다. Processor가 아직 pending인 `started` resource와 `starting`은 degraded readiness, `stopping`/`stopped`는 not-ready, worker-start failure는 `workerStartFailures`와 `lastWorkerStartFailure` details를 포함해 not-ready/unhealthy로 보고합니다. Snapshot details에는 Redis dependency id, lifecycle state, ready/discovered worker 수, pending dead-letter write 수, dead-letter drain timeout, `workerShutdownTimeoutMs`가 포함됩니다.
144
152
 
145
153
  singleton `@QueueWorker()` provider/controller만 등록됩니다. request/transient worker는 discovery 중 건너뜁니다.
146
154
 
package/README.md CHANGED
@@ -92,7 +92,7 @@ QueueModule.forRoot({ clientName: 'jobs' })
92
92
 
93
93
  ### Bootstrap and Shutdown Lifecycle
94
94
 
95
- Queue discovers workers and creates queue-owned BullMQ resources during application bootstrap, but BullMQ worker processors are started only after the runtime marks the full application bootstrap/readiness sequence complete. Jobs enqueued by other `onApplicationBootstrap()` hooks can be accepted once the Queue service is initialized, and their processors run after the bootstrap-ready handoff instead of racing ahead of later async bootstrap hooks or application readiness.
95
+ Queue discovers workers and creates queue-owned BullMQ resources during application bootstrap, but BullMQ worker processors are started only after the runtime marks the full application bootstrap/readiness sequence complete. Jobs enqueued by other `onApplicationBootstrap()` hooks can be accepted once the Queue service is initialized, and their processors run after the bootstrap-ready handoff instead of racing ahead of later async bootstrap hooks or application readiness. Queue status reports degraded readiness until those BullMQ processors have actually started; if a processor fails to start, the lifecycle moves to `failed` and status snapshots expose the failure instead of reporting the workers as ready.
96
96
 
97
97
  Application shutdown marks Queue as `stopping`, rejects new enqueue attempts, closes queue-owned workers/queues/connections, and drains pending dead-letter writes. Worker shutdown is bounded by `workerShutdownTimeoutMs` so an active processor that never settles cannot block application shutdown indefinitely. When the timeout elapses, Queue logs the timeout and asks BullMQ to force-close the worker before continuing resource cleanup.
98
98
 
@@ -122,25 +122,33 @@ Treat low-level provider assembly as an internal implementation detail: low-leve
122
122
  ### Core
123
123
  - `QueueModule`: Main entry point for queue registration.
124
124
  - `QueueModule.forRoot(options)`: Registers queue support for an application module.
125
- - `QueueLifecycleService`: Primary service for enqueuing jobs (`enqueue(job)`).
125
+ - `QueueLifecycleService`: Primary service for enqueuing jobs and creating lifecycle/status snapshots (`enqueue(job)`, `createPlatformStatusSnapshot()`).
126
126
  - `@QueueWorker(JobClass, options?)`: Decorator to mark a class as a job handler.
127
127
  - `QUEUE`: Compatibility injection token for the queue facade.
128
128
  - `createQueuePlatformStatusSnapshot(...)`: Status snapshot helper for lifecycle/readiness diagnostics.
129
129
 
130
130
 
131
131
  ### Types
132
- - `QueueModuleOptions`: Global queue settings (clientName, default attempts, `defaultBackoff`, concurrency, rate limiting, dead-letter retention).
132
+ - `Queue`: Compatibility facade with `enqueue(job)` for application code and the `QUEUE` token.
133
+ - `QueueJobType`: Constructor type used to identify and rehydrate a job payload class.
134
+ - `QueueModuleOptions`: Global queue settings (`global`, clientName, default attempts, `defaultBackoff`, concurrency, rate limiting, dead-letter retention).
133
135
  - `QueueWorkerOptions`: Per-job settings (attempts, backoff, concurrency, jobName, rate limiting).
136
+ - `QueueBackoffType`: Supported retry backoff strategy names (`fixed`, `exponential`).
134
137
  - `QueueBackoffOptions`: Retry backoff settings (`type`, `delayMs`).
138
+ - `QueueRateLimiterOptions`: Worker-level distributed rate limiter settings (`max`, `duration`).
139
+ - `QueueLifecycleState`: Lifecycle states reported by Queue status adapters (`idle`, `starting`, `started`, `stopping`, `stopped`, `failed`).
140
+ - `QueueStatusAdapterInput`: Normalized queue metrics and worker-start diagnostics passed to `createQueuePlatformStatusSnapshot(...)`.
141
+ - `QueuePlatformStatusSnapshot`: Queue-specific readiness, health, ownership, and detail snapshot returned by the status helper and `QueueLifecycleService.createPlatformStatusSnapshot()`.
135
142
 
136
143
  `QueueModuleOptions` also includes lifecycle and dead-letter retention controls such as `workerShutdownTimeoutMs` and `defaultDeadLetterMaxEntries`.
137
144
 
138
145
  `QueueModuleOptions` lifecycle/status controls:
139
146
 
147
+ - `global`: whether the queue module registration is global. Defaults to `true`; set `false` when queue providers should stay scoped to the importing module graph.
140
148
  - `workerShutdownTimeoutMs`: maximum time to wait for active worker processors during shutdown before force-closing the BullMQ worker. Defaults to `30_000`.
141
149
  - `defaultDeadLetterMaxEntries`: maximum retained dead-letter records per job, or `false` to disable trimming. Defaults to `1_000`.
142
150
 
143
- `createQueuePlatformStatusSnapshot(...)` reports readiness as `ready` only after Queue reaches `started`; `starting` reports degraded readiness, and `stopping`/`stopped` report not-ready. Snapshot details include the Redis dependency id, lifecycle state, ready/discovered worker counts, pending dead-letter writes, the dead-letter drain timeout, and `workerShutdownTimeoutMs`.
151
+ `QueueLifecycleService.createPlatformStatusSnapshot()` uses the same public snapshot contract as `createQueuePlatformStatusSnapshot(...)`. It reports readiness as `ready` only after Queue reaches `started` and every discovered BullMQ worker processor has started. `started` resources with pending processors report degraded readiness, `starting` reports degraded readiness, `stopping`/`stopped` report not-ready, and worker-start failures report not-ready/unhealthy with `workerStartFailures` and `lastWorkerStartFailure` details. Snapshot details include the Redis dependency id, lifecycle state, ready/discovered worker counts, pending dead-letter writes, the dead-letter drain timeout, and `workerShutdownTimeoutMs`.
144
152
 
145
153
  Only singleton `@QueueWorker()` providers/controllers are registered. Request/transient workers are skipped during discovery.
146
154
 
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAuB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAExF,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAiBpF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,kBAAuB,GAAG,kBAAkB,CAWvG"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAuB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAExF,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAepF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,kBAAuB,GAAG,kBAAkB,CAavG"}
@@ -1,6 +1,5 @@
1
1
  import { ensureMetadataSymbol } from '@fluojs/core/internal';
2
2
  import { queueWorkerMetadataSymbol } from './metadata.js';
3
- ensureMetadataSymbol();
4
3
  function getStandardMetadataBag(metadata) {
5
4
  return metadata;
6
5
  }
@@ -39,6 +38,7 @@ function defineStandardQueueWorkerMetadata(metadata, workerMetadata) {
39
38
  */
40
39
  export function QueueWorker(jobType, options = {}) {
41
40
  const decorator = (_value, context) => {
41
+ ensureMetadataSymbol();
42
42
  const metadata = {
43
43
  jobType,
44
44
  options: {
package/dist/helpers.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface DiscoveryCandidate {
15
15
  targetType: Function;
16
16
  token: Token;
17
17
  }
18
+ /** Selects whether one compiled module participates in worker discovery. */
19
+ export type DiscoveryModuleFilter = (compiledModule: CompiledModule) => boolean;
18
20
  /**
19
21
  * Scope from provider.
20
22
  *
@@ -38,7 +40,7 @@ export declare function isClassProvider(provider: Provider): provider is Extract
38
40
  * @param compiledModules The compiled modules.
39
41
  * @returns The collect discovery candidates result.
40
42
  */
41
- export declare function collectDiscoveryCandidates(compiledModules: readonly CompiledModule[]): DiscoveryCandidate[];
43
+ export declare function collectDiscoveryCandidates(compiledModules: readonly CompiledModule[], moduleFilter?: DiscoveryModuleFilter): DiscoveryCandidate[];
42
44
  /**
43
45
  * Normalize positive integer.
44
46
  *
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAU3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAEzH;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,GAAG,kBAAkB,EAAE,CAoC3G;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5F;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EACjC,QAAQ,EAAE,MAAM,GAAG,KAAK,GACvB,MAAM,GAAG,KAAK,CAgBhB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAAG,uBAAuB,GAAG,SAAS,CAS1H;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,MAAM,KAAK,GAC/B,OAAO,CAAC,CAAC,CAAC,CAeZ"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,4EAA4E;AAC5E,MAAM,MAAM,qBAAqB,GAAG,CAAC,cAAc,EAAE,cAAc,KAAK,OAAO,CAAC;AAEhF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAU3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAEzH;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,YAAY,GAAE,qBAAkC,GAC/C,kBAAkB,EAAE,CAwCtB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5F;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EACjC,QAAQ,EAAE,MAAM,GAAG,KAAK,GACvB,MAAM,GAAG,KAAK,CAgBhB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAAG,uBAAuB,GAAG,SAAS,CAS1H;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,MAAM,KAAK,GAC/B,OAAO,CAAC,CAAC,CAAC,CAeZ"}
package/dist/helpers.js CHANGED
@@ -8,6 +8,8 @@ import { getClassDiMetadata } from '@fluojs/core/internal';
8
8
  * Describes the discovery candidate contract.
9
9
  */
10
10
 
11
+ /** Selects whether one compiled module participates in worker discovery. */
12
+
11
13
  /**
12
14
  * Scope from provider.
13
15
  *
@@ -40,9 +42,12 @@ export function isClassProvider(provider) {
40
42
  * @param compiledModules The compiled modules.
41
43
  * @returns The collect discovery candidates result.
42
44
  */
43
- export function collectDiscoveryCandidates(compiledModules) {
45
+ export function collectDiscoveryCandidates(compiledModules, moduleFilter = () => true) {
44
46
  const candidates = [];
45
47
  for (const compiledModule of compiledModules) {
48
+ if (!moduleFilter(compiledModule)) {
49
+ continue;
50
+ }
46
51
  for (const provider of compiledModule.definition.providers ?? []) {
47
52
  if (typeof provider === 'function') {
48
53
  candidates.push({
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAgC,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAsCnF;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,UAAU;CAS7D"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAgC,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAuCnF;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,UAAU;CAS7D"}
package/dist/module.js CHANGED
@@ -14,6 +14,7 @@ function normalizeQueueModuleOptions(options = {}) {
14
14
  defaultConcurrency: normalizePositiveInteger(options.defaultConcurrency, 1),
15
15
  defaultDeadLetterMaxEntries: normalizePositiveIntegerOrFalse(options.defaultDeadLetterMaxEntries, 1_000),
16
16
  defaultRateLimiter,
17
+ global: options.global ?? true,
17
18
  workerShutdownTimeoutMs: normalizePositiveInteger(options.workerShutdownTimeoutMs, 30_000)
18
19
  };
19
20
  }
package/dist/service.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Container } from '@fluojs/di';
2
2
  import type { ApplicationLogger, CompiledModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy } from '@fluojs/runtime';
3
3
  import { type BootstrapReadySignal } from '@fluojs/runtime/internal';
4
+ import { type QueuePlatformStatusSnapshot } from './status.js';
4
5
  import type { NormalizedQueueModuleOptions, Queue } from './types.js';
5
6
  /**
6
7
  * Lifecycle-managed queue runtime for worker discovery and job dispatch.
@@ -20,10 +21,14 @@ export declare class QueueLifecycleService implements Queue, OnApplicationBootst
20
21
  private readonly ownedConnections;
21
22
  private readonly deadLetterManager;
22
23
  private readonly readyWorkers;
24
+ private readonly runningWorkerJobNames;
25
+ private readonly failedWorkerJobNames;
26
+ private readonly workerStartFailures;
23
27
  private lifecycleState;
24
28
  private redisClient;
25
29
  private startPromise;
26
30
  private shutdownPromise;
31
+ private startupFailureRollbackPromise;
27
32
  constructor(options: NormalizedQueueModuleOptions, runtimeContainer: Container, compiledModules: readonly CompiledModule[], logger: ApplicationLogger, bootstrapReadySignal?: BootstrapReadySignal);
28
33
  onApplicationBootstrap(): Promise<void>;
29
34
  onApplicationShutdown(): Promise<void>;
@@ -42,9 +47,10 @@ export declare class QueueLifecycleService implements Queue, OnApplicationBootst
42
47
  *
43
48
  * @returns A structured snapshot describing lifecycle state, discovered workers, and pending dead-letter writes.
44
49
  */
45
- createPlatformStatusSnapshot(): import("./status.js").QueuePlatformStatusSnapshot;
50
+ createPlatformStatusSnapshot(): QueuePlatformStatusSnapshot;
46
51
  private ensureStarted;
47
52
  private startLifecycle;
53
+ private shouldDiscoverWorkersInModule;
48
54
  private handleStartupFailure;
49
55
  private resolveRedisClient;
50
56
  private getRedisClient;
@@ -58,12 +64,17 @@ export declare class QueueLifecycleService implements Queue, OnApplicationBootst
58
64
  private registerInitializedWorker;
59
65
  private scheduleReadyWorkers;
60
66
  private runWorker;
67
+ private markWorkerReadyWhenStarted;
68
+ private recordWorkerStartFailure;
69
+ private rollbackAfterWorkerStartupFailure;
70
+ private toErrorMessage;
61
71
  private cleanupWorkerInitializationFailure;
62
72
  private createOwnedConnection;
63
73
  private executeWorker;
64
74
  private resolveWorkerHandler;
65
75
  private rehydrateWorkerPayload;
66
76
  private shutdown;
77
+ private waitForStartupFailureRollback;
67
78
  private waitForInFlightStartup;
68
79
  private closeInitializedResources;
69
80
  private tryCloseWorker;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EACV,4BAA4B,EAC5B,KAAK,EAIN,MAAM,YAAY,CAAC;AAqHpB;;;;;GAKG;AACH,qBACa,qBAAsB,YAAW,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe;IAa/G,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAhBvC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAkD;IACvF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IACpE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqC;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA8B;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,eAAe,CAA4B;gBAGhC,OAAO,EAAE,4BAA4B,EACrC,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,oBAAoB,GAAE,oBAAuD;IAK1F,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IA2B9D;;;;OAIG;IACH,4BAA4B;YAYd,aAAa;YAwBb,cAAc;YAgBd,oBAAoB;YASpB,kBAAkB;IAgBhC,OAAO,CAAC,cAAc;YAQR,iBAAiB;YAOjB,yBAAyB;IAyBvC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,0BAA0B;IAkBlC,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,SAAS;YAgBH,kCAAkC;YAkBlC,qBAAqB;YAiBrB,aAAa;YAOb,oBAAoB;IAyBlC,OAAO,CAAC,sBAAsB;YAQhB,QAAQ;YAyBR,sBAAsB;YAgBtB,yBAAyB;YAsBzB,cAAc;YAkBd,aAAa;YAQb,uBAAuB;CAOtC"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EACV,4BAA4B,EAC5B,KAAK,EAIN,MAAM,YAAY,CAAC;AA8HpB;;;;;GAKG;AACH,qBACa,qBAAsB,YAAW,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe;IAiB/G,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IApBvC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAkD;IACvF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IACpE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqC;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA8B;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;IAC1D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA4B;IAChE,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,6BAA6B,CAA4B;gBAG9C,OAAO,EAAE,4BAA4B,EACrC,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,oBAAoB,GAAE,oBAAuD;IAK1F,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IA2B9D;;;;OAIG;IACH,4BAA4B,IAAI,2BAA2B;YAgB7C,aAAa;YAwBb,cAAc;IAqB5B,OAAO,CAAC,6BAA6B;YAQvB,oBAAoB;YASpB,kBAAkB;IAgBhC,OAAO,CAAC,cAAc;YAQR,iBAAiB;YAOjB,yBAAyB;IAyBvC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,0BAA0B;IAkBlC,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,SAAS;YA+BH,0BAA0B;IAkBxC,OAAO,CAAC,wBAAwB;IA6BhC,OAAO,CAAC,iCAAiC;IAgBzC,OAAO,CAAC,cAAc;YAIR,kCAAkC;YAkBlC,qBAAqB;YAiBrB,aAAa;YAOb,oBAAoB;IAyBlC,OAAO,CAAC,sBAAsB;YAQhB,QAAQ;YA0BR,6BAA6B;YAS7B,sBAAsB;YAgBtB,yBAAyB;YAuBzB,cAAc;YAkBd,aAAa;YAQb,uBAAuB;CAOtC"}
package/dist/service.js CHANGED
@@ -12,7 +12,7 @@ import { Queue as BullQueue, Worker as BullWorker } from 'bullmq';
12
12
  import { QueueDeadLetterManager } from './dead-letter-manager.js';
13
13
  import { normalizePositiveInteger, withTimeout } from './helpers.js';
14
14
  import { createQueuePlatformStatusSnapshot } from './status.js';
15
- import { QUEUE_OPTIONS } from './tokens.js';
15
+ import { QUEUE, QUEUE_OPTIONS } from './tokens.js';
16
16
  import { discoverQueueWorkerDescriptors } from './worker-discovery.js';
17
17
  const IMMEDIATE_BOOTSTRAP_READY_SIGNAL = {
18
18
  wait: () => Promise.resolve()
@@ -77,10 +77,14 @@ class QueueLifecycleService {
77
77
  ownedConnections = [];
78
78
  deadLetterManager;
79
79
  readyWorkers = [];
80
+ runningWorkerJobNames = new Set();
81
+ failedWorkerJobNames = new Set();
82
+ workerStartFailures = [];
80
83
  lifecycleState = 'idle';
81
84
  redisClient;
82
85
  startPromise;
83
86
  shutdownPromise;
87
+ startupFailureRollbackPromise;
84
88
  constructor(options, runtimeContainer, compiledModules, logger, bootstrapReadySignal = IMMEDIATE_BOOTSTRAP_READY_SIGNAL) {
85
89
  this.options = options;
86
90
  this.runtimeContainer = runtimeContainer;
@@ -133,21 +137,26 @@ class QueueLifecycleService {
133
137
  * @returns A structured snapshot describing lifecycle state, discovered workers, and pending dead-letter writes.
134
138
  */
135
139
  createPlatformStatusSnapshot() {
140
+ const lastWorkerStartFailure = this.workerStartFailures[this.workerStartFailures.length - 1];
136
141
  return createQueuePlatformStatusSnapshot({
137
142
  dependencyId: getRedisComponentId(this.options.clientName),
138
143
  lifecycleState: this.lifecycleState,
144
+ ...(lastWorkerStartFailure ? {
145
+ lastWorkerStartFailure: lastWorkerStartFailure.message
146
+ } : {}),
139
147
  pendingDeadLetterWrites: this.deadLetterManager.pendingWriteCount,
140
148
  queuesReady: this.queuesByJobName.size,
149
+ workerStartFailures: this.workerStartFailures.length,
141
150
  workerShutdownTimeoutMs: this.options.workerShutdownTimeoutMs,
142
151
  workersDiscovered: this.descriptorsByJobType.size,
143
- workersReady: this.workersByJobName.size
152
+ workersReady: this.runningWorkerJobNames.size
144
153
  });
145
154
  }
146
155
  async ensureStarted() {
147
156
  if (this.lifecycleState === 'started') {
148
157
  return;
149
158
  }
150
- if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
159
+ if (this.lifecycleState === 'failed' || this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
151
160
  throw new Error(`Queue lifecycle state is ${this.lifecycleState}.`);
152
161
  }
153
162
  if (!this.startPromise) {
@@ -166,7 +175,7 @@ class QueueLifecycleService {
166
175
  const redis = await this.resolveRedisClient();
167
176
  this.redisClient = redis;
168
177
  this.descriptorsByJobType.clear();
169
- for (const [jobType, descriptor] of discoverQueueWorkerDescriptors(this.compiledModules, this.options, this.logger)) {
178
+ for (const [jobType, descriptor] of discoverQueueWorkerDescriptors(this.compiledModules, this.options, this.logger, compiledModule => this.shouldDiscoverWorkersInModule(compiledModule))) {
170
179
  this.descriptorsByJobType.set(jobType, descriptor);
171
180
  }
172
181
  await this.initializeWorkers(redis);
@@ -175,6 +184,12 @@ class QueueLifecycleService {
175
184
  this.scheduleReadyWorkers();
176
185
  }
177
186
  }
187
+ shouldDiscoverWorkersInModule(compiledModule) {
188
+ if (this.options.global) {
189
+ return true;
190
+ }
191
+ return compiledModule.accessibleTokens.has(_QueueLifecycleServic) || compiledModule.accessibleTokens.has(QUEUE);
192
+ }
178
193
  async handleStartupFailure() {
179
194
  await this.closeInitializedResources();
180
195
  if (this.lifecycleState === 'starting') {
@@ -278,23 +293,91 @@ class QueueLifecycleService {
278
293
  descriptor,
279
294
  worker
280
295
  } of workers) {
296
+ if (this.lifecycleState !== 'started') {
297
+ break;
298
+ }
281
299
  this.runWorker(descriptor, worker);
300
+ if (this.lifecycleState !== 'started') {
301
+ break;
302
+ }
282
303
  }
283
304
  }).catch(error => {
284
305
  if (this.lifecycleState !== 'started') {
285
306
  return;
286
307
  }
308
+ this.lifecycleState = 'failed';
309
+ this.workerStartFailures.push({
310
+ jobName: '*',
311
+ message: this.toErrorMessage(error),
312
+ workerName: 'bootstrap-ready-signal'
313
+ });
287
314
  this.logger.error('Failed to start queue workers after application bootstrap readiness.', error, 'QueueLifecycleService');
315
+ this.rollbackAfterWorkerStartupFailure();
288
316
  });
289
317
  }
290
318
  runWorker(descriptor, worker) {
291
319
  const runnableWorker = worker;
292
320
  if (typeof runnableWorker.run !== 'function') {
321
+ this.recordWorkerStartFailure(descriptor, new Error(`Queue worker ${descriptor.workerName} cannot start because BullMQ Worker.run() is unavailable.`));
322
+ return;
323
+ }
324
+ let runResult;
325
+ try {
326
+ runResult = runnableWorker.run();
327
+ } catch (error) {
328
+ this.recordWorkerStartFailure(descriptor, error);
329
+ return;
330
+ }
331
+ const runPromise = Promise.resolve(runResult);
332
+ void runPromise.catch(error => {
333
+ this.recordWorkerStartFailure(descriptor, error);
334
+ });
335
+ void this.markWorkerReadyWhenStarted(descriptor, runnableWorker, runPromise).catch(error => {
336
+ this.recordWorkerStartFailure(descriptor, error);
337
+ });
338
+ }
339
+ async markWorkerReadyWhenStarted(descriptor, worker, runPromise) {
340
+ if (typeof worker.waitUntilReady === 'function') {
341
+ await Promise.race([worker.waitUntilReady(), runPromise]);
342
+ } else {
343
+ await Promise.race([Promise.resolve(), runPromise]);
344
+ }
345
+ if (this.lifecycleState !== 'started' || this.failedWorkerJobNames.has(descriptor.jobName)) {
293
346
  return;
294
347
  }
295
- void Promise.resolve(runnableWorker.run()).catch(error => {
296
- this.logger.error(`Failed to start queue worker ${descriptor.workerName} after application bootstrap.`, error, 'QueueLifecycleService');
348
+ this.runningWorkerJobNames.add(descriptor.jobName);
349
+ }
350
+ recordWorkerStartFailure(descriptor, error) {
351
+ if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
352
+ return;
353
+ }
354
+ if (this.failedWorkerJobNames.has(descriptor.jobName)) {
355
+ return;
356
+ }
357
+ this.failedWorkerJobNames.add(descriptor.jobName);
358
+ this.runningWorkerJobNames.delete(descriptor.jobName);
359
+ this.workerStartFailures.push({
360
+ jobName: descriptor.jobName,
361
+ message: this.toErrorMessage(error),
362
+ workerName: descriptor.workerName
297
363
  });
364
+ if (this.lifecycleState === 'started' || this.lifecycleState === 'starting') {
365
+ this.lifecycleState = 'failed';
366
+ }
367
+ this.logger.error(`Failed to start queue worker ${descriptor.workerName} after application bootstrap.`, error, 'QueueLifecycleService');
368
+ this.rollbackAfterWorkerStartupFailure();
369
+ }
370
+ rollbackAfterWorkerStartupFailure() {
371
+ if (!this.startupFailureRollbackPromise) {
372
+ this.startupFailureRollbackPromise = this.closeInitializedResources().then(() => {
373
+ this.redisClient = undefined;
374
+ }).catch(rollbackError => {
375
+ this.logger.error('Failed to roll back queue resources after worker startup failure.', rollbackError, 'QueueLifecycleService');
376
+ });
377
+ }
378
+ }
379
+ toErrorMessage(error) {
380
+ return error instanceof Error ? error.message : String(error);
298
381
  }
299
382
  async cleanupWorkerInitializationFailure(resources) {
300
383
  if (resources.worker) {
@@ -362,6 +445,7 @@ class QueueLifecycleService {
362
445
  this.lifecycleState = 'stopping';
363
446
  this.shutdownPromise = (async () => {
364
447
  await this.waitForInFlightStartup();
448
+ await this.waitForStartupFailureRollback();
365
449
  await this.closeInitializedResources();
366
450
  await this.deadLetterManager.drainPendingWrites();
367
451
  this.lifecycleState = 'stopped';
@@ -370,6 +454,13 @@ class QueueLifecycleService {
370
454
  })();
371
455
  await this.shutdownPromise;
372
456
  }
457
+ async waitForStartupFailureRollback() {
458
+ if (!this.startupFailureRollbackPromise) {
459
+ return;
460
+ }
461
+ await this.startupFailureRollbackPromise;
462
+ this.startupFailureRollbackPromise = undefined;
463
+ }
373
464
  async waitForInFlightStartup() {
374
465
  const startup = this.startPromise;
375
466
  if (!startup) {
@@ -390,6 +481,7 @@ class QueueLifecycleService {
390
481
  this.workersByJobName.clear();
391
482
  this.queuesByJobName.clear();
392
483
  this.readyWorkers.splice(0);
484
+ this.runningWorkerJobNames.clear();
393
485
  for (const worker of workers) {
394
486
  await this.tryCloseWorker(worker);
395
487
  }
package/dist/status.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import type { PlatformHealthReport, PlatformReadinessReport, PlatformSnapshot } from '@fluojs/runtime';
2
2
  /** Lifecycle phases reported by the queue platform status adapter. */
3
- export type QueueLifecycleState = 'idle' | 'starting' | 'started' | 'stopping' | 'stopped';
3
+ export type QueueLifecycleState = 'idle' | 'starting' | 'started' | 'stopping' | 'stopped' | 'failed';
4
4
  /** Input payload used to derive queue readiness, health, and dependency details. */
5
5
  export interface QueueStatusAdapterInput {
6
6
  dependencyId?: string;
7
+ lastWorkerStartFailure?: string;
7
8
  lifecycleState: QueueLifecycleState;
8
9
  pendingDeadLetterWrites: number;
9
10
  queuesReady: number;
11
+ workerStartFailures?: number;
10
12
  workerShutdownTimeoutMs: number;
11
13
  workersDiscovered: number;
12
14
  workersReady: number;
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3F,oFAAoF;AACpF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,mBAAmB,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sFAAsF;AACtF,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAkFD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,uBAAuB,GAAG,2BAA2B,CAmB7G"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtG,oFAAoF;AACpF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,mBAAmB,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sFAAsF;AACtF,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAoHD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,uBAAuB,GAAG,2BAA2B,CAuB7G"}
package/dist/status.js CHANGED
@@ -5,7 +5,22 @@
5
5
  /** Queue-specific platform snapshot returned to health and readiness integrations. */
6
6
 
7
7
  function createReadiness(input) {
8
+ const workerStartFailures = input.workerStartFailures ?? 0;
9
+ if (input.lifecycleState === 'failed' || workerStartFailures > 0) {
10
+ return {
11
+ critical: true,
12
+ reason: 'Queue worker startup failed.',
13
+ status: 'not-ready'
14
+ };
15
+ }
8
16
  if (input.lifecycleState === 'started') {
17
+ if (input.workersReady < input.workersDiscovered) {
18
+ return {
19
+ critical: true,
20
+ reason: 'Queue workers are waiting for BullMQ processors to start.',
21
+ status: 'degraded'
22
+ };
23
+ }
9
24
  return {
10
25
  critical: true,
11
26
  status: 'ready'
@@ -39,6 +54,13 @@ function createReadiness(input) {
39
54
  };
40
55
  }
41
56
  function createHealth(input) {
57
+ const workerStartFailures = input.workerStartFailures ?? 0;
58
+ if (input.lifecycleState === 'failed' || workerStartFailures > 0) {
59
+ return {
60
+ reason: 'Queue worker startup failed.',
61
+ status: 'unhealthy'
62
+ };
63
+ }
42
64
  if (input.lifecycleState === 'stopped') {
43
65
  return {
44
66
  reason: 'Queue workers are stopped.',
@@ -57,6 +79,12 @@ function createHealth(input) {
57
79
  status: 'degraded'
58
80
  };
59
81
  }
82
+ if (input.lifecycleState === 'started' && input.workersReady < input.workersDiscovered) {
83
+ return {
84
+ reason: 'Queue workers are waiting for BullMQ processors to start.',
85
+ status: 'degraded'
86
+ };
87
+ }
60
88
  if (input.lifecycleState === 'started' && input.pendingDeadLetterWrites > 0) {
61
89
  return {
62
90
  reason: 'Queue dead-letter writes are still pending.',
@@ -81,13 +109,18 @@ function createHealth(input) {
81
109
  * @returns Readiness, health, ownership, and queue detail fields.
82
110
  */
83
111
  export function createQueuePlatformStatusSnapshot(input) {
112
+ const workerStartFailures = input.workerStartFailures ?? 0;
84
113
  return {
85
114
  details: {
86
115
  deadLetterDrainTimeoutMs: 5_000,
87
116
  dependencies: [input.dependencyId ?? 'redis.default'],
88
117
  lifecycleState: input.lifecycleState,
118
+ ...(input.lastWorkerStartFailure ? {
119
+ lastWorkerStartFailure: input.lastWorkerStartFailure
120
+ } : {}),
89
121
  pendingDeadLetterWrites: input.pendingDeadLetterWrites,
90
122
  queuesReady: input.queuesReady,
123
+ workerStartFailures,
91
124
  workerShutdownTimeoutMs: input.workerShutdownTimeoutMs,
92
125
  workersDiscovered: input.workersDiscovered,
93
126
  workersReady: input.workersReady
package/dist/types.d.ts CHANGED
@@ -49,6 +49,7 @@ export interface NormalizedQueueModuleOptions {
49
49
  defaultConcurrency: number;
50
50
  defaultDeadLetterMaxEntries: number | false;
51
51
  defaultRateLimiter?: QueueRateLimiterOptions;
52
+ global: boolean;
52
53
  workerShutdownTimeoutMs: number;
53
54
  }
54
55
  /** Metadata captured by {@link QueueWorker} during decorator evaluation. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACxD,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;CAC9B;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,aAAa,CAAC;AAEvD,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2BAA2B,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,kHAAkH;IAClH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,yEAAyE;AACzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2BAA2B,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB;;;;;OAKG;IACH,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1D"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACxD,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;CAC9B;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,aAAa,CAAC;AAEvD,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2BAA2B,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,kHAAkH;IAClH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,yEAAyE;AACzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2BAA2B,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB;;;;;OAKG;IACH,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1D"}
@@ -1,4 +1,5 @@
1
1
  import type { ApplicationLogger, CompiledModule } from '@fluojs/runtime';
2
+ import { type DiscoveryModuleFilter } from './helpers.js';
2
3
  import type { NormalizedQueueModuleOptions, QueueJobType, QueueWorkerDescriptor } from './types.js';
3
4
  /**
4
5
  * Discover queue worker descriptors.
@@ -8,5 +9,5 @@ import type { NormalizedQueueModuleOptions, QueueJobType, QueueWorkerDescriptor
8
9
  * @param logger The logger.
9
10
  * @returns The discover queue worker descriptors result.
10
11
  */
11
- export declare function discoverQueueWorkerDescriptors(compiledModules: readonly CompiledModule[], options: NormalizedQueueModuleOptions, logger: ApplicationLogger): Map<QueueJobType, QueueWorkerDescriptor>;
12
+ export declare function discoverQueueWorkerDescriptors(compiledModules: readonly CompiledModule[], options: NormalizedQueueModuleOptions, logger: ApplicationLogger, moduleFilter?: DiscoveryModuleFilter): Map<QueueJobType, QueueWorkerDescriptor>;
12
13
  //# sourceMappingURL=worker-discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-discovery.d.ts","sourceRoot":"","sources":["../src/worker-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIzE,OAAO,KAAK,EAAE,4BAA4B,EAAE,YAAY,EAAE,qBAAqB,EAAuB,MAAM,YAAY,CAAC;AAEzH;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,OAAO,EAAE,4BAA4B,EACrC,MAAM,EAAE,iBAAiB,GACxB,GAAG,CAAC,YAAY,EAAE,qBAAqB,CAAC,CA4C1C"}
1
+ {"version":3,"file":"worker-discovery.d.ts","sourceRoot":"","sources":["../src/worker-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,EAA8B,KAAK,qBAAqB,EAAkD,MAAM,cAAc,CAAC;AACtI,OAAO,KAAK,EAAE,4BAA4B,EAAE,YAAY,EAAE,qBAAqB,EAAuB,MAAM,YAAY,CAAC;AAEzH;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,OAAO,EAAE,4BAA4B,EACrC,MAAM,EAAE,iBAAiB,EACzB,YAAY,CAAC,EAAE,qBAAqB,GACnC,GAAG,CAAC,YAAY,EAAE,qBAAqB,CAAC,CA4C1C"}
@@ -8,10 +8,10 @@ import { collectDiscoveryCandidates, normalizePositiveInteger, normalizeRateLimi
8
8
  * @param logger The logger.
9
9
  * @returns The discover queue worker descriptors result.
10
10
  */
11
- export function discoverQueueWorkerDescriptors(compiledModules, options, logger) {
11
+ export function discoverQueueWorkerDescriptors(compiledModules, options, logger, moduleFilter) {
12
12
  const descriptorsByJobType = new Map();
13
13
  const seenJobNames = new Set();
14
- for (const candidate of collectDiscoveryCandidates(compiledModules)) {
14
+ for (const candidate of collectDiscoveryCandidates(compiledModules, moduleFilter)) {
15
15
  const metadata = getQueueWorkerMetadata(candidate.targetType);
16
16
  if (!metadata) {
17
17
  continue;
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "redis",
11
11
  "dlq"
12
12
  ],
13
- "version": "1.0.0",
13
+ "version": "1.0.2",
14
14
  "private": false,
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -38,10 +38,10 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "bullmq": "^5.58.0",
41
- "@fluojs/core": "^1.0.0",
42
- "@fluojs/redis": "^1.0.0",
43
- "@fluojs/runtime": "^1.0.0",
44
- "@fluojs/di": "^1.0.0"
41
+ "@fluojs/core": "^1.0.3",
42
+ "@fluojs/di": "^1.1.0",
43
+ "@fluojs/redis": "^1.0.2",
44
+ "@fluojs/runtime": "^1.1.8"
45
45
  },
46
46
  "devDependencies": {
47
47
  "vitest": "^3.2.4"