@pattern-stack/codegen 0.9.2 → 0.10.1

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 (61) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +5 -0
  3. package/consumer-skills/bridge/SKILL.md +265 -0
  4. package/consumer-skills/codegen/SKILL.md +115 -0
  5. package/consumer-skills/entities/SKILL.md +111 -0
  6. package/consumer-skills/entities/families-and-queries.md +82 -0
  7. package/consumer-skills/entities/yaml-reference.md +118 -0
  8. package/consumer-skills/events/SKILL.md +71 -0
  9. package/consumer-skills/events/authoring-events.md +164 -0
  10. package/consumer-skills/events/typed-bus-and-outbox.md +163 -0
  11. package/consumer-skills/jobs/SKILL.md +66 -0
  12. package/consumer-skills/jobs/handler-authoring.md +236 -0
  13. package/consumer-skills/jobs/pools-and-ordering.md +161 -0
  14. package/consumer-skills/subsystems/SKILL.md +161 -0
  15. package/consumer-skills/subsystems/wiring-and-order.md +120 -0
  16. package/consumer-skills/sync/SKILL.md +134 -0
  17. package/consumer-skills/sync/audit-and-detection.md +302 -0
  18. package/consumer-skills/sync/change-sources-and-sinks.md +442 -0
  19. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +0 -1
  20. package/dist/runtime/subsystems/bridge/bridge.module.js +294 -710
  21. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  22. package/dist/runtime/subsystems/bridge/index.d.ts +0 -1
  23. package/dist/runtime/subsystems/bridge/index.js +248 -664
  24. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  25. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +18 -10
  26. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
  27. package/dist/runtime/subsystems/events/events.module.js +43 -244
  28. package/dist/runtime/subsystems/events/events.module.js.map +1 -1
  29. package/dist/runtime/subsystems/events/index.d.ts +0 -1
  30. package/dist/runtime/subsystems/events/index.js +39 -241
  31. package/dist/runtime/subsystems/events/index.js.map +1 -1
  32. package/dist/runtime/subsystems/index.js +174 -791
  33. package/dist/runtime/subsystems/index.js.map +1 -1
  34. package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +22 -3
  35. package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -1
  36. package/dist/runtime/subsystems/jobs/index.d.ts +1 -4
  37. package/dist/runtime/subsystems/jobs/index.js +87 -506
  38. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  39. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -1
  40. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -0
  41. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -1
  42. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +11 -4
  43. package/dist/runtime/subsystems/jobs/job-worker.module.js +248 -664
  44. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  45. package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +0 -1
  46. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +89 -391
  47. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  48. package/dist/src/cli/index.js +1065 -440
  49. package/dist/src/cli/index.js.map +1 -1
  50. package/dist/src/index.js +26 -4
  51. package/dist/src/index.js.map +1 -1
  52. package/package.json +2 -1
  53. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +32 -10
  54. package/runtime/subsystems/events/events.module.ts +38 -6
  55. package/runtime/subsystems/events/index.ts +7 -1
  56. package/runtime/subsystems/jobs/bullmq.config.ts +23 -3
  57. package/runtime/subsystems/jobs/index.ts +13 -8
  58. package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +5 -2
  59. package/runtime/subsystems/jobs/job-worker.module.ts +27 -7
  60. package/runtime/subsystems/jobs/jobs-domain.module.ts +27 -2
  61. package/templates/subsystem/events/domain-events.schema.ejs.t +43 -2
@@ -898,11 +898,6 @@ DrizzleJobStepService = __decorateClass([
898
898
  __decorateParam(0, Inject3(DRIZZLE))
899
899
  ], DrizzleJobStepService);
900
900
 
901
- // runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts
902
- import { createHash } from "crypto";
903
- import { Inject as Inject4, Injectable as Injectable4, Logger as Logger2, Optional } from "@nestjs/common";
904
- import { eq as eq4 } from "drizzle-orm";
905
-
906
901
  // runtime/subsystems/jobs/pool-config.loader.ts
907
902
  import { existsSync, readFileSync } from "fs";
908
903
  import { resolve } from "path";
@@ -1050,443 +1045,9 @@ function resolvePoolQueueName(pool, config, poolConfig = loadPoolConfig()) {
1050
1045
  return prefix ? `${prefix}:${alias}` : alias;
1051
1046
  }
1052
1047
 
