@pattern-stack/codegen 0.4.2 → 0.4.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 (31) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +1 -0
  3. package/dist/runtime/subsystems/bridge/bridge.module.js +42 -23
  4. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  5. package/dist/runtime/subsystems/bridge/index.d.ts +1 -0
  6. package/dist/runtime/subsystems/bridge/index.js +33 -14
  7. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  8. package/dist/runtime/subsystems/index.js +35 -16
  9. package/dist/runtime/subsystems/index.js.map +1 -1
  10. package/dist/runtime/subsystems/jobs/index.d.ts +1 -0
  11. package/dist/runtime/subsystems/jobs/index.js +31 -12
  12. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  13. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +3 -1
  14. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +10 -4
  15. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js.map +1 -1
  16. package/dist/runtime/subsystems/jobs/job-worker.d.ts +3 -1
  17. package/dist/runtime/subsystems/jobs/job-worker.js +7 -2
  18. package/dist/runtime/subsystems/jobs/job-worker.js.map +1 -1
  19. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +3 -1
  20. package/dist/runtime/subsystems/jobs/job-worker.module.js +31 -12
  21. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  22. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +10 -4
  23. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  24. package/dist/src/cli/index.js +71 -7
  25. package/dist/src/cli/index.js.map +1 -1
  26. package/dist/src/index.js +27 -2
  27. package/dist/src/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/runtime/subsystems/jobs/job-orchestrator.memory-backend.ts +16 -2
  30. package/runtime/subsystems/jobs/job-worker.module.ts +20 -2
  31. package/runtime/subsystems/jobs/job-worker.ts +18 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pattern-stack/codegen",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Entity-driven code generation for full-stack TypeScript applications",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,8 @@
14
14
  * the orchestrator's mutex.
15
15
  */
16
16
  import { randomUUID } from 'node:crypto';
17
- import { Inject, Injectable, Logger } from '@nestjs/common';
17
+ import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
18
+ import { ModuleRef } from '@nestjs/core';
18
19
  import type {
19
20
  JobDefinitionRow,
20
21
  JobRunRow,
@@ -139,6 +140,7 @@ export class MemoryJobOrchestrator implements IJobOrchestrator {
139
140
  private readonly store: MemoryJobStore,
140
141
  private readonly stepService: MemoryJobStepService,
141
142
  @Inject(JOBS_MULTI_TENANT) private readonly multiTenant: boolean,
143
+ @Optional() private readonly moduleRef?: ModuleRef,
142
144
  ) {}
143
145
 
144
146
  /**
@@ -590,7 +592,19 @@ export class MemoryJobOrchestrator implements IJobOrchestrator {
590
592
  }
591
593
  const meta = registration.meta;
592
594
  const HandlerClass = registration.handlerClass;
593
- const handler = new HandlerClass();
595
+ // Match the Drizzle backend: resolve the handler through Nest's
596
+ // ModuleRef so `@Inject` constructor params work. ModuleRef is
597
+ // @Optional() — zero-dep test stubs that construct this orchestrator
598
+ // manually still hit the legacy `new HandlerClass()` path.
599
+ // `get({ strict: false })` (not `create()`) — the handler must be a
600
+ // provider in its owning module so cross-module @Inject dependencies
601
+ // resolve. See job-worker.ts for the full rationale.
602
+ const handler = this.moduleRef
603
+ ? (this.moduleRef.get(
604
+ HandlerClass as unknown as new (...args: unknown[]) => unknown,
605
+ { strict: false },
606
+ ) as JobHandlerBase<unknown>)
607
+ : new HandlerClass();
594
608
 
595
609
  const ctx: JobContext<unknown> = {
596
610
  input: run.input,
@@ -29,6 +29,7 @@ import {
29
29
  type OnModuleDestroy,
30
30
  type OnModuleInit,
31
31
  } from '@nestjs/common';
32
+ import { ModuleRef } from '@nestjs/core';
32
33
  import { DRIZZLE } from '../../constants/tokens';
33
34
  import type { DrizzleClient } from '../../types/drizzle';
34
35
  import { HandlerRegistry, type HandlerRegistryEntry } from './job-handler.base';
@@ -126,6 +127,7 @@ export class JobWorkerOrchestrator implements OnModuleInit, OnModuleDestroy {
126
127
  * without supplying a `DRIZZLE` provider.
127
128
  */
128
129
  @Optional() @Inject(DRIZZLE) private readonly db: DrizzleClient | null = null,
130
+ private readonly moduleRef?: ModuleRef,
129
131
  ) {}
130
132
 
131
133
  // ============================================================================
@@ -175,8 +177,16 @@ export class JobWorkerOrchestrator implements OnModuleInit, OnModuleDestroy {
175
177
  `the resolved pool config. Configured pools: [${[...poolConfig.keys()].join(', ')}].`,
176
178
  );
177
179
  }
