@pattern-stack/codegen 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.d.ts +3 -0
- package/dist/runtime/subsystems/bridge/bridge.module.js +930 -275
- package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
- package/dist/runtime/subsystems/bridge/event-flow.service.js.map +1 -1
- package/dist/runtime/subsystems/bridge/index.d.ts +3 -0
- package/dist/runtime/subsystems/bridge/index.js +837 -182
- package/dist/runtime/subsystems/bridge/index.js.map +1 -1
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +3 -1
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +92 -1
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +3 -1
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js +99 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -1
- package/dist/runtime/subsystems/events/event-keyset-cursor.d.ts +32 -0
- package/dist/runtime/subsystems/events/event-keyset-cursor.js +38 -0
- package/dist/runtime/subsystems/events/event-keyset-cursor.js.map +1 -0
- package/dist/runtime/subsystems/events/event-read.protocol.d.ts +94 -0
- package/dist/runtime/subsystems/events/event-read.protocol.js +9 -0
- package/dist/runtime/subsystems/events/event-read.protocol.js.map +1 -0
- package/dist/runtime/subsystems/events/events.module.js +177 -3
- package/dist/runtime/subsystems/events/events.module.js.map +1 -1
- package/dist/runtime/subsystems/events/events.tokens.d.ts +16 -1
- package/dist/runtime/subsystems/events/events.tokens.js +2 -0
- package/dist/runtime/subsystems/events/events.tokens.js.map +1 -1
- package/dist/runtime/subsystems/events/generated/bus.js.map +1 -1
- package/dist/runtime/subsystems/events/generated/index.js.map +1 -1
- package/dist/runtime/subsystems/events/index.d.ts +2 -1
- package/dist/runtime/subsystems/events/index.js +178 -3
- package/dist/runtime/subsystems/events/index.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +1 -0
- package/dist/runtime/subsystems/index.js +1194 -264
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +98 -0
- package/dist/runtime/subsystems/jobs/bullmq.config.js +143 -0
- package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -0
- package/dist/runtime/subsystems/jobs/index.d.ts +6 -2
- package/dist/runtime/subsystems/jobs/index.js +861 -201
- package/dist/runtime/subsystems/jobs/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +107 -0
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +922 -0
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +52 -0
- package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js +57 -0
- package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +2 -1
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +81 -1
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +2 -1
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +81 -0
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +74 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +48 -0
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +374 -0
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +42 -4
- package/dist/runtime/subsystems/jobs/job-worker.module.js +832 -178
- package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +10 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +519 -20
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/pool-config.loader.d.ts +9 -1
- package/dist/runtime/subsystems/jobs/pool-config.loader.js +4 -0
- package/dist/runtime/subsystems/jobs/pool-config.loader.js.map +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +4 -3
- package/dist/runtime/subsystems/observability/index.js +109 -2
- package/dist/runtime/subsystems/observability/index.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.module.js +109 -2
- package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +63 -2
- package/dist/runtime/subsystems/observability/observability.service.d.ts +21 -3
- package/dist/runtime/subsystems/observability/observability.service.js +109 -2
- package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +1 -0
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +1 -0
- package/dist/src/cli/index.js +30 -6
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/subsystems/bridge/bridge.module.ts +5 -0
- package/runtime/subsystems/events/event-bus.drizzle-backend.ts +109 -3
- package/runtime/subsystems/events/event-bus.memory-backend.ts +103 -1
- package/runtime/subsystems/events/event-keyset-cursor.ts +59 -0
- package/runtime/subsystems/events/event-read.protocol.ts +97 -0
- package/runtime/subsystems/events/events.module.ts +18 -2
- package/runtime/subsystems/events/events.tokens.ts +16 -0
- package/runtime/subsystems/events/index.ts +7 -0
- package/runtime/subsystems/jobs/bullmq.config.ts +125 -0
- package/runtime/subsystems/jobs/index.ts +22 -0
- package/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts +381 -0
- package/runtime/subsystems/jobs/job-run-keyset-cursor.ts +88 -0
- package/runtime/subsystems/jobs/job-run-service.drizzle-backend.ts +59 -1
- package/runtime/subsystems/jobs/job-run-service.memory-backend.ts +53 -0
- package/runtime/subsystems/jobs/job-run-service.protocol.ts +77 -0
- package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +311 -0
- package/runtime/subsystems/jobs/job-worker.module.ts +124 -10
- package/runtime/subsystems/jobs/jobs-domain.module.ts +40 -21
- package/runtime/subsystems/jobs/pool-config.loader.ts +11 -0
- package/runtime/subsystems/observability/index.ts +8 -0
- package/runtime/subsystems/observability/observability.protocol.ts +76 -0
- package/runtime/subsystems/observability/observability.service.ts +148 -1
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +14 -12
- package/templates/relationship/new/prompt.js +8 -5
- package/templates/subsystem/jobs/worker.ejs.t +30 -7
- package/templates/subsystem/sync/sync-audit.schema.ejs.t +12 -16
|
@@ -12,18 +12,18 @@ var __decorateParam = (index4, decorator) => (target, key2) => decorator(target,
|
|
|
12
12
|
|
|
13
13
|
// runtime/subsystems/bridge/bridge.module.ts
|
|
14
14
|
import {
|
|
15
|
-
Inject as
|
|
15
|
+
Inject as Inject13,
|
|
16
16
|
Module as Module3,
|
|
17
|
-
Optional as
|
|
17
|
+
Optional as Optional8
|
|
18
18
|
} from "@nestjs/common";
|
|
19
19
|
|
|
20
20
|
// runtime/subsystems/jobs/job-worker.module.ts
|
|
21
21
|
import {
|
|
22
|
-
Inject as
|
|
23
|
-
Injectable as
|
|
24
|
-
Logger as
|
|
22
|
+
Inject as Inject8,
|
|
23
|
+
Injectable as Injectable9,
|
|
24
|
+
Logger as Logger6,
|
|
25
25
|
Module as Module2,
|
|
26
|
-
Optional as
|
|
26
|
+
Optional as Optional3
|
|
27
27
|
} from "@nestjs/common";
|
|
28
28
|
|
|
29
29
|
// runtime/constants/tokens.ts
|
|
@@ -642,7 +642,58 @@ function notInStatus(statuses) {
|
|
|
642
642
|
|
|
643
643
|
// runtime/subsystems/jobs/job-run-service.drizzle-backend.ts
|
|
644
644
|
import { Inject as Inject2, Injectable as Injectable2 } from "@nestjs/common";
|
|
645
|
-
import { and as and2, asc, desc as desc2, eq as eq2, inArray as inArray2, isNull, sql as sql3 } from "drizzle-orm";
|
|
645
|
+
import { and as and2, asc, desc as desc2, eq as eq2, gte, inArray as inArray2, isNull, lt, or, sql as sql3 } from "drizzle-orm";
|
|
646
|
+
|
|
647
|
+
// runtime/subsystems/jobs/job-run-keyset-cursor.ts
|
|
648
|
+
var DEFAULT_LIST_LIMIT = 50;
|
|
649
|
+
var MAX_LIST_LIMIT = 200;
|
|
650
|
+
function clampLimit(limit) {
|
|
651
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
652
|
+
return DEFAULT_LIST_LIMIT;
|
|
653
|
+
}
|
|
654
|
+
const floored = Math.floor(limit);
|
|
655
|
+
if (floored < 1) return 1;
|
|
656
|
+
if (floored > MAX_LIST_LIMIT) return MAX_LIST_LIMIT;
|
|
657
|
+
return floored;
|
|
658
|
+
}
|
|
659
|
+
function encodeKeysetCursor(keyset) {
|
|
660
|
+
const tuple = [keyset.createdAt.toISOString(), keyset.id];
|
|
661
|
+
return Buffer.from(JSON.stringify(tuple), "utf8").toString("base64url");
|
|
662
|
+
}
|
|
663
|
+
function decodeKeysetCursor(cursor) {
|
|
664
|
+
try {
|
|
665
|
+
const json = Buffer.from(cursor, "base64url").toString("utf8");
|
|
666
|
+
const parsed = JSON.parse(json);
|
|
667
|
+
if (!Array.isArray(parsed) || parsed.length !== 2) return null;
|
|
668
|
+
const [iso, id] = parsed;
|
|
669
|
+
if (typeof iso !== "string" || typeof id !== "string") return null;
|
|
670
|
+
const createdAt = new Date(iso);
|
|
671
|
+
if (Number.isNaN(createdAt.getTime())) return null;
|
|
672
|
+
return { createdAt, id };
|
|
673
|
+
} catch {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function toJobRunSummary(r) {
|
|
678
|
+
return {
|
|
679
|
+
runId: r.id,
|
|
680
|
+
rootRunId: r.rootRunId,
|
|
681
|
+
jobType: r.jobType,
|
|
682
|
+
pool: r.pool,
|
|
683
|
+
status: r.status,
|
|
684
|
+
scopeEntityType: r.scopeEntityType,
|
|
685
|
+
scopeEntityId: r.scopeEntityId,
|
|
686
|
+
tenantId: r.tenantId,
|
|
687
|
+
attempts: r.attempts,
|
|
688
|
+
errorMessage: r.error?.message ?? null,
|
|
689
|
+
runAt: r.runAt,
|
|
690
|
+
startedAt: r.startedAt,
|
|
691
|
+
finishedAt: r.finishedAt,
|
|
692
|
+
createdAt: r.createdAt
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// runtime/subsystems/jobs/job-run-service.drizzle-backend.ts
|
|
646
697
|
var NON_TERMINAL_STATUSES = [
|
|
647
698
|
"pending",
|
|
648
699
|
"running",
|
|
@@ -765,6 +816,37 @@ var DrizzleJobRunService = class {
|
|
|
765
816
|
createdAt: r.createdAt
|
|
766
817
|
}));
|
|
767
818
|
}
|
|
819
|
+
async listJobRuns(query = {}) {
|
|
820
|
+
const limit = clampLimit(query.limit);
|
|
821
|
+
const conditions = [];
|
|
822
|
+
const tenantCond = this.tenantCondition("listJobRuns", query.tenantId);
|
|
823
|
+
if (tenantCond) conditions.push(tenantCond);
|
|
824
|
+
if (query.poolId) conditions.push(eq2(jobRuns.pool, query.poolId));
|
|
825
|
+
if (query.rootRunId) conditions.push(eq2(jobRuns.rootRunId, query.rootRunId));
|
|
826
|
+
if (query.status) conditions.push(eq2(jobRuns.status, query.status));
|
|
827
|
+
if (query.since) conditions.push(gte(jobRuns.createdAt, query.since));
|
|
828
|
+
if (query.cursor) {
|
|
829
|
+
const keyset = decodeKeysetCursor(query.cursor);
|
|
830
|
+
if (keyset) {
|
|
831
|
+
conditions.push(
|
|
832
|
+
or(
|
|
833
|
+
lt(jobRuns.createdAt, keyset.createdAt),
|
|
834
|
+
and2(
|
|
835
|
+
eq2(jobRuns.createdAt, keyset.createdAt),
|
|
836
|
+
lt(jobRuns.id, keyset.id)
|
|
837
|
+
)
|
|
838
|
+
)
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
const rows = await this.db.select().from(jobRuns).where(conditions.length > 0 ? and2(...conditions) : void 0).orderBy(desc2(jobRuns.createdAt), desc2(jobRuns.id)).limit(limit + 1);
|
|
843
|
+
const hasMore = rows.length > limit;
|
|
844
|
+
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
845
|
+
const items = page.map(toJobRunSummary);
|
|
846
|
+
const last = page[page.length - 1];
|
|
847
|
+
const nextCursor = hasMore && last ? encodeKeysetCursor({ createdAt: last.createdAt, id: last.id }) : null;
|
|
848
|
+
return { items, nextCursor };
|
|
849
|
+
}
|
|
768
850
|
/**
|
|
769
851
|
* Internal helper used by cascade paths (not on the public protocol).
|
|
770
852
|
* Exposed as a public method on the concrete class so infrastructure
|
|
@@ -826,9 +908,394 @@ DrizzleJobStepService = __decorateClass([
|
|
|
826
908
|
__decorateParam(0, Inject3(DRIZZLE))
|
|
827
909
|
], DrizzleJobStepService);
|
|
828
910
|
|
|
911
|
+
// runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts
|
|
912
|
+
import { createHash } from "crypto";
|
|
913
|
+
import { Inject as Inject4, Injectable as Injectable4, Logger as Logger2, Optional } from "@nestjs/common";
|
|
914
|
+
import { eq as eq4 } from "drizzle-orm";
|
|
915
|
+
|
|
916
|
+
// runtime/subsystems/jobs/pool-config.loader.ts
|
|
917
|
+
import { existsSync, readFileSync } from "fs";
|
|
918
|
+
import { resolve } from "path";
|
|
919
|
+
import { parse as parseYaml } from "yaml";
|
|
920
|
+
var FRAMEWORK_POOLS = Object.freeze({
|
|
921
|
+
events_inbound: Object.freeze({
|
|
922
|
+
queue: "jobs-events-inbound",
|
|
923
|
+
concurrency: 20,
|
|
924
|
+
reserved: true,
|
|
925
|
+
description: "Inbound events drain (events subsystem outbox)."
|
|
926
|
+
}),
|
|
927
|
+
events_change: Object.freeze({
|
|
928
|
+
queue: "jobs-events-change",
|
|
929
|
+
concurrency: 30,
|
|
930
|
+
reserved: true,
|
|
931
|
+
description: "Change events drain (events subsystem outbox)."
|
|
932
|
+
}),
|
|
933
|
+
events_outbound: Object.freeze({
|
|
934
|
+
queue: "jobs-events-outbound",
|
|
935
|
+
concurrency: 10,
|
|
936
|
+
reserved: true,
|
|
937
|
+
description: "Outbound events drain (events subsystem outbox)."
|
|
938
|
+
}),
|
|
939
|
+
interactive: Object.freeze({
|
|
940
|
+
queue: "jobs-interactive",
|
|
941
|
+
concurrency: 20,
|
|
942
|
+
reserved: false,
|
|
943
|
+
description: "User-facing latency-sensitive jobs."
|
|
944
|
+
}),
|
|
945
|
+
batch: Object.freeze({
|
|
946
|
+
queue: "jobs-batch",
|
|
947
|
+
concurrency: 5,
|
|
948
|
+
reserved: false,
|
|
949
|
+
description: "Default pool for background jobs."
|
|
950
|
+
})
|
|
951
|
+
});
|
|
952
|
+
var RESERVED_POOL_NAMES = new Set(
|
|
953
|
+
Object.entries(FRAMEWORK_POOLS).filter(([, def]) => def.reserved).map(([name]) => name)
|
|
954
|
+
);
|
|
955
|
+
var cache = /* @__PURE__ */ new Map();
|
|
956
|
+
function loadPoolConfig(configPath) {
|
|
957
|
+
const resolved = resolve(configPath ?? `${process.cwd()}/codegen.config.yaml`);
|
|
958
|
+
const cached = cache.get(resolved);
|
|
959
|
+
if (cached) return cached;
|
|
960
|
+
const merged = /* @__PURE__ */ new Map();
|
|
961
|
+
for (const [name, def] of Object.entries(FRAMEWORK_POOLS)) {
|
|
962
|
+
merged.set(name, { ...def });
|
|
963
|
+
}
|
|
964
|
+
if (!existsSync(resolved)) {
|
|
965
|
+
cache.set(resolved, merged);
|
|
966
|
+
return merged;
|
|
967
|
+
}
|
|
968
|
+
let raw;
|
|
969
|
+
try {
|
|
970
|
+
raw = parseYaml(readFileSync(resolved, "utf8"));
|
|
971
|
+
} catch (err) {
|
|
972
|
+
throw new Error(
|
|
973
|
+
`pool-config.loader: failed to parse YAML at ${resolved}: ${err.message}`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
const userPools = extractUserPools(raw);
|
|
977
|
+
for (const [name, userDef] of Object.entries(userPools)) {
|
|
978
|
+
const existing = merged.get(name);
|
|
979
|
+
if (existing) {
|
|
980
|
+
const next = {
|
|
981
|
+
queue: existing.queue,
|
|
982
|
+
concurrency: typeof userDef.concurrency === "number" ? userDef.concurrency : existing.concurrency,
|
|
983
|
+
reserved: existing.reserved,
|
|
984
|
+
description: userDef.description ?? existing.description
|
|
985
|
+
};
|
|
986
|
+
merged.set(name, next);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (typeof userDef.queue !== "string" || userDef.queue.length === 0) {
|
|
990
|
+
throw new Error(
|
|
991
|
+
`pool-config.loader: pool '${name}' must declare a non-empty 'queue'.`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
if (typeof userDef.concurrency !== "number" || userDef.concurrency <= 0) {
|
|
995
|
+
throw new Error(
|
|
996
|
+
`pool-config.loader: pool '${name}' must declare a positive 'concurrency'.`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
if (userDef.reserved === true) {
|
|
1000
|
+
throw new Error(
|
|
1001
|
+
`pool-config.loader: user-defined pool '${name}' cannot set 'reserved: true' \u2014 reserved is framework-only.`
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
merged.set(name, {
|
|
1005
|
+
queue: userDef.queue,
|
|
1006
|
+
concurrency: userDef.concurrency,
|
|
1007
|
+
reserved: false,
|
|
1008
|
+
description: userDef.description
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
cache.set(resolved, merged);
|
|
1012
|
+
return merged;
|
|
1013
|
+
}
|
|
1014
|
+
function allNonReservedPoolNames(config) {
|
|
1015
|
+
const out = [];
|
|
1016
|
+
for (const [name, def] of config) {
|
|
1017
|
+
if (!def.reserved) out.push(name);
|
|
1018
|
+
}
|
|
1019
|
+
return out;
|
|
1020
|
+
}
|
|
1021
|
+
function allPoolNames(config) {
|
|
1022
|
+
return [...config.keys()];
|
|
1023
|
+
}
|
|
1024
|
+
function extractUserPools(raw) {
|
|
1025
|
+
if (!raw || typeof raw !== "object") return {};
|
|
1026
|
+
const jobs2 = raw.jobs;
|
|
1027
|
+
if (!jobs2 || typeof jobs2 !== "object") return {};
|
|
1028
|
+
const pools = jobs2.pools;
|
|
1029
|
+
if (!pools || typeof pools !== "object") return {};
|
|
1030
|
+
const out = {};
|
|
1031
|
+
for (const [name, def] of Object.entries(pools)) {
|
|
1032
|
+
if (!def || typeof def !== "object") continue;
|
|
1033
|
+
out[name] = def;
|
|
1034
|
+
}
|
|
1035
|
+
return out;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// runtime/subsystems/jobs/bullmq.config.ts
|
|
1039
|
+
var BULLMQ_CONNECTION = /* @__PURE__ */ Symbol("BULLMQ_CONNECTION");
|
|
1040
|
+
var BULLMQ_RESOLVED_CONFIG = /* @__PURE__ */ Symbol("BULLMQ_RESOLVED_CONFIG");
|
|
1041
|
+
var DEFAULT_REDIS_URL = "redis://localhost:6379";
|
|
1042
|
+
var DEFAULT_BULL_BOARD_MOUNT = "/admin/queues";
|
|
1043
|
+
function resolveBullMqConfig(ext) {
|
|
1044
|
+
const url = ext?.redis_url ?? process.env.REDIS_URL ?? DEFAULT_REDIS_URL;
|
|
1045
|
+
const resolved = {
|
|
1046
|
+
connection: { url },
|
|
1047
|
+
queuePrefix: ext?.queue_prefix
|
|
1048
|
+
};
|
|
1049
|
+
if (ext?.bull_board?.enabled) {
|
|
1050
|
+
resolved.bullBoard = {
|
|
1051
|
+
enabled: true,
|
|
1052
|
+
mountPath: ext.bull_board.mount_path ?? DEFAULT_BULL_BOARD_MOUNT
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
return resolved;
|
|
1056
|
+
}
|
|
1057
|
+
function resolvePoolQueueName(pool, config, poolConfig = loadPoolConfig()) {
|
|
1058
|
+
const alias = poolConfig.get(pool)?.queue ?? pool;
|
|
1059
|
+
const prefix = config?.queuePrefix;
|
|
1060
|
+
return prefix ? `${prefix}:${alias}` : alias;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts
|
|
1064
|
+
function sha1JobId(idempotencyKey) {
|
|
1065
|
+
return createHash("sha1").update(idempotencyKey).digest("hex");
|
|
1066
|
+
}
|
|
1067
|
+
var BullMQJobOrchestrator = class extends DrizzleJobOrchestrator {
|
|
1068
|
+
constructor(db, multiTenant, connection, bullConfig = null) {
|
|
1069
|
+
super(db, multiTenant);
|
|
1070
|
+
this.connection = connection;
|
|
1071
|
+
this.bullConfig = bullConfig;
|
|
1072
|
+
this.bullDb = db;
|
|
1073
|
+
}
|
|
1074
|
+
connection;
|
|
1075
|
+
bullConfig;
|
|
1076
|
+
// TODO(logging-subsystem): swap to ILogger once ADR-028 lands
|
|
1077
|
+
bullLogger = new Logger2(BullMQJobOrchestrator.name);
|
|
1078
|
+
/** Lazily-opened `Queue` handles, one per pool. */
|
|
1079
|
+
queues = /* @__PURE__ */ new Map();
|
|
1080
|
+
/** Single FlowProducer for parent/child hierarchies. Lazily opened. */
|
|
1081
|
+
_flow = null;
|
|
1082
|
+
/**
|
|
1083
|
+
* Cached `bullmq` value constructors, populated by `loadBullMq()` on first
|
|
1084
|
+
* use (the `start`/`cancel`/`replay` entrypoints `await` it before touching
|
|
1085
|
+
* a queue). Kept off the import graph so a `drizzle`-only consumer never
|
|
1086
|
+
* resolves the optional `'bullmq'` package.
|
|
1087
|
+
*/
|
|
1088
|
+
QueueCtor = null;
|
|
1089
|
+
FlowProducerCtor = null;
|
|
1090
|
+
bullMqLoad = null;
|
|
1091
|
+
/**
|
|
1092
|
+
* Own reference to the Drizzle client. `DrizzleJobOrchestrator.db` is
|
|
1093
|
+
* `private` (can't be redeclared even privately in a subclass), and the
|
|
1094
|
+
* spec forbids touching that file — so the subclass keeps its own handle
|
|
1095
|
+
* under a distinct name (same instance, passed through to `super`) for the
|
|
1096
|
+
* cancel-cascade snapshot + definition/run loads below.
|
|
1097
|
+
*/
|
|
1098
|
+
bullDb;
|
|
1099
|
+
/**
|
|
1100
|
+
* Lazily load the optional `bullmq` package and cache its value
|
|
1101
|
+
* constructors. Idempotent (single in-flight promise). Throws a friendly,
|
|
1102
|
+
* actionable error when the consumer selected `backend: 'bullmq'` but did
|
|
1103
|
+
* not install the package — mirrors `createRedisClient` in the redis event
|
|
1104
|
+
* backend. Must be `await`ed before any `queueFor`/`flow` access.
|
|
1105
|
+
*/
|
|
1106
|
+
async loadBullMq() {
|
|
1107
|
+
if (this.QueueCtor && this.FlowProducerCtor) return;
|
|
1108
|
+
if (!this.bullMqLoad) {
|
|
1109
|
+
this.bullMqLoad = (async () => {
|
|
1110
|
+
try {
|
|
1111
|
+
const mod = await import("bullmq");
|
|
1112
|
+
this.QueueCtor = mod.Queue;
|
|
1113
|
+
this.FlowProducerCtor = mod.FlowProducer;
|
|
1114
|
+
} catch {
|
|
1115
|
+
throw new Error(
|
|
1116
|
+
'BullMQ backend requires the "bullmq" package. Install it with: npm install bullmq'
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
})();
|
|
1120
|
+
}
|
|
1121
|
+
await this.bullMqLoad;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Open (or reuse) the `Queue` for a pool. Synchronous — callers `await
|
|
1125
|
+
* loadBullMq()` first so `QueueCtor` is populated.
|
|
1126
|
+
*/
|
|
1127
|
+
queueFor(pool) {
|
|
1128
|
+
if (!this.QueueCtor) {
|
|
1129
|
+
throw new Error("BullMQJobOrchestrator: queueFor called before loadBullMq()");
|
|
1130
|
+
}
|
|
1131
|
+
const name = resolvePoolQueueName(pool, this.bullConfig);
|
|
1132
|
+
let q = this.queues.get(name);
|
|
1133
|
+
if (!q) {
|
|
1134
|
+
q = new this.QueueCtor(name, { connection: this.connection });
|
|
1135
|
+
this.queues.set(name, q);
|
|
1136
|
+
}
|
|
1137
|
+
return q;
|
|
1138
|
+
}
|
|
1139
|
+
flow() {
|
|
1140
|
+
if (!this.FlowProducerCtor) {
|
|
1141
|
+
throw new Error("BullMQJobOrchestrator: flow called before loadBullMq()");
|
|
1142
|
+
}
|
|
1143
|
+
if (!this._flow) {
|
|
1144
|
+
this._flow = new this.FlowProducerCtor({ connection: this.connection });
|
|
1145
|
+
}
|
|
1146
|
+
return this._flow;
|
|
1147
|
+
}
|
|
1148
|
+
// ==========================================================================
|
|
1149
|
+
// start — Postgres insert (super) + BullMQ dispatch
|
|
1150
|
+
// ==========================================================================
|
|
1151
|
+
async start(type, input, opts = {}, tx) {
|
|
1152
|
+
const run = await super.start(type, input, opts, tx);
|
|
1153
|
+
await this.dispatch(run, type);
|
|
1154
|
+
return run;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Map a `job_run` row onto a BullMQ job via `queue.add`. When the run has a
|
|
1158
|
+
* `parentRunId` we attach it to the parent's existing BullMQ job through the
|
|
1159
|
+
* `parent: { id, queue }` opt — BullMQ then tracks the parent/child link in
|
|
1160
|
+
* its own graph. (The FlowProducer is reserved for whole-tree atomic
|
|
1161
|
+
* submits, exposed as an opt-in extension via `flowProducer()`; runtime
|
|
1162
|
+
* `ctx.spawnChild` is incremental, so `queue.add` with a parent ref is the
|
|
1163
|
+
* correct primitive here.)
|
|
1164
|
+
*
|
|
1165
|
+
* The `jobId` is colon-safe + stable: `sha1(dedupeKey)` when a dedupe key is
|
|
1166
|
+
* present (so the same logical key dedups), else the `job_run.id` UUID
|
|
1167
|
+
* (already colon-free).
|
|
1168
|
+
*
|
|
1169
|
+
* The domain `parentClosePolicy` cascade is still enforced in Postgres by
|
|
1170
|
+
* the shared `cancel` path — BullMQ's parent link is dispatch bookkeeping,
|
|
1171
|
+
* not the authority.
|
|
1172
|
+
*/
|
|
1173
|
+
async dispatch(run, type) {
|
|
1174
|
+
await this.loadBullMq();
|
|
1175
|
+
const def = await this.loadDefinition(type);
|
|
1176
|
+
const jobId = run.dedupeKey ? sha1JobId(run.dedupeKey) : run.id;
|
|
1177
|
+
const jobOpts = {
|
|
1178
|
+
jobId,
|
|
1179
|
+
...this.retryOpts(def),
|
|
1180
|
+
...this.dedupeOpts(run, def)
|
|
1181
|
+
};
|
|
1182
|
+
if (run.parentRunId) {
|
|
1183
|
+
const parentRow = await this.loadRun(run.parentRunId);
|
|
1184
|
+
if (parentRow) {
|
|
1185
|
+
const parentJobId = parentRow.dedupeKey ? sha1JobId(parentRow.dedupeKey) : parentRow.id;
|
|
1186
|
+
jobOpts.parent = {
|
|
1187
|
+
id: parentJobId,
|
|
1188
|
+
queue: resolvePoolQueueName(parentRow.pool, this.bullConfig)
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
const payload = { runId: run.id, type, input: run.input };
|
|
1193
|
+
await this.queueFor(run.pool).add(type, payload, jobOpts);
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Opt-in extension (spec §Extensions): expose the FlowProducer for
|
|
1197
|
+
* consumers that want to submit a whole parent/child DAG atomically up
|
|
1198
|
+
* front, rather than incrementally via `ctx.spawnChild`. Backend-specific —
|
|
1199
|
+
* code using it is not portable to the Drizzle backend. Async because it
|
|
1200
|
+
* lazily loads the optional `bullmq` package on first use.
|
|
1201
|
+
*/
|
|
1202
|
+
async flowProducer() {
|
|
1203
|
+
await this.loadBullMq();
|
|
1204
|
+
return this.flow();
|
|
1205
|
+
}
|
|
1206
|
+
retryOpts(def) {
|
|
1207
|
+
const policy = def.retryPolicy;
|
|
1208
|
+
if (!policy) return {};
|
|
1209
|
+
return {
|
|
1210
|
+
attempts: policy.attempts,
|
|
1211
|
+
backoff: {
|
|
1212
|
+
type: policy.backoff === "exponential" ? "exponential" : "fixed",
|
|
1213
|
+
delay: policy.baseMs
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
dedupeOpts(run, def) {
|
|
1218
|
+
if (!run.dedupeKey || !def.dedupeWindowMs) return {};
|
|
1219
|
+
return {
|
|
1220
|
+
deduplication: {
|
|
1221
|
+
id: sha1JobId(run.dedupeKey),
|
|
1222
|
+
ttl: def.dedupeWindowMs
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
// ==========================================================================
|
|
1227
|
+
// cancel — Postgres cascade (super) + remove from queue
|
|
1228
|
+
// ==========================================================================
|
|
1229
|
+
async cancel(runId, opts = {}) {
|
|
1230
|
+
const target = await this.loadRun(runId);
|
|
1231
|
+
await super.cancel(runId, opts);
|
|
1232
|
+
if (!target) return;
|
|
1233
|
+
await this.loadBullMq();
|
|
1234
|
+
await this.removeFromQueue(target);
|
|
1235
|
+
if (opts.cascade === false) return;
|
|
1236
|
+
const descendants = await this.bullDb.select().from(jobRuns).where(eq4(jobRuns.rootRunId, target.rootRunId));
|
|
1237
|
+
for (const child of descendants) {
|
|
1238
|
+
if (child.id === runId) continue;
|
|
1239
|
+
await this.removeFromQueue(child);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
async removeFromQueue(run) {
|
|
1243
|
+
const jobId = run.dedupeKey ? sha1JobId(run.dedupeKey) : run.id;
|
|
1244
|
+
try {
|
|
1245
|
+
const job = await this.queueFor(run.pool).getJob(jobId);
|
|
1246
|
+
if (job) await job.remove();
|
|
1247
|
+
} catch (err) {
|
|
1248
|
+
this.bullLogger.warn(
|
|
1249
|
+
`cancel: could not remove BullMQ job ${jobId} (pool=${run.pool}): ${err.message}`
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
// ==========================================================================
|
|
1254
|
+
// replay — Postgres reset (super) + re-enqueue
|
|
1255
|
+
// ==========================================================================
|
|
1256
|
+
async replay(runId) {
|
|
1257
|
+
const run = await super.replay(runId);
|
|
1258
|
+
await this.dispatch(run, run.jobType);
|
|
1259
|
+
return run;
|
|
1260
|
+
}
|
|
1261
|
+
// ==========================================================================
|
|
1262
|
+
// Internals
|
|
1263
|
+
// ==========================================================================
|
|
1264
|
+
async loadDefinition(type) {
|
|
1265
|
+
const [def] = await this.bullDb.select().from(jobs).where(eq4(jobs.type, type)).limit(1);
|
|
1266
|
+
if (!def) {
|
|
1267
|
+
throw new Error(`BullMQJobOrchestrator: no job definition for '${type}'`);
|
|
1268
|
+
}
|
|
1269
|
+
return def;
|
|
1270
|
+
}
|
|
1271
|
+
async loadRun(id) {
|
|
1272
|
+
const [row] = await this.bullDb.select().from(jobRuns).where(eq4(jobRuns.id, id)).limit(1);
|
|
1273
|
+
return row ?? null;
|
|
1274
|
+
}
|
|
1275
|
+
/** Close all open queue + flow connections. Called on module destroy. */
|
|
1276
|
+
async closeConnections() {
|
|
1277
|
+
for (const q of this.queues.values()) {
|
|
1278
|
+
await q.close().catch(() => void 0);
|
|
1279
|
+
}
|
|
1280
|
+
this.queues.clear();
|
|
1281
|
+
if (this._flow) {
|
|
1282
|
+
await this._flow.close().catch(() => void 0);
|
|
1283
|
+
this._flow = null;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
BullMQJobOrchestrator = __decorateClass([
|
|
1288
|
+
Injectable4(),
|
|
1289
|
+
__decorateParam(0, Inject4(DRIZZLE)),
|
|
1290
|
+
__decorateParam(1, Inject4(JOBS_MULTI_TENANT)),
|
|
1291
|
+
__decorateParam(2, Inject4(BULLMQ_CONNECTION)),
|
|
1292
|
+
__decorateParam(3, Optional()),
|
|
1293
|
+
__decorateParam(3, Inject4(BULLMQ_RESOLVED_CONFIG))
|
|
1294
|
+
], BullMQJobOrchestrator);
|
|
1295
|
+
|
|
829
1296
|
// runtime/subsystems/jobs/job-orchestrator.memory-backend.ts
|
|
830
1297
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
831
|
-
import { Inject as
|
|
1298
|
+
import { Inject as Inject5, Injectable as Injectable5, Logger as Logger3, Optional as Optional2 } from "@nestjs/common";
|
|
832
1299
|
var QUEUED_RUN_AT = /* @__PURE__ */ new Date(864e13);
|
|
833
1300
|
var TERMINAL_STATUSES2 = [
|
|
834
1301
|
"completed",
|
|
@@ -875,7 +1342,7 @@ var MemoryJobOrchestrator = class {
|
|
|
875
1342
|
stepService;
|
|
876
1343
|
multiTenant;
|
|
877
1344
|
moduleRef;
|
|
878
|
-
logger = new
|
|
1345
|
+
logger = new Logger3(MemoryJobOrchestrator.name);
|
|
879
1346
|
mutex = new PromiseMutex();
|
|
880
1347
|
handlerRegistry = /* @__PURE__ */ new Map();
|
|
881
1348
|
/**
|
|
@@ -1224,7 +1691,7 @@ var MemoryJobOrchestrator = class {
|
|
|
1224
1691
|
run,
|
|
1225
1692
|
step: this.makeStepFn(run),
|
|
1226
1693
|
spawnChild: this.makeSpawnFn(run),
|
|
1227
|
-
logger: new
|
|
1694
|
+
logger: new Logger3(`JobRun:${run.id}`)
|
|
1228
1695
|
};
|
|
1229
1696
|
const attemptsBefore = run.attempts ?? 0;
|
|
1230
1697
|
try {
|
|
@@ -1397,9 +1864,9 @@ var MemoryJobOrchestrator = class {
|
|
|
1397
1864
|
}
|
|
1398
1865
|
};
|
|
1399
1866
|
MemoryJobOrchestrator = __decorateClass([
|
|
1400
|
-
|
|
1401
|
-
__decorateParam(2,
|
|
1402
|
-
__decorateParam(3,
|
|
1867
|
+
Injectable5(),
|
|
1868
|
+
__decorateParam(2, Inject5(JOBS_MULTI_TENANT)),
|
|
1869
|
+
__decorateParam(3, Optional2())
|
|
1403
1870
|
], MemoryJobOrchestrator);
|
|
1404
1871
|
function classifyError(err, policy, currentAttempts) {
|
|
1405
1872
|
if (!policy) return "fail";
|
|
@@ -1433,7 +1900,7 @@ function serialiseError(err, attempt, retryable) {
|
|
|
1433
1900
|
}
|
|
1434
1901
|
|
|
1435
1902
|
// runtime/subsystems/jobs/job-run-service.memory-backend.ts
|
|
1436
|
-
import { Inject as
|
|
1903
|
+
import { Inject as Inject6, Injectable as Injectable6 } from "@nestjs/common";
|
|
1437
1904
|
var NON_TERMINAL_STATUSES2 = [
|
|
1438
1905
|
"pending",
|
|
1439
1906
|
"running",
|
|
@@ -1550,6 +2017,38 @@ var MemoryJobRunService = class {
|
|
|
1550
2017
|
createdAt: r.createdAt
|
|
1551
2018
|
}));
|
|
1552
2019
|
}
|
|
2020
|
+
async listJobRuns(query = {}) {
|
|
2021
|
+
const limit = clampLimit(query.limit);
|
|
2022
|
+
const tenantCheck = this.tenantPredicate("listJobRuns", query.tenantId);
|
|
2023
|
+
const keyset = query.cursor ? decodeKeysetCursor(query.cursor) : null;
|
|
2024
|
+
const matched = [];
|
|
2025
|
+
for (const r of this.store.runs.values()) {
|
|
2026
|
+
if (tenantCheck && !tenantCheck(r)) continue;
|
|
2027
|
+
if (query.poolId && r.pool !== query.poolId) continue;
|
|
2028
|
+
if (query.rootRunId && r.rootRunId !== query.rootRunId) continue;
|
|
2029
|
+
if (query.status && r.status !== query.status) continue;
|
|
2030
|
+
if (query.since && r.createdAt.getTime() < query.since.getTime()) continue;
|
|
2031
|
+
matched.push(r);
|
|
2032
|
+
}
|
|
2033
|
+
matched.sort((a, b) => {
|
|
2034
|
+
const dt = b.createdAt.getTime() - a.createdAt.getTime();
|
|
2035
|
+
if (dt !== 0) return dt;
|
|
2036
|
+
return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
|
|
2037
|
+
});
|
|
2038
|
+
const seeked = keyset ? matched.filter((r) => {
|
|
2039
|
+
const ct = r.createdAt.getTime();
|
|
2040
|
+
const kt = keyset.createdAt.getTime();
|
|
2041
|
+
if (ct < kt) return true;
|
|
2042
|
+
if (ct > kt) return false;
|
|
2043
|
+
return r.id < keyset.id;
|
|
2044
|
+
}) : matched;
|
|
2045
|
+
const hasMore = seeked.length > limit;
|
|
2046
|
+
const page = hasMore ? seeked.slice(0, limit) : seeked;
|
|
2047
|
+
const items = page.map(toJobRunSummary);
|
|
2048
|
+
const last = page[page.length - 1];
|
|
2049
|
+
const nextCursor = hasMore && last ? encodeKeysetCursor({ createdAt: last.createdAt, id: last.id }) : null;
|
|
2050
|
+
return { items, nextCursor };
|
|
2051
|
+
}
|
|
1553
2052
|
/**
|
|
1554
2053
|
* Direct lookup. Not on the protocol — concrete-class convenience for
|
|
1555
2054
|
* tests. Matches `DrizzleJobRunService.findByRootRunId` in spirit; both
|
|
@@ -1568,9 +2067,9 @@ var MemoryJobRunService = class {
|
|
|
1568
2067
|
}
|
|
1569
2068
|
};
|
|
1570
2069
|
MemoryJobRunService = __decorateClass([
|
|
1571
|
-
|
|
1572
|
-
__decorateParam(1,
|
|
1573
|
-
__decorateParam(2,
|
|
2070
|
+
Injectable6(),
|
|
2071
|
+
__decorateParam(1, Inject6(JOB_ORCHESTRATOR)),
|
|
2072
|
+
__decorateParam(2, Inject6(JOBS_MULTI_TENANT))
|
|
1574
2073
|
], MemoryJobRunService);
|
|
1575
2074
|
function compareBy(a, b, order) {
|
|
1576
2075
|
switch (order) {
|
|
@@ -1588,7 +2087,7 @@ function compareBy(a, b, order) {
|
|
|
1588
2087
|
|
|
1589
2088
|
// runtime/subsystems/jobs/job-step-service.memory-backend.ts
|
|
1590
2089
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1591
|
-
import { Injectable as
|
|
2090
|
+
import { Injectable as Injectable7 } from "@nestjs/common";
|
|
1592
2091
|
var MemoryJobStepService = class {
|
|
1593
2092
|
constructor(store) {
|
|
1594
2093
|
this.store = store;
|
|
@@ -1677,195 +2176,86 @@ var MemoryJobStepService = class {
|
|
|
1677
2176
|
for (const r of rows) {
|
|
1678
2177
|
if (r.seq > max) max = r.seq;
|
|
1679
2178
|
}
|
|
1680
|
-
return max + 1;
|
|
1681
|
-
}
|
|
1682
|
-
};
|
|
1683
|
-
MemoryJobStepService = __decorateClass([
|
|
1684
|
-
Injectable6()
|
|
1685
|
-
], MemoryJobStepService);
|
|
1686
|
-
|
|
1687
|
-
// runtime/subsystems/jobs/memory-job-store.ts
|
|
1688
|
-
var MemoryJobStore = class {
|
|
1689
|
-
/** Runs keyed by `id` (single source of truth for status/scope/lineage). */
|
|
1690
|
-
runs = /* @__PURE__ */ new Map();
|
|
1691
|
-
/** Steps keyed by `job_run_id`; array order matches insertion order. */
|
|
1692
|
-
steps = /* @__PURE__ */ new Map();
|
|
1693
|
-
/** Job definitions keyed by `type` — memory mirror of the `job` table. */
|
|
1694
|
-
jobs = /* @__PURE__ */ new Map();
|
|
1695
|
-
/** Reset everything. Tests call this in `beforeEach`. */
|
|
1696
|
-
clear() {
|
|
1697
|
-
this.runs.clear();
|
|
1698
|
-
this.steps.clear();
|
|
1699
|
-
this.jobs.clear();
|
|
1700
|
-
}
|
|
1701
|
-
};
|
|
1702
|
-
|
|
1703
|
-
// runtime/subsystems/jobs/jobs-domain.module.ts
|
|
1704
|
-
var JobsDomainModule = class {
|
|
1705
|
-
static forRoot(opts) {
|
|
1706
|
-
void opts.extensions;
|
|
1707
|
-
const multiTenant = opts.multiTenant ?? false;
|
|
1708
|
-
const providers = [
|
|
1709
|
-
// JOB-8 — boolean provider consumed by the four service-layer backends.
|
|
1710
|
-
// Always provided (even when `multiTenant === false`) so `@Inject`
|
|
1711
|
-
// always resolves; backends short-circuit the enforcement path when
|
|
1712
|
-
// the value is `false`. See `jobs-domain.tokens.ts` for the claim-loop
|
|
1713
|
-
// cross-tenant-by-design decision.
|
|
1714
|
-
{ provide: JOBS_MULTI_TENANT, useValue: multiTenant }
|
|
1715
|
-
];
|
|
1716
|
-
if (opts.backend === "memory") {
|
|
1717
|
-
const store = new MemoryJobStore();
|
|
1718
|
-
providers.push({ provide: MemoryJobStore, useValue: store });
|
|
1719
|
-
providers.push(MemoryJobStepService);
|
|
1720
|
-
providers.push({ provide: JOB_STEP_SERVICE, useExisting: MemoryJobStepService });
|
|
1721
|
-
providers.push(MemoryJobOrchestrator);
|
|
1722
|
-
providers.push({ provide: JOB_ORCHESTRATOR, useExisting: MemoryJobOrchestrator });
|
|
1723
|
-
providers.push(MemoryJobRunService);
|
|
1724
|
-
providers.push({ provide: JOB_RUN_SERVICE, useExisting: MemoryJobRunService });
|
|
1725
|
-
} else {
|
|
1726
|
-
providers.push({ provide: JOB_ORCHESTRATOR, useClass: DrizzleJobOrchestrator });
|
|
1727
|
-
providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });
|
|
1728
|
-
providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });
|
|
1729
|
-
}
|
|
1730
|
-
return {
|
|
1731
|
-
module: JobsDomainModule,
|
|
1732
|
-
global: true,
|
|
1733
|
-
providers,
|
|
1734
|
-
exports: [
|
|
1735
|
-
JOB_ORCHESTRATOR,
|
|
1736
|
-
JOB_RUN_SERVICE,
|
|
1737
|
-
JOB_STEP_SERVICE,
|
|
1738
|
-
JOBS_MULTI_TENANT
|
|
1739
|
-
]
|
|
1740
|
-
};
|
|
1741
|
-
}
|
|
1742
|
-
};
|
|
1743
|
-
JobsDomainModule = __decorateClass([
|
|
1744
|
-
Module({})
|
|
1745
|
-
], JobsDomainModule);
|
|
1746
|
-
|
|
1747
|
-
// runtime/subsystems/jobs/pool-config.loader.ts
|
|
1748
|
-
import { existsSync, readFileSync } from "fs";
|
|
1749
|
-
import { resolve } from "path";
|
|
1750
|
-
import { parse as parseYaml } from "yaml";
|
|
1751
|
-
var FRAMEWORK_POOLS = Object.freeze({
|
|
1752
|
-
events_inbound: Object.freeze({
|
|
1753
|
-
queue: "jobs-events-inbound",
|
|
1754
|
-
concurrency: 20,
|
|
1755
|
-
reserved: true,
|
|
1756
|
-
description: "Inbound events drain (events subsystem outbox)."
|
|
1757
|
-
}),
|
|
1758
|
-
events_change: Object.freeze({
|
|
1759
|
-
queue: "jobs-events-change",
|
|
1760
|
-
concurrency: 30,
|
|
1761
|
-
reserved: true,
|
|
1762
|
-
description: "Change events drain (events subsystem outbox)."
|
|
1763
|
-
}),
|
|
1764
|
-
events_outbound: Object.freeze({
|
|
1765
|
-
queue: "jobs-events-outbound",
|
|
1766
|
-
concurrency: 10,
|
|
1767
|
-
reserved: true,
|
|
1768
|
-
description: "Outbound events drain (events subsystem outbox)."
|
|
1769
|
-
}),
|
|
1770
|
-
interactive: Object.freeze({
|
|
1771
|
-
queue: "jobs-interactive",
|
|
1772
|
-
concurrency: 20,
|
|
1773
|
-
reserved: false,
|
|
1774
|
-
description: "User-facing latency-sensitive jobs."
|
|
1775
|
-
}),
|
|
1776
|
-
batch: Object.freeze({
|
|
1777
|
-
queue: "jobs-batch",
|
|
1778
|
-
concurrency: 5,
|
|
1779
|
-
reserved: false,
|
|
1780
|
-
description: "Default pool for background jobs."
|
|
1781
|
-
})
|
|
1782
|
-
});
|
|
1783
|
-
var RESERVED_POOL_NAMES = new Set(
|
|
1784
|
-
Object.entries(FRAMEWORK_POOLS).filter(([, def]) => def.reserved).map(([name]) => name)
|
|
1785
|
-
);
|
|
1786
|
-
var cache = /* @__PURE__ */ new Map();
|
|
1787
|
-
function loadPoolConfig(configPath) {
|
|
1788
|
-
const resolved = resolve(configPath ?? `${process.cwd()}/codegen.config.yaml`);
|
|
1789
|
-
const cached = cache.get(resolved);
|
|
1790
|
-
if (cached) return cached;
|
|
1791
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1792
|
-
for (const [name, def] of Object.entries(FRAMEWORK_POOLS)) {
|
|
1793
|
-
merged.set(name, { ...def });
|
|
1794
|
-
}
|
|
1795
|
-
if (!existsSync(resolved)) {
|
|
1796
|
-
cache.set(resolved, merged);
|
|
1797
|
-
return merged;
|
|
1798
|
-
}
|
|
1799
|
-
let raw;
|
|
1800
|
-
try {
|
|
1801
|
-
raw = parseYaml(readFileSync(resolved, "utf8"));
|
|
1802
|
-
} catch (err) {
|
|
1803
|
-
throw new Error(
|
|
1804
|
-
`pool-config.loader: failed to parse YAML at ${resolved}: ${err.message}`
|
|
1805
|
-
);
|
|
1806
|
-
}
|
|
1807
|
-
const userPools = extractUserPools(raw);
|
|
1808
|
-
for (const [name, userDef] of Object.entries(userPools)) {
|
|
1809
|
-
const existing = merged.get(name);
|
|
1810
|
-
if (existing) {
|
|
1811
|
-
const next = {
|
|
1812
|
-
queue: existing.queue,
|
|
1813
|
-
concurrency: typeof userDef.concurrency === "number" ? userDef.concurrency : existing.concurrency,
|
|
1814
|
-
reserved: existing.reserved,
|
|
1815
|
-
description: userDef.description ?? existing.description
|
|
1816
|
-
};
|
|
1817
|
-
merged.set(name, next);
|
|
1818
|
-
continue;
|
|
1819
|
-
}
|
|
1820
|
-
if (typeof userDef.queue !== "string" || userDef.queue.length === 0) {
|
|
1821
|
-
throw new Error(
|
|
1822
|
-
`pool-config.loader: pool '${name}' must declare a non-empty 'queue'.`
|
|
1823
|
-
);
|
|
1824
|
-
}
|
|
1825
|
-
if (typeof userDef.concurrency !== "number" || userDef.concurrency <= 0) {
|
|
1826
|
-
throw new Error(
|
|
1827
|
-
`pool-config.loader: pool '${name}' must declare a positive 'concurrency'.`
|
|
1828
|
-
);
|
|
1829
|
-
}
|
|
1830
|
-
if (userDef.reserved === true) {
|
|
1831
|
-
throw new Error(
|
|
1832
|
-
`pool-config.loader: user-defined pool '${name}' cannot set 'reserved: true' \u2014 reserved is framework-only.`
|
|
1833
|
-
);
|
|
1834
|
-
}
|
|
1835
|
-
merged.set(name, {
|
|
1836
|
-
queue: userDef.queue,
|
|
1837
|
-
concurrency: userDef.concurrency,
|
|
1838
|
-
reserved: false,
|
|
1839
|
-
description: userDef.description
|
|
1840
|
-
});
|
|
2179
|
+
return max + 1;
|
|
1841
2180
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
2181
|
+
};
|
|
2182
|
+
MemoryJobStepService = __decorateClass([
|
|
2183
|
+
Injectable7()
|
|
2184
|
+
], MemoryJobStepService);
|
|
2185
|
+
|
|
2186
|
+
// runtime/subsystems/jobs/memory-job-store.ts
|
|
2187
|
+
var MemoryJobStore = class {
|
|
2188
|
+
/** Runs keyed by `id` (single source of truth for status/scope/lineage). */
|
|
2189
|
+
runs = /* @__PURE__ */ new Map();
|
|
2190
|
+
/** Steps keyed by `job_run_id`; array order matches insertion order. */
|
|
2191
|
+
steps = /* @__PURE__ */ new Map();
|
|
2192
|
+
/** Job definitions keyed by `type` — memory mirror of the `job` table. */
|
|
2193
|
+
jobs = /* @__PURE__ */ new Map();
|
|
2194
|
+
/** Reset everything. Tests call this in `beforeEach`. */
|
|
2195
|
+
clear() {
|
|
2196
|
+
this.runs.clear();
|
|
2197
|
+
this.steps.clear();
|
|
2198
|
+
this.jobs.clear();
|
|
1849
2199
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2200
|
+
};
|
|
2201
|
+
|
|
2202
|
+
// runtime/subsystems/jobs/jobs-domain.module.ts
|
|
2203
|
+
var JobsDomainModule = class {
|
|
2204
|
+
static forRoot(opts) {
|
|
2205
|
+
const multiTenant = opts.multiTenant ?? false;
|
|
2206
|
+
const providers = [
|
|
2207
|
+
// JOB-8 — boolean provider consumed by the four service-layer backends.
|
|
2208
|
+
// Always provided (even when `multiTenant === false`) so `@Inject`
|
|
2209
|
+
// always resolves; backends short-circuit the enforcement path when
|
|
2210
|
+
// the value is `false`. See `jobs-domain.tokens.ts` for the claim-loop
|
|
2211
|
+
// cross-tenant-by-design decision.
|
|
2212
|
+
{ provide: JOBS_MULTI_TENANT, useValue: multiTenant }
|
|
2213
|
+
];
|
|
2214
|
+
if (opts.backend === "memory") {
|
|
2215
|
+
const store = new MemoryJobStore();
|
|
2216
|
+
providers.push({ provide: MemoryJobStore, useValue: store });
|
|
2217
|
+
providers.push(MemoryJobStepService);
|
|
2218
|
+
providers.push({ provide: JOB_STEP_SERVICE, useExisting: MemoryJobStepService });
|
|
2219
|
+
providers.push(MemoryJobOrchestrator);
|
|
2220
|
+
providers.push({ provide: JOB_ORCHESTRATOR, useExisting: MemoryJobOrchestrator });
|
|
2221
|
+
providers.push(MemoryJobRunService);
|
|
2222
|
+
providers.push({ provide: JOB_RUN_SERVICE, useExisting: MemoryJobRunService });
|
|
2223
|
+
} else if (opts.backend === "bullmq") {
|
|
2224
|
+
const resolved = resolveBullMqConfig(opts.extensions?.bullmq);
|
|
2225
|
+
providers.push({ provide: BULLMQ_CONNECTION, useValue: resolved.connection });
|
|
2226
|
+
providers.push({ provide: BULLMQ_RESOLVED_CONFIG, useValue: resolved });
|
|
2227
|
+
providers.push({ provide: JOB_ORCHESTRATOR, useClass: BullMQJobOrchestrator });
|
|
2228
|
+
providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });
|
|
2229
|
+
providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });
|
|
2230
|
+
} else {
|
|
2231
|
+
providers.push({ provide: JOB_ORCHESTRATOR, useClass: DrizzleJobOrchestrator });
|
|
2232
|
+
providers.push({ provide: JOB_RUN_SERVICE, useClass: DrizzleJobRunService });
|
|
2233
|
+
providers.push({ provide: JOB_STEP_SERVICE, useClass: DrizzleJobStepService });
|
|
2234
|
+
}
|
|
2235
|
+
const exports = [
|
|
2236
|
+
JOB_ORCHESTRATOR,
|
|
2237
|
+
JOB_RUN_SERVICE,
|
|
2238
|
+
JOB_STEP_SERVICE,
|
|
2239
|
+
JOBS_MULTI_TENANT
|
|
2240
|
+
];
|
|
2241
|
+
if (opts.backend === "bullmq") {
|
|
2242
|
+
exports.push(BULLMQ_CONNECTION, BULLMQ_RESOLVED_CONFIG);
|
|
2243
|
+
}
|
|
2244
|
+
return {
|
|
2245
|
+
module: JobsDomainModule,
|
|
2246
|
+
global: true,
|
|
2247
|
+
providers,
|
|
2248
|
+
exports
|
|
2249
|
+
};
|
|
1862
2250
|
}
|
|
1863
|
-
|
|
1864
|
-
|
|
2251
|
+
};
|
|
2252
|
+
JobsDomainModule = __decorateClass([
|
|
2253
|
+
Module({})
|
|
2254
|
+
], JobsDomainModule);
|
|
1865
2255
|
|
|
1866
2256
|
// runtime/subsystems/jobs/job-worker.ts
|
|
1867
|
-
import { Inject as
|
|
1868
|
-
import { and as and4, asc as asc2, desc as desc3, eq as
|
|
2257
|
+
import { Inject as Inject7, Injectable as Injectable8, Logger as Logger4 } from "@nestjs/common";
|
|
2258
|
+
import { and as and4, asc as asc2, desc as desc3, eq as eq5, inArray as inArray3, lt as lt2, lte, sql as sql4 } from "drizzle-orm";
|
|
1869
2259
|
var JOB_WORKER_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_OPTIONS");
|
|
1870
2260
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
1871
2261
|
var DEFAULT_STALE_SWEEPER_INTERVAL_MS = 6e4;
|
|
@@ -1928,7 +2318,7 @@ var JobWorker = class {
|
|
|
1928
2318
|
stepService;
|
|
1929
2319
|
options;
|
|
1930
2320
|
moduleRef;
|
|
1931
|
-
logger = new
|
|
2321
|
+
logger = new Logger4(JobWorker.name);
|
|
1932
2322
|
shuttingDown = false;
|
|
1933
2323
|
inFlight = /* @__PURE__ */ new Set();
|
|
1934
2324
|
pollTimer = null;
|
|
@@ -1969,7 +2359,7 @@ var JobWorker = class {
|
|
|
1969
2359
|
await this.drainInFlight();
|
|
1970
2360
|
try {
|
|
1971
2361
|
await this.db.update(jobRuns).set({ status: "pending", claimedAt: null, startedAt: null }).where(
|
|
1972
|
-
and4(
|
|
2362
|
+
and4(eq5(jobRuns.status, "running"), eq5(jobRuns.pool, this.options.pool))
|
|
1973
2363
|
);
|
|
1974
2364
|
} catch (err) {
|
|
1975
2365
|
this.logger.error(`shutdown reset failed: ${err.message}`);
|
|
@@ -2019,8 +2409,8 @@ var JobWorker = class {
|
|
|
2019
2409
|
return this.db.transaction(async (tx) => {
|
|
2020
2410
|
const candidates = await tx.select({ id: jobRuns.id }).from(jobRuns).where(
|
|
2021
2411
|
and4(
|
|
2022
|
-
|
|
2023
|
-
|
|
2412
|
+
eq5(jobRuns.status, "pending"),
|
|
2413
|
+
eq5(jobRuns.pool, pool),
|
|
2024
2414
|
lte(jobRuns.runAt, /* @__PURE__ */ new Date())
|
|
2025
2415
|
)
|
|
2026
2416
|
).orderBy(desc3(jobRuns.priority), asc2(jobRuns.runAt)).limit(1).for("update", { skipLocked: true });
|
|
@@ -2031,7 +2421,7 @@ var JobWorker = class {
|
|
|
2031
2421
|
claimedAt: /* @__PURE__ */ new Date(),
|
|
2032
2422
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2033
2423
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2034
|
-
}).where(
|
|
2424
|
+
}).where(eq5(jobRuns.id, candidate.id)).returning();
|
|
2035
2425
|
return claimed ?? null;
|
|
2036
2426
|
});
|
|
2037
2427
|
}
|
|
@@ -2049,7 +2439,7 @@ var JobWorker = class {
|
|
|
2049
2439
|
await this.db.transaction(async (tx) => {
|
|
2050
2440
|
const threshold = new Date(Date.now() - this.staleThresholdMs);
|
|
2051
2441
|
const stale = await tx.select({ id: jobRuns.id }).from(jobRuns).where(
|
|
2052
|
-
and4(
|
|
2442
|
+
and4(eq5(jobRuns.status, "running"), lt2(jobRuns.claimedAt, threshold))
|
|
2053
2443
|
).for("update", { skipLocked: true });
|
|
2054
2444
|
if (stale.length === 0) return;
|
|
2055
2445
|
const ids = stale.map((r) => r.id);
|
|
@@ -2082,8 +2472,8 @@ var JobWorker = class {
|
|
|
2082
2472
|
if (claimed.concurrencyKey) {
|
|
2083
2473
|
const inflight = await this.db.select({ id: jobRuns.id }).from(jobRuns).where(
|
|
2084
2474
|
and4(
|
|
2085
|
-
|
|
2086
|
-
|
|
2475
|
+
eq5(jobRuns.concurrencyKey, claimed.concurrencyKey),
|
|
2476
|
+
eq5(jobRuns.status, "running")
|
|
2087
2477
|
)
|
|
2088
2478
|
);
|
|
2089
2479
|
const other = inflight.find((r) => r.id !== claimed.id);
|
|
@@ -2093,7 +2483,7 @@ var JobWorker = class {
|
|
|
2093
2483
|
claimedAt: null,
|
|
2094
2484
|
startedAt: null,
|
|
2095
2485
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2096
|
-
}).where(
|
|
2486
|
+
}).where(eq5(jobRuns.id, claimed.id));
|
|
2097
2487
|
return;
|
|
2098
2488
|
}
|
|
2099
2489
|
}
|
|
@@ -2108,7 +2498,7 @@ var JobWorker = class {
|
|
|
2108
2498
|
run: claimed,
|
|
2109
2499
|
step: this.makeStepFn(claimed),
|
|
2110
2500
|
spawnChild: this.makeSpawnFn(claimed),
|
|
2111
|
-
logger: new
|
|
2501
|
+
logger: new Logger4(`JobRun:${claimed.id}`)
|
|
2112
2502
|
};
|
|
2113
2503
|
const attemptsBefore = claimed.attempts ?? 0;
|
|
2114
2504
|
try {
|
|
@@ -2119,7 +2509,7 @@ var JobWorker = class {
|
|
|
2119
2509
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2120
2510
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
2121
2511
|
attempts: attemptsBefore + 1
|
|
2122
|
-
}).where(
|
|
2512
|
+
}).where(eq5(jobRuns.id, claimed.id));
|
|
2123
2513
|
} catch (err) {
|
|
2124
2514
|
const policy = meta.retry;
|
|
2125
2515
|
const decision = classifyError2(err, policy, attemptsBefore);
|
|
@@ -2134,7 +2524,7 @@ var JobWorker = class {
|
|
|
2134
2524
|
claimedAt: null,
|
|
2135
2525
|
error: serialiseError2(err, nextAttempts, true),
|
|
2136
2526
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2137
|
-
}).where(
|
|
2527
|
+
}).where(eq5(jobRuns.id, claimed.id));
|
|
2138
2528
|
} else {
|
|
2139
2529
|
await this.markFailed(claimed, err, nextAttempts);
|
|
2140
2530
|
}
|
|
@@ -2147,7 +2537,7 @@ var JobWorker = class {
|
|
|
2147
2537
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2148
2538
|
error: serialiseError2(err, finalAttempts, false),
|
|
2149
2539
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2150
|
-
}).where(
|
|
2540
|
+
}).where(eq5(jobRuns.id, claimed.id));
|
|
2151
2541
|
if (claimed.parentClosePolicy === "terminate") {
|
|
2152
2542
|
try {
|
|
2153
2543
|
await this.orchestrator.cancel(claimed.id, {
|
|
@@ -2250,25 +2640,228 @@ var JobWorker = class {
|
|
|
2250
2640
|
// ============================================================================
|
|
2251
2641
|
};
|
|
2252
2642
|
JobWorker = __decorateClass([
|
|
2253
|
-
|
|
2254
|
-
__decorateParam(0,
|
|
2255
|
-
__decorateParam(1,
|
|
2256
|
-
__decorateParam(2,
|
|
2257
|
-
__decorateParam(3,
|
|
2258
|
-
__decorateParam(4,
|
|
2643
|
+
Injectable8(),
|
|
2644
|
+
__decorateParam(0, Inject7(DRIZZLE)),
|
|
2645
|
+
__decorateParam(1, Inject7(JOB_ORCHESTRATOR)),
|
|
2646
|
+
__decorateParam(2, Inject7(JOB_RUN_SERVICE)),
|
|
2647
|
+
__decorateParam(3, Inject7(JOB_STEP_SERVICE)),
|
|
2648
|
+
__decorateParam(4, Inject7(JOB_WORKER_OPTIONS))
|
|
2259
2649
|
], JobWorker);
|
|
2260
2650
|
|
|
2651
|
+
// runtime/subsystems/jobs/job-worker.bullmq-backend.ts
|
|
2652
|
+
import { Logger as Logger5 } from "@nestjs/common";
|
|
2653
|
+
import { eq as eq6 } from "drizzle-orm";
|
|
2654
|
+
function serialiseError3(err, attempt, retryable) {
|
|
2655
|
+
const e = err;
|
|
2656
|
+
return {
|
|
2657
|
+
message: e?.message ?? String(err),
|
|
2658
|
+
stack: e?.stack,
|
|
2659
|
+
retryable,
|
|
2660
|
+
attempt
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
var BullMQJobWorker = class _BullMQJobWorker {
|
|
2664
|
+
constructor(db, orchestrator, stepService, options, moduleRef) {
|
|
2665
|
+
this.db = db;
|
|
2666
|
+
this.orchestrator = orchestrator;
|
|
2667
|
+
this.stepService = stepService;
|
|
2668
|
+
this.options = options;
|
|
2669
|
+
this.moduleRef = moduleRef;
|
|
2670
|
+
}
|
|
2671
|
+
db;
|
|
2672
|
+
orchestrator;
|
|
2673
|
+
stepService;
|
|
2674
|
+
options;
|
|
2675
|
+
moduleRef;
|
|
2676
|
+
logger = new Logger5(_BullMQJobWorker.name);
|
|
2677
|
+
worker = null;
|
|
2678
|
+
async onModuleInit() {
|
|
2679
|
+
let WorkerCtor;
|
|
2680
|
+
try {
|
|
2681
|
+
const mod = await import("bullmq");
|
|
2682
|
+
WorkerCtor = mod.Worker;
|
|
2683
|
+
} catch {
|
|
2684
|
+
throw new Error(
|
|
2685
|
+
'BullMQ backend requires the "bullmq" package. Install it with: npm install bullmq'
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
this.worker = new WorkerCtor(
|
|
2689
|
+
this.options.queueName,
|
|
2690
|
+
(job) => this.process(job),
|
|
2691
|
+
{
|
|
2692
|
+
connection: this.options.connection,
|
|
2693
|
+
concurrency: this.options.concurrency
|
|
2694
|
+
}
|
|
2695
|
+
);
|
|
2696
|
+
this.worker.on("failed", (job, err) => {
|
|
2697
|
+
if (!job) return;
|
|
2698
|
+
const attemptsMade = job.attemptsMade;
|
|
2699
|
+
const maxAttempts = job.opts.attempts ?? 1;
|
|
2700
|
+
if (attemptsMade >= maxAttempts) {
|
|
2701
|
+
void this.markFailed(job.data.runId, err, attemptsMade);
|
|
2702
|
+
}
|
|
2703
|
+
});
|
|
2704
|
+
this.logger.log(
|
|
2705
|
+
`BullMQ worker started: pool='${this.options.pool}' queue='${this.options.queueName}' concurrency=${this.options.concurrency}`
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
async onModuleDestroy() {
|
|
2709
|
+
if (this.worker) {
|
|
2710
|
+
await this.worker.close();
|
|
2711
|
+
this.worker = null;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* Process one BullMQ job. Returns the handler output (stored by BullMQ as
|
|
2716
|
+
* the job return value AND written to `job_run.output`). Throws on handler
|
|
2717
|
+
* failure so BullMQ applies the retry policy.
|
|
2718
|
+
*/
|
|
2719
|
+
async process(job) {
|
|
2720
|
+
const { runId } = job.data;
|
|
2721
|
+
const [row] = await this.db.select().from(jobRuns).where(eq6(jobRuns.id, runId)).limit(1);
|
|
2722
|
+
if (!row) {
|
|
2723
|
+
this.logger.warn(`process: job_run ${runId} not found; skipping`);
|
|
2724
|
+
return {};
|
|
2725
|
+
}
|
|
2726
|
+
const run = row;
|
|
2727
|
+
if (run.status === "canceled") {
|
|
2728
|
+
return {};
|
|
2729
|
+
}
|
|
2730
|
+
const registryEntry = JOB_HANDLER_REGISTRY.get(run.jobType);
|
|
2731
|
+
if (!registryEntry) {
|
|
2732
|
+
throw new Error(
|
|
2733
|
+
`No handler registered for jobType='${run.jobType}' (run ${run.id})`
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
await this.db.update(jobRuns).set({
|
|
2737
|
+
status: "running",
|
|
2738
|
+
claimedAt: /* @__PURE__ */ new Date(),
|
|
2739
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
2740
|
+
attempts: job.attemptsMade + 1,
|
|
2741
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2742
|
+
}).where(eq6(jobRuns.id, run.id));
|
|
2743
|
+
const HandlerClass = registryEntry.handlerClass;
|
|
2744
|
+
const handler = this.moduleRef.get(
|
|
2745
|
+
HandlerClass,
|
|
2746
|
+
{ strict: false }
|
|
2747
|
+
);
|
|
2748
|
+
const ctx = {
|
|
2749
|
+
input: run.input,
|
|
2750
|
+
run,
|
|
2751
|
+
step: this.makeStepFn(run),
|
|
2752
|
+
spawnChild: this.makeSpawnFn(run),
|
|
2753
|
+
logger: new Logger5(`JobRun:${run.id}`)
|
|
2754
|
+
};
|
|
2755
|
+
const output = await handler.run(ctx);
|
|
2756
|
+
await this.db.update(jobRuns).set({
|
|
2757
|
+
status: "completed",
|
|
2758
|
+
output: output ?? {},
|
|
2759
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
2760
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2761
|
+
}).where(eq6(jobRuns.id, run.id));
|
|
2762
|
+
return output ?? {};
|
|
2763
|
+
}
|
|
2764
|
+
async markFailed(runId, err, finalAttempts) {
|
|
2765
|
+
const [row] = await this.db.select().from(jobRuns).where(eq6(jobRuns.id, runId)).limit(1);
|
|
2766
|
+
if (!row) return;
|
|
2767
|
+
const run = row;
|
|
2768
|
+
await this.db.update(jobRuns).set({
|
|
2769
|
+
status: "failed",
|
|
2770
|
+
attempts: finalAttempts,
|
|
2771
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
2772
|
+
error: serialiseError3(err, finalAttempts, false),
|
|
2773
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2774
|
+
}).where(eq6(jobRuns.id, runId));
|
|
2775
|
+
if (run.parentClosePolicy === "terminate") {
|
|
2776
|
+
try {
|
|
2777
|
+
await this.orchestrator.cancel(run.id, {
|
|
2778
|
+
cascade: true,
|
|
2779
|
+
reason: "parent-failed",
|
|
2780
|
+
tenantId: run.tenantId
|
|
2781
|
+
});
|
|
2782
|
+
} catch (cascadeErr) {
|
|
2783
|
+
this.logger.warn(
|
|
2784
|
+
`cascade on failed run ${run.id}: ${cascadeErr.message}`
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
// ── ctx.step / ctx.spawnChild (mirror JobWorker) ──────────────────────────
|
|
2790
|
+
makeStepFn(run) {
|
|
2791
|
+
return async (stepId, fn, _opts) => {
|
|
2792
|
+
void _opts;
|
|
2793
|
+
const existing = await this.stepService.findStep(run.id, stepId);
|
|
2794
|
+
if (existing?.status === "completed") {
|
|
2795
|
+
return existing.output;
|
|
2796
|
+
}
|
|
2797
|
+
const nextAttempts = (existing?.attempts ?? 0) + 1;
|
|
2798
|
+
const seq = nextAttempts;
|
|
2799
|
+
await this.stepService.recordStep({
|
|
2800
|
+
jobRunId: run.id,
|
|
2801
|
+
stepId,
|
|
2802
|
+
kind: "task",
|
|
2803
|
+
seq,
|
|
2804
|
+
status: "running",
|
|
2805
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
2806
|
+
attempts: nextAttempts
|
|
2807
|
+
});
|
|
2808
|
+
try {
|
|
2809
|
+
const output = await fn();
|
|
2810
|
+
await this.stepService.recordStep({
|
|
2811
|
+
jobRunId: run.id,
|
|
2812
|
+
stepId,
|
|
2813
|
+
kind: "task",
|
|
2814
|
+
seq,
|
|
2815
|
+
status: "completed",
|
|
2816
|
+
output,
|
|
2817
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
2818
|
+
attempts: nextAttempts
|
|
2819
|
+
});
|
|
2820
|
+
return output;
|
|
2821
|
+
} catch (err) {
|
|
2822
|
+
await this.stepService.recordStep({
|
|
2823
|
+
jobRunId: run.id,
|
|
2824
|
+
stepId,
|
|
2825
|
+
kind: "task",
|
|
2826
|
+
seq,
|
|
2827
|
+
status: "failed",
|
|
2828
|
+
error: serialiseError3(err, nextAttempts, false),
|
|
2829
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
2830
|
+
attempts: nextAttempts
|
|
2831
|
+
});
|
|
2832
|
+
throw err;
|
|
2833
|
+
}
|
|
2834
|
+
};
|
|
2835
|
+
}
|
|
2836
|
+
makeSpawnFn(run) {
|
|
2837
|
+
return async (type, input, opts) => {
|
|
2838
|
+
return this.orchestrator.start(type, input, {
|
|
2839
|
+
parentRunId: run.id,
|
|
2840
|
+
parentClosePolicy: opts?.closePolicy,
|
|
2841
|
+
runAt: opts?.runAt,
|
|
2842
|
+
priority: opts?.priority,
|
|
2843
|
+
tags: opts?.tags,
|
|
2844
|
+
triggerSource: "parent",
|
|
2845
|
+
triggerRef: run.id,
|
|
2846
|
+
tenantId: run.tenantId
|
|
2847
|
+
});
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
|
|
2261
2852
|
// runtime/subsystems/jobs/job-worker.module.ts
|
|
2262
2853
|
var DEFAULT_SHUTDOWN_TIMEOUT_MS2 = 3e4;
|
|
2263
2854
|
var JOB_WORKER_MODULE_OPTIONS = /* @__PURE__ */ Symbol("JOB_WORKER_MODULE_OPTIONS");
|
|
2264
2855
|
var JobWorkerOrchestrator = class {
|
|
2265
|
-
constructor(orchestrator, runService, stepService, options, db = null, moduleRef) {
|
|
2856
|
+
constructor(orchestrator, runService, stepService, options, db = null, moduleRef, bullConnection = null, bullConfig = null) {
|
|
2266
2857
|
this.orchestrator = orchestrator;
|
|
2267
2858
|
this.runService = runService;
|
|
2268
2859
|
this.stepService = stepService;
|
|
2269
2860
|
this.options = options;
|
|
2270
2861
|
this.db = db;
|
|
2271
2862
|
this.moduleRef = moduleRef;
|
|
2863
|
+
this.bullConnection = bullConnection;
|
|
2864
|
+
this.bullConfig = bullConfig;
|
|
2272
2865
|
}
|
|
2273
2866
|
orchestrator;
|
|
2274
2867
|
runService;
|
|
@@ -2276,7 +2869,9 @@ var JobWorkerOrchestrator = class {
|
|
|
2276
2869
|
options;
|
|
2277
2870
|
db;
|
|
2278
2871
|
moduleRef;
|
|
2279
|
-
|
|
2872
|
+
bullConnection;
|
|
2873
|
+
bullConfig;
|
|
2874
|
+
logger = new Logger6(JobWorkerOrchestrator.name);
|
|
2280
2875
|
workers = [];
|
|
2281
2876
|
// ============================================================================
|
|
2282
2877
|
// Lifecycle
|
|
@@ -2293,7 +2888,7 @@ var JobWorkerOrchestrator = class {
|
|
|
2293
2888
|
if (backend !== "memory" && orphaned.length > 0) {
|
|
2294
2889
|
throw new BootValidationError(orphaned);
|
|
2295
2890
|
}
|
|
2296
|
-
const activePools = this.options.pools
|
|
2891
|
+
const activePools = this.options.pools ? this.options.pools : this.options.allPools ? allPoolNames(poolConfig) : allNonReservedPoolNames(poolConfig);
|
|
2297
2892
|
for (const poolName of activePools) {
|
|
2298
2893
|
const def = poolConfig.get(poolName);
|
|
2299
2894
|
if (!def) {
|
|
@@ -2306,11 +2901,11 @@ var JobWorkerOrchestrator = class {
|
|
|
2306
2901
|
concurrency: def.concurrency,
|
|
2307
2902
|
shutdownTimeoutMs: this.options.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS2
|
|
2308
2903
|
};
|
|
2309
|
-
const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : this.spawnWorker(workerOptions);
|
|
2310
|
-
worker.onModuleInit();
|
|
2904
|
+
const worker = this.options.workerFactory ? this.options.workerFactory(workerOptions) : backend === "bullmq" ? this.spawnBullMQWorker(poolName, def.queue, def.concurrency, poolConfig) : this.spawnWorker(workerOptions);
|
|
2905
|
+
await worker.onModuleInit();
|
|
2311
2906
|
this.workers.push(worker);
|
|
2312
2907
|
this.logger.log(
|
|
2313
|
-
`JobWorker started: pool='${poolName}' (queue='${def.queue}') concurrency=${def.concurrency}`
|
|
2908
|
+
`JobWorker started: pool='${poolName}' (queue='${def.queue}') concurrency=${def.concurrency} backend='${backend}'`
|
|
2314
2909
|
);
|
|
2315
2910
|
}
|
|
2316
2911
|
}
|
|
@@ -2327,6 +2922,16 @@ var JobWorkerOrchestrator = class {
|
|
|
2327
2922
|
}
|
|
2328
2923
|
}
|
|
2329
2924
|
this.workers.length = 0;
|
|
2925
|
+
const orch = this.orchestrator;
|
|
2926
|
+
if (typeof orch.closeConnections === "function") {
|
|
2927
|
+
try {
|
|
2928
|
+
await orch.closeConnections();
|
|
2929
|
+
} catch (err) {
|
|
2930
|
+
this.logger.error(
|
|
2931
|
+
`BullMQ orchestrator connection close failed: ${err.message}`
|
|
2932
|
+
);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2330
2935
|
}
|
|
2331
2936
|
// ============================================================================
|
|
2332
2937
|
// Internals
|
|
@@ -2388,15 +2993,57 @@ var JobWorkerOrchestrator = class {
|
|
|
2388
2993
|
this.moduleRef
|
|
2389
2994
|
);
|
|
2390
2995
|
}
|
|
2996
|
+
/**
|
|
2997
|
+
* BULLMQ-1 — spawn a per-pool `BullMQJobWorker`. Requires the Drizzle
|
|
2998
|
+
* client (the worker drives `job_run` as the source of truth) AND the
|
|
2999
|
+
* resolved BullMQ connection (bound by `JobsDomainModule` when
|
|
3000
|
+
* `backend: 'bullmq'`). The queue name is derived identically to the
|
|
3001
|
+
* orchestrator's `dispatch` via `resolvePoolQueueName(pool, …)` so producer
|
|
3002
|
+
* and consumer agree.
|
|
3003
|
+
*/
|
|
3004
|
+
spawnBullMQWorker(pool, _queueAlias, concurrency, poolConfig) {
|
|
3005
|
+
if (!this.db) {
|
|
3006
|
+
throw new Error(
|
|
3007
|
+
`JobWorkerModule: BullMQ worker spawning requires the Drizzle client (no DRIZZLE provider available) \u2014 job_run remains the source of truth.`
|
|
3008
|
+
);
|
|
3009
|
+
}
|
|
3010
|
+
if (!this.bullConnection) {
|
|
3011
|
+
throw new Error(
|
|
3012
|
+
`JobWorkerModule: BullMQ worker spawning requires a resolved BULLMQ_CONNECTION. Ensure JobsDomainModule was booted with backend: 'bullmq'.`
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
if (!this.moduleRef) {
|
|
3016
|
+
throw new Error(
|
|
3017
|
+
`JobWorkerModule: ModuleRef not available \u2014 cannot construct BullMQJobWorker with handler DI support.`
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
const queueName = resolvePoolQueueName(pool, this.bullConfig, poolConfig);
|
|
3021
|
+
return new BullMQJobWorker(
|
|
3022
|
+
this.db,
|
|
3023
|
+
this.orchestrator,
|
|
3024
|
+
this.stepService,
|
|
3025
|
+
{
|
|
3026
|
+
pool,
|
|
3027
|
+
queueName,
|
|
3028
|
+
concurrency,
|
|
3029
|
+
connection: this.bullConnection
|
|
3030
|
+
},
|
|
3031
|
+
this.moduleRef
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
2391
3034
|
};
|
|
2392
3035
|
JobWorkerOrchestrator = __decorateClass([
|
|
2393
|
-
|
|
2394
|
-
__decorateParam(0,
|
|
2395
|
-
__decorateParam(1,
|
|
2396
|
-
__decorateParam(2,
|
|
2397
|
-
__decorateParam(3,
|
|
2398
|
-
__decorateParam(4,
|
|
2399
|
-
__decorateParam(4,
|
|
3036
|
+
Injectable9(),
|
|
3037
|
+
__decorateParam(0, Inject8(JOB_ORCHESTRATOR)),
|
|
3038
|
+
__decorateParam(1, Inject8(JOB_RUN_SERVICE)),
|
|
3039
|
+
__decorateParam(2, Inject8(JOB_STEP_SERVICE)),
|
|
3040
|
+
__decorateParam(3, Inject8(JOB_WORKER_MODULE_OPTIONS)),
|
|
3041
|
+
__decorateParam(4, Optional3()),
|
|
3042
|
+
__decorateParam(4, Inject8(DRIZZLE)),
|
|
3043
|
+
__decorateParam(6, Optional3()),
|
|
3044
|
+
__decorateParam(6, Inject8(BULLMQ_CONNECTION)),
|
|
3045
|
+
__decorateParam(7, Optional3()),
|
|
3046
|
+
__decorateParam(7, Inject8(BULLMQ_RESOLVED_CONFIG))
|
|
2400
3047
|
], JobWorkerOrchestrator);
|
|
2401
3048
|
var JobWorkerModule = class {
|
|
2402
3049
|
static forRoot(opts) {
|
|
@@ -2413,7 +3060,14 @@ var JobWorkerModule = class {
|
|
|
2413
3060
|
{ provide: JOB_WORKER_MODULE_OPTIONS, useValue: opts },
|
|
2414
3061
|
JobWorkerOrchestrator
|
|
2415
3062
|
],
|
|
2416
|
-
|
|
3063
|
+
// BULLMQ-1 Phase 1 — export the options token so `BridgeModule`'s
|
|
3064
|
+
// reserved-pool guard (`onModuleInit`) can actually inject it.
|
|
3065
|
+
// Previously `exports: []` left the `@Optional()` inject resolving to
|
|
3066
|
+
// `undefined` and the guard silently no-opped (a dead check). With the
|
|
3067
|
+
// token exported the guard fires for real; consumers that omit the
|
|
3068
|
+
// reserved pools (and don't set `allPools`) now fail fast with
|
|
3069
|
+
// `BridgeReservedPoolsNotPolledError` — which is correct.
|
|
3070
|
+
exports: [JOB_WORKER_MODULE_OPTIONS]
|
|
2417
3071
|
};
|
|
2418
3072
|
}
|
|
2419
3073
|
};
|
|
@@ -2575,8 +3229,8 @@ var MemoryBridgeDeliveryRepo = class {
|
|
|
2575
3229
|
};
|
|
2576
3230
|
|
|
2577
3231
|
// runtime/subsystems/bridge/bridge-delivery.drizzle-backend.ts
|
|
2578
|
-
import { Inject as
|
|
2579
|
-
import { eq as
|
|
3232
|
+
import { Inject as Inject9, Injectable as Injectable10, Optional as Optional4 } from "@nestjs/common";
|
|
3233
|
+
import { eq as eq7, and as and5, gte as gte2, isNull as isNull2, sql as sql7 } from "drizzle-orm";
|
|
2580
3234
|
|
|
2581
3235
|
// runtime/subsystems/bridge/bridge-delivery.schema.ts
|
|
2582
3236
|
import {
|
|
@@ -2763,14 +3417,14 @@ var DrizzleBridgeDeliveryRepo = class {
|
|
|
2763
3417
|
async findDelivery(eventId, triggerId) {
|
|
2764
3418
|
const rows = await this.db.select().from(bridgeDelivery).where(
|
|
2765
3419
|
and5(
|
|
2766
|
-
|
|
2767
|
-
|
|
3420
|
+
eq7(bridgeDelivery.eventId, eventId),
|
|
3421
|
+
eq7(bridgeDelivery.triggerId, triggerId)
|
|
2768
3422
|
)
|
|
2769
3423
|
).limit(1);
|
|
2770
3424
|
return rows[0] ?? null;
|
|
2771
3425
|
}
|
|
2772
3426
|
async findDeliveryById(id) {
|
|
2773
|
-
const rows = await this.db.select().from(bridgeDelivery).where(
|
|
3427
|
+
const rows = await this.db.select().from(bridgeDelivery).where(eq7(bridgeDelivery.id, id)).limit(1);
|
|
2774
3428
|
return rows[0] ?? null;
|
|
2775
3429
|
}
|
|
2776
3430
|
async markDelivered(id, userRunId, tx) {
|
|
@@ -2779,15 +3433,15 @@ var DrizzleBridgeDeliveryRepo = class {
|
|
|
2779
3433
|
status: "delivered",
|
|
2780
3434
|
userRunId,
|
|
2781
3435
|
deliveredAt: /* @__PURE__ */ new Date()
|
|
2782
|
-
}).where(
|
|
3436
|
+
}).where(eq7(bridgeDelivery.id, id));
|
|
2783
3437
|
}
|
|
2784
3438
|
async markSkipped(id, reason, tx) {
|
|
2785
3439
|
const client = tx ?? this.db;
|
|
2786
|
-
await client.update(bridgeDelivery).set({ status: "skipped", skipReason: reason }).where(
|
|
3440
|
+
await client.update(bridgeDelivery).set({ status: "skipped", skipReason: reason }).where(eq7(bridgeDelivery.id, id));
|
|
2787
3441
|
}
|
|
2788
3442
|
async markFailed(id, error, tx) {
|
|
2789
3443
|
const client = tx ?? this.db;
|
|
2790
|
-
await client.update(bridgeDelivery).set({ status: "failed", error }).where(
|
|
3444
|
+
await client.update(bridgeDelivery).set({ status: "failed", error }).where(eq7(bridgeDelivery.id, id));
|
|
2791
3445
|
}
|
|
2792
3446
|
/**
|
|
2793
3447
|
* Observability read — see `IJobBridge.getStatusHistogram` JSDoc for the
|
|
@@ -2811,11 +3465,11 @@ var DrizzleBridgeDeliveryRepo = class {
|
|
|
2811
3465
|
throw new RangeError("windowHours must be positive");
|
|
2812
3466
|
}
|
|
2813
3467
|
const cutoff = sql7`now() - make_interval(hours => ${windowHours})`;
|
|
2814
|
-
const conditions = [
|
|
3468
|
+
const conditions = [gte2(bridgeDelivery.attemptedAt, cutoff)];
|
|
2815
3469
|
if (tenantId === null) {
|
|
2816
3470
|
conditions.push(isNull2(bridgeDelivery.tenantId));
|
|
2817
3471
|
} else if (typeof tenantId === "string") {
|
|
2818
|
-
conditions.push(
|
|
3472
|
+
conditions.push(eq7(bridgeDelivery.tenantId, tenantId));
|
|
2819
3473
|
}
|
|
2820
3474
|
const rows = await this.db.select({
|
|
2821
3475
|
status: bridgeDelivery.status,
|
|
@@ -2834,18 +3488,18 @@ var DrizzleBridgeDeliveryRepo = class {
|
|
|
2834
3488
|
}
|
|
2835
3489
|
};
|
|
2836
3490
|
DrizzleBridgeDeliveryRepo = __decorateClass([
|
|
2837
|
-
|
|
2838
|
-
__decorateParam(0,
|
|
2839
|
-
__decorateParam(1,
|
|
2840
|
-
__decorateParam(1,
|
|
3491
|
+
Injectable10(),
|
|
3492
|
+
__decorateParam(0, Inject9(DRIZZLE)),
|
|
3493
|
+
__decorateParam(1, Optional4()),
|
|
3494
|
+
__decorateParam(1, Inject9(BRIDGE_MULTI_TENANT))
|
|
2841
3495
|
], DrizzleBridgeDeliveryRepo);
|
|
2842
3496
|
|
|
2843
3497
|
// runtime/subsystems/bridge/bridge-outbox-drain-hook.ts
|
|
2844
|
-
import { Inject as
|
|
3498
|
+
import { Inject as Inject11, Injectable as Injectable12, Logger as Logger8, Optional as Optional6 } from "@nestjs/common";
|
|
2845
3499
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2846
3500
|
|
|
2847
3501
|
// runtime/subsystems/bridge/bridge-delivery-handler.ts
|
|
2848
|
-
import { Inject as
|
|
3502
|
+
import { Inject as Inject10, Injectable as Injectable11, Logger as Logger7, Optional as Optional5 } from "@nestjs/common";
|
|
2849
3503
|
|
|
2850
3504
|
// runtime/subsystems/events/events.tokens.ts
|
|
2851
3505
|
var EVENT_BUS = "EVENT_BUS";
|
|
@@ -2867,7 +3521,7 @@ var BridgeDeliveryHandler = class extends JobHandlerBase {
|
|
|
2867
3521
|
events;
|
|
2868
3522
|
registry;
|
|
2869
3523
|
multiTenant;
|
|
2870
|
-
classLogger = new
|
|
3524
|
+
classLogger = new Logger7(BridgeDeliveryHandler.name);
|
|
2871
3525
|
async run(ctx) {
|
|
2872
3526
|
const { deliveryId } = ctx.input;
|
|
2873
3527
|
const delivery = await this.repo.findDeliveryById(deliveryId);
|
|
@@ -2928,18 +3582,18 @@ var BridgeDeliveryHandler = class extends JobHandlerBase {
|
|
|
2928
3582
|
}
|
|
2929
3583
|
};
|
|
2930
3584
|
BridgeDeliveryHandler = __decorateClass([
|
|
2931
|
-
|
|
3585
|
+
Injectable11(),
|
|
2932
3586
|
JobHandler(BRIDGE_DELIVERY_JOB_TYPE, {
|
|
2933
3587
|
pool: "events_change",
|
|
2934
3588
|
retry: { attempts: 3, backoff: "exponential", baseMs: 250 },
|
|
2935
3589
|
replayFrom: "last_step"
|
|
2936
3590
|
}),
|
|
2937
|
-
__decorateParam(0,
|
|
2938
|
-
__decorateParam(1,
|
|
2939
|
-
__decorateParam(2,
|
|
2940
|
-
__decorateParam(3,
|
|
2941
|
-
__decorateParam(4,
|
|
2942
|
-
__decorateParam(4,
|
|
3591
|
+
__decorateParam(0, Inject10(BRIDGE_DELIVERY_REPO)),
|
|
3592
|
+
__decorateParam(1, Inject10(JOB_ORCHESTRATOR)),
|
|
3593
|
+
__decorateParam(2, Inject10(EVENT_BUS)),
|
|
3594
|
+
__decorateParam(3, Inject10(BRIDGE_REGISTRY)),
|
|
3595
|
+
__decorateParam(4, Optional5()),
|
|
3596
|
+
__decorateParam(4, Inject10(BRIDGE_MULTI_TENANT))
|
|
2943
3597
|
], BridgeDeliveryHandler);
|
|
2944
3598
|
|
|
2945
3599
|
// runtime/subsystems/bridge/bridge-outbox-drain-hook.ts
|
|
@@ -2953,7 +3607,7 @@ var BridgeOutboxDrainHook = class {
|
|
|
2953
3607
|
this.registry = registry;
|
|
2954
3608
|
}
|
|
2955
3609
|
registry;
|
|
2956
|
-
logger = new
|
|
3610
|
+
logger = new Logger8(BridgeOutboxDrainHook.name);
|
|
2957
3611
|
warnedNullDirection = false;
|
|
2958
3612
|
warnedAuditTypes = /* @__PURE__ */ new Set();
|
|
2959
3613
|
async processEvent(event, tx) {
|
|
@@ -3046,13 +3700,13 @@ var BridgeOutboxDrainHook = class {
|
|
|
3046
3700
|
}
|
|
3047
3701
|
};
|
|
3048
3702
|
BridgeOutboxDrainHook = __decorateClass([
|
|
3049
|
-
|
|
3050
|
-
__decorateParam(0,
|
|
3051
|
-
__decorateParam(0,
|
|
3703
|
+
Injectable12(),
|
|
3704
|
+
__decorateParam(0, Optional6()),
|
|
3705
|
+
__decorateParam(0, Inject11(BRIDGE_REGISTRY))
|
|
3052
3706
|
], BridgeOutboxDrainHook);
|
|
3053
3707
|
|
|
3054
3708
|
// runtime/subsystems/bridge/event-flow.service.ts
|
|
3055
|
-
import { Inject as
|
|
3709
|
+
import { Inject as Inject12, Injectable as Injectable13, Optional as Optional7 } from "@nestjs/common";
|
|
3056
3710
|
var EventFlowService = class {
|
|
3057
3711
|
constructor(db, eventBus, orchestrator, bridgeRepo, registry = {}, multiTenant = false) {
|
|
3058
3712
|
this.db = db;
|
|
@@ -3123,15 +3777,15 @@ var EventFlowService = class {
|
|
|
3123
3777
|
}
|
|
3124
3778
|
};
|
|
3125
3779
|
EventFlowService = __decorateClass([
|
|
3126
|
-
|
|
3127
|
-
__decorateParam(0,
|
|
3128
|
-
__decorateParam(1,
|
|
3129
|
-
__decorateParam(2,
|
|
3130
|
-
__decorateParam(3,
|
|
3131
|
-
__decorateParam(4,
|
|
3132
|
-
__decorateParam(4,
|
|
3133
|
-
__decorateParam(5,
|
|
3134
|
-
__decorateParam(5,
|
|
3780
|
+
Injectable13(),
|
|
3781
|
+
__decorateParam(0, Inject12(DRIZZLE)),
|
|
3782
|
+
__decorateParam(1, Inject12(EVENT_BUS)),
|
|
3783
|
+
__decorateParam(2, Inject12(JOB_ORCHESTRATOR)),
|
|
3784
|
+
__decorateParam(3, Inject12(BRIDGE_DELIVERY_REPO)),
|
|
3785
|
+
__decorateParam(4, Optional7()),
|
|
3786
|
+
__decorateParam(4, Inject12(BRIDGE_REGISTRY)),
|
|
3787
|
+
__decorateParam(5, Optional7()),
|
|
3788
|
+
__decorateParam(5, Inject12(BRIDGE_MULTI_TENANT))
|
|
3135
3789
|
], EventFlowService);
|
|
3136
3790
|
|
|
3137
3791
|
// runtime/subsystems/bridge/generated/registry.ts
|
|
@@ -3197,6 +3851,7 @@ var BridgeModule = class {
|
|
|
3197
3851
|
}
|
|
3198
3852
|
async onModuleInit() {
|
|
3199
3853
|
if (!this.workerOpts) return;
|
|
3854
|
+
if (this.workerOpts.allPools) return;
|
|
3200
3855
|
const activePools = this.workerOpts.pools ?? [];
|
|
3201
3856
|
const missing = BRIDGE_RESERVED_POOLS.filter(
|
|
3202
3857
|
(p) => !activePools.includes(p)
|
|
@@ -3208,8 +3863,8 @@ var BridgeModule = class {
|
|
|
3208
3863
|
};
|
|
3209
3864
|
BridgeModule = __decorateClass([
|
|
3210
3865
|
Module3({}),
|
|
3211
|
-
__decorateParam(0,
|
|
3212
|
-
__decorateParam(0,
|
|
3866
|
+
__decorateParam(0, Optional8()),
|
|
3867
|
+
__decorateParam(0, Inject13(JOB_WORKER_MODULE_OPTIONS))
|
|
3213
3868
|
], BridgeModule);
|
|
3214
3869
|
export {
|
|
3215
3870
|
BridgeModule
|