1053
- // runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts
1054
- function sha1JobId(idempotencyKey) {
1055
- return createHash("sha1").update(idempotencyKey).digest("hex");
1056
- }
1057
- var BullMQJobOrchestrator = class extends DrizzleJobOrchestrator {
1058
- constructor(db, multiTenant, connection, bullConfig = null) {
1059
- super(db, multiTenant);
1060
- this.connection = connection;
1061
- this.bullConfig = bullConfig;
1062
- this.bullDb = db;
1063
- }
1064
- connection;
1065
- bullConfig;
1066
- // TODO(logging-subsystem): swap to ILogger once ADR-028 lands
1067
- bullLogger = new Logger2(BullMQJobOrchestrator.name);
1068
- /** Lazily-opened `Queue` handles, one per pool. */
1069
- queues = /* @__PURE__ */ new Map();
1070
- /** Single FlowProducer for parent/child hierarchies. Lazily opened. */
1071
- _flow = null;
1072
- /**
1073
- * Cached `bullmq` value constructors, populated by `loadBullMq()` on first
1074
- * use (the `start`/`cancel`/`replay` entrypoints `await` it before touching
1075
- * a queue). Kept off the import graph so a `drizzle`-only consumer never
1076
- * resolves the optional `'bullmq'` package.
1077
- */
1078
- QueueCtor = null;
1079
- FlowProducerCtor = null;
1080
- bullMqLoad = null;
1081
- /**
1082
- * Own reference to the Drizzle client. `DrizzleJobOrchestrator.db` is
1083
- * `private` (can't be redeclared even privately in a subclass), and the
1084
- * spec forbids touching that file — so the subclass keeps its own handle
1085
- * under a distinct name (same instance, passed through to `super`) for the
1086
- * cancel-cascade snapshot + definition/run loads below.
1087
- */
1088
- bullDb;
1089
- /**
1090
- * Lazily load the optional `bullmq` package and cache its value
1091
- * constructors. Idempotent (single in-flight promise). Throws a friendly,
1092
- * actionable error when the consumer selected `backend: 'bullmq'` but did
1093
- * not install the package — mirrors `createRedisClient` in the redis event
1094
- * backend. Must be `await`ed before any `queueFor`/`flow` access.
1095
- */
1096
- async loadBullMq() {
1097
- if (this.QueueCtor && this.FlowProducerCtor) return;
1098
- if (!this.bullMqLoad) {
1099
- this.bullMqLoad = (async () => {
1100
- try {
1101
- const mod = await import("bullmq");
1102
- this.QueueCtor = mod.Queue;
1103
- this.FlowProducerCtor = mod.FlowProducer;
1104
- } catch {
1105
- throw new Error(
1106
- 'BullMQ backend requires the "bullmq" package. Install it with: npm install bullmq'
1107
- );
1108
- }
1109
- })();
1110
- }
1111
- await this.bullMqLoad;
1112
- }
1113
- /**
1114
- * Open (or reuse) the `Queue` for a pool. Synchronous — callers `await
1115
- * loadBullMq()` first so `QueueCtor` is populated.
1116
- */
1117
- queueFor(pool) {
1118
- if (!this.QueueCtor) {
1119
- throw new Error("BullMQJobOrchestrator: queueFor called before loadBullMq()");
1120
- }
1121
- const name = resolvePoolQueueName(pool, this.bullConfig);
1122
- let q = this.queues.get(name);
1123
- if (!q) {
1124
- q = new this.QueueCtor(name, { connection: this.connection });
1125
- this.queues.set(name, q);
1126
- }
1127
- return q;
1128
- }
1129
- flow() {
1130
- if (!this.FlowProducerCtor) {
1131
- throw new Error("BullMQJobOrchestrator: flow called before loadBullMq()");
1132
- }
1133
- if (!this._flow) {
1134
- this._flow = new this.FlowProducerCtor({ connection: this.connection });
1135
- }
1136
- return this._flow;
1137
- }
1138
- // ==========================================================================
1139
- // start — Postgres insert (super) + BullMQ dispatch
1140
- // ==========================================================================
1141
- async start(type, input, opts = {}, tx) {
1142
- const run = await super.start(type, input, opts, tx);
1143
- await this.dispatch(run, type);
1144
- return run;
1145
- }
1146
- /**
1147
- * Map a `job_run` row onto a BullMQ job via `queue.add`. When the run has a
1148
- * `parentRunId` we attach it to the parent's existing BullMQ job through the
1149
- * `parent: { id, queue }` opt — BullMQ then tracks the parent/child link in
1150
- * its own graph. (The FlowProducer is reserved for whole-tree atomic
1151
- * submits, exposed as an opt-in extension via `flowProducer()`; runtime
1152
- * `ctx.spawnChild` is incremental, so `queue.add` with a parent ref is the
1153
- * correct primitive here.)
1154
- *
1155
- * The `jobId` is colon-safe + stable: `sha1(dedupeKey)` when a dedupe key is
1156
- * present (so the same logical key dedups), else the `job_run.id` UUID
1157
- * (already colon-free).
1158
- *
1159
- * The domain `parentClosePolicy` cascade is still enforced in Postgres by
1160
- * the shared `cancel` path — BullMQ's parent link is dispatch bookkeeping,
1161
- * not the authority.
1162
- */
1163
- async dispatch(run, type) {
1164
- await this.loadBullMq();
1165
- const def = await this.loadDefinition(type);
1166
- const jobId = run.dedupeKey ? sha1JobId(run.dedupeKey) : run.id;
1167
- const jobOpts = {
1168
- jobId,
1169
- ...this.retryOpts(def),
1170
- ...this.dedupeOpts(run, def)
1171
- };
1172
- if (run.parentRunId) {
1173
- const parentRow = await this.loadRun(run.parentRunId);
1174
- if (parentRow) {
1175
- const parentJobId = parentRow.dedupeKey ? sha1JobId(parentRow.dedupeKey) : parentRow.id;
1176
- jobOpts.parent = {
1177
- id: parentJobId,
1178
- queue: resolvePoolQueueName(parentRow.pool, this.bullConfig)
1179
- };
1180
- }
1181
- }
1182
- const payload = { runId: run.id, type, input: run.input };
1183
- await this.queueFor(run.pool).add(type, payload, jobOpts);
1184
- }
1185
- /**
1186
- * Opt-in extension (spec §Extensions): expose the FlowProducer for
1187
- * consumers that want to submit a whole parent/child DAG atomically up
1188
- * front, rather than incrementally via `ctx.spawnChild`. Backend-specific —
1189
- * code using it is not portable to the Drizzle backend. Async because it
1190
- * lazily loads the optional `bullmq` package on first use.
1191
- */
1192
- async flowProducer() {
1193
- await this.loadBullMq();
1194
- return this.flow();
1195
- }
1196
- retryOpts(def) {
1197
- const policy = def.retryPolicy;
1198
- if (!policy) return {};
1199
- return {
1200
- attempts: policy.attempts,
1201
- backoff: {
1202
- type: policy.backoff === "exponential" ? "exponential" : "fixed",
1203
- delay: policy.baseMs
1204
- }
1205
- };
1206
- }
1207
- dedupeOpts(run, def) {
1208
- if (!run.dedupeKey || !def.dedupeWindowMs) return {};
1209
- return {
1210
- deduplication: {
1211
- id: sha1JobId(run.dedupeKey),
1212
- ttl: def.dedupeWindowMs
1213
- }
1214
- };
1215
- }
1216
- // ==========================================================================
1217
- // cancel — Postgres cascade (super) + remove from queue
1218
- // ==========================================================================
1219
- async cancel(runId, opts = {}) {
1220
- const target = await this.loadRun(runId);
1221
- await super.cancel(runId, opts);
1222
- if (!target) return;
1223
- await this.loadBullMq();
1224
- await this.removeFromQueue(target);
1225
- if (opts.cascade === false) return;
1226
- const descendants = await this.bullDb.select().from(jobRuns).where(eq4(jobRuns.rootRunId, target.rootRunId));
1227
- for (const child of descendants) {
1228
- if (child.id === runId) continue;
1229
- await this.removeFromQueue(child);
1230
- }
1231
- }
1232
- async removeFromQueue(run) {
1233
- const jobId = run.dedupeKey ? sha1JobId(run.dedupeKey) : run.id;
1234
- try {
1235
- const job = await this.queueFor(run.pool).getJob(jobId);
1236
- if (job) await job.remove();
1237
- } catch (err) {
1238
- this.bullLogger.warn(
1239
- `cancel: could not remove BullMQ job ${jobId} (pool=${run.pool}): ${err.message}`
1240
- );
1241
- }
1242
- }
1243
- // ==========================================================================
1244
- // replay — Postgres reset (super) + re-enqueue
1245
- // ==========================================================================
1246
- async replay(runId) {
1247
- const run = await super.replay(runId);
1248
- await this.dispatch(run, run.jobType);
1249
- return run;
1250
- }
1251
- // ==========================================================================
1252
- // Internals
1253
- // ==========================================================================
1254
- async loadDefinition(type) {
1255
- const [def] = await this.bullDb.select().from(jobs).where(eq4(jobs.type, type)).limit(1);
1256
- if (!def) {
1257
- throw new Error(`BullMQJobOrchestrator: no job definition for '${type}'`);
1258
- }
1259
- return def;
1260
- }
1261
- async loadRun(id) {
1262
- const [row] = await this.bullDb.select().from(jobRuns).where(eq4(jobRuns.id, id)).limit(1);
1263
- return row ?? null;
1264
- }
1265
- /** Close all open queue + flow connections. Called on module destroy. */
1266
- async closeConnections() {
1267
- for (const q of this.queues.values()) {
1268
- await q.close().catch(() => void 0);
1269
- }
1270
- this.queues.clear();
1271
- if (this._flow) {
1272
- await this._flow.close().catch(() => void 0);
1273
- this._flow = null;
1274
- }
1275
- }
1276
- };
1277
- BullMQJobOrchestrator = __decorateClass([
1278
- Injectable4(),
1279
- __decorateParam(0, Inject4(DRIZZLE)),
1280
- __decorateParam(1, Inject4(JOBS_MULTI_TENANT)),
1281
- __decorateParam(2, Inject4(BULLMQ_CONNECTION)),
1282
- __decorateParam(3, Optional()),
1283
- __decorateParam(3, Inject4(BULLMQ_RESOLVED_CONFIG))
1284
- ], BullMQJobOrchestrator);
1285
-
1286
- // runtime/subsystems/jobs/job-worker.bullmq-backend.ts
1287
- import { Logger as Logger3 } from "@nestjs/common";
1288
- import { eq as eq5 } from "drizzle-orm";
1289
- function serialiseError(err, attempt, retryable) {
1290
- const e = err;
1291
- return {
1292
- message: e?.message ?? String(err),
1293
- stack: e?.stack,
1294
- retryable,
1295
- attempt
1296
- };
1297
- }
1298
- var BullMQJobWorker = class _BullMQJobWorker {
1299
- constructor(db, orchestrator, stepService, options, moduleRef) {
1300
- this.db = db;
1301
- this.orchestrator = orchestrator;
1302
- this.stepService = stepService;
1303
- this.options = options;
1304
- this.moduleRef = moduleRef;
1305
- }
1306
- db;
1307
- orchestrator;
1308
- stepService;
1309
- options;
1310
- moduleRef;
1311
- logger = new Logger3(_BullMQJobWorker.name);
1312
- worker = null;
1313
- async onModuleInit() {
1314
- let WorkerCtor;
1315
- try {
1316
- const mod = await import("bullmq");
1317
- WorkerCtor = mod.Worker;
1318
- } catch {
1319
- throw new Error(
1320
- 'BullMQ backend requires the "bullmq" package. Install it with: npm install bullmq'
1321
- );
1322
- }
1323
- this.worker = new WorkerCtor(
1324
- this.options.queueName,
1325
- (job) => this.process(job),
1326
- {
1327
- connection: this.options.connection,
1328
- concurrency: this.options.concurrency
1329
- }
1330
- );
1331
- this.worker.on("failed", (job, err) => {
1332
- if (!job) return;
1333
- const attemptsMade = job.attemptsMade;
1334
- const maxAttempts = job.opts.attempts ?? 1;
1335
- if (attemptsMade >= maxAttempts) {
1336
- void this.markFailed(job.data.runId, err, attemptsMade);
1337
- }
1338
- });
1339
- this.logger.log(
1340
- `BullMQ worker started: pool='${this.options.pool}' queue='${this.options.queueName}' concurrency=${this.options.concurrency}`
1341
- );
1342
- }
1343
- async onModuleDestroy() {
1344
- if (this.worker) {
1345
- await this.worker.close();
1346
- this.worker = null;
1347
- }
1348
- }
1349
- /**
1350
- * Process one BullMQ job. Returns the handler output (stored by BullMQ as
1351
- * the job return value AND written to `job_run.output`). Throws on handler
1352
- * failure so BullMQ applies the retry policy.
1353
- */
1354
- async process(job) {
1355
- const { runId } = job.data;
1356
- const [row] = await this.db.select().from(jobRuns).where(eq5(jobRuns.id, runId)).limit(1);
1357
- if (!row) {
1358
- this.logger.warn(`process: job_run ${runId} not found; skipping`);
1359
- return {};
1360
- }
1361
- const run = row;
1362
- if (run.status === "canceled") {
1363
- return {};
1364
- }
1365
- const registryEntry = JOB_HANDLER_REGISTRY.get(run.jobType);
1366
- if (!registryEntry) {
1367
- throw new Error(
1368
- `No handler registered for jobType='${run.jobType}' (run ${run.id})`
1369
- );
1370
- }
1371
- await this.db.update(jobRuns).set({
1372
- status: "running",
1373
- claimedAt: /* @__PURE__ */ new Date(),
1374
- startedAt: /* @__PURE__ */ new Date(),
1375
- attempts: job.attemptsMade + 1,
1376
- updatedAt: /* @__PURE__ */ new Date()
1377
- }).where(eq5(jobRuns.id, run.id));
1378
- const HandlerClass = registryEntry.handlerClass;
1379
- const handler = this.moduleRef.get(
1380
- HandlerClass,
1381
- { strict: false }
1382
- );
1383
- const ctx = {
1384
- input: run.input,
1385
- run,
1386
- step: this.makeStepFn(run),
1387
- spawnChild: this.makeSpawnFn(run),
1388
- logger: new Logger3(`JobRun:${run.id}`)
1389
- };
1390
- const output = await handler.run(ctx);
1391
- await this.db.update(jobRuns).set({
1392
- status: "completed",
1393
- output: output ?? {},
1394
- finishedAt: /* @__PURE__ */ new Date(),
1395
- updatedAt: /* @__PURE__ */ new Date()
1396
- }).where(eq5(jobRuns.id, run.id));
1397
- return output ?? {};
1398
- }
1399
- async markFailed(runId, err, finalAttempts) {
1400
- const [row] = await this.db.select().from(jobRuns).where(eq5(jobRuns.id, runId)).limit(1);
1401
- if (!row) return;
1402
- const run = row;
1403
- await this.db.update(jobRuns).set({
1404
- status: "failed",
1405
- attempts: finalAttempts,
1406
- finishedAt: /* @__PURE__ */ new Date(),
1407
- error: serialiseError(err, finalAttempts, false),
1408
- updatedAt: /* @__PURE__ */ new Date()
1409
- }).where(eq5(jobRuns.id, runId));
1410
- if (run.parentClosePolicy === "terminate") {
1411
- try {
1412
- await this.orchestrator.cancel(run.id, {
1413
- cascade: true,
1414
- reason: "parent-failed",
1415
- tenantId: run.tenantId
1416
- });
1417
- } catch (cascadeErr) {
1418
- this.logger.warn(
1419
- `cascade on failed run ${run.id}: ${cascadeErr.message}`
1420
- );
1421
- }
1422
- }
1423
- }
1424
- // ── ctx.step / ctx.spawnChild (mirror JobWorker) ──────────────────────────
1425
- makeStepFn(run) {
1426
- return async (stepId, fn, _opts) => {
1427
- void _opts;
1428
- const existing = await this.stepService.findStep(run.id, stepId);
1429
- if (existing?.status === "completed") {
1430
- return existing.output;
1431
- }
1432
- const nextAttempts = (existing?.attempts ?? 0) + 1;
1433
- const seq = nextAttempts;
1434
- await this.stepService.recordStep({
1435
- jobRunId: run.id,
1436
- stepId,
1437
- kind: "task",
1438
- seq,
1439
- status: "running",
1440
- startedAt: /* @__PURE__ */ new Date(),
1441
- attempts: nextAttempts
1442
- });
1443
- try {
1444
- const output = await fn();
1445
- await this.stepService.recordStep({
1446
- jobRunId: run.id,
1447
- stepId,
1448
- kind: "task",
1449
- seq,
1450
- status: "completed",
1451
- output,
1452
- finishedAt: /* @__PURE__ */ new Date(),
1453
- attempts: nextAttempts
1454
- });
1455
- return output;
1456
- } catch (err) {
1457
- await this.stepService.recordStep({
1458
- jobRunId: run.id,
1459
- stepId,
1460
- kind: "task",
1461
- seq,
1462
- status: "failed",
1463
- error: serialiseError(err, nextAttempts, false),
1464
- finishedAt: /* @__PURE__ */ new Date(),
1465
- attempts: nextAttempts
1466
- });
1467
- throw err;
1468
- }
1469
- };
1470
- }
1471
- makeSpawnFn(run) {
1472
- return async (type, input, opts) => {
1473
- return this.orchestrator.start(type, input, {
1474
- parentRunId: run.id,
1475
- parentClosePolicy: opts?.closePolicy,
1476
- runAt: opts?.runAt,
1477
- priority: opts?.priority,
1478
- tags: opts?.tags,
1479
- triggerSource: "parent",
1480
- triggerRef: run.id,
1481
- tenantId: run.tenantId
1482
- });
1483
- };
1484
- }
1485
- };
1486
-
1487
1048
  // runtime/subsystems/jobs/job-worker.ts
