@pattern-stack/codegen 0.8.1 → 0.9.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 (120) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/{job-orchestrator.protocol-BwsBd37o.d.ts → job-orchestrator.protocol-CHOEqBDk.d.ts} +36 -1
  3. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.d.ts +2 -2
  4. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js.map +1 -1
  5. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js.map +1 -1
  6. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +5 -1
  7. package/dist/runtime/subsystems/bridge/bridge.module.js +930 -275
  8. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  9. package/dist/runtime/subsystems/bridge/event-flow.service.d.ts +1 -1
  10. package/dist/runtime/subsystems/bridge/event-flow.service.js.map +1 -1
  11. package/dist/runtime/subsystems/bridge/index.d.ts +4 -1
  12. package/dist/runtime/subsystems/bridge/index.js +837 -182
  13. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  14. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +3 -1
  15. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +92 -1
  16. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
  17. package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +3 -1
  18. package/dist/runtime/subsystems/events/event-bus.memory-backend.js +99 -0
  19. package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -1
  20. package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -1
  21. package/dist/runtime/subsystems/events/event-keyset-cursor.d.ts +32 -0
  22. package/dist/runtime/subsystems/events/event-keyset-cursor.js +38 -0
  23. package/dist/runtime/subsystems/events/event-keyset-cursor.js.map +1 -0
  24. package/dist/runtime/subsystems/events/event-read.protocol.d.ts +94 -0
  25. package/dist/runtime/subsystems/events/event-read.protocol.js +9 -0
  26. package/dist/runtime/subsystems/events/event-read.protocol.js.map +1 -0
  27. package/dist/runtime/subsystems/events/events.module.js +177 -3
  28. package/dist/runtime/subsystems/events/events.module.js.map +1 -1
  29. package/dist/runtime/subsystems/events/events.tokens.d.ts +16 -1
  30. package/dist/runtime/subsystems/events/events.tokens.js +2 -0
  31. package/dist/runtime/subsystems/events/events.tokens.js.map +1 -1
  32. package/dist/runtime/subsystems/events/generated/bus.js.map +1 -1
  33. package/dist/runtime/subsystems/events/generated/index.js.map +1 -1
  34. package/dist/runtime/subsystems/events/index.d.ts +2 -1
  35. package/dist/runtime/subsystems/events/index.js +178 -3
  36. package/dist/runtime/subsystems/events/index.js.map +1 -1
  37. package/dist/runtime/subsystems/index.d.ts +3 -2
  38. package/dist/runtime/subsystems/index.js +1194 -264
  39. package/dist/runtime/subsystems/index.js.map +1 -1
  40. package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +98 -0
  41. package/dist/runtime/subsystems/jobs/bullmq.config.js +143 -0
  42. package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -0
  43. package/dist/runtime/subsystems/jobs/index.d.ts +8 -3
  44. package/dist/runtime/subsystems/jobs/index.js +861 -201
  45. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  46. package/dist/runtime/subsystems/jobs/job-handler.base.d.ts +2 -1
  47. package/dist/runtime/subsystems/jobs/job-handler.base.js.map +1 -1
  48. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +108 -0
  49. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +922 -0
  50. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -0
  51. package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.d.ts +2 -1
  52. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +2 -1
  53. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js.map +1 -1
  54. package/dist/runtime/subsystems/jobs/job-orchestrator.protocol.d.ts +2 -1
  55. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +53 -0
  56. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js +57 -0
  57. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js.map +1 -0
  58. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +4 -2
  59. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +81 -1
  60. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js.map +1 -1
  61. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +4 -2
  62. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +81 -0
  63. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js.map +1 -1
  64. package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +76 -2
  65. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +49 -0
  66. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +374 -0
  67. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -0
  68. package/dist/runtime/subsystems/jobs/job-worker.d.ts +2 -1
  69. package/dist/runtime/subsystems/jobs/job-worker.js.map +1 -1
  70. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +44 -5
  71. package/dist/runtime/subsystems/jobs/job-worker.module.js +832 -178
  72. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  73. package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +10 -1
  74. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +519 -20
  75. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  76. package/dist/runtime/subsystems/jobs/jobs-errors.d.ts +2 -1
  77. package/dist/runtime/subsystems/jobs/pool-config.loader.d.ts +9 -1
  78. package/dist/runtime/subsystems/jobs/pool-config.loader.js +4 -0
  79. package/dist/runtime/subsystems/jobs/pool-config.loader.js.map +1 -1
  80. package/dist/runtime/subsystems/observability/index.d.ts +4 -3
  81. package/dist/runtime/subsystems/observability/index.js +109 -2
  82. package/dist/runtime/subsystems/observability/index.js.map +1 -1
  83. package/dist/runtime/subsystems/observability/observability.module.js +109 -2
  84. package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
  85. package/dist/runtime/subsystems/observability/observability.protocol.d.ts +64 -3
  86. package/dist/runtime/subsystems/observability/observability.service.d.ts +22 -4
  87. package/dist/runtime/subsystems/observability/observability.service.js +109 -2
  88. package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
  89. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -2
  90. package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -2
  91. package/dist/src/cli/index.js +30 -6
  92. package/dist/src/cli/index.js.map +1 -1
  93. package/package.json +5 -1
  94. package/runtime/subsystems/bridge/bridge.module.ts +5 -0
  95. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +109 -3
  96. package/runtime/subsystems/events/event-bus.memory-backend.ts +103 -1
  97. package/runtime/subsystems/events/event-keyset-cursor.ts +59 -0
  98. package/runtime/subsystems/events/event-read.protocol.ts +97 -0
  99. package/runtime/subsystems/events/events.module.ts +18 -2
  100. package/runtime/subsystems/events/events.tokens.ts +16 -0
  101. package/runtime/subsystems/events/index.ts +7 -0
  102. package/runtime/subsystems/jobs/bullmq.config.ts +125 -0
  103. package/runtime/subsystems/jobs/index.ts +22 -0
  104. package/runtime/subsystems/jobs/job-handler.base.ts +36 -0
  105. package/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts +381 -0
  106. package/runtime/subsystems/jobs/job-run-keyset-cursor.ts +88 -0
  107. package/runtime/subsystems/jobs/job-run-service.drizzle-backend.ts +59 -1
  108. package/runtime/subsystems/jobs/job-run-service.memory-backend.ts +53 -0
  109. package/runtime/subsystems/jobs/job-run-service.protocol.ts +77 -0
  110. package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +311 -0
  111. package/runtime/subsystems/jobs/job-worker.module.ts +124 -10
  112. package/runtime/subsystems/jobs/jobs-domain.module.ts +40 -21
  113. package/runtime/subsystems/jobs/pool-config.loader.ts +11 -0
  114. package/runtime/subsystems/observability/index.ts +8 -0
  115. package/runtime/subsystems/observability/observability.protocol.ts +76 -0
  116. package/runtime/subsystems/observability/observability.service.ts +148 -1
  117. package/templates/entity/new/clean-lite-ps/prompt-extension.js +14 -12
  118. package/templates/relationship/new/prompt.js +8 -5
  119. package/templates/subsystem/jobs/worker.ejs.t +30 -7
  120. package/templates/subsystem/sync/sync-audit.schema.ejs.t +12 -16
