@pattern-stack/codegen 0.16.1 → 0.17.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.
- package/CHANGELOG.md +121 -0
- package/consumer-skills/entities/families-and-queries.md +5 -3
- package/consumer-skills/integration/audit-and-detection.md +29 -4
- package/dist/{chunk-H6FO2ZDJ.js → chunk-4PFF3ED4.js} +4 -4
- package/dist/{chunk-CO6LUM72.js → chunk-7P5ODGLA.js} +34 -2
- package/dist/chunk-7P5ODGLA.js.map +1 -0
- package/dist/{chunk-QSJ3J4HE.js → chunk-BHZP6LOV.js} +7 -7
- package/dist/{chunk-RUSUZZAF.js → chunk-BK5ICA2F.js} +4 -4
- package/dist/{chunk-T4YJRD22.js → chunk-DUMI2J5M.js} +45 -14
- package/dist/chunk-DUMI2J5M.js.map +1 -0
- package/dist/{chunk-TKVTEUBD.js → chunk-EJBK7I4F.js} +2 -2
- package/dist/{chunk-IT6FRTEW.js → chunk-FVNAU7VO.js} +39 -18
- package/dist/chunk-FVNAU7VO.js.map +1 -0
- package/dist/{chunk-JM3T27ZW.js → chunk-FWRL7BZ5.js} +7 -7
- package/dist/{chunk-DGYTSCKN.js → chunk-HOIRY5XP.js} +14 -14
- package/dist/{chunk-AYC2HEAL.js → chunk-HPS554L4.js} +9 -9
- package/dist/{chunk-2WDX6I7T.js → chunk-IOQMMH6C.js} +16 -6
- package/dist/{chunk-2WDX6I7T.js.map → chunk-IOQMMH6C.js.map} +1 -1
- package/dist/{chunk-24WXSC3C.js → chunk-JA7GJDNI.js} +15 -9
- package/dist/chunk-JA7GJDNI.js.map +1 -0
- package/dist/{chunk-36U5UGIO.js → chunk-JEINYUJH.js} +8 -5
- package/dist/chunk-JEINYUJH.js.map +1 -0
- package/dist/{chunk-BOPZWRJK.js → chunk-JYBFPNBJ.js} +8 -8
- package/dist/chunk-JYBFPNBJ.js.map +1 -0
- package/dist/{chunk-K2I6XIK5.js → chunk-KSTZIULO.js} +4 -4
- package/dist/chunk-MKWQKKK7.js +72 -0
- package/dist/chunk-MKWQKKK7.js.map +1 -0
- package/dist/{chunk-CRBVI4GE.js → chunk-PSDVGPQR.js} +5 -5
- package/dist/{chunk-DLG62MQY.js → chunk-SFQRETXJ.js} +7 -7
- package/dist/{chunk-NXNVTXKG.js → chunk-SGSWVNNB.js} +5 -5
- package/dist/{chunk-5LXOJGO2.js → chunk-VNBC3VXM.js} +6 -6
- package/dist/{job-orchestrator.protocol-DubMVbm9.d.ts → job-orchestrator.protocol-ZuJ3ow-O.d.ts} +77 -3
- package/dist/runtime/base-classes/activity-entity-repository.d.ts +39 -7
- package/dist/runtime/base-classes/activity-entity-repository.js +1 -1
- package/dist/runtime/base-classes/activity-entity-service.d.ts +12 -10
- package/dist/runtime/base-classes/activity-entity-service.js +1 -1
- package/dist/runtime/base-classes/index.js +18 -18
- package/dist/runtime/shared/openapi/index.js +3 -3
- package/dist/runtime/subsystems/auth/index.js +3 -3
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +6 -6
- package/dist/runtime/subsystems/bridge/bridge.module.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +19 -19
- package/dist/runtime/subsystems/bridge/event-flow.service.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/index.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/index.js +21 -21
- package/dist/runtime/subsystems/cache/cache.module.js +1 -1
- package/dist/runtime/subsystems/cache/index.js +3 -3
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/events/events.module.js +3 -3
- package/dist/runtime/subsystems/events/index.js +3 -3
- package/dist/runtime/subsystems/index.d.ts +1 -1
- package/dist/runtime/subsystems/index.js +50 -50
- package/dist/runtime/subsystems/integration/deep-equal.differ.d.ts +19 -0
- package/dist/runtime/subsystems/integration/deep-equal.differ.js +1 -1
- package/dist/runtime/subsystems/integration/index.js +22 -22
- package/dist/runtime/subsystems/integration/integration.module.d.ts +20 -0
- package/dist/runtime/subsystems/integration/integration.module.js +4 -4
- package/dist/runtime/subsystems/jobs/index.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/index.js +43 -43
- package/dist/runtime/subsystems/jobs/job-handler.base.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-handler.base.js +11 -3
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +7 -6
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.js +4 -3
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +11 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-orchestrator.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-worker.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.js +3 -3
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.js +13 -13
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +11 -11
- package/dist/runtime/subsystems/jobs/jobs-errors.d.ts +1 -1
- package/dist/runtime/subsystems/observability/index.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/observability/observability.service.d.ts +1 -1
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +1 -1
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +1 -1
- package/dist/runtime/subsystems/storage/index.js +4 -4
- package/dist/runtime/subsystems/storage/storage.module.js +2 -2
- package/dist/src/cli/index.js +34 -12
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +23 -8
- package/dist/src/index.js +7 -7
- package/package.json +2 -1
- package/runtime/base-classes/activity-entity-repository.ts +72 -13
- package/runtime/base-classes/activity-entity-service.ts +14 -12
- package/runtime/subsystems/integration/deep-equal.differ.ts +34 -5
- package/runtime/subsystems/integration/integration.module.ts +26 -2
- package/runtime/subsystems/jobs/job-handler.base.ts +115 -2
- package/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.ts +43 -16
- package/runtime/subsystems/jobs/job-orchestrator.memory-backend.ts +58 -18
- package/src/patterns/library/activity.pattern.ts +40 -10
- package/templates/subsystem/integration-config/codegen-config-integration-block.ejs.t +17 -0
- package/dist/chunk-24WXSC3C.js.map +0 -1
- package/dist/chunk-36U5UGIO.js.map +0 -1
- package/dist/chunk-BOPZWRJK.js.map +0 -1
- package/dist/chunk-CO6LUM72.js.map +0 -1
- package/dist/chunk-IT6FRTEW.js.map +0 -1
- package/dist/chunk-T4YJRD22.js.map +0 -1
- package/dist/chunk-XCEI7NUH.js +0 -41
- package/dist/chunk-XCEI7NUH.js.map +0 -1
- /package/dist/{chunk-H6FO2ZDJ.js.map → chunk-4PFF3ED4.js.map} +0 -0
- /package/dist/{chunk-QSJ3J4HE.js.map → chunk-BHZP6LOV.js.map} +0 -0
- /package/dist/{chunk-RUSUZZAF.js.map → chunk-BK5ICA2F.js.map} +0 -0
- /package/dist/{chunk-TKVTEUBD.js.map → chunk-EJBK7I4F.js.map} +0 -0
- /package/dist/{chunk-JM3T27ZW.js.map → chunk-FWRL7BZ5.js.map} +0 -0
- /package/dist/{chunk-DGYTSCKN.js.map → chunk-HOIRY5XP.js.map} +0 -0
- /package/dist/{chunk-AYC2HEAL.js.map → chunk-HPS554L4.js.map} +0 -0
- /package/dist/{chunk-K2I6XIK5.js.map → chunk-KSTZIULO.js.map} +0 -0
- /package/dist/{chunk-CRBVI4GE.js.map → chunk-PSDVGPQR.js.map} +0 -0
- /package/dist/{chunk-DLG62MQY.js.map → chunk-SFQRETXJ.js.map} +0 -0
- /package/dist/{chunk-NXNVTXKG.js.map → chunk-SGSWVNNB.js.map} +0 -0
- /package/dist/{chunk-5LXOJGO2.js.map → chunk-VNBC3VXM.js.map} +0 -0
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
jobRuns,
|
|
3
|
-
jobSteps,
|
|
4
|
-
jobs
|
|
5
|
-
} from "./chunk-OKXZ63IA.js";
|
|
6
|
-
import {
|
|
7
|
-
JOBS_LISTEN_NOTIFY,
|
|
8
|
-
JOBS_MULTI_TENANT
|
|
9
|
-
} from "./chunk-ZPL74UQN.js";
|
|
10
1
|
import {
|
|
11
2
|
JobCollisionError,
|
|
12
3
|
JobNotReplayableError,
|
|
@@ -14,6 +5,19 @@ import {
|
|
|
14
5
|
JobTypeNotFoundError,
|
|
15
6
|
MissingTenantIdError
|
|
16
7
|
} from "./chunk-T4BIIU5E.js";
|
|
8
|
+
import {
|
|
9
|
+
JOBS_LISTEN_NOTIFY,
|
|
10
|
+
JOBS_MULTI_TENANT
|
|
11
|
+
} from "./chunk-ZPL74UQN.js";
|
|
12
|
+
import {
|
|
13
|
+
keySelectorToTemplate,
|
|
14
|
+
resolveJobKey
|
|
15
|
+
} from "./chunk-7P5ODGLA.js";
|
|
16
|
+
import {
|
|
17
|
+
jobRuns,
|
|
18
|
+
jobSteps,
|
|
19
|
+
jobs
|
|
20
|
+
} from "./chunk-OKXZ63IA.js";
|
|
17
21
|
import {
|
|
18
22
|
JOBS_WAKE_CHANNEL,
|
|
19
23
|
pgNotify
|
|
@@ -81,7 +85,13 @@ var DrizzleJobOrchestrator = class {
|
|
|
81
85
|
if (!def) throw new JobTypeNotFoundError(type);
|
|
82
86
|
const definition = def;
|
|
83
87
|
if (definition.dedupeKeyTemplate && definition.dedupeWindowMs) {
|
|
84
|
-
const dedupeKey2 =
|
|
88
|
+
const dedupeKey2 = resolveJobKey(
|
|
89
|
+
"dedupe",
|
|
90
|
+
type,
|
|
91
|
+
definition.dedupeKeyTemplate,
|
|
92
|
+
payload,
|
|
93
|
+
evaluateKeyTemplate
|
|
94
|
+
);
|
|
85
95
|
const windowStart = new Date(Date.now() - definition.dedupeWindowMs);
|
|
86
96
|
const existing = await client.select().from(jobRuns).where(
|
|
87
97
|
and(
|
|
@@ -98,9 +108,12 @@ var DrizzleJobOrchestrator = class {
|
|
|
98
108
|
}
|
|
99
109
|
let concurrencyKey = null;
|
|
100
110
|
if (definition.concurrencyKeyTemplate) {
|
|
101
|
-
concurrencyKey =
|
|
111
|
+
concurrencyKey = resolveJobKey(
|
|
112
|
+
"concurrency",
|
|
113
|
+
type,
|
|
102
114
|
definition.concurrencyKeyTemplate,
|
|
103
|
-
payload
|
|
115
|
+
payload,
|
|
116
|
+
evaluateKeyTemplate
|
|
104
117
|
);
|
|
105
118
|
const inFlight = await client.select().from(jobRuns).where(
|
|
106
119
|
and(
|
|
@@ -136,7 +149,13 @@ var DrizzleJobOrchestrator = class {
|
|
|
136
149
|
}
|
|
137
150
|
rootRunId = parent.rootRunId;
|
|
138
151
|
}
|
|
139
|
-
const dedupeKey =
|
|
152
|
+
const dedupeKey = resolveJobKey(
|
|
153
|
+
"dedupe",
|
|
154
|
+
type,
|
|
155
|
+
definition.dedupeKeyTemplate,
|
|
156
|
+
payload,
|
|
157
|
+
evaluateKeyTemplate
|
|
158
|
+
);
|
|
140
159
|
const [inserted] = await client.insert(jobRuns).values({
|
|
141
160
|
id: newId,
|
|
142
161
|
jobType: type,
|
|
@@ -293,11 +312,13 @@ var DrizzleJobOrchestrator = class {
|
|
|
293
312
|
backoff: "fixed",
|
|
294
313
|
baseMs: 0
|
|
295
314
|
};
|
|
296
|
-
const
|
|
297
|
-
|
|
315
|
+
const concurrencyKeyTemplateStr = keySelectorToTemplate(
|
|
316
|
+
meta.concurrency?.key
|
|
317
|
+
);
|
|
298
318
|
const collisionMode = meta.concurrency?.collisionMode ?? "queue";
|
|
299
|
-
const
|
|
300
|
-
|
|
319
|
+
const dedupeKeyTemplateStr = keySelectorToTemplate(
|
|
320
|
+
meta.dedupe?.key
|
|
321
|
+
);
|
|
301
322
|
const dedupeWindowMs = meta.dedupe?.windowMs ?? null;
|
|
302
323
|
const timeoutMs = meta.timeoutMs ?? null;
|
|
303
324
|
const replayFrom = meta.replayFrom ?? "last_checkpoint";
|
|
@@ -372,4 +393,4 @@ export {
|
|
|
372
393
|
evaluateKeyTemplate,
|
|
373
394
|
DrizzleJobOrchestrator
|
|
374
395
|
};
|
|
375
|
-
//# sourceMappingURL=chunk-
|
|
396
|
+
//# sourceMappingURL=chunk-FVNAU7VO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../runtime/subsystems/jobs/job-orchestrator.drizzle-backend.ts"],"sourcesContent":["/**\n * DrizzleJobOrchestrator — Postgres-backed implementation of\n * `IJobOrchestrator` (ADR-022, JOB-3).\n *\n * Single-layer architecture: `start` writes a single `job_run` row; the\n * `JobWorker` polling loop claims it directly via `FOR UPDATE SKIP LOCKED`.\n * No `job_queue` table, no executor port. See `docs/specs/JOB-3.md`.\n */\nimport { randomUUID } from 'node:crypto';\nimport { Inject, Injectable, Logger, Optional } from '@nestjs/common';\nimport { and, desc, eq, gt, inArray, isNotNull, ne, notInArray, sql } from 'drizzle-orm';\nimport type { DrizzleClient } from '../../types/drizzle';\nimport type { DrizzleTransaction } from '../events/event-bus.protocol';\nimport { DRIZZLE } from '../../constants/tokens';\nimport {\n jobRuns,\n jobs,\n type JobDefinitionRow,\n type JobRunRow,\n} from './job-orchestration.schema';\nimport type {\n CancelOptions,\n IJobOrchestrator,\n JobPoolDef,\n JobRun,\n JobUpsertEntry,\n StartOptions,\n} from './job-orchestrator.protocol';\nimport {\n JobCollisionError,\n JobNotReplayableError,\n JobTemplateFieldMissingError,\n JobTypeNotFoundError,\n MissingTenantIdError,\n} from './jobs-errors';\nimport { jobSteps } from './job-orchestration.schema';\nimport { JOBS_MULTI_TENANT, JOBS_LISTEN_NOTIFY } from './jobs-domain.tokens';\nimport { JOBS_WAKE_CHANNEL, pgNotify } from './pg-notify';\nimport {\n keySelectorToTemplate,\n resolveJobKey,\n type JobKeySelector,\n} from './job-handler.base';\n\n/**\n * Terminal statuses — transitions into these are final. Used by `cancel`\n * (to short-circuit idempotently) and by `replay` (as the guard gate).\n */\nexport const TERMINAL_STATUSES = [\n 'completed',\n 'failed',\n 'timed_out',\n 'canceled',\n] as const;\ntype TerminalStatus = (typeof TERMINAL_STATUSES)[number];\ntype JobRunStatus = JobRunRow['status'];\n\n/** Statuses excluded from dedupe window matches per ADR-022. */\nconst DEDUPE_EXCLUDED_STATUSES: JobRunStatus[] = ['canceled', 'failed'];\n/** Statuses that count as in-flight for concurrency collision checks. */\nconst IN_FLIGHT_STATUSES: JobRunStatus[] = ['pending', 'running'];\n\n/**\n * Substitute `{{field}}` placeholders against the input payload.\n *\n * Implementation decision (JOB-3, 2026-04-19): simple `{{field}}` single-key\n * substitution, no dotted paths, no Mustache/Handlebars dependency. A missing\n * field throws `JobTemplateFieldMissingError` synchronously — cheaper than\n * discovering the misconfiguration at claim time.\n */\nexport function evaluateKeyTemplate(\n template: string,\n input: Record<string, unknown>,\n): string {\n return template.replace(/\\{\\{\\s*([a-zA-Z0-9_]+)\\s*\\}\\}/g, (_match, field: string) => {\n const value = input[field];\n if (value === undefined || value === null) {\n throw new JobTemplateFieldMissingError(template, field);\n }\n return String(value);\n });\n}\n\n@Injectable()\nexport class DrizzleJobOrchestrator implements IJobOrchestrator {\n // TODO(logging-subsystem): swap to ILogger once ADR-028 lands\n private readonly logger = new Logger(DrizzleJobOrchestrator.name);\n\n constructor(\n @Inject(DRIZZLE) private readonly db: DrizzleClient,\n @Inject(JOBS_MULTI_TENANT) private readonly multiTenant: boolean,\n // LISTEN-NOTIFY-1 — when true, `start()` emits an in-tx\n // `pg_notify(codegen_jobs_wake, <pool>)` so a `listen_notify` worker wakes\n // on enqueue-commit. `@Optional()` defaulting to false so direct\n // construction (integration tests not going through DI) keeps working.\n @Optional()\n @Inject(JOBS_LISTEN_NOTIFY)\n private readonly listenNotify: boolean = false,\n ) {}\n\n /**\n * JOB-8 — resolve `tenantId` for a mutating / targeted-read call.\n * Returns the tenant value that should be written to the row (or compared\n * against in a WHERE clause). When `multiTenant` is off, the column is\n * forced to `null` regardless of what callers pass. When on, `undefined`\n * throws; `null` and strings pass through untouched.\n */\n private resolveTenantId(\n method: string,\n tenantId: string | null | undefined,\n ): string | null {\n if (!this.multiTenant) return null;\n if (tenantId === undefined) throw new MissingTenantIdError(method);\n return tenantId;\n }\n\n // ==========================================================================\n // start\n // ==========================================================================\n\n async start(\n type: string,\n input: unknown,\n opts: StartOptions = {},\n tx?: DrizzleTransaction,\n ): Promise<JobRun> {\n const payload = (input ?? {}) as Record<string, unknown>;\n\n // JOB-8 — resolve tenant gate up front so `multi_tenant=true` +\n // undefined surfaces before any row is touched.\n const tenantId = this.resolveTenantId('start', opts.tenantId);\n\n // BRIDGE-7: thread the optional caller tx through every read/write\n // in this method so EventFlowService.publishAndStart can bundle the\n // outbox insert, the eager job_run insert, and (for Case B) the\n // bridge_delivery pre-write into a single transaction.\n const client = (tx ?? this.db) as DrizzleClient;\n\n // 1a. Load job definition.\n const [def] = await client\n .select()\n .from(jobs)\n .where(eq(jobs.type, type))\n .limit(1);\n if (!def) throw new JobTypeNotFoundError(type);\n const definition = def as JobDefinitionRow;\n\n // 1b. Dedupe check. JOB-FN-KEY: `resolveJobKey` honors both the `{{field}}`\n // template AND a function key persisted as `FN_KEY_SENTINEL` (re-resolved\n // live from JOB_HANDLER_REGISTRY).\n if (definition.dedupeKeyTemplate && definition.dedupeWindowMs) {\n const dedupeKey = resolveJobKey(\n 'dedupe',\n type,\n definition.dedupeKeyTemplate,\n payload,\n evaluateKeyTemplate,\n ) as string;\n const windowStart = new Date(Date.now() - definition.dedupeWindowMs);\n const existing = await client\n .select()\n .from(jobRuns)\n .where(\n and(\n eq(jobRuns.jobType, type),\n eq(jobRuns.dedupeKey, dedupeKey),\n gt(jobRuns.createdAt, windowStart),\n // status NOT IN ('canceled', 'failed')\n notInStatus(DEDUPE_EXCLUDED_STATUSES),\n ),\n )\n .orderBy(desc(jobRuns.createdAt))\n .limit(1);\n if (existing.length > 0) {\n return existing[0] as JobRun;\n }\n }\n\n // 1c. Concurrency collision check.\n let concurrencyKey: string | null = null;\n if (definition.concurrencyKeyTemplate) {\n // Non-null cast: the branch guard proves the template is present, so the\n // resolver never returns null here (it only nulls on a null template).\n concurrencyKey = resolveJobKey(\n 'concurrency',\n type,\n definition.concurrencyKeyTemplate,\n payload,\n evaluateKeyTemplate,\n ) as string;\n const inFlight = await client\n .select()\n .from(jobRuns)\n .where(\n and(\n eq(jobRuns.concurrencyKey, concurrencyKey),\n inArray(jobRuns.status, IN_FLIGHT_STATUSES),\n ),\n )\n .limit(1);\n if (inFlight.length > 0) {\n const incumbent = inFlight[0] as JobRun;\n switch (definition.collisionMode) {\n case 'reject':\n throw new JobCollisionError(type, concurrencyKey, incumbent);\n case 'replace':\n // JOB-8 — thread the incumbent's own tenantId through the\n // internal cascade. Without this, every `replace`-collision\n // start() under multiTenant=true throws MissingTenantIdError\n // from the inner cancel() call instead of cancelling the\n // incumbent. Mirrors the memory backend's `cancelLocked(\n // incumbent.id, ..., incumbent.tenantId)` pattern.\n await this.cancel(incumbent.id, {\n cascade: true,\n reason: 'replaced',\n tenantId: incumbent.tenantId,\n });\n break;\n case 'queue':\n // Fall through — row is inserted; claim query gates it until\n // the incumbent transitions (see JobWorker.processRun queue gate).\n break;\n }\n }\n }\n\n // 1d. Resolve id + rootRunId, INSERT.\n const newId = randomUUID();\n let rootRunId: string = newId;\n if (opts.parentRunId) {\n const [parent] = await client\n .select({ rootRunId: jobRuns.rootRunId })\n .from(jobRuns)\n .where(eq(jobRuns.id, opts.parentRunId))\n .limit(1);\n if (!parent) {\n throw new Error(\n `parentRunId ${opts.parentRunId} does not reference an existing job_run`,\n );\n }\n rootRunId = parent.rootRunId;\n }\n\n const dedupeKey = resolveJobKey(\n 'dedupe',\n type,\n definition.dedupeKeyTemplate,\n payload,\n evaluateKeyTemplate,\n );\n\n const [inserted] = await client\n .insert(jobRuns)\n .values({\n id: newId,\n jobType: type,\n jobVersion: definition.version,\n parentRunId: opts.parentRunId ?? null,\n rootRunId,\n parentClosePolicy: opts.parentClosePolicy ?? 'terminate',\n scopeEntityType: opts.scope?.entityType ?? null,\n scopeEntityId: opts.scope?.entityId ?? null,\n tenantId,\n tags: opts.tags ?? {},\n pool: opts.pool ?? definition.pool,\n priority: opts.priority ?? definition.priorityDefault,\n concurrencyKey,\n dedupeKey,\n status: 'pending',\n input: payload,\n output: null,\n error: null,\n triggerSource: opts.triggerSource ?? 'manual',\n triggerRef: opts.triggerRef ?? null,\n runAt: opts.runAt ?? new Date(),\n startedAt: null,\n finishedAt: null,\n claimedAt: null,\n attempts: 0,\n })\n .returning();\n\n // LISTEN-NOTIFY-1 — wake a listening worker the instant this enqueue\n // commits. Emitted through the SAME `client` (the caller's tx when one was\n // passed, else the pool) so delivery is gated on commit — a rolled-back\n // enqueue emits no phantom wake (D2). The pool name is the payload; the\n // worker re-runs its own pool-filtered claim query on wake. Polling is the\n // fallback, so a failed notify is non-fatal: log + continue.\n if (this.listenNotify) {\n const wakePool = (inserted as JobRunRow).pool;\n try {\n await pgNotify(client, JOBS_WAKE_CHANNEL, wakePool);\n } catch (err) {\n this.logger.warn(\n `pg_notify(${JOBS_WAKE_CHANNEL}, ${wakePool}) failed for run ` +\n `${(inserted as JobRunRow).id}: ${(err as Error).message} ` +\n `(non-fatal — interval polling still claims the run).`,\n );\n }\n }\n\n return inserted as JobRun;\n }\n\n // ==========================================================================\n // cancel\n // ==========================================================================\n\n async cancel(runId: string, opts: CancelOptions = {}): Promise<void> {\n // JOB-8 — resolve tenant gate up front (strict undefined-throws).\n const tenantId = this.resolveTenantId('cancel', opts.tenantId);\n\n // Load target.\n const [target] = await this.db\n .select()\n .from(jobRuns)\n .where(eq(jobRuns.id, runId))\n .limit(1);\n if (!target) return;\n // JOB-8 — cross-tenant cancel is a silent no-op (no existence leak).\n if (this.multiTenant && target.tenantId !== tenantId) return;\n if (TERMINAL_STATUSES.includes(target.status as TerminalStatus)) {\n return; // idempotent\n }\n\n // Atomic transition, guarded against concurrent terminal moves.\n const [cancelled] = await this.db\n .update(jobRuns)\n .set({\n status: 'canceled',\n finishedAt: new Date(),\n updatedAt: new Date(),\n })\n .where(\n and(eq(jobRuns.id, runId), notInStatus([...TERMINAL_STATUSES])),\n )\n .returning();\n\n if (!cancelled) return; // lost the race; already terminal\n\n if (opts.cascade === false) return;\n\n // Fetch descendants and branch on parent_close_policy.\n const descendants = await this.db\n .select()\n .from(jobRuns)\n .where(\n and(\n eq(jobRuns.rootRunId, target.rootRunId),\n ne(jobRuns.id, runId),\n notInStatus([...TERMINAL_STATUSES]),\n ),\n );\n\n for (const child of descendants) {\n const policy = (child as JobRunRow).parentClosePolicy;\n if (policy === 'abandon') continue;\n // 'terminate' | 'cancel' — both transition the child to canceled.\n await this.db\n .update(jobRuns)\n .set({\n status: 'canceled',\n finishedAt: new Date(),\n updatedAt: new Date(),\n })\n .where(\n and(\n eq(jobRuns.id, (child as JobRunRow).id),\n notInStatus([...TERMINAL_STATUSES]),\n ),\n );\n }\n\n void opts.reason; // reserved for future audit logging\n }\n\n // ==========================================================================\n // replay\n // ==========================================================================\n\n async replay(runId: string): Promise<JobRun> {\n // Load target + its job definition (we need replay_from).\n const [target] = await this.db\n .select()\n .from(jobRuns)\n .where(eq(jobRuns.id, runId))\n .limit(1);\n if (!target) {\n throw new Error(`replay: run ${runId} not found`);\n }\n const run = target as JobRunRow;\n if (!TERMINAL_STATUSES.includes(run.status as TerminalStatus)) {\n throw new JobNotReplayableError(runId, run.status);\n }\n\n const [def] = await this.db\n .select()\n .from(jobs)\n .where(eq(jobs.type, run.jobType))\n .limit(1);\n if (!def) throw new JobTypeNotFoundError(run.jobType);\n const mode = (def as JobDefinitionRow).replayFrom;\n\n // Atomic: step reset + run reset must commit together.\n const result = await this.db.transaction(async (tx) => {\n if (mode === 'scratch') {\n await tx.delete(jobSteps).where(eq(jobSteps.jobRunId, runId));\n } else if (mode === 'last_step') {\n // Delete only non-completed step rows — completed steps stay memoised.\n await tx\n .delete(jobSteps)\n .where(\n and(eq(jobSteps.jobRunId, runId), ne(jobSteps.status, 'completed')),\n );\n } else {\n // 'last_checkpoint' — Phase 1 has no explicit checkpoint markers, so\n // behaviour collapses to `last_step`. See docs/specs/JOB-3.md\n // \"Implementation Decisions\" — planned divergence in a later phase.\n await tx\n .delete(jobSteps)\n .where(\n and(eq(jobSteps.jobRunId, runId), ne(jobSteps.status, 'completed')),\n );\n }\n\n const [updated] = await tx\n .update(jobRuns)\n .set({\n status: 'pending',\n attempts: 0,\n runAt: new Date(),\n startedAt: null,\n finishedAt: null,\n claimedAt: null,\n error: null,\n output: null,\n updatedAt: new Date(),\n })\n .where(eq(jobRuns.id, runId))\n .returning();\n return updated as JobRunRow;\n });\n\n return result as JobRun;\n }\n\n // ==========================================================================\n // upsertJobRows — boot-time materialisation of `job` definitions\n // ==========================================================================\n\n /**\n * Hash-gated `INSERT … ON CONFLICT (type) DO UPDATE … WHERE` per Q3\n * resolution (2026-04-19): the `UPDATE` branch executes only when one\n * of the persisted metadata fields differs from the incoming payload;\n * `version` bumps only on real change; concurrent boots with identical\n * content are idempotent no-ops.\n *\n * Why this shape (not `DO NOTHING`, not advisory locks):\n * - `DO NOTHING` would let an old-version instance leave a stale row\n * that a new-version instance can't overwrite during a rolling deploy.\n * - Advisory locks add latency and leak risk under crashes.\n * - The `WHERE … IS DISTINCT FROM …` clause makes the conditional\n * atomic — no read-modify-write race on `version` between concurrent\n * boots.\n *\n * Orphan detection: a single `SELECT type FROM job WHERE type NOT IN (...)`\n * returns the types present in DB but absent from `entries`. Caller (boot\n * validator) decides whether to throw `BootValidationError`.\n */\n async upsertJobRows(\n entries: JobUpsertEntry[],\n poolConfig: ReadonlyMap<string, JobPoolDef>,\n ): Promise<{ orphaned: string[] }> {\n void poolConfig; // pool validation is the module's responsibility; orchestrator just persists\n\n for (const entry of entries) {\n const meta = entry.meta;\n const pool = meta.pool ?? 'batch';\n const retryPolicy = meta.retry ?? {\n attempts: 1,\n backoff: 'fixed' as const,\n baseMs: 0,\n };\n // JOB-FN-KEY (0.16.2): both authored key forms are honored. A `{{field}}`\n // string is persisted verbatim; a function is persisted as\n // `FN_KEY_SENTINEL` (non-null so the collision/dedupe path engages, and\n // hash-stable so the definition-hash gate doesn't churn on every boot —\n // the function identity can't be hashed). `start()` re-resolves the live\n // function from `JOB_HANDLER_REGISTRY`. The pre-0.16.2 `typeof === string\n // ? … : null` dropped function keys to null, so `collisionMode` silently\n // never engaged.\n const concurrencyKeyTemplateStr = keySelectorToTemplate(\n meta.concurrency?.key as JobKeySelector<unknown> | undefined,\n );\n const collisionMode =\n (meta.concurrency?.collisionMode as JobDefinitionRow['collisionMode']) ??\n 'queue';\n const dedupeKeyTemplateStr = keySelectorToTemplate(\n meta.dedupe?.key as JobKeySelector<unknown> | undefined,\n );\n const dedupeWindowMs = meta.dedupe?.windowMs ?? null;\n const timeoutMs = meta.timeoutMs ?? null;\n const replayFrom = meta.replayFrom ?? 'last_checkpoint';\n const scopeEntityType = meta.scope?.entity ?? null;\n // Q3 resolution: priority_default and replay_from are part of the\n // hashed metadata even though they aren't currently set via decorator\n // metadata above (priority_default has no `@JobHandler` field yet).\n // Default to 0 to keep UPDATE branch quiet across deploys.\n const priorityDefault = 0;\n\n // Hash-gated upsert: every metadata column appears in the WHERE clause\n // so the UPDATE branch only fires on a real change. `version` bumps\n // exactly when the WHERE matches.\n await this.db\n .insert(jobs)\n .values({\n type: entry.type,\n version: 1,\n pool,\n scopeEntityType,\n retryPolicy,\n timeoutMs,\n concurrencyKeyTemplate: concurrencyKeyTemplateStr,\n collisionMode,\n dedupeKeyTemplate: dedupeKeyTemplateStr,\n dedupeWindowMs,\n priorityDefault,\n replayFrom,\n })\n .onConflictDoUpdate({\n target: jobs.type,\n set: {\n pool: sql`EXCLUDED.pool`,\n scopeEntityType: sql`EXCLUDED.scope_entity_type`,\n retryPolicy: sql`EXCLUDED.retry_policy`,\n timeoutMs: sql`EXCLUDED.timeout_ms`,\n concurrencyKeyTemplate: sql`EXCLUDED.concurrency_key_template`,\n collisionMode: sql`EXCLUDED.collision_mode`,\n dedupeKeyTemplate: sql`EXCLUDED.dedupe_key_template`,\n dedupeWindowMs: sql`EXCLUDED.dedupe_window_ms`,\n priorityDefault: sql`EXCLUDED.priority_default`,\n replayFrom: sql`EXCLUDED.replay_from`,\n version: sql`${jobs.version} + 1`,\n updatedAt: sql`now()`,\n },\n // The hash gate: every field listed in the Q3 resolution appears\n // here. `IS DISTINCT FROM` is the null-safe inequality operator;\n // jsonb cast to text gives stable comparison without invoking a\n // dedicated hash column (avoids a JOB-1 schema migration).\n setWhere: sql`\n ${jobs.pool} IS DISTINCT FROM EXCLUDED.pool OR\n ${jobs.retryPolicy}::text IS DISTINCT FROM EXCLUDED.retry_policy::text OR\n ${jobs.timeoutMs} IS DISTINCT FROM EXCLUDED.timeout_ms OR\n ${jobs.concurrencyKeyTemplate} IS DISTINCT FROM EXCLUDED.concurrency_key_template OR\n ${jobs.collisionMode} IS DISTINCT FROM EXCLUDED.collision_mode OR\n ${jobs.dedupeKeyTemplate} IS DISTINCT FROM EXCLUDED.dedupe_key_template OR\n ${jobs.dedupeWindowMs} IS DISTINCT FROM EXCLUDED.dedupe_window_ms OR\n ${jobs.priorityDefault} IS DISTINCT FROM EXCLUDED.priority_default OR\n ${jobs.replayFrom} IS DISTINCT FROM EXCLUDED.replay_from OR\n ${jobs.scopeEntityType} IS DISTINCT FROM EXCLUDED.scope_entity_type\n `,\n });\n }\n\n // Orphan detection: any `job` row whose type is not in the registry.\n const types = entries.map((e) => e.type);\n const orphans =\n types.length === 0\n ? await this.db.select({ type: jobs.type }).from(jobs)\n : await this.db\n .select({ type: jobs.type })\n .from(jobs)\n .where(notInArray(jobs.type, types));\n\n return { orphaned: orphans.map((o) => o.type) };\n }\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction notInStatus(statuses: JobRunStatus[]) {\n // Drizzle's inArray composes with `not` via negation helper; use raw sql\n // to stay readable. `inArray` + `.not()` isn't idiomatic in 0.45.\n const negated = statuses.map((s) => ne(jobRuns.status, s));\n return and(...negated);\n}\n\n// `isNotNull` + `gt` imports are retained for potential future use; silence\n// unused-import lint by re-exporting via `void`.\nvoid isNotNull;\n"],"mappings":"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","names":["dedupeKey"]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
jobRuns
|
|
3
|
-
} from "./chunk-OKXZ63IA.js";
|
|
4
|
-
import {
|
|
5
|
-
JOB_HANDLER_REGISTRY
|
|
6
|
-
} from "./chunk-CO6LUM72.js";
|
|
7
1
|
import {
|
|
8
2
|
JOB_ORCHESTRATOR,
|
|
9
3
|
JOB_RUN_SERVICE,
|
|
10
4
|
JOB_STEP_SERVICE
|
|
11
5
|
} from "./chunk-ZPL74UQN.js";
|
|
6
|
+
import {
|
|
7
|
+
JOB_HANDLER_REGISTRY
|
|
8
|
+
} from "./chunk-7P5ODGLA.js";
|
|
9
|
+
import {
|
|
10
|
+
jobRuns
|
|
11
|
+
} from "./chunk-OKXZ63IA.js";
|
|
12
12
|
import {
|
|
13
13
|
JOBS_WAKE_CHANNEL,
|
|
14
14
|
PgNotifyListener
|
|
@@ -518,4 +518,4 @@ export {
|
|
|
518
518
|
buildStaleSweepQuery,
|
|
519
519
|
JobWorker
|
|
520
520
|
};
|
|
521
|
-
//# sourceMappingURL=chunk-
|
|
521
|
+
//# sourceMappingURL=chunk-FWRL7BZ5.js.map
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DrizzleJobRunService
|
|
3
|
-
} from "./chunk-5LXOJGO2.js";
|
|
4
|
-
import {
|
|
5
|
-
MemoryJobRunService
|
|
6
|
-
} from "./chunk-QSJ3J4HE.js";
|
|
7
1
|
import {
|
|
8
2
|
DrizzleJobStepService
|
|
9
3
|
} from "./chunk-DV4RV2DC.js";
|
|
10
|
-
import {
|
|
11
|
-
BULLMQ_CONNECTION,
|
|
12
|
-
BULLMQ_RESOLVED_CONFIG,
|
|
13
|
-
resolveBullMqConfig
|
|
14
|
-
} from "./chunk-I6MVCB5A.js";
|
|
15
4
|
import {
|
|
16
5
|
DrizzleJobOrchestrator
|
|
17
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FVNAU7VO.js";
|
|
18
7
|
import {
|
|
19
8
|
MemoryJobOrchestrator
|
|
20
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DUMI2J5M.js";
|
|
21
10
|
import {
|
|
22
11
|
MemoryJobStepService
|
|
23
12
|
} from "./chunk-PNZSGAB2.js";
|
|
13
|
+
import {
|
|
14
|
+
DrizzleJobRunService
|
|
15
|
+
} from "./chunk-VNBC3VXM.js";
|
|
16
|
+
import {
|
|
17
|
+
MemoryJobRunService
|
|
18
|
+
} from "./chunk-BHZP6LOV.js";
|
|
24
19
|
import {
|
|
25
20
|
MemoryJobStore
|
|
26
21
|
} from "./chunk-SNQ3TOWP.js";
|
|
22
|
+
import {
|
|
23
|
+
BULLMQ_CONNECTION,
|
|
24
|
+
BULLMQ_RESOLVED_CONFIG,
|
|
25
|
+
resolveBullMqConfig
|
|
26
|
+
} from "./chunk-I6MVCB5A.js";
|
|
27
27
|
import {
|
|
28
28
|
JOBS_LISTEN_NOTIFY,
|
|
29
29
|
JOBS_MULTI_TENANT,
|
|
@@ -114,4 +114,4 @@ JobsDomainModule = __decorateClass([
|
|
|
114
114
|
export {
|
|
115
115
|
JobsDomainModule
|
|
116
116
|
};
|
|
117
|
-
//# sourceMappingURL=chunk-
|
|
117
|
+
//# sourceMappingURL=chunk-HOIRY5XP.js.map
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JobWorker
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FWRL7BZ5.js";
|
|
4
4
|
import {
|
|
5
5
|
JobsDomainModule
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HOIRY5XP.js";
|
|
7
|
+
import {
|
|
8
|
+
BootValidationError,
|
|
9
|
+
ReservedPoolViolationError
|
|
10
|
+
} from "./chunk-T4BIIU5E.js";
|
|
7
11
|
import {
|
|
8
12
|
BULLMQ_CONNECTION,
|
|
9
13
|
BULLMQ_RESOLVED_CONFIG,
|
|
@@ -14,18 +18,14 @@ import {
|
|
|
14
18
|
allPoolNames,
|
|
15
19
|
loadPoolConfig
|
|
16
20
|
} from "./chunk-RHVN6NA7.js";
|
|
17
|
-
import {
|
|
18
|
-
HandlerRegistry
|
|
19
|
-
} from "./chunk-CO6LUM72.js";
|
|
20
21
|
import {
|
|
21
22
|
JOB_ORCHESTRATOR,
|
|
22
23
|
JOB_RUN_SERVICE,
|
|
23
24
|
JOB_STEP_SERVICE
|
|
24
25
|
} from "./chunk-ZPL74UQN.js";
|
|
25
26
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} from "./chunk-T4BIIU5E.js";
|
|
27
|
+
HandlerRegistry
|
|
28
|
+
} from "./chunk-7P5ODGLA.js";
|
|
29
29
|
import {
|
|
30
30
|
tokenKey
|
|
31
31
|
} from "./chunk-GYGNEQSC.js";
|
|
@@ -290,4 +290,4 @@ export {
|
|
|
290
290
|
JobWorkerOrchestrator,
|
|
291
291
|
JobWorkerModule
|
|
292
292
|
};
|
|
293
|
-
//# sourceMappingURL=chunk-
|
|
293
|
+
//# sourceMappingURL=chunk-HPS554L4.js.map
|
|
@@ -3807,6 +3807,15 @@ function formatMermaidGraph(result) {
|
|
|
3807
3807
|
}
|
|
3808
3808
|
|
|
3809
3809
|
// src/patterns/library/activity.pattern.ts
|
|
3810
|
+
import { z as z6 } from "zod";
|
|
3811
|
+
var ActivityPatternConfigSchema = z6.object({
|
|
3812
|
+
/** Subject entity name → derives the FK column `<subject>_id`. */
|
|
3813
|
+
subject: z6.string().optional(),
|
|
3814
|
+
/** Explicit snake_case FK column, when it does not follow `<subject>_id`. */
|
|
3815
|
+
subjectColumn: z6.string().optional(),
|
|
3816
|
+
/** snake_case recency-ordering column; defaults to `occurred_at`. */
|
|
3817
|
+
occurredAt: z6.string().optional()
|
|
3818
|
+
}).strict();
|
|
3810
3819
|
var ActivityPattern = definePattern({
|
|
3811
3820
|
name: "Activity",
|
|
3812
3821
|
extends: ["Base"],
|
|
@@ -3814,15 +3823,16 @@ var ActivityPattern = definePattern({
|
|
|
3814
3823
|
serviceClass: "ActivityEntityService",
|
|
3815
3824
|
repositoryImport: "@shared/base-classes/activity-entity-repository",
|
|
3816
3825
|
serviceImport: "@shared/base-classes/activity-entity-service",
|
|
3826
|
+
configSchema: ActivityPatternConfigSchema,
|
|
3817
3827
|
repositoryInheritedMethods: [
|
|
3818
3828
|
"findById, findByIds, list, count, exists, create, update, delete, upsertMany",
|
|
3819
|
-
"findByDateRange, findByUserId,
|
|
3829
|
+
"findByDateRange, findByUserId, findBySubjectId, findRecentBySubjectId"
|
|
3820
3830
|
],
|
|
3821
3831
|
serviceInheritedMethods: [
|
|
3822
3832
|
"findById, findByIds, list, count, exists, create, update, delete",
|
|
3823
|
-
"findByDateRange,
|
|
3833
|
+
"findByDateRange, findByUser, findBySubject, findRecent"
|
|
3824
3834
|
],
|
|
3825
|
-
description: "
|
|
3835
|
+
description: "Subject-scoped interaction entities \u2014 date-range + actor + config-driven subject lookups"
|
|
3826
3836
|
});
|
|
3827
3837
|
|
|
3828
3838
|
// src/patterns/library/base.pattern.ts
|
|
@@ -3842,8 +3852,8 @@ var BasePattern = definePattern({
|
|
|
3842
3852
|
});
|
|
3843
3853
|
|
|
3844
3854
|
// src/patterns/library/junction.pattern.ts
|
|
3845
|
-
import { z as
|
|
3846
|
-
var JunctionPatternConfigSchema =
|
|
3855
|
+
import { z as z7 } from "zod";
|
|
3856
|
+
var JunctionPatternConfigSchema = z7.object({}).strict();
|
|
3847
3857
|
var JunctionPattern = definePattern({
|
|
3848
3858
|
name: "Junction",
|
|
3849
3859
|
description: "Explicit many-to-many junction with role + temporal + sourcing metadata",
|
|
@@ -4039,4 +4049,4 @@ export {
|
|
|
4039
4049
|
analyzeDomain,
|
|
4040
4050
|
validateEntities
|
|
4041
4051
|
};
|
|
4042
|
-
//# sourceMappingURL=chunk-
|
|
4052
|
+
//# sourceMappingURL=chunk-IOQMMH6C.js.map
|