1488
- import { Inject as Inject5, Injectable as Injectable5, Logger as Logger4 } from "@nestjs/common";
1489
- import { and as and4, asc as asc2, desc as desc3, eq as eq6, inArray as inArray3, lt as lt2, lte, sql as sql4 } from "drizzle-orm";
1049
+ import { Inject as Inject4, Injectable as Injectable4, Logger as Logger2 } from "@nestjs/common";
1050
+ import { and as and4, asc as asc2, desc as desc3, eq as eq4, inArray as inArray3, lt as lt2, lte, sql as sql4 } from "drizzle-orm";
1490
1051
  var JOB_WORKER_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_OPTIONS");
1491
1052
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
1492
1053
  var DEFAULT_STALE_SWEEPER_INTERVAL_MS = 6e4;
@@ -1518,8 +1079,8 @@ function classifyError(err, policy, currentAttempts) {
1518
1079
  function buildClaimQuery(db, pool) {
1519
1080
  return db.select({ id: jobRuns.id }).from(jobRuns).where(
1520
1081
  and4(
1521
- eq6(jobRuns.status, "pending"),
1522
- eq6(jobRuns.pool, pool),
1082
+ eq4(jobRuns.status, "pending"),
1083
+ eq4(jobRuns.pool, pool),
1523
1084
  lte(jobRuns.runAt, /* @__PURE__ */ new Date())
1524
1085
  )
1525
1086
  ).orderBy(desc3(jobRuns.priority), asc2(jobRuns.runAt)).limit(1).for("update", { skipLocked: true });
@@ -1528,12 +1089,12 @@ function buildStaleSweepQuery(db, staleThresholdMs) {
1528
1089
  const threshold = new Date(Date.now() - staleThresholdMs);
1529
1090
  return db.select({ id: jobRuns.id }).from(jobRuns).where(
1530
1091
  and4(
1531
- eq6(jobRuns.status, "running"),
1092
+ eq4(jobRuns.status, "running"),
1532
1093
  lt2(jobRuns.claimedAt, threshold)
1533
1094
  )
1534
1095
  ).for("update", { skipLocked: true });
1535
1096
  }
1536
- function serialiseError2(err, attempt, retryable) {
1097
+ function serialiseError(err, attempt, retryable) {
1537
1098
  const e = err;
1538
1099
  return {
1539
1100
  message: e?.message ?? String(err),
@@ -1567,7 +1128,7 @@ var JobWorker = class {
1567
1128
  stepService;
1568
1129
  options;
1569
1130
  moduleRef;
1570
- logger = new Logger4(JobWorker.name);
1131
+ logger = new Logger2(JobWorker.name);
1571
1132
  shuttingDown = false;
1572
1133
  inFlight = /* @__PURE__ */ new Set();
1573
1134
  pollTimer = null;
@@ -1608,7 +1169,7 @@ var JobWorker = class {
1608
1169
  await this.drainInFlight();
1609
1170
  try {
1610
1171
  await this.db.update(jobRuns).set({ status: "pending", claimedAt: null, startedAt: null }).where(
1611
- and4(eq6(jobRuns.status, "running"), eq6(jobRuns.pool, this.options.pool))
1172
+ and4(eq4(jobRuns.status, "running"), eq4(jobRuns.pool, this.options.pool))
1612
1173
  );
1613
1174
  } catch (err) {
1614
1175
  this.logger.error(`shutdown reset failed: ${err.message}`);
@@ -1658,8 +1219,8 @@ var JobWorker = class {
1658
1219
  return this.db.transaction(async (tx) => {
1659
1220
  const candidates = await tx.select({ id: jobRuns.id }).from(jobRuns).where(
1660
1221
  and4(
1661
- eq6(jobRuns.status, "pending"),
1662
- eq6(jobRuns.pool, pool),
1222
+ eq4(jobRuns.status, "pending"),
1223
+ eq4(jobRuns.pool, pool),
1663
1224
  lte(jobRuns.runAt, /* @__PURE__ */ new Date())
1664
1225
  )
1665
1226
  ).orderBy(desc3(jobRuns.priority), asc2(jobRuns.runAt)).limit(1).for("update", { skipLocked: true });
@@ -1670,7 +1231,7 @@ var JobWorker = class {
1670
1231
  claimedAt: /* @__PURE__ */ new Date(),
1671
1232
  startedAt: /* @__PURE__ */ new Date(),
1672
1233
  updatedAt: /* @__PURE__ */ new Date()
1673
- }).where(eq6(jobRuns.id, candidate.id)).returning();
1234
+ }).where(eq4(jobRuns.id, candidate.id)).returning();
1674
1235
  return claimed ?? null;
1675
1236
  });
1676
1237
  }
@@ -1688,7 +1249,7 @@ var JobWorker = class {
1688
1249
  await this.db.transaction(async (tx) => {
1689
1250
  const threshold = new Date(Date.now() - this.staleThresholdMs);
1690
1251
  const stale = await tx.select({ id: jobRuns.id }).from(jobRuns).where(
1691
- and4(eq6(jobRuns.status, "running"), lt2(jobRuns.claimedAt, threshold))
1252
+ and4(eq4(jobRuns.status, "running"), lt2(jobRuns.claimedAt, threshold))
1692
1253
  ).for("update", { skipLocked: true });
1693
1254
  if (stale.length === 0) return;
1694
1255
  const ids = stale.map((r) => r.id);
@@ -1721,8 +1282,8 @@ var JobWorker = class {
1721
1282
  if (claimed.concurrencyKey) {
1722
1283
  const inflight = await this.db.select({ id: jobRuns.id }).from(jobRuns).where(
1723
1284
  and4(
1724
- eq6(jobRuns.concurrencyKey, claimed.concurrencyKey),
1725
- eq6(jobRuns.status, "running")
1285
+ eq4(jobRuns.concurrencyKey, claimed.concurrencyKey),
1286
+ eq4(jobRuns.status, "running")
1726
1287
  )
1727
1288
  );
1728
1289
  const other = inflight.find((r) => r.id !== claimed.id);
@@ -1732,7 +1293,7 @@ var JobWorker = class {
1732
1293
  claimedAt: null,
1733
1294
  startedAt: null,
1734
1295
  updatedAt: /* @__PURE__ */ new Date()
1735
- }).where(eq6(jobRuns.id, claimed.id));
1296
+ }).where(eq4(jobRuns.id, claimed.id));
1736
1297
  return;
1737
1298
  }
1738
1299
  }
@@ -1747,7 +1308,7 @@ var JobWorker = class {
1747
1308
  run: claimed,
1748
1309
  step: this.makeStepFn(claimed),
1749
1310
  spawnChild: this.makeSpawnFn(claimed),
1750
- logger: new Logger4(`JobRun:${claimed.id}`)
1311
+ logger: new Logger2(`JobRun:${claimed.id}`)
1751
1312
  };
1752
1313
  const attemptsBefore = claimed.attempts ?? 0;
1753
1314
  try {
@@ -1758,7 +1319,7 @@ var JobWorker = class {
1758
1319
  finishedAt: /* @__PURE__ */ new Date(),
1759
1320
  updatedAt: /* @__PURE__ */ new Date(),
1760
1321
  attempts: attemptsBefore + 1
1761
- }).where(eq6(jobRuns.id, claimed.id));
1322
+ }).where(eq4(jobRuns.id, claimed.id));
1762
1323
  } catch (err) {
1763
1324
  const policy = meta.retry;
1764
1325
  const decision = classifyError(err, policy, attemptsBefore);
@@ -1771,9 +1332,9 @@ var JobWorker = class {
1771
1332
  runAt: new Date(Date.now() + delay),
1772
1333
  startedAt: null,
1773
1334
  claimedAt: null,
1774
- error: serialiseError2(err, nextAttempts, true),
1335
+ error: serialiseError(err, nextAttempts, true),
1775
1336
  updatedAt: /* @__PURE__ */ new Date()
1776
- }).where(eq6(jobRuns.id, claimed.id));
1337
+ }).where(eq4(jobRuns.id, claimed.id));
1777
1338
  } else {
1778
1339
  await this.markFailed(claimed, err, nextAttempts);
1779
1340
  }