@@ -933,18 +933,18 @@ EventFlowService = __decorateClass([
933
933
 
934
934
  // runtime/subsystems/bridge/bridge.module.ts
935
935
  import {
936
- Inject as Inject12,
936
+ Inject as Inject13,
937
937
  Module as Module3,
938
- Optional as Optional7
938
+ Optional as Optional8
939
939
  } from "@nestjs/common";
940
940
 
941
941
  // runtime/subsystems/jobs/job-worker.module.ts
942
942
  import {
943
- Inject as Inject11,
944
- Injectable as Injectable12,
945
- Logger as Logger6,
943
+ Inject as Inject12,
944
+ Injectable as Injectable13,
945
+ Logger as Logger8,
946
946
  Module as Module2,
947
- Optional as Optional6
947
+ Optional as Optional7
948
948
  } from "@nestjs/common";
949
949
 
950
950
  // runtime/subsystems/jobs/jobs-domain.module.ts
@@ -1360,7 +1360,58 @@ function notInStatus(statuses) {
1360
1360
 
1361
1361
  // runtime/subsystems/jobs/job-run-service.drizzle-backend.ts
1362
1362
  import { Inject as Inject6, Injectable as Injectable6 } from "@nestjs/common";
1363
- import { and as and3, asc, desc as desc2, eq as eq3, inArray as inArray2, isNull as isNull2, sql as sql6 } from "drizzle-orm";
1363
+ import { and as and3, asc, desc as desc2, eq as eq3, gte as gte2, inArray as inArray2, isNull as isNull2, lt, or, sql as sql6 } from "drizzle-orm";
1364
+
1365
+ // runtime/subsystems/jobs/job-run-keyset-cursor.ts
1366
+ var DEFAULT_LIST_LIMIT = 50;
1367
+ var MAX_LIST_LIMIT = 200;
1368
+ function clampLimit(limit) {
1369
+ if (typeof limit !== "number" || !Number.isFinite(limit)) {
1370
+ return DEFAULT_LIST_LIMIT;
1371
+ }
1372
+ const floored = Math.floor(limit);
1373
+ if (floored < 1) return 1;
1374
+ if (floored > MAX_LIST_LIMIT) return MAX_LIST_LIMIT;
1375
+ return floored;
1376
+ }
1377
+ function encodeKeysetCursor(keyset) {
1378
+ const tuple = [keyset.createdAt.toISOString(), keyset.id];
1379
+ return Buffer.from(JSON.stringify(tuple), "utf8").toString("base64url");
1380
+ }
1381
+ function decodeKeysetCursor(cursor) {
1382
+ try {
1383
+ const json = Buffer.from(cursor, "base64url").toString("utf8");
1384
+ const parsed = JSON.parse(json);
1385
+ if (!Array.isArray(parsed) || parsed.length !== 2) return null;
1386
+ const [iso, id] = parsed;
1387
+ if (typeof iso !== "string" || typeof id !== "string") return null;
1388
+ const createdAt = new Date(iso);
1389
+ if (Number.isNaN(createdAt.getTime())) return null;
1390
+ return { createdAt, id };
1391
+ } catch {
1392
+ return null;
1393
+ }
1394
+ }
1395
+ function toJobRunSummary(r) {
1396
+ return {
1397
+ runId: r.id,
1398
+ rootRunId: r.rootRunId,
1399
+ jobType: r.jobType,
1400
+ pool: r.pool,
1401
+ status: r.status,
1402
+ scopeEntityType: r.scopeEntityType,
1403
+ scopeEntityId: r.scopeEntityId,
1404
+ tenantId: r.tenantId,
1405
+ attempts: r.attempts,
1406
+ errorMessage: r.error?.message ?? null,
1407
+ runAt: r.runAt,
1408
+ startedAt: r.startedAt,
1409
+ finishedAt: r.finishedAt,
1410
+ createdAt: r.createdAt
1411
+ };
1412
+ }
1413
+
1414
+ // runtime/subsystems/jobs/job-run-service.drizzle-backend.ts
1364
1415
  var NON_TERMINAL_STATUSES = [
1365
1416
  "pending",
1366
1417
  "running",
@@ -1483,6 +1534,37 @@ var DrizzleJobRunService = class {
1483
1534
  createdAt: r.createdAt
1484
1535
  }));
1485
1536
  }
1537
+ async listJobRuns(query = {}) {
1538
+ const limit = clampLimit(query.limit);
1539
+ const conditions = [];
1540
+ const tenantCond = this.tenantCondition("listJobRuns", query.tenantId);
1541
+ if (tenantCond) conditions.push(tenantCond);
1542
+ if (query.poolId) conditions.push(eq3(jobRuns.pool, query.poolId));
1543
+ if (query.rootRunId) conditions.push(eq3(jobRuns.rootRunId, query.rootRunId));
1544
+ if (query.status) conditions.push(eq3(jobRuns.status, query.status));
1545
+ if (query.since) conditions.push(gte2(jobRuns.createdAt, query.since));
1546
+ if (query.cursor) {
1547
+ const keyset = decodeKeysetCursor(query.cursor);
1548
+ if (keyset) {
1549
+ conditions.push(
1550
+ or(
1551
+ lt(jobRuns.createdAt, keyset.createdAt),
1552
+ and3(
1553
+ eq3(jobRuns.createdAt, keyset.createdAt),
1554
+ lt(jobRuns.id, keyset.id)
1555
+ )
1556
+ )
1557
+ );
1558
+ }
1559
+ }
1560
+ const rows = await this.db.select().from(jobRuns).where(conditions.length > 0 ? and3(...conditions) : void 0).orderBy(desc2(jobRuns.createdAt), desc2(jobRuns.id)).limit(limit + 1);
1561
+ const hasMore = rows.length > limit;
1562
+ const page = hasMore ? rows.slice(0, limit) : rows;
1563
+ const items = page.map(toJobRunSummary);
1564
+ const last = page[page.length - 1];
1565
+ const nextCursor = hasMore && last ? encodeKeysetCursor({ createdAt: last.createdAt, id: last.id }) : null;
1566
+ return { items, nextCursor };
1567
+ }
1486
1568
  /**
1487
1569
  * Internal helper used by cascade paths (not on the public protocol).
1488
1570
  * Exposed as a public method on the concrete class so infrastructure
@@ -1544,9 +1626,394 @@ DrizzleJobStepService = __decorateClass([
1544
1626
  __decorateParam(0, Inject7(DRIZZLE))
1545
1627
  ], DrizzleJobStepService);
1546
1628
 
1629
+ // runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts
1630
+ import { createHash } from "crypto";
1631
+ import { Inject as Inject8, Injectable as Injectable8, Logger as Logger4, Optional as Optional5 } from "@nestjs/common";
1632
+ import { eq as eq5 } from "drizzle-orm";
1633
+
1634
+ // runtime/subsystems/jobs/pool-config.loader.ts
1635
+ import { existsSync, readFileSync } from "fs";
1636
+ import { resolve } from "path";
1637
+ import { parse as parseYaml } from "yaml";
1638
+ var FRAMEWORK_POOLS = Object.freeze({
1639
+ events_inbound: Object.freeze({
1640
+ queue: "jobs-events-inbound",
1641
+ concurrency: 20,
1642
+ reserved: true,
1643
+ description: "Inbound events drain (events subsystem outbox)."
1644
+ }),
1645
+ events_change: Object.freeze({
1646
+ queue: "jobs-events-change",
1647
+ concurrency: 30,
1648
+ reserved: true,
1649
+ description: "Change events drain (events subsystem outbox)."
1650
+ }),
1651
+ events_outbound: Object.freeze({
1652
+ queue: "jobs-events-outbound",
1653
+ concurrency: 10,
1654
+ reserved: true,
1655
+ description: "Outbound events drain (events subsystem outbox)."
1656
+ }),
1657
+ interactive: Object.freeze({
1658
+ queue: "jobs-interactive",
1659
+ concurrency: 20,
1660
+ reserved: false,
1661
+ description: "User-facing latency-sensitive jobs."
1662
+ }),
1663
+ batch: Object.freeze({
1664
+ queue: "jobs-batch",
1665
+ concurrency: 5,
1666
+ reserved: false,
1667
+ description: "Default pool for background jobs."
1668
+ })
1669
+ });
1670
+ var RESERVED_POOL_NAMES = new Set(
1671
+ Object.entries(FRAMEWORK_POOLS).filter(([, def]) => def.reserved).map(([name]) => name)
1672
+ );
1673
+ var cache = /* @__PURE__ */ new Map();
1674
+ function loadPoolConfig(configPath) {
1675
+ const resolved = resolve(configPath ?? `${process.cwd()}/codegen.config.yaml`);
1676
+ const cached = cache.get(resolved);
1677
+ if (cached) return cached;
1678
+ const merged = /* @__PURE__ */ new Map();
1679
+ for (const [name, def] of Object.entries(FRAMEWORK_POOLS)) {
1680
+ merged.set(name, { ...def });
1681
+ }
1682
+ if (!existsSync(resolved)) {
1683
+ cache.set(resolved, merged);
1684
+ return merged;
1685
+ }
1686
+ let raw;
1687
+ try {
1688
+ raw = parseYaml(readFileSync(resolved, "utf8"));
1689
+ } catch (err) {
1690
+ throw new Error(
1691
+ `pool-config.loader: failed to parse YAML at ${resolved}: ${err.message}`
1692
+ );
1693
+ }
1694
+ const userPools = extractUserPools(raw);
1695
+ for (const [name, userDef] of Object.entries(userPools)) {
1696
+ const existing = merged.get(name);
1697
+ if (existing) {
1698
+ const next = {
1699
+ queue: existing.queue,
1700
+ concurrency: typeof userDef.concurrency === "number" ? userDef.concurrency : existing.concurrency,
1701
+ reserved: existing.reserved,
1702
+ description: userDef.description ?? existing.description
1703
+ };
1704
+ merged.set(name, next);
1705
+ continue;
1706
+ }
1707
+ if (typeof userDef.queue !== "string" || userDef.queue.length === 0) {
1708
+ throw new Error(
1709
+ `pool-config.loader: pool '${name}' must declare a non-empty 'queue'.`
1710
+ );
1711
+ }
1712
+ if (typeof userDef.concurrency !== "number" || userDef.concurrency <= 0) {
1713
+ throw new Error(
1714
+ `pool-config.loader: pool '${name}' must declare a positive 'concurrency'.`
1715
+ );
1716
+ }
1717
+ if (userDef.reserved === true) {
1718
+ throw new Error(
1719
+ `pool-config.loader: user-defined pool '${name}' cannot set 'reserved: true' \u2014 reserved is framework-only.`
1720
+ );
1721
+ }
1722
+ merged.set(name, {
1723
+ queue: userDef.queue,
1724
+ concurrency: userDef.concurrency,
1725
+ reserved: false,
1726
+ description: userDef.description
1727
+ });
1728
+ }
1729
+ cache.set(resolved, merged);
1730
+ return merged;
1731
+ }
1732
+ function allNonReservedPoolNames(config) {
1733
+ const out = [];
1734
+ for (const [name, def] of config) {
1735
+ if (!def.reserved) out.push(name);
1736
+ }
1737
+ return out;
1738
+ }
1739
+ function allPoolNames(config) {
1740
+ return [...config.keys()];
1741
+ }
1742
+ function extractUserPools(raw) {
1743
+ if (!raw || typeof raw !== "object") return {};
1744
+ const jobs2 = raw.jobs;
1745
+ if (!jobs2 || typeof jobs2 !== "object") return {};
1746
+ const pools = jobs2.pools;
1747
+ if (!pools || typeof pools !== "object") return {};
1748
+ const out = {};
1749
+ for (const [name, def] of Object.entries(pools)) {
1750
+ if (!def || typeof def !== "object") continue;
1751
+ out[name] = def;
1752
+ }
1753
+ return out;
1754
+ }
1755
+
1756
+ // runtime/subsystems/jobs/bullmq.config.ts
1757
+ var BULLMQ_CONNECTION = /* @__PURE__ */ Symbol("BULLMQ_CONNECTION");
1758
+ var BULLMQ_RESOLVED_CONFIG = /* @__PURE__ */ Symbol("BULLMQ_RESOLVED_CONFIG");
1759
+ var DEFAULT_REDIS_URL = "redis://localhost:6379";
1760
+ var DEFAULT_BULL_BOARD_MOUNT = "/admin/queues";
1761
+ function resolveBullMqConfig(ext) {
1762
+ const url = ext?.redis_url ?? process.env.REDIS_URL ?? DEFAULT_REDIS_URL;
1763
+ const resolved = {
1764
+ connection: { url },
1765
+ queuePrefix: ext?.queue_prefix
1766
+ };
1767
+ if (ext?.bull_board?.enabled) {
1768
+ resolved.bullBoard = {
1769
+ enabled: true,
1770
+ mountPath: ext.bull_board.mount_path ?? DEFAULT_BULL_BOARD_MOUNT
1771
+ };
1772
+ }
1773
+ return resolved;
1774
+ }
1775
+ function resolvePoolQueueName(pool, config, poolConfig = loadPoolConfig()) {
1776
+ const alias = poolConfig.get(pool)?.queue ?? pool;
1777
+ const prefix = config?.queuePrefix;
1778
+ return prefix ? `${prefix}:${alias}` : alias;
1779
+ }
1780
+
1781
+ // runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts
1782
+ function sha1JobId(idempotencyKey) {
1783
+ return createHash("sha1").update(idempotencyKey).digest("hex");
1784
+ }
1785
+ var BullMQJobOrchestrator = class extends DrizzleJobOrchestrator {
1786
+ constructor(db, multiTenant, connection, bullConfig = null) {
1787
+ super(db, multiTenant);
1788
+ this.connection = connection;
1789
+ this.bullConfig = bullConfig;
1790
+ this.bullDb = db;
1791
+ }
1792
+ connection;
1793
+ bullConfig;
1794
+ // TODO(logging-subsystem): swap to ILogger once ADR-028 lands
1795
+ bullLogger = new Logger4(BullMQJobOrchestrator.name);
1796
+ /** Lazily-opened `Queue` handles, one per pool. */
1797
+ queues = /* @__PURE__ */ new Map();
1798
+ /** Single FlowProducer for parent/child hierarchies. Lazily opened. */
1799
+ _flow = null;
1800
+ /**
1801
+ * Cached `bullmq` value constructors, populated by `loadBullMq()` on first
1802
+ * use (the `start`/`cancel`/`replay` entrypoints `await` it before touching
1803
+ * a queue). Kept off the import graph so a `drizzle`-only consumer never
1804
+ * resolves the optional `'bullmq'` package.
1805
+ */
1806
+ QueueCtor = null;
1807
+ FlowProducerCtor = null;
1808
+ bullMqLoad = null;
1809
+ /**
1810
+ * Own reference to the Drizzle client. `DrizzleJobOrchestrator.db` is
1811
+ * `private` (can't be redeclared even privately in a subclass), and the
1812
+ * spec forbids touching that file — so the subclass keeps its own handle
1813
+ * under a distinct name (same instance, passed through to `super`) for the
1814
+ * cancel-cascade snapshot + definition/run loads below.
1815
+ */
1816
+ bullDb;
1817
+ /**
1818
+ * Lazily load the optional `bullmq` package and cache its value
1819
+ * constructors. Idempotent (single in-flight promise). Throws a friendly,
1820
+ * actionable error when the consumer selected `backend: 'bullmq'` but did
1821
+ * not install the package — mirrors `createRedisClient` in the redis event
1822
+ * backend. Must be `await`ed before any `queueFor`/`flow` access.
1823
+ */
1824
+ async loadBullMq() {
1825
+ if (this.QueueCtor && this.FlowProducerCtor) return;
1826
+ if (!this.bullMqLoad) {
1827
+ this.bullMqLoad = (async () => {
1828
+ try {
1829
+ const mod = await import("bullmq");
1830
+ this.QueueCtor = mod.Queue;
1831
+ this.FlowProducerCtor = mod.FlowProducer;
1832
+ } catch {
1833
+ throw new Error(
1834
+ 'BullMQ backend requires the "bullmq" package. Install it with: npm install bullmq'
1835
+ );
1836
+ }
1837
+ })();
1838
+ }
1839
+ await this.bullMqLoad;
1840
+ }
1841
+ /**
1842
+ * Open (or reuse) the `Queue` for a pool. Synchronous — callers `await
1843
+ * loadBullMq()` first so `QueueCtor` is populated.
1844
+ */
1845
+ queueFor(pool) {
1846
+ if (!this.QueueCtor) {
1847
+ throw new Error("BullMQJobOrchestrator: queueFor called before loadBullMq()");
1848
+ }
1849
+ const name = resolvePoolQueueName(pool, this.bullConfig);
1850
+ let q = this.queues.get(name);
1851
+ if (!q) {
1852
+ q = new this.QueueCtor(name, { connection: this.connection });
1853
+ this.queues.set(name, q);
1854
+ }
1855
+ return q;
1856
+ }
1857
+ flow() {
1858
+ if (!this.FlowProducerCtor) {
1859
+ throw new Error("BullMQJobOrchestrator: flow called before loadBullMq()");
1860
+ }
1861
+ if (!this._flow) {
1862
+ this._flow = new this.FlowProducerCtor({ connection: this.connection });
1863
+ }
1864
+ return this._flow;
1865
+ }
1866
+ // ==========================================================================
1867
+ // start — Postgres insert (super) + BullMQ dispatch
1868
+ // ==========================================================================
1869
+ async start(type, input, opts = {}, tx) {
1870
+ const run = await super.start(type, input, opts, tx);
1871
+ await this.dispatch(run, type);
1872
+ return run;
1873
+ }
1874
+ /**
1875
+ * Map a `job_run` row onto a BullMQ job via `queue.add`. When the run has a
1876
+ * `parentRunId` we attach it to the parent's existing BullMQ job through the
1877
+ * `parent: { id, queue }` opt — BullMQ then tracks the parent/child link in
1878
+ * its own graph. (The FlowProducer is reserved for whole-tree atomic
1879
+ * submits, exposed as an opt-in extension via `flowProducer()`; runtime
1880
+ * `ctx.spawnChild` is incremental, so `queue.add` with a parent ref is the
1881
+ * correct primitive here.)
1882
+ *
1883
+ * The `jobId` is colon-safe + stable: `sha1(dedupeKey)` when a dedupe key is
1884
+ * present (so the same logical key dedups), else the `job_run.id` UUID
1885
+ * (already colon-free).
1886
+ *
1887
+ * The domain `parentClosePolicy` cascade is still enforced in Postgres by
1888
+ * the shared `cancel` path — BullMQ's parent link is dispatch bookkeeping,
1889
+ * not the authority.
1890
+ */
1891
+ async dispatch(run, type) {
1892
+ await this.loadBullMq();
1893
+ const def = await this.loadDefinition(type);
1894
+ const jobId = run.dedupeKey ? sha1JobId(run.dedupeKey) : run.id;
1895
+ const jobOpts = {
1896
+ jobId,
1897
+ ...this.retryOpts(def),
1898
+ ...this.dedupeOpts(run, def)
1899
+ };
1900
+ if (run.parentRunId) {
1901
+ const parentRow = await this.loadRun(run.parentRunId);
1902
+ if (parentRow) {
1903
+ const parentJobId = parentRow.dedupeKey ? sha1JobId(parentRow.dedupeKey) : parentRow.id;
1904
+ jobOpts.parent = {
1905
+ id: parentJobId,
1906
+ queue: resolvePoolQueueName(parentRow.pool, this.bullConfig)
1907
+ };
1908
+ }
1909
+ }
1910
+ const payload = { runId: run.id, type, input: run.input };
1911
+ await this.queueFor(run.pool).add(type, payload, jobOpts);
1912
+ }
1913
+ /**
1914
+ * Opt-in extension (spec §Extensions): expose the FlowProducer for
1915
+ * consumers that want to submit a whole parent/child DAG atomically up
1916
+ * front, rather than incrementally via `ctx.spawnChild`. Backend-specific —
1917
+ * code using it is not portable to the Drizzle backend. Async because it
1918
+ * lazily loads the optional `bullmq` package on first use.
1919
+ */
1920
+ async flowProducer() {
1921
+ await this.loadBullMq();
1922
+ return this.flow();
1923
+ }
1924
+ retryOpts(def) {
1925
+ const policy = def.retryPolicy;
1926
+ if (!policy) return {};
1927
+ return {
1928
+ attempts: policy.attempts,
1929
+ backoff: {
1930
+ type: policy.backoff === "exponential" ? "exponential" : "fixed",
1931
+ delay: policy.baseMs
1932
+ }
1933
+ };
1934
+ }
1935
+ dedupeOpts(run, def) {
1936
+ if (!run.dedupeKey || !def.dedupeWindowMs) return {};
1937
+ return {
1938
+ deduplication: {
1939
+ id: sha1JobId(run.dedupeKey),
1940
+ ttl: def.dedupeWindowMs
1941
+ }
1942
+ };
1943
+ }
1944
+ // ==========================================================================
1945
+ // cancel — Postgres cascade (super) + remove from queue
1946
+ // ==========================================================================
1947
+ async cancel(runId, opts = {}) {
1948
+ const target = await this.loadRun(runId);
1949
+ await super.cancel(runId, opts);
1950
+ if (!target) return;
1951
+ await this.loadBullMq();
1952
+ await this.removeFromQueue(target);
1953
+ if (opts.cascade === false) return;
1954
+ const descendants = await this.bullDb.select().from(jobRuns).where(eq5(jobRuns.rootRunId, target.rootRunId));
1955
+ for (const child of descendants) {
1956
+ if (child.id === runId) continue;
1957
+ await this.removeFromQueue(child);
1958
+ }
1959
+ }
1960
+ async removeFromQueue(run) {
1961
+ const jobId = run.dedupeKey ? sha1JobId(run.dedupeKey) : run.id;
1962
+ try {
1963
+ const job = await this.queueFor(run.pool).getJob(jobId);
1964
+ if (job) await job.remove();
1965
+ } catch (err) {
1966
+ this.bullLogger.warn(
1967
+ `cancel: could not remove BullMQ job ${jobId} (pool=${run.pool}): ${err.message}`
1968
+ );
1969
+ }
1970
+ }
1971
+ // ==========================================================================
1972
+ // replay — Postgres reset (super) + re-enqueue
1973
+ // ==========================================================================
1974
+ async replay(runId) {
1975
+ const run = await super.replay(runId);
1976
+ await this.dispatch(run, run.jobType);
1977
+ return run;
1978
+ }
1979
+ // ==========================================================================
1980
+ // Internals
1981
+ // ==========================================================================
1982
+ async loadDefinition(type) {
1983
+ const [def] = await this.bullDb.select().from(jobs).where(eq5(jobs.type, type)).limit(1);
1984
+ if (!def) {
1985
+ throw new Error(`BullMQJobOrchestrator: no job definition for '${type}'`);
1986
+ }
1987
+ return def;
1988
+ }
1989
+ async loadRun(id) {
1990
+ const [row] = await this.bullDb.select().from(jobRuns).where(eq5(jobRuns.id, id)).limit(1);
1991
+ return row ?? null;
1992
+ }
1993
+ /** Close all open queue + flow connections. Called on module destroy. */
1994
+ async closeConnections() {
1995
+ for (const q of this.queues.values()) {
1996
+ await q.close().catch(() => void 0);
1997
+ }
1998
+ this.queues.clear();
1999
+ if (this._flow) {
2000
+ await this._flow.close().catch(() => void 0);
2001
+ this._flow = null;
2002
+ }
2003
+ }
2004
+ };
2005
+ BullMQJobOrchestrator = __decorateClass([
2006
+ Injectable8(),
2007
+ __decorateParam(0, Inject8(DRIZZLE)),
2008
+ __decorateParam(1, Inject8(JOBS_MULTI_TENANT)),
2009
+ __decorateParam(2, Inject8(BULLMQ_CONNECTION)),
2010
+ __decorateParam(3, Optional5()),
2011
+ __decorateParam(3, Inject8(BULLMQ_RESOLVED_CONFIG))
2012
+ ], BullMQJobOrchestrator);
2013
+
1547
2014
  // runtime/subsystems/jobs/job-orchestrator.memory-backend.ts
1548
2015
  import { randomUUID as randomUUID4 } from "crypto";
1549
- import { Inject as Inject8, Injectable as Injectable8, Logger as Logger4, Optional as Optional5 } from "@nestjs/common";
2016
+ import { Inject as Inject9, Injectable as Injectable9, Logger as Logger5, Optional as Optional6 } from "@nestjs/common";
1550
2017
  var QUEUED_RUN_AT = /* @__PURE__ */ new Date(864e13);
1551
2018
  var TERMINAL_STATUSES2 = [
1552
2019
  "completed",
@@ -1593,7 +2060,7 @@ var MemoryJobOrchestrator = class {
1593
2060
  stepService;
1594
2061
  multiTenant;
1595
2062
  moduleRef;
1596
- logger = new Logger4(MemoryJobOrchestrator.name);
2063
+ logger = new Logger5(MemoryJobOrchestrator.name);
1597
2064
  mutex = new PromiseMutex();
1598
2065
  handlerRegistry = /* @__PURE__ */ new Map();
1599
2066
  /**
@@ -1942,7 +2409,7 @@ var MemoryJobOrchestrator = class {
1942
2409
  run,
1943
2410
  step: this.makeStepFn(run),
1944
2411
  spawnChild: this.makeSpawnFn(run),
1945
- logger: new Logger4(`JobRun:${run.id}`)
2412
+ logger: new Logger5(`JobRun:${run.id}`)
1946
2413
  };
1947
2414
  const attemptsBefore = run.attempts ?? 0;
1948
2415
  try {
@@ -2115,9 +2582,9 @@ var MemoryJobOrchestrator = class {
2115
2582
  }
2116
2583
  };
2117
2584
  MemoryJobOrchestrator = __decorateClass([
2118
- Injectable8(),
2119
- __decorateParam(2, Inject8(JOBS_MULTI_TENANT)),
2120
- __decorateParam(3, Optional5())
2585
+ Injectable9(),
2586
+ __decorateParam(2, Inject9(JOBS_MULTI_TENANT)),
2587
+ __decorateParam(3, Optional6())
2121
2588
  ], MemoryJobOrchestrator);
2122
2589
  function classifyError(err, policy, currentAttempts) {
2123
2590
  if (!policy) return "fail";
@@ -2151,7 +2618,7 @@ function serialiseError(err, attempt, retryable) {
2151
2618
  }
2152
2619
 
2153
2620
  // runtime/subsystems/jobs/job-run-service.memory-backend.ts
2154
- import { Inject as Inject9, Injectable as Injectable9 } from "@nestjs/common";
2621
+ import { Inject as Inject10, Injectable as Injectable10 } from "@nestjs/common";
2155
2622
  var NON_TERMINAL_STATUSES2 = [
2156
2623
  "pending",
2157
2624
  "running",
@@ -2268,6 +2735,38 @@ var MemoryJobRunService = class {
2268
2735
  createdAt: r.createdAt
2269
2736
  }));
2270
2737
  }
2738
+ async listJobRuns(query = {}) {
2739
+ const limit = clampLimit(query.limit);
2740
+ const tenantCheck = this.tenantPredicate("listJobRuns", query.tenantId);
2741
+ const keyset = query.cursor ? decodeKeysetCursor(query.cursor) : null;
2742
+ const matched = [];
2743
+ for (const r of this.store.runs.values()) {
2744
+ if (tenantCheck && !tenantCheck(r)) continue;
2745
+ if (query.poolId && r.pool !== query.poolId) continue;
2746
+ if (query.rootRunId && r.rootRunId !== query.rootRunId) continue;
2747
+ if (query.status && r.status !== query.status) continue;
2748
+ if (query.since && r.createdAt.getTime() < query.since.getTime()) continue;
2749
+ matched.push(r);
2750
+ }
2751
+ matched.sort((a, b) => {
2752
+ const dt = b.createdAt.getTime() - a.createdAt.getTime();
2753
+ if (dt !== 0) return dt;
2754
+ return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
2755
+ });
2756
+ const seeked = keyset ? matched.filter((r) => {
2757
+ const ct = r.createdAt.getTime();
2758
+ const kt = keyset.createdAt.getTime();
2759
+ if (ct < kt) return true;
2760
+ if (ct > kt) return false;
2761
+ return r.id < keyset.id;
2762
+ }) : matched;
2763
+ const hasMore = seeked.length > limit;
2764
+ const page = hasMore ? seeked.slice(0, limit) : seeked;
2765
+ const items = page.map(toJobRunSummary);
2766
+ const last = page[page.length - 1];
2767
+ const nextCursor = hasMore && last ? encodeKeysetCursor({ createdAt: last.createdAt, id: last.id }) : null;
2768
+ return { items, nextCursor };
2769
+ }
2271
2770
  /**
2272
2771
  * Direct lookup. Not on the protocol — concrete-class convenience for
2273
2772
  * tests. Matches `DrizzleJobRunService.findByRootRunId` in spirit; both
@@ -2286,9 +2785,9 @@ var MemoryJobRunService = class {
2286
2785
  }
2287
2786
  };
2288
2787
  MemoryJobRunService = __decorateClass([
2289
- Injectable9(),
2290
- __decorateParam(1, Inject9(JOB_ORCHESTRATOR)),
2291
- __decorateParam(2, Inject9(JOBS_MULTI_TENANT))
2788
+ Injectable10(),
2789
+ __decorateParam(1, Inject10(JOB_ORCHESTRATOR)),
2790
+ __decorateParam(2, Inject10(JOBS_MULTI_TENANT))
2292
2791
  ], MemoryJobRunService);
2293
2792
  function compareBy(a, b, order) {
2294
2793
  switch (order) {
@@ -2306,7 +2805,7 @@ function compareBy(a, b, order) {
2306
2805
 
2307
2806
  // runtime/subsystems/jobs/job-step-service.memory-backend.ts
2308
2807
  import { randomUUID as randomUUID5 } from "crypto";
2309
- import { Injectable as Injectable10 } from "@nestjs/common";
2808
+ import { Injectable as Injectable11 } from "@nestjs/common";
2310
2809
  var MemoryJobStepService = class {
2311
2810
  constructor(store) {
2312
2811
  this.store = store;
@@ -2399,7 +2898,7 @@ var MemoryJobStepService = class {
2399
2898
  }
2400
2899
  };
2401
2900
  MemoryJobStepService = __decorateClass([
2402
- Injectable10()
2901
+ Injectable11()
2403
2902
  ], MemoryJobStepService);
2404
2903
 
2405
2904
  // runtime/subsystems/jobs/memory-job-store.ts
@@ -2421,7 +2920,6 @@ var MemoryJobStore = class {
2421
2920
  // runtime/subsystems/jobs/jobs-domain.module.ts
2422
2921
  var JobsDomainModule = class {
2423
2922
  static forRoot(opts) {
2424
- void opts.extensions;
2425
2923
  const multiTenant = opts.multiTenant ?? false;
2426
2924
  const providers = [
2427
2925
  // JOB-8 — boolean provider consumed by the four service-layer backends.
@@ -2440,21 +2938,32 @@ var JobsDomainModule = class {
2440
2938
  providers.push({ provide: JOB_ORCHESTRATOR, useExisting: MemoryJobOrchestrator });
2441
2939
  providers.push(MemoryJobRunService);
2442
2940
  providers.push({ provide: JOB_RUN_SERVICE, useExisting: MemoryJobRunService });
2941
+ } else if (opts.backend === "bullmq") {
2942
+ const resolved = resolveBullMqConfig(opts.extensions?.bullmq);
2943
+ providers.push({ provide: BULLMQ_CONNECTION, useValue: resolved.connection });
2944
+ providers.push({ provide: BULLMQ_RESOLVED_CONFIG, useValue: resolved });
2945
+ providers.push({ provide: JOB_ORCHESTRATOR, useClass: BullMQJobOrchestrator });
2946
+ providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });
2947
+ providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });
2443
2948
  } else {
2444
2949
  providers.push({ provide: JOB_ORCHESTRATOR, useClass: DrizzleJobOrchestrator });
2445
2950
  providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });
2446
2951
  providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });
2447
2952
  }
2953
+ const exports = [
2954
+ JOB_ORCHESTRATOR,
2955
+ JOB_RUN_SERVICE,
2956
+ JOB_STEP_SERVICE,
2957
+ JOBS_MULTI_TENANT
2958
+ ];
2959
+ if (opts.backend === "bullmq") {
2960
+ exports.push(BULLMQ_CONNECTION, BULLMQ_RESOLVED_CONFIG);
2961
+ }
2448
2962
  return {
2449
2963
  module: JobsDomainModule,
2450
2964
  global: true,
2451
2965
  providers,
2452
- exports: [
2453
- JOB_ORCHESTRATOR,
2454
- JOB_RUN_SERVICE,
2455
- JOB_STEP_SERVICE,
2456
- JOBS_MULTI_TENANT
2457
- ]
2966
+ exports
2458
2967
  };
2459
2968
  }
2460
2969
  };
@@ -2462,128 +2971,9 @@ JobsDomainModule = __decorateClass([
2462
2971
  Module({})
2463
2972
  ], JobsDomainModule);
2464
2973
 
2465
- // runtime/subsystems/jobs/pool-config.loader.ts
2466
- import { existsSync, readFileSync } from "fs";
2467
- import { resolve } from "path";
2468
- import { parse as parseYaml } from "yaml";
2469
- var FRAMEWORK_POOLS = Object.freeze({
2470
- events_inbound: Object.freeze({
2471
- queue: "jobs-events-inbound",
2472
- concurrency: 20,
2473
- reserved: true,
2474
- description: "Inbound events drain (events subsystem outbox)."
2475
- }),
2476
- events_change: Object.freeze({
2477
- queue: "jobs-events-change",
2478
- concurrency: 30,
2479
- reserved: true,
2480
- description: "Change events drain (events subsystem outbox)."
2481
- }),
2482
- events_outbound: Object.freeze({
2483
- queue: "jobs-events-outbound",
2484
- concurrency: 10,
2485
- reserved: true,
2486
- description: "Outbound events drain (events subsystem outbox)."
2487
- }),
2488
- interactive: Object.freeze({
2489
- queue: "jobs-interactive",
2490
- concurrency: 20,
2491
- reserved: false,
2492
- description: "User-facing latency-sensitive jobs."
2493
- }),
2494
- batch: Object.freeze({
2495
- queue: "jobs-batch",
2496
- concurrency: 5,
2497
- reserved: false,
2498
- description: "Default pool for background jobs."
2499
- })
2500
- });
2501
- var RESERVED_POOL_NAMES = new Set(
2502
- Object.entries(FRAMEWORK_POOLS).filter(([, def]) => def.reserved).map(([name]) => name)
2503
- );
2504
- var cache = /* @__PURE__ */ new Map();
2505
- function loadPoolConfig(configPath) {
2506
- const resolved = resolve(configPath ?? `${process.cwd()}/codegen.config.yaml`);
2507
- const cached = cache.get(resolved);
2508
- if (cached) return cached;
2509
- const merged = /* @__PURE__ */ new Map();
2510
- for (const [name, def] of Object.entries(FRAMEWORK_POOLS)) {
2511
- merged.set(name, { ...def });
2512
- }
2513
- if (!existsSync(resolved)) {
2514
- cache.set(resolved, merged);
2515
- return merged;
2516
- }
2517
- let raw;
2518
- try {
2519
- raw = parseYaml(readFileSync(resolved, "utf8"));
2520
- } catch (err) {
2521
- throw new Error(
2522
- `pool-config.loader: failed to parse YAML at ${resolved}: ${err.message}`
2523
- );
2524
- }
2525
- const userPools = extractUserPools(raw);
2526
- for (const [name, userDef] of Object.entries(userPools)) {
2527
- const existing = merged.get(name);
2528
- if (existing) {
2529
- const next = {
2530
- queue: existing.queue,
2531
- concurrency: typeof userDef.concurrency === "number" ? userDef.concurrency : existing.concurrency,
2532
- reserved: existing.reserved,
2533
- description: userDef.description ?? existing.description
2534
- };
2535
- merged.set(name, next);
2536
- continue;
2537
- }
2538
- if (typeof userDef.queue !== "string" || userDef.queue.length === 0) {
2539
- throw new Error(
2540
- `pool-config.loader: pool '${name}' must declare a non-empty 'queue'.`
2541
- );
2542
- }
2543
- if (typeof userDef.concurrency !== "number" || userDef.concurrency <= 0) {
2544
- throw new Error(
2545
- `pool-config.loader: pool '${name}' must declare a positive 'concurrency'.`
2546
- );
2547
- }
2548
- if (userDef.reserved === true) {
2549
- throw new Error(
2550
- `pool-config.loader: user-defined pool '${name}' cannot set 'reserved: true' \u2014 reserved is framework-only.`
2551
- );
2552
- }
2553
- merged.set(name, {
2554
- queue: userDef.queue,
2555
- concurrency: userDef.concurrency,
2556
- reserved: false,
2557
- description: userDef.description
2558
- });
2559
- }
2560
- cache.set(resolved, merged);
2561
- return merged;
2562
- }
2563
- function allNonReservedPoolNames(config) {
2564
- const out = [];
2565
- for (const [name, def] of config) {
2566
- if (!def.reserved) out.push(name);
2567
- }
2568
- return out;
2569
- }
2570
- function extractUserPools(raw) {
2571
- if (!raw || typeof raw !== "object") return {};
2572
- const jobs2 = raw.jobs;
2573
- if (!jobs2 || typeof jobs2 !== "object") return {};
2574
- const pools = jobs2.pools;
2575
- if (!pools || typeof pools !== "object") return {};
2576
- const out = {};
2577
- for (const [name, def] of Object.entries(pools)) {
2578
- if (!def || typeof def !== "object") continue;
2579
- out[name] = def;
2580
- }
2581
- return out;
2582
- }
2583
-
2584
2974
  // runtime/subsystems/jobs/job-worker.ts
2585
- import { Inject as Inject10, Injectable as Injectable11, Logger as Logger5 } from "@nestjs/common";
2586
- import { and as and5, asc as asc2, desc as desc3, eq as eq5, inArray as inArray3, lt, lte, sql as sql7 } from "drizzle-orm";
2975
+ import { Inject as Inject11, Injectable as Injectable12, Logger as Logger6 } from "@nestjs/common";
2976
+ import { and as and5, asc as asc2, desc as desc3, eq as eq6, inArray as inArray3, lt as lt2, lte, sql as sql7 } from "drizzle-orm";
2587
2977
  var JOB_WORKER_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_OPTIONS");
2588
2978
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
2589
2979
  var DEFAULT_STALE_SWEEPER_INTERVAL_MS = 6e4;
@@ -2646,7 +3036,7 @@ var JobWorker = class {
2646
3036
  stepService;
2647
3037
  options;
2648
3038
  moduleRef;
2649
- logger = new Logger5(JobWorker.name);
3039
+ logger = new Logger6(JobWorker.name);
2650
3040
  shuttingDown = false;
2651
3041
  inFlight = /* @__PURE__ */ new Set();
2652
3042
  pollTimer = null;
@@ -2687,7 +3077,7 @@ var JobWorker = class {
2687
3077
  await this.drainInFlight();
2688
3078
  try {
2689
3079
  await this.db.update(jobRuns).set({ status: "pending", claimedAt: null, startedAt: null }).where(
2690
- and5(eq5(jobRuns.status, "running"), eq5(jobRuns.pool, this.options.pool))
3080
+ and5(eq6(jobRuns.status, "running"), eq6(jobRuns.pool, this.options.pool))
2691
3081
  );
2692
3082
  } catch (err) {
2693
3083
  this.logger.error(`shutdown reset failed: ${err.message}`);
@@ -2737,8 +3127,8 @@ var JobWorker = class {
2737
3127
  return this.db.transaction(async (tx) => {
2738
3128
  const candidates = await tx.select({ id: jobRuns.id }).from(jobRuns).where(
2739
3129
  and5(
2740
- eq5(jobRuns.status, "pending"),
2741
- eq5(jobRuns.pool, pool),
3130
+ eq6(jobRuns.status, "pending"),
3131
+ eq6(jobRuns.pool, pool),
2742
3132
  lte(jobRuns.runAt, /* @__PURE__ */ new Date())
2743
3133
  )
2744
3134
  ).orderBy(desc3(jobRuns.priority), asc2(jobRuns.runAt)).limit(1).for("update", { skipLocked: true });
@@ -2749,7 +3139,7 @@ var JobWorker = class {
2749
3139
  claimedAt: /* @__PURE__ */ new Date(),
2750
3140
  startedAt: /* @__PURE__ */ new Date(),
2751
3141
  updatedAt: /* @__PURE__ */ new Date()
2752
- }).where(eq5(jobRuns.id, candidate.id)).returning();
3142
+ }).where(eq6(jobRuns.id, candidate.id)).returning();
2753
3143
  return claimed ?? null;
2754
3144
  });
2755
3145
  }
@@ -2767,7 +3157,7 @@ var JobWorker = class {
2767
3157
  await this.db.transaction(async (tx) => {
2768
3158
  const threshold = new Date(Date.now() - this.staleThresholdMs);
2769
3159
  const stale = await tx.select({ id: jobRuns.id }).from(jobRuns).where(
2770
- and5(eq5(jobRuns.status, "running"), lt(jobRuns.claimedAt, threshold))
3160
+ and5(eq6(jobRuns.status, "running"), lt2(jobRuns.claimedAt, threshold))
2771
3161
  ).for("update", { skipLocked: true });
2772
3162
  if (stale.length === 0) return;
2773
3163
  const ids = stale.map((r) => r.id);
@@ -2800,8 +3190,8 @@ var JobWorker = class {
2800
3190
  if (claimed.concurrencyKey) {
2801
3191
  const inflight = await this.db.select({ id: jobRuns.id }).from(jobRuns).where(
2802
3192
  and5(
2803
- eq5(jobRuns.concurrencyKey, claimed.concurrencyKey),
2804
- eq5(jobRuns.status, "running")
3193
+ eq6(jobRuns.concurrencyKey, claimed.concurrencyKey),
3194
+ eq6(jobRuns.status, "running")
2805
3195
  )
2806
3196
  );
2807
3197
  const other = inflight.find((r) => r.id !== claimed.id);
@@ -2811,7 +3201,7 @@ var JobWorker = class {
2811
3201
  claimedAt: null,
2812
3202
  startedAt: null,
2813
3203
  updatedAt: /* @__PURE__ */ new Date()
2814
- }).where(eq5(jobRuns.id, claimed.id));
3204
+ }).where(eq6(jobRuns.id, claimed.id));
2815
3205
  return;
2816
3206
  }
2817
3207
  }
@@ -2826,7 +3216,7 @@ var JobWorker = class {
2826
3216
  run: claimed,
2827
3217
  step: this.makeStepFn(claimed),
2828
3218
  spawnChild: this.makeSpawnFn(claimed),
2829
- logger: new Logger5(`JobRun:${claimed.id}`)
3219
+ logger: new Logger6(`JobRun:${claimed.id}`)
2830
3220
  };
2831
3221
  const attemptsBefore = claimed.attempts ?? 0;
2832
3222
  try {
@@ -2837,7 +3227,7 @@ var JobWorker = class {
2837
3227
  finishedAt: /* @__PURE__ */ new Date(),
2838
3228
  updatedAt: /* @__PURE__ */ new Date(),
2839
3229
  attempts: attemptsBefore + 1
2840
- }).where(eq5(jobRuns.id, claimed.id));
3230
+ }).where(eq6(jobRuns.id, claimed.id));
2841
3231
  } catch (err) {
2842
3232
  const policy = meta.retry;
2843
3233
  const decision = classifyError2(err, policy, attemptsBefore);
@@ -2852,7 +3242,7 @@ var JobWorker = class {
2852
3242
  claimedAt: null,
2853
3243
  error: serialiseError2(err, nextAttempts, true),
2854
3244
  updatedAt: /* @__PURE__ */ new Date()
2855
- }).where(eq5(jobRuns.id, claimed.id));
3245
+ }).where(eq6(jobRuns.id, claimed.id));
2856
3246
  } else {
2857
3247
  await this.markFailed(claimed, err, nextAttempts);
2858
3248
  }