180
+ // `pool` here is the logical pool name (e.g. 'crm_sync') — the same
181
+ // value the orchestrator persists into `job_run.pool` from
182
+ // `@JobHandler.meta.pool`, and therefore the value the worker's
183
+ // claim query filters on. `def.queue` is a display/routing alias
184
+ // (e.g. 'jobs-crm-sync') used by BullMQ-style backends for queue
185
+ // naming; it MUST NOT be passed as the claim-filter pool, or the
186
+ // worker will never match any row and the pool silently never
187
+ // drains. See v0.4.4 fix notes.
178
188
  const workerOptions: JobWorkerOptions = {
179
- pool: def.queue,
189
+ pool: poolName,
180
190
  concurrency: def.concurrency,
181
191
  shutdownTimeoutMs:
182
192
  this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS,
@@ -190,7 +200,7 @@ export class JobWorkerOrchestrator implements OnModuleInit, OnModuleDestroy {
190
200
  worker.onModuleInit();
191
201
  this.workers.push(worker);
192
202
  this.logger.log(
193
- `JobWorker started: pool='${def.queue}' concurrency=${def.concurrency}`,
203
+ `JobWorker started: pool='${poolName}' (queue='${def.queue}') concurrency=${def.concurrency}`,
194
204
  );
195
205
  }
196
206
  }
@@ -270,12 +280,20 @@ export class JobWorkerOrchestrator implements OnModuleInit, OnModuleDestroy {
270
280
  `pass 'workerFactory' to inject a stub.`,
271
281
  );
272
282
  }
283
+ if (!this.moduleRef) {
284
+ throw new Error(
285
+ `JobWorkerModule: ModuleRef not available — cannot construct JobWorker ` +
286
+ `with handler DI support. Ensure the orchestrator is resolved through ` +
287
+ `the Nest container (not instantiated manually in tests).`,
288
+ );
289
+ }
273
290
  return new JobWorker(
274
291
  this.db,
275
292
  this.orchestrator,
276
293
  this.runService,
277
294
  this.stepService,
278
295
  workerOptions,
296
+ this.moduleRef,
279
297
  );
280
298
  }
281
299
  }
@@ -14,6 +14,7 @@
14
14
  */
15
15
  // TODO(logging-subsystem): swap to ILogger once ADR-028 lands
16
16
  import { Inject, Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from '@nestjs/common';
17
+ import { ModuleRef } from '@nestjs/core';
17
18
  import { and, asc, desc, eq, inArray, lt, lte, sql } from 'drizzle-orm';
18
19
  import type { DrizzleClient } from '../../types/drizzle';
19
20
  import { DRIZZLE } from '../../constants/tokens';
@@ -194,6 +195,7 @@ export class JobWorker implements OnModuleInit, OnModuleDestroy {
194
195
  @Inject(JOB_RUN_SERVICE) private readonly runService: IJobRunService,
195
196
  @Inject(JOB_STEP_SERVICE) private readonly stepService: IJobStepService,
196
197
  @Inject(JOB_WORKER_OPTIONS) private readonly options: JobWorkerOptions,
198
+ private readonly moduleRef: ModuleRef,
197
199
  ) {
198
200
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
199
201
  this.staleSweeperIntervalMs =
@@ -419,9 +421,22 @@ export class JobWorker implements OnModuleInit, OnModuleDestroy {
419
421
  const meta = registryEntry.meta as JobHandlerMeta<unknown>;
420
422
  const HandlerClass = registryEntry.handlerClass;
421
423
 
422
- // (c) Build JobContext. Phase 1: instantiate handler with no args.
423
- // DI-for-handlers lands with JOB-5's boot wiring.
424
- const handler = new HandlerClass() as JobHandlerBase<unknown>;
424
+ // (c) Build JobContext. Resolve the handler instance from Nest's DI
425
+ // graph so its `@Inject` constructor params (which may come from
426
+ // any module in the app graph) are satisfied. `moduleRef.create()`
427
+ // would otherwise instantiate a fresh class within JobWorkerModule's
428
+ // scope only — which blows up with "not a provider of the current
429
+ // module" for any handler that consumes a service from a peer
430
+ // module (e.g. CrmSyncJob injecting CrmSyncFactory from CrmModule).
431
+ // Consequence: handlers MUST be registered as providers in their
432
+ // owning module (@Injectable + `providers: [HandlerClass]`). The
433
+ // @JobHandler decorator handles registry registration only, not DI.
434
+ // See the jobs skill's handler-authoring.md for the registration
435
+ // rule.
436
+ const handler = this.moduleRef.get(
437
+ HandlerClass as unknown as new (...args: unknown[]) => unknown,
438
+ { strict: false },
439
+ ) as JobHandlerBase<unknown>;
425
440
  const ctx: JobContext<unknown> = {
426
441
  input: claimed.input,
427
442
  run: claimed as JobRun,