@@ -1784,9 +1345,9 @@ var JobWorker = class {
1784
1345
  status: "failed",
1785
1346
  attempts: finalAttempts,
1786
1347
  finishedAt: /* @__PURE__ */ new Date(),
1787
- error: serialiseError2(err, finalAttempts, false),
1348
+ error: serialiseError(err, finalAttempts, false),
1788
1349
  updatedAt: /* @__PURE__ */ new Date()
1789
- }).where(eq6(jobRuns.id, claimed.id));
1350
+ }).where(eq4(jobRuns.id, claimed.id));
1790
1351
  if (claimed.parentClosePolicy === "terminate") {
1791
1352
  try {
1792
1353
  await this.orchestrator.cancel(claimed.id, {
@@ -1843,7 +1404,7 @@ var JobWorker = class {
1843
1404
  kind: "task",
1844
1405
  seq,
1845
1406
  status: "failed",
1846
- error: serialiseError2(err, nextAttempts, false),
1407
+ error: serialiseError(err, nextAttempts, false),
1847
1408
  finishedAt: /* @__PURE__ */ new Date(),
1848
1409
  attempts: nextAttempts
1849
1410
  });
@@ -1889,12 +1450,12 @@ var JobWorker = class {
1889
1450
  // ============================================================================
1890
1451
  };
1891
1452
  JobWorker = __decorateClass([
1892
- Injectable5(),
1893
- __decorateParam(0, Inject5(DRIZZLE)),
1894
- __decorateParam(1, Inject5(JOB_ORCHESTRATOR)),
1895
- __decorateParam(2, Inject5(JOB_RUN_SERVICE)),
1896
- __decorateParam(3, Inject5(JOB_STEP_SERVICE)),
1897
- __decorateParam(4, Inject5(JOB_WORKER_OPTIONS))
1453
+ Injectable4(),
1454
+ __decorateParam(0, Inject4(DRIZZLE)),
1455
+ __decorateParam(1, Inject4(JOB_ORCHESTRATOR)),
1456
+ __decorateParam(2, Inject4(JOB_RUN_SERVICE)),
1457
+ __decorateParam(3, Inject4(JOB_STEP_SERVICE)),
1458
+ __decorateParam(4, Inject4(JOB_WORKER_OPTIONS))
1898
1459
  ], JobWorker);
1899
1460
 
1900
1461
  // runtime/subsystems/jobs/memory-job-store.ts
@@ -1915,7 +1476,7 @@ var MemoryJobStore = class {
1915
1476
 
1916
1477
  // runtime/subsystems/jobs/job-orchestrator.memory-backend.ts
1917
1478
  import { randomUUID as randomUUID2 } from "crypto";
1918
- import { Inject as Inject6, Injectable as Injectable6, Logger as Logger5, Optional as Optional2 } from "@nestjs/common";
1479
+ import { Inject as Inject5, Injectable as Injectable5, Logger as Logger3, Optional } from "@nestjs/common";
1919
1480
  var QUEUED_RUN_AT = /* @__PURE__ */ new Date(864e13);
1920
1481
  var TERMINAL_STATUSES2 = [
1921
1482
  "completed",
@@ -1962,7 +1523,7 @@ var MemoryJobOrchestrator = class {
1962
1523
  stepService;
1963
1524
  multiTenant;
1964
1525
  moduleRef;
1965
- logger = new Logger5(MemoryJobOrchestrator.name);
1526
+ logger = new Logger3(MemoryJobOrchestrator.name);
1966
1527
  mutex = new PromiseMutex();
1967
1528
  handlerRegistry = /* @__PURE__ */ new Map();
1968
1529
  /**
@@ -2311,7 +1872,7 @@ var MemoryJobOrchestrator = class {
2311
1872
  run,
2312
1873
  step: this.makeStepFn(run),
2313
1874
  spawnChild: this.makeSpawnFn(run),
2314
- logger: new Logger5(`JobRun:${run.id}`)
1875
+ logger: new Logger3(`JobRun:${run.id}`)
2315
1876
  };
2316
1877
  const attemptsBefore = run.attempts ?? 0;
2317
1878
  try {
@@ -2368,7 +1929,7 @@ var MemoryJobOrchestrator = class {
2368
1929
  kind: "task",
2369
1930
  seq,
2370
1931
  status: "failed",
2371
- error: serialiseError3(err, nextAttempts, false),
1932
+ error: serialiseError2(err, nextAttempts, false),
2372
1933
  finishedAt: /* @__PURE__ */ new Date(),
2373
1934
  attempts: nextAttempts
2374
1935
  });
@@ -2423,7 +1984,7 @@ var MemoryJobOrchestrator = class {
2423
1984
  finishedAt: now,
2424
1985
  updatedAt: now,
2425
1986
  attempts,
2426
- error: serialiseError3(err, attempts, false)
1987
+ error: serialiseError2(err, attempts, false)
2427
1988
  });
2428
1989
  this.unblockQueuedDependents(run.id);
2429
1990
  });
@@ -2454,7 +2015,7 @@ var MemoryJobOrchestrator = class {
2454
2015
  startedAt: null,
2455
2016
  claimedAt: null,
2456
2017
  updatedAt: now,
2457
- error: serialiseError3(err, attempts, true)
2018
+ error: serialiseError2(err, attempts, true)
2458
2019
  });