@@ -2865,7 +3255,7 @@ var JobWorker = class {
2865
3255
  finishedAt: /* @__PURE__ */ new Date(),
2866
3256
  error: serialiseError2(err, finalAttempts, false),
2867
3257
  updatedAt: /* @__PURE__ */ new Date()
2868
- }).where(eq5(jobRuns.id, claimed.id));
3258
+ }).where(eq6(jobRuns.id, claimed.id));
2869
3259
  if (claimed.parentClosePolicy === "terminate") {
2870
3260
  try {
2871
3261
  await this.orchestrator.cancel(claimed.id, {
@@ -2968,25 +3358,228 @@ var JobWorker = class {
2968
3358
  // ============================================================================
2969
3359
  };
2970
3360
  JobWorker = __decorateClass([
2971
- Injectable11(),
2972
- __decorateParam(0, Inject10(DRIZZLE)),
2973
- __decorateParam(1, Inject10(JOB_ORCHESTRATOR)),
2974
- __decorateParam(2, Inject10(JOB_RUN_SERVICE)),
2975
- __decorateParam(3, Inject10(JOB_STEP_SERVICE)),
2976
- __decorateParam(4, Inject10(JOB_WORKER_OPTIONS))
3361
+ Injectable12(),
3362
+ __decorateParam(0, Inject11(DRIZZLE)),
3363
+ __decorateParam(1, Inject11(JOB_ORCHESTRATOR)),
3364
+ __decorateParam(2, Inject11(JOB_RUN_SERVICE)),
3365
+ __decorateParam(3, Inject11(JOB_STEP_SERVICE)),
3366
+ __decorateParam(4, Inject11(JOB_WORKER_OPTIONS))
2977
3367
  ], JobWorker);
2978
3368
 
3369
+ // runtime/subsystems/jobs/job-worker.bullmq-backend.ts
3370
+ import { Logger as Logger7 } from "@nestjs/common";
3371
+ import { eq as eq7 } from "drizzle-orm";
3372
+ function serialiseError3(err, attempt, retryable) {
3373
+ const e = err;
3374
+ return {
3375
+ message: e?.message ?? String(err),
3376
+ stack: e?.stack,
3377
+ retryable,
3378
+ attempt
3379
+ };
3380
+ }
3381
+ var BullMQJobWorker = class _BullMQJobWorker {
3382
+ constructor(db, orchestrator, stepService, options, moduleRef) {
3383
+ this.db = db;
3384
+ this.orchestrator = orchestrator;
3385
+ this.stepService = stepService;
3386
+ this.options = options;
3387
+ this.moduleRef = moduleRef;
3388
+ }
3389
+ db;
3390
+ orchestrator;
3391
+ stepService;
3392
+ options;
3393
+ moduleRef;
3394
+ logger = new Logger7(_BullMQJobWorker.name);
3395
+ worker = null;
3396
+ async onModuleInit() {
3397
+ let WorkerCtor;
3398
+ try {
3399
+ const mod = await import("bullmq");
3400
+ WorkerCtor = mod.Worker;
3401
+ } catch {
3402
+ throw new Error(
3403
+ 'BullMQ backend requires the "bullmq" package. Install it with: npm install bullmq'
3404
+ );
3405
+ }
3406
+ this.worker = new WorkerCtor(
3407
+ this.options.queueName,
3408
+ (job) => this.process(job),
3409
+ {
3410
+ connection: this.options.connection,
3411
+ concurrency: this.options.concurrency
3412
+ }
3413
+ );
3414
+ this.worker.on("failed", (job, err) => {
3415
+ if (!job) return;
3416
+ const attemptsMade = job.attemptsMade;
3417
+ const maxAttempts = job.opts.attempts ?? 1;
3418
+ if (attemptsMade >= maxAttempts) {
3419
+ void this.markFailed(job.data.runId, err, attemptsMade);
3420
+ }
3421
+ });
3422
+ this.logger.log(
3423
+ `BullMQ worker started: pool='${this.options.pool}' queue='${this.options.queueName}' concurrency=${this.options.concurrency}`
3424
+ );
3425
+ }
3426
+ async onModuleDestroy() {
3427
+ if (this.worker) {
3428
+ await this.worker.close();
3429
+ this.worker = null;
3430
+ }
3431
+ }
3432
+ /**
3433
+ * Process one BullMQ job. Returns the handler output (stored by BullMQ as
3434
+ * the job return value AND written to `job_run.output`). Throws on handler
3435
+ * failure so BullMQ applies the retry policy.
3436
+ */
3437
+ async process(job) {
3438
+ const { runId } = job.data;
3439
+ const [row] = await this.db.select().from(jobRuns).where(eq7(jobRuns.id, runId)).limit(1);
3440
+ if (!row) {
3441
+ this.logger.warn(`process: job_run ${runId} not found; skipping`);
3442
+ return {};
3443
+ }
3444
+ const run = row;
3445
+ if (run.status === "canceled") {
3446
+ return {};
3447
+ }
3448
+ const registryEntry = JOB_HANDLER_REGISTRY.get(run.jobType);
3449
+ if (!registryEntry) {
3450
+ throw new Error(
3451
+ `No handler registered for jobType='${run.jobType}' (run ${run.id})`
3452
+ );
3453
+ }
3454
+ await this.db.update(jobRuns).set({
3455
+ status: "running",
3456
+ claimedAt: /* @__PURE__ */ new Date(),
3457
+ startedAt: /* @__PURE__ */ new Date(),
3458
+ attempts: job.attemptsMade + 1,
3459
+ updatedAt: /* @__PURE__ */ new Date()
3460
+ }).where(eq7(jobRuns.id, run.id));
3461
+ const HandlerClass = registryEntry.handlerClass;
3462
+ const handler = this.moduleRef.get(
3463
+ HandlerClass,
3464
+ { strict: false }
3465
+ );
3466
+ const ctx = {
3467
+ input: run.input,
3468
+ run,
3469
+ step: this.makeStepFn(run),
3470
+ spawnChild: this.makeSpawnFn(run),
3471
+ logger: new Logger7(`JobRun:${run.id}`)
3472
+ };
3473
+ const output = await handler.run(ctx);
3474
+ await this.db.update(jobRuns).set({
3475
+ status: "completed",
3476
+ output: output ?? {},
3477
+ finishedAt: /* @__PURE__ */ new Date(),
3478
+ updatedAt: /* @__PURE__ */ new Date()
3479
+ }).where(eq7(jobRuns.id, run.id));
3480
+ return output ?? {};
3481
+ }
3482
+ async markFailed(runId, err, finalAttempts) {
3483
+ const [row] = await this.db.select().from(jobRuns).where(eq7(jobRuns.id, runId)).limit(1);
3484
+ if (!row) return;
3485
+ const run = row;
3486
+ await this.db.update(jobRuns).set({
3487
+ status: "failed",
3488
+ attempts: finalAttempts,
3489
+ finishedAt: /* @__PURE__ */ new Date(),
3490
+ error: serialiseError3(err, finalAttempts, false),
3491
+ updatedAt: /* @__PURE__ */ new Date()
3492
+ }).where(eq7(jobRuns.id, runId));
3493
+ if (run.parentClosePolicy === "terminate") {
3494
+ try {
3495
+ await this.orchestrator.cancel(run.id, {
3496
+ cascade: true,
3497
+ reason: "parent-failed",
3498
+ tenantId: run.tenantId
3499
+ });
3500
+ } catch (cascadeErr) {
3501
+ this.logger.warn(
3502
+ `cascade on failed run ${run.id}: ${cascadeErr.message}`
3503
+ );
3504
+ }
3505
+ }
3506
+ }
3507
+ // ── ctx.step / ctx.spawnChild (mirror JobWorker) ──────────────────────────
3508
+ makeStepFn(run) {
3509
+ return async (stepId, fn, _opts) => {
3510
+ void _opts;
3511
+ const existing = await this.stepService.findStep(run.id, stepId);
3512
+ if (existing?.status === "completed") {
3513
+ return existing.output;
3514
+ }
3515
+ const nextAttempts = (existing?.attempts ?? 0) + 1;
3516
+ const seq = nextAttempts;
3517
+ await this.stepService.recordStep({
3518
+ jobRunId: run.id,
3519
+ stepId,
3520
+ kind: "task",
3521
+ seq,
3522
+ status: "running",
3523
+ startedAt: /* @__PURE__ */ new Date(),
3524
+ attempts: nextAttempts
3525
+ });
3526
+ try {
3527
+ const output = await fn();
3528
+ await this.stepService.recordStep({
3529
+ jobRunId: run.id,
3530
+ stepId,
3531
+ kind: "task",
3532
+ seq,
3533
+ status: "completed",
3534
+ output,
3535
+ finishedAt: /* @__PURE__ */ new Date(),
3536
+ attempts: nextAttempts
3537
+ });
3538
+ return output;
3539
+ } catch (err) {
3540
+ await this.stepService.recordStep({
3541
+ jobRunId: run.id,
3542
+ stepId,
3543
+ kind: "task",
3544
+ seq,
3545
+ status: "failed",
3546
+ error: serialiseError3(err, nextAttempts, false),
3547
+ finishedAt: /* @__PURE__ */ new Date(),
3548
+ attempts: nextAttempts
3549
+ });
3550
+ throw err;
3551
+ }
3552
+ };
3553
+ }
3554
+ makeSpawnFn(run) {
3555
+ return async (type, input, opts) => {
3556
+ return this.orchestrator.start(type, input, {
3557
+ parentRunId: run.id,
3558
+ parentClosePolicy: opts?.closePolicy,
3559
+ runAt: opts?.runAt,
3560
+ priority: opts?.priority,
3561
+ tags: opts?.tags,
3562
+ triggerSource: "parent",
3563
+ triggerRef: run.id,
3564
+ tenantId: run.tenantId
3565
+ });
3566
+ };
3567
+ }
3568
+ };
3569
+
2979
3570
  // runtime/subsystems/jobs/job-worker.module.ts
2980
3571
  var DEFAULT_SHUTDOWN_TIMEOUT_MS2 = 3e4;
2981
3572
  var JOB_WORKER_MODULE_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_MODULE_OPTIONS");
2982
3573
  var JobWorkerOrchestrator = class {
2983
- constructor(orchestrator, runService, stepService, options, db = null, moduleRef) {
3574
+ constructor(orchestrator, runService, stepService, options, db = null, moduleRef, bullConnection = null, bullConfig = null) {
2984
3575
  this.orchestrator = orchestrator;
2985
3576
  this.runService = runService;
2986
3577
  this.stepService = stepService;
2987
3578
  this.options = options;
2988
3579
  this.db = db;
2989
3580
  this.moduleRef = moduleRef;
3581
+ this.bullConnection = bullConnection;
3582
+ this.bullConfig = bullConfig;
2990
3583
  }
2991
3584
  orchestrator;
2992
3585
  runService;
@@ -2994,7 +3587,9 @@ var JobWorkerOrchestrator = class {
2994
3587
  options;
2995
3588
  db;
2996
3589
  moduleRef;
2997
- logger = new Logger6(JobWorkerOrchestrator.name);
3590
+ bullConnection;
3591
+ bullConfig;
3592
+ logger = new Logger8(JobWorkerOrchestrator.name);
2998
3593
  workers = [];
2999
3594
  // ============================================================================
3000
3595
  // Lifecycle
@@ -3011,7 +3606,7 @@ var JobWorkerOrchestrator = class {
3011
3606
  if (backend !== "memory" && orphaned.length > 0) {
3012
3607
  throw new BootValidationError(orphaned);
3013
3608
  }
3014
- const activePools = this.options.pools ?? allNonReservedPoolNames(poolConfig);
3609
+ const activePools = this.options.pools ? this.options.pools : this.options.allPools ? allPoolNames(poolConfig) : allNonReservedPoolNames(poolConfig);
3015
3610
  for (const poolName of activePools) {
3016
3611
  const def = poolConfig.get(poolName);
3017
3612
  if (!def) {
@@ -3024,11 +3619,11 @@ var JobWorkerOrchestrator = class {
3024
3619
  concurrency: def.concurrency,
3025
3620
  shutdownTimeoutMs: this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS2
3026
3621
  };
3027
- const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : this.spawnWorker(workerOptions);
3028
- worker.onModuleInit();
3622
+ const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : backend === "bullmq" ? this.spawnBullMQWorker(poolName, def.queue, def.concurrency, poolConfig) : this.spawnWorker(workerOptions);
3623
+ await worker.onModuleInit();
3029
3624
  this.workers.push(worker);
3030
3625
  this.logger.log(
3031
- `JobWorker started: pool='${poolName}' (queue='${def.queue}') concurrency=${def.concurrency}`
3626
+ `JobWorker started: pool='${poolName}' (queue='${def.queue}') concurrency=${def.concurrency} backend='${backend}'`
3032
3627
  );
3033
3628
  }
3034
3629
  }
@@ -3045,6 +3640,16 @@ var JobWorkerOrchestrator = class {
3045
3640
  }
3046
3641
  }
3047
3642
  this.workers.length = 0;
3643
+ const orch = this.orchestrator;
3644
+ if (typeof orch.closeConnections === "function") {
3645
+ try {
3646
+ await orch.closeConnections();
3647
+ } catch (err) {
3648
+ this.logger.error(
3649
+ `BullMQ orchestrator connection close failed: ${err.message}`
3650
+ );
3651
+ }
3652
+ }
3048
3653
  }
3049
3654
  // ============================================================================
3050
3655
  // Internals
@@ -3106,15 +3711,57 @@ var JobWorkerOrchestrator = class {
3106
3711
  this.moduleRef
3107
3712
  );
3108
3713
  }
3714
+ /**
3715
+ * BULLMQ-1 — spawn a per-pool `BullMQJobWorker`. Requires the Drizzle
3716
+ * client (the worker drives `job_run` as the source of truth) AND the
3717
+ * resolved BullMQ connection (bound by `JobsDomainModule` when
3718
+ * `backend: 'bullmq'`). The queue name is derived identically to the
3719
+ * orchestrator's `dispatch` via `resolvePoolQueueName(pool, …)` so producer
3720
+ * and consumer agree.
3721
+ */
3722
+ spawnBullMQWorker(pool, _queueAlias, concurrency, poolConfig) {
3723
+ if (!this.db) {
3724
+ throw new Error(
3725
+ `JobWorkerModule: BullMQ worker spawning requires the Drizzle client (no DRIZZLE provider available) \u2014 job_run remains the source of truth.`
3726
+ );
3727
+ }
3728
+ if (!this.bullConnection) {
3729
+ throw new Error(
3730
+ `JobWorkerModule: BullMQ worker spawning requires a resolved BULLMQ_CONNECTION. Ensure JobsDomainModule was booted with backend: 'bullmq'.`
3731
+ );
3732
+ }
3733
+ if (!this.moduleRef) {
3734
+ throw new Error(
3735
+ `JobWorkerModule: ModuleRef not available \u2014 cannot construct BullMQJobWorker with handler DI support.`
3736
+ );
3737
+ }
3738
+ const queueName = resolvePoolQueueName(pool, this.bullConfig, poolConfig);
3739
+ return new BullMQJobWorker(
3740
+ this.db,
3741
+ this.orchestrator,
3742
+ this.stepService,
3743
+ {
3744
+ pool,
3745
+ queueName,
3746
+ concurrency,
3747
+ connection: this.bullConnection
3748
+ },
3749
+ this.moduleRef
3750
+ );
3751
+ }
3109
3752
  };
3110
3753
  JobWorkerOrchestrator = __decorateClass([
3111
- Injectable12(),
3112
- __decorateParam(0, Inject11(JOB_ORCHESTRATOR)),
3113
- __decorateParam(1, Inject11(JOB_RUN_SERVICE)),
3114
- __decorateParam(2, Inject11(JOB_STEP_SERVICE)),
3115
- __decorateParam(3, Inject11(JOB_WORKER_MODULE_OPTIONS)),
3116
- __decorateParam(4, Optional6()),
3117
- __decorateParam(4, Inject11(DRIZZLE))
3754
+ Injectable13(),
3755
+ __decorateParam(0, Inject12(JOB_ORCHESTRATOR)),
3756
+ __decorateParam(1, Inject12(JOB_RUN_SERVICE)),
3757
+ __decorateParam(2, Inject12(JOB_STEP_SERVICE)),
3758
+ __decorateParam(3, Inject12(JOB_WORKER_MODULE_OPTIONS)),
3759
+ __decorateParam(4, Optional7()),
3760
+ __decorateParam(4, Inject12(DRIZZLE)),
3761
+ __decorateParam(6, Optional7()),
3762
+ __decorateParam(6, Inject12(BULLMQ_CONNECTION)),
3763
+ __decorateParam(7, Optional7()),
3764
+ __decorateParam(7, Inject12(BULLMQ_RESOLVED_CONFIG))
3118
3765
  ], JobWorkerOrchestrator);
3119
3766
  var JobWorkerModule = class {
3120
3767
  static forRoot(opts) {
@@ -3131,7 +3778,14 @@ var JobWorkerModule = class {
3131
3778
  { provide: JOB_WORKER_MODULE_OPTIONS, useValue: opts },
3132
3779
  JobWorkerOrchestrator
3133
3780
  ],
3134
- exports: []
3781
+ // BULLMQ-1 Phase 1 — export the options token so `BridgeModule`'s
3782
+ // reserved-pool guard (`onModuleInit`) can actually inject it.
3783
+ // Previously `exports: []` left the `@Optional()` inject resolving to
3784
+ // `undefined` and the guard silently no-opped (a dead check). With the
3785
+ // token exported the guard fires for real; consumers that omit the
3786
+ // reserved pools (and don't set `allPools`) now fail fast with
3787
+ // `BridgeReservedPoolsNotPolledError` — which is correct.
3788
+ exports: [JOB_WORKER_MODULE_OPTIONS]
3135
3789
  };
3136
3790
  }
3137
3791
  };
@@ -3195,6 +3849,7 @@ var BridgeModule = class {
3195
3849
  }
3196
3850
  async onModuleInit() {
3197
3851
  if (!this.workerOpts) return;
3852
+ if (this.workerOpts.allPools) return;
3198
3853
  const activePools = this.workerOpts.pools ?? [];
3199
3854
  const missing = BRIDGE_RESERVED_POOLS.filter(
3200
3855
  (p) => !activePools.includes(p)
@@ -3206,8 +3861,8 @@ var BridgeModule = class {
3206
3861
  };
3207
3862
  BridgeModule = __decorateClass([
3208
3863
  Module3({}),
3209
- __decorateParam(0, Optional7()),
3210
- __decorateParam(0, Inject12(JOB_WORKER_MODULE_OPTIONS))
3864
+ __decorateParam(0, Optional8()),
3865
+ __decorateParam(0, Inject13(JOB_WORKER_MODULE_OPTIONS))
3211
3866
  ], BridgeModule);
3212
3867
  export {
3213
3868
  BRIDGE_DELIVERY_JOB_TYPE,