2459
2020
  });
2460
2021
  }
@@ -2484,9 +2045,9 @@ var MemoryJobOrchestrator = class {
2484
2045
  }
2485
2046
  };
2486
2047
  MemoryJobOrchestrator = __decorateClass([
2487
- Injectable6(),
2488
- __decorateParam(2, Inject6(JOBS_MULTI_TENANT)),
2489
- __decorateParam(3, Optional2())
2048
+ Injectable5(),
2049
+ __decorateParam(2, Inject5(JOBS_MULTI_TENANT)),
2050
+ __decorateParam(3, Optional())
2490
2051
  ], MemoryJobOrchestrator);
2491
2052
  function classifyError2(err, policy, currentAttempts) {
2492
2053
  if (!policy) return "fail";
@@ -2509,7 +2070,7 @@ function computeBackoff2(policy, attempts) {
2509
2070
  }
2510
2071
  return raw;
2511
2072
  }
2512
- function serialiseError3(err, attempt, retryable) {
2073
+ function serialiseError2(err, attempt, retryable) {
2513
2074
  const e = err;
2514
2075
  return {
2515
2076
  message: e?.message ?? String(err),
@@ -2520,7 +2081,7 @@ function serialiseError3(err, attempt, retryable) {
2520
2081
  }
2521
2082
 
2522
2083
  // runtime/subsystems/jobs/job-run-service.memory-backend.ts
2523
- import { Inject as Inject7, Injectable as Injectable7 } from "@nestjs/common";
2084
+ import { Inject as Inject6, Injectable as Injectable6 } from "@nestjs/common";
2524
2085
  var NON_TERMINAL_STATUSES2 = [
2525
2086
  "pending",
2526
2087
  "running",
@@ -2687,9 +2248,9 @@ var MemoryJobRunService = class {
2687
2248
  }
2688
2249
  };
2689
2250
  MemoryJobRunService = __decorateClass([
2690
- Injectable7(),
2691
- __decorateParam(1, Inject7(JOB_ORCHESTRATOR)),
2692
- __decorateParam(2, Inject7(JOBS_MULTI_TENANT))
2251
+ Injectable6(),
2252
+ __decorateParam(1, Inject6(JOB_ORCHESTRATOR)),
2253
+ __decorateParam(2, Inject6(JOBS_MULTI_TENANT))
2693
2254
  ], MemoryJobRunService);
2694
2255
  function compareBy(a, b, order) {
2695
2256
  switch (order) {
@@ -2707,7 +2268,7 @@ function compareBy(a, b, order) {
2707
2268
 
2708
2269
  // runtime/subsystems/jobs/job-step-service.memory-backend.ts
2709
2270
  import { randomUUID as randomUUID3 } from "crypto";
2710
- import { Injectable as Injectable8 } from "@nestjs/common";
2271
+ import { Injectable as Injectable7 } from "@nestjs/common";
2711
2272
  var MemoryJobStepService = class {
2712
2273
  constructor(store) {
2713
2274
  this.store = store;
@@ -2800,7 +2361,7 @@ var MemoryJobStepService = class {
2800
2361
  }
2801
2362
  };
2802
2363
  MemoryJobStepService = __decorateClass([
2803
- Injectable8()
2364
+ Injectable7()
2804
2365
  ], MemoryJobStepService);
2805
2366
 
2806
2367
  // runtime/subsystems/jobs/jobs-domain.module.ts
@@ -2829,7 +2390,20 @@ var JobsDomainModule = class {
2829
2390
  const resolved = resolveBullMqConfig(opts.extensions?.bullmq);
2830
2391
  providers.push({ provide: BULLMQ_CONNECTION, useValue: resolved.connection });
2831
2392
  providers.push({ provide: BULLMQ_RESOLVED_CONFIG, useValue: resolved });
2832
- providers.push({ provide: JOB_ORCHESTRATOR, useClass: BullMQJobOrchestrator });
2393
+ providers.push({
2394
+ provide: JOB_ORCHESTRATOR,
2395
+ useFactory: async (...args) => {
2396
+ const specifier = "./job-orchestrator.bullmq-backend";
2397
+ const mod = await import(specifier);
2398
+ return new mod.BullMQJobOrchestrator(...args);
2399
+ },
2400
+ // The bullmq orchestrator constructor mirrors DrizzleJobOrchestrator's
2401
+ // injection list: DRIZZLE + JOBS_MULTI_TENANT + the resolved BullMQ
2402
+ // tokens. Importing token references would force a static dep on the
2403
+ // tokens file in this module's import graph; using the existing
2404
+ // symbols already in scope is sufficient.
2405
+ inject: [DRIZZLE, JOBS_MULTI_TENANT, BULLMQ_CONNECTION, BULLMQ_RESOLVED_CONFIG]
2406
+ });
2833
2407
  providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });
2834
2408
  providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });
2835
2409
  } else {
@@ -2860,11 +2434,11 @@ JobsDomainModule = __decorateClass([
2860
2434
 
2861
2435
  // runtime/subsystems/jobs/job-worker.module.ts
2862
2436
  import {
2863
- Inject as Inject8,
2864
- Injectable as Injectable9,
2865
- Logger as Logger6,
2437
+ Inject as Inject7,
2438
+ Injectable as Injectable8,
2439
+ Logger as Logger4,
2866
2440
  Module as Module2,
2867
- Optional as Optional3
2441
+ Optional as Optional2
2868
2442
  } from "@nestjs/common";
2869
2443
  var DEFAULT_SHUTDOWN_TIMEOUT_MS2 = 3e4;
2870
2444
  var JOB_WORKER_MODULE_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_MODULE_OPTIONS");
@@ -2887,7 +2461,7 @@ var JobWorkerOrchestrator = class {
2887
2461
  moduleRef;
2888
2462
  bullConnection;
2889
2463
  bullConfig;
2890
- logger = new Logger6(JobWorkerOrchestrator.name);
2464
+ logger = new Logger4(JobWorkerOrchestrator.name);
2891
2465
  workers = [];
2892
2466
  // ============================================================================
2893
2467
  // Lifecycle
@@ -2917,7 +2491,7 @@ var JobWorkerOrchestrator = class {
2917
2491
  concurrency: def.concurrency,
2918
2492
  shutdownTimeoutMs: this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS2
2919
2493
  };
2920
- const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : backend === "bullmq" ? this.spawnBullMQWorker(poolName, def.queue, def.concurrency, poolConfig) : this.spawnWorker(workerOptions);
2494
+ const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : backend === "bullmq" ? await this.spawnBullMQWorker(poolName, def.queue, def.concurrency, poolConfig) : this.spawnWorker(workerOptions);
2921
2495
  await worker.onModuleInit();
2922
2496
  this.workers.push(worker);
2923
2497
  this.logger.log(
@@ -3017,7 +2591,15 @@ var JobWorkerOrchestrator = class {
3017
2591
  * orchestrator's `dispatch` via `resolvePoolQueueName(pool, …)` so producer
3018
2592
  * and consumer agree.
3019
2593
  */
3020
- spawnBullMQWorker(pool, _queueAlias, concurrency, poolConfig) {
2594
+ /**
2595
+ * #6 — async + dynamic-import. The `job-worker.bullmq-backend.ts` file is
2596
+ * filtered out of the vendor set for drizzle/memory installs (no `bullmq`
2597
+ * peer dep needed). The non-literal import specifier makes TS treat the
2598
+ * module as `any` so the consumer's tsc never tries to resolve an absent
2599
+ * file. This method is only entered when `backend === 'bullmq'` — at which
2600
+ * point the file IS vendored.
2601
+ */
2602
+ async spawnBullMQWorker(pool, _queueAlias, concurrency, poolConfig) {
3021
2603
  if (!this.db) {
3022
2604
  throw new Error(
3023
2605
  `JobWorkerModule: BullMQ worker spawning requires the Drizzle client (no DRIZZLE provider available) \u2014 job_run remains the source of truth.`
@@ -3034,7 +2616,9 @@ var JobWorkerOrchestrator = class {
3034
2616
  );
3035
2617
  }
3036
2618
  const queueName = resolvePoolQueueName(pool, this.bullConfig, poolConfig);
3037
- return new BullMQJobWorker(
2619
+ const specifier = "./job-worker.bullmq-backend";
2620
+ const mod = await import(specifier);
2621
+ return new mod.BullMQJobWorker(
3038
2622
  this.db,
3039
2623
  this.orchestrator,
3040
2624
  this.stepService,
@@ -3049,17 +2633,17 @@ var JobWorkerOrchestrator = class {
3049
2633
  }
3050
2634
  };
3051
2635
  JobWorkerOrchestrator = __decorateClass([
3052
- Injectable9(),
3053
- __decorateParam(0, Inject8(JOB_ORCHESTRATOR)),
3054
- __decorateParam(1, Inject8(JOB_RUN_SERVICE)),
3055
- __decorateParam(2, Inject8(JOB_STEP_SERVICE)),
3056
- __decorateParam(3, Inject8(JOB_WORKER_MODULE_OPTIONS)),
3057
- __decorateParam(4, Optional3()),
3058
- __decorateParam(4, Inject8(DRIZZLE)),
3059
- __decorateParam(6, Optional3()),
3060
- __decorateParam(6, Inject8(BULLMQ_CONNECTION)),
3061
- __decorateParam(7, Optional3()),
3062
- __decorateParam(7, Inject8(BULLMQ_RESOLVED_CONFIG))
2636
+ Injectable8(),
2637
+ __decorateParam(0, Inject7(JOB_ORCHESTRATOR)),
2638
+ __decorateParam(1, Inject7(JOB_RUN_SERVICE)),
2639
+ __decorateParam(2, Inject7(JOB_STEP_SERVICE)),
2640
+ __decorateParam(3, Inject7(JOB_WORKER_MODULE_OPTIONS)),
2641
+ __decorateParam(4, Optional2()),
2642
+ __decorateParam(4, Inject7(DRIZZLE)),
2643
+ __decorateParam(6, Optional2()),
2644
+ __decorateParam(6, Inject7(BULLMQ_CONNECTION)),
2645
+ __decorateParam(7, Optional2()),
2646
+ __decorateParam(7, Inject7(BULLMQ_RESOLVED_CONFIG))
3063
2647
  ], JobWorkerOrchestrator);
3064
2648
  var JobWorkerModule = class {
3065
2649
  static forRoot(opts) {
@@ -3094,8 +2678,6 @@ export {
3094
2678
  BULLMQ_CONNECTION,
3095
2679
  BULLMQ_RESOLVED_CONFIG,
3096
2680
  BootValidationError,
3097
- BullMQJobOrchestrator,
3098
- BullMQJobWorker,
3099
2681
  DrizzleJobOrchestrator,
3100
2682
  DrizzleJobRunService,
3101
2683
  DrizzleJobStepService,
@@ -3144,7 +2726,6 @@ export {
3144
2726
  replayFromEnum,
3145
2727
  resolveBullMqConfig,
3146
2728
  resolvePoolQueueName,
3147
- sha1JobId,
3148
2729
  triggerSourceEnum,
3149
2730
  waitKindEnum
3150
2731
  };