@pattern-stack/codegen 0.15.0 → 0.15.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 +58 -0
- package/dist/{job-orchestrator.protocol-CARhMLCO.d.ts → job-orchestrator.protocol-DubMVbm9.d.ts} +1 -1
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.d.ts +2 -2
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge-delivery.memory-backend.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.d.ts +2 -2
- package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/event-flow.service.d.ts +2 -2
- package/dist/runtime/subsystems/bridge/event-flow.service.js.map +1 -1
- package/dist/runtime/subsystems/bridge/generated/registry.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/index.d.ts +2 -2
- package/dist/runtime/subsystems/bridge/index.js.map +1 -1
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +1 -1
- package/dist/runtime/subsystems/events/event-registry.d.ts +77 -0
- package/dist/runtime/subsystems/events/event-registry.js +1 -0
- package/dist/runtime/subsystems/events/event-registry.js.map +1 -0
- package/dist/runtime/subsystems/events/index.d.ts +1 -0
- package/dist/runtime/subsystems/index.d.ts +2 -2
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/index.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-handler.base.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-handler.base.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.protocol.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-worker.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +2 -2
- package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/jobs-errors.d.ts +2 -2
- package/dist/runtime/subsystems/observability/index.d.ts +2 -2
- package/dist/runtime/subsystems/observability/observability.protocol.d.ts +2 -2
- package/dist/runtime/subsystems/observability/observability.service.d.ts +2 -2
- package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +2 -2
- package/dist/runtime/subsystems/observability/reporters/index.d.ts +2 -2
- package/dist/src/cli/index.js +26 -0
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/subsystems/bridge/bridge-delivery-handler.ts +1 -1
- package/runtime/subsystems/bridge/bridge-outbox-drain-hook.ts +1 -1
- package/runtime/subsystems/bridge/bridge.protocol.ts +1 -1
- package/runtime/subsystems/bridge/event-flow.service.ts +1 -1
- package/runtime/subsystems/events/event-registry.ts +77 -0
- package/runtime/subsystems/events/index.ts +12 -0
- package/runtime/subsystems/jobs/job-handler.base.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,64 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.15.1] — 2026-06-02
|
|
8
|
+
|
|
9
|
+
Package-mode **bridge/trigger event typing** — the fourth and final package-mode
|
|
10
|
+
seam (after 0.14.1 subsystems, 0.14.2 bridge, 0.15.0 events). Lets a package-mode
|
|
11
|
+
consumer (`runtime: package`, ADR-037) author a `@JobHandler({ triggers: [{ event:
|
|
12
|
+
'<their_event>', map, when }] })` on their OWN event and have it typecheck with
|
|
13
|
+
full per-event payload typing. Vendored mode is byte-stable; everything here is
|
|
14
|
+
additive.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`@JobHandler.triggers` and the generated `bridge-registry.ts` now accept a
|
|
19
|
+
package-mode consumer's own events.** Previously the bridge + job-trigger types
|
|
20
|
+
(`BridgeRegistry`, `BridgeTriggerEntry<T>`, `JobTrigger<TInput>`) keyed off
|
|
21
|
+
`EventTypeName` imported from the bundled `events/generated/types.ts` — which
|
|
22
|
+
in the published package is codegen-patterns' OWN test-fixture union
|
|
23
|
+
(`contact_created`, `deal_created`, …). A consumer's
|
|
24
|
+
`'inbound_webhook_received'` trigger therefore failed to typecheck
|
|
25
|
+
(`'inbound_webhook_received' is not assignable to '"contact_created" | …'`) and
|
|
26
|
+
their generated `bridge-registry.ts` reported "does not exist in type
|
|
27
|
+
'BridgeRegistry'". The bridge/trigger types now key off an **augmentable
|
|
28
|
+
`DomainEventRegistry`** (see below) instead of the bundled fixture union, so in
|
|
29
|
+
the consumer's tsc program they resolve THEIR event union with full
|
|
30
|
+
`EventOfType<T>` payload typing (`e.payload.<field>` is typed, not `unknown`).
|
|
31
|
+
The bundled fixture-based runtime tests stay green: the bundled `TypedEventBus`
|
|
32
|
+
still keys off its local `events/generated/types.ts`, and the fixtures are
|
|
33
|
+
never registered into `DomainEventRegistry`, so they never leak into a
|
|
34
|
+
consumer's `EventTypeName`.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **`DomainEventRegistry` — an augmentable, empty event registry interface**
|
|
39
|
+
(`runtime/subsystems/events/event-registry.ts`, re-exported from the events
|
|
40
|
+
index barrel). `EventTypeName` now derives from it
|
|
41
|
+
(`keyof DomainEventRegistry extends never ? string : keyof DomainEventRegistry &
|
|
42
|
+
string`) and `EventOfType<T>` resolves a registered event's concrete interface
|
|
43
|
+
(falling back to the structural `DomainEvent` base otherwise). Empty in the
|
|
44
|
+
package and in any no-events project ⇒ `EventTypeName` degrades to `string` and
|
|
45
|
+
the bridge/trigger types stay sound (`Record<string, …>` / `(e: DomainEvent) =>
|
|
46
|
+
…`) — exactly the loose shape the package's own fixture tests rely on. The five
|
|
47
|
+
bridge/jobs runtime files that consumed `EventTypeName` / `EventOfType` from the
|
|
48
|
+
bundled `events/generated/types` (`bridge.protocol`, `job-handler.base`,
|
|
49
|
+
`event-flow.service`, `bridge-outbox-drain-hook`, `bridge-delivery-handler`) now
|
|
50
|
+
import them from this augmentable seam.
|
|
51
|
+
- **Event codegen emits a package-mode `declare module` augmentation.** In
|
|
52
|
+
package mode the generated `src/generated/events/types.ts` now appends a
|
|
53
|
+
`declare module '@pattern-stack/codegen/runtime/subsystems/events/index' {
|
|
54
|
+
interface DomainEventRegistry { '<type>': <Type>Event; … } }` block that
|
|
55
|
+
declaration-merges the consumer's events into the runtime's registry through
|
|
56
|
+
the events index module specifier (the stable, public augmentation target).
|
|
57
|
+
Gated on `mode === 'package'` and a non-empty event set; vendored mode emits
|
|
58
|
+
nothing (the bridge/job types import the consumer's vendored `./generated/types`
|
|
59
|
+
directly), so vendored output is byte-stable. Proven end-to-end with a real
|
|
60
|
+
cross-`node_modules` consumer typecheck (declaration merging across the package
|
|
61
|
+
boundary is finicky and is covered by a real-tsc unit test, not string
|
|
62
|
+
assertions alone): an unregistered event type is rejected, and a registered
|
|
63
|
+
one's trigger `map` reads `e.payload.<field>` fully typed.
|
|
64
|
+
|
|
7
65
|
## [0.15.0] — 2026-06-02
|
|
8
66
|
|
|
9
67
|
Package-mode consumer **events** — the seam that lets a project consuming the
|
package/dist/{job-orchestrator.protocol-CARhMLCO.d.ts → job-orchestrator.protocol-DubMVbm9.d.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DrizzleTransaction } from './runtime/subsystems/events/event-bus.protocol.js';
|
|
2
2
|
import { JobRunRow } from './runtime/subsystems/jobs/job-orchestration.schema.js';
|
|
3
3
|
import { Logger } from '@nestjs/common';
|
|
4
|
-
import { EventTypeName, EventOfType } from './runtime/subsystems/events/
|
|
4
|
+
import { EventTypeName, EventOfType } from './runtime/subsystems/events/event-registry.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Handler base class, JobContext, @JobHandler decorator, and policy types
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { f as JobHandlerBase, I as IJobOrchestrator, d as JobContext } from '../../../job-orchestrator.protocol-
|
|
1
|
+
import { f as JobHandlerBase, I as IJobOrchestrator, d as JobContext } from '../../../job-orchestrator.protocol-DubMVbm9.js';
|
|
2
2
|
import { IEventBus } from '../events/event-bus.protocol.js';
|
|
3
3
|
import { IJobBridge, BridgeRegistry } from './bridge.protocol.js';
|
|
4
4
|
import '../jobs/job-orchestration.schema.js';
|
|
5
5
|
import 'drizzle-orm/pg-core';
|
|
6
6
|
import 'drizzle-orm';
|
|
7
7
|
import '@nestjs/common';
|
|
8
|
-
import '../events/
|
|
8
|
+
import '../events/event-registry.js';
|
|
9
9
|
import '../../types/drizzle.js';
|
|
10
10
|
import 'drizzle-orm/node-postgres';
|
|
11
11
|
import './bridge-delivery.schema.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../runtime/subsystems/bridge/bridge-delivery-handler.ts","../../../../runtime/subsystems/token-key.ts","../../../../runtime/subsystems/jobs/jobs-domain.tokens.ts","../../../../runtime/subsystems/jobs/job-handler.base.ts","../../../../runtime/subsystems/events/events.tokens.ts","../../../../runtime/subsystems/bridge/bridge.tokens.ts","../../../../runtime/subsystems/bridge/bridge-errors.ts","../../../../runtime/subsystems/bridge/assert-tenant-id.ts"],"sourcesContent":["/**\n * BridgeDeliveryHandler — the framework `@JobHandler` that runs every\n * bridge-fanout wrapper on the reserved `events_*` pools (BRIDGE-5,\n * ADR-023 §Decision 2 flow diagram).\n *\n * Role: when the outbox drain (BRIDGE-4) inserts a `bridge_delivery + wrapper\n * job_run` pair, the worker that polls the wrapper's pool claims that\n * wrapper and dispatches it to this handler. The handler:\n *\n * 1. Loads the `bridge_delivery` row by `ctx.input.deliveryId`.\n * 2. Looks up the trigger entry in the codegen-emitted `bridgeRegistry`\n * (`runtime/subsystems/bridge/generated/registry.ts`, BRIDGE-6).\n * A missing entry means the trigger was renamed or removed since the\n * delivery row was written; mark `skipped` with\n * `skip_reason='trigger_unregistered'` per ADR-023 §Trigger rename\n * or removal.\n * 3. Re-fetches the authoritative `domain_events` row (`IEventBus.findById`)\n * so `when:` / `map:` callbacks see the committed payload — never a\n * copy that drifted between drain and claim time.\n * 4. Evaluates `entry.when?.(event)`. False ⇒ mark `skipped` with\n * `skip_reason='predicate_false'`.\n * 5. Calls `IJobOrchestrator.start(entry.jobType, entry.map(event), …)`\n * INSIDE `ctx.step('spawn_user_run', …)`. The step memoization is\n * what makes wrapper retries (BRIDGE-1 ledger says no auto-retry past\n * the wrapper's own retry policy, but Phase 1 wrappers DO retry per\n * JOB-3) idempotent — a successful spawn followed by a transient\n * ledger-update failure would otherwise re-spawn on the next attempt.\n * 6. Marks `delivered` with the spawned `runId`.\n *\n * Pool registration: BRIDGE-5 ships ONE `@JobHandler` registration with\n * `pool: 'events_change'` (the default). The wrapper rows the drain\n * inserts carry `pool: events_<direction>` per row, so workers polling\n * `events_inbound` / `events_outbound` claim and dispatch to this same\n * handler class regardless of the metadata pool — the worker filter is on\n * `job_run.pool`, not on `@JobHandler.meta.pool`. BRIDGE-8 confirms the\n * three pools are active and registered at module init. The\n * `@framework/*` job-type prefix exempts this registration from the\n * reserved-pool validator (BRIDGE-5 added that exemption to\n * `job-worker.module.ts`).\n *\n * Tenant threading: when `BRIDGE_MULTI_TENANT=true`, the handler asserts\n * `delivery.tenantId !== undefined` before the spawn (the column is\n * nullable, so explicit `null` is allowed for cross-tenant work — same\n * contract as JOB-8). BRIDGE-8 wires the assertion via the\n * `BRIDGE_MULTI_TENANT` token.\n *\n * Failure path: any throw inside the handler propagates up; the worker's\n * normal retry policy (declared on the `@JobHandler` here as `attempts:\n * 3, backoff: exponential, baseMs: 250`) absorbs transient infra blips.\n * After exhaustion, the wrapper transitions to `failed`; the outer error\n * handler catches and calls `repo.markFailed(...)` so the delivery row\n * reflects the final state. Operators see `bridge_delivery.status='failed'`\n * surface via the `idx_bridge_delivery_status` partial index (BRIDGE-1).\n */\nimport { Inject, Injectable, Logger, Optional } from '@nestjs/common';\n\nimport { JOB_ORCHESTRATOR } from '../jobs/jobs-domain.tokens';\nimport type { IJobOrchestrator } from '../jobs/job-orchestrator.protocol';\nimport {\n JobHandler,\n JobHandlerBase,\n type JobContext,\n} from '../jobs/job-handler.base';\n\nimport { EVENT_BUS } from '../events/events.tokens';\nimport type { IEventBus, DomainEvent } from '../events/event-bus.protocol';\nimport type { EventTypeName } from '../events/generated/types';\n\nimport {\n BRIDGE_DELIVERY_REPO,\n BRIDGE_MULTI_TENANT,\n BRIDGE_REGISTRY,\n} from './bridge.tokens';\nimport type {\n BridgeRegistry,\n BridgeTriggerEntry,\n IJobBridge,\n} from './bridge.protocol';\nimport { assertTenantId } from './assert-tenant-id';\n\n/** Stable canonical job type — referenced by BRIDGE-4 wrapper inserts. */\nexport const BRIDGE_DELIVERY_JOB_TYPE = '@framework/bridge_delivery' as const;\n\n/** Stable canonical step id — referenced for memoization across attempts. */\nconst SPAWN_USER_RUN_STEP = 'spawn_user_run' as const;\n\nexport interface BridgeDeliveryInput {\n /** PK of the `bridge_delivery` row this wrapper services. */\n deliveryId: string;\n}\n\n@Injectable()\n@JobHandler<BridgeDeliveryInput>(BRIDGE_DELIVERY_JOB_TYPE, {\n pool: 'events_change',\n retry: { attempts: 3, backoff: 'exponential', baseMs: 250 },\n replayFrom: 'last_step',\n})\nexport class BridgeDeliveryHandler extends JobHandlerBase<\n BridgeDeliveryInput,\n { runId: string } | { skipped: true; reason: string }\n> {\n private readonly classLogger = new Logger(BridgeDeliveryHandler.name);\n\n constructor(\n @Inject(BRIDGE_DELIVERY_REPO) private readonly repo: IJobBridge,\n @Inject(JOB_ORCHESTRATOR) private readonly orchestrator: IJobOrchestrator,\n @Inject(EVENT_BUS) private readonly events: IEventBus,\n @Inject(BRIDGE_REGISTRY) private readonly registry: BridgeRegistry,\n @Optional()\n @Inject(BRIDGE_MULTI_TENANT)\n private readonly multiTenant: boolean = false,\n ) {\n super();\n }\n\n async run(\n ctx: JobContext<BridgeDeliveryInput>,\n ): Promise<{ runId: string } | { skipped: true; reason: string }> {\n const { deliveryId } = ctx.input;\n\n // Step 1 — locate the delivery row by primary key.\n const delivery = await this.repo.findDeliveryById(deliveryId);\n if (!delivery) {\n // The drain wrote a wrapper job_run but the delivery row is gone\n // (manual ops cleanup, or delete-cascade from the parent event).\n // No row → no work; return without throwing so the wrapper marks\n // completed cleanly.\n this.classLogger.warn(\n `bridge_delivery row '${deliveryId}' not found; wrapper completes ` +\n `without spawning a user job.`,\n );\n return { skipped: true, reason: 'delivery_row_missing' };\n }\n\n // Step 2 — multi-tenancy gate. Site (b) of the three ADR-023\n // §Multi-tenancy enforcement sites; shared helper from BRIDGE-8.\n // The DB always returns string|null, never undefined; this branch\n // exists for the in-memory backend's older test fixtures and to\n // pin the contract in shape-typed tests.\n assertTenantId(\n 'BridgeDeliveryHandler.run',\n this.multiTenant,\n delivery.tenantId,\n );\n\n // Step 3 — load the typed event row.\n const event = await this.events.findById(delivery.eventId);\n if (!event) {\n // FK from bridge_delivery.event_id → domain_events.id should make\n // this impossible at the DB layer, but defensive: if the row is\n // missing we mark skipped, not failed (no work the bridge can do).\n this.classLogger.warn(\n `domain_events row '${delivery.eventId}' missing for delivery ` +\n `'${deliveryId}'; marking skipped.`,\n );\n await this.repo.markSkipped(delivery.id, 'event_row_missing');\n return { skipped: true, reason: 'event_row_missing' };\n }\n\n // Step 4 — registry lookup. Handles trigger rename/removal cleanly.\n const entry = this.findRegistryEntry(event.type, delivery.triggerId);\n if (!entry) {\n await this.repo.markSkipped(delivery.id, 'trigger_unregistered');\n return { skipped: true, reason: 'trigger_unregistered' };\n }\n\n // Step 5 — `when:` predicate.\n if (entry.when && !entry.when(event as never)) {\n await this.repo.markSkipped(delivery.id, 'predicate_false');\n return { skipped: true, reason: 'predicate_false' };\n }\n\n // Step 6 — memoized spawn. `ctx.step` records the result in\n // `job_step` and on retry returns the cached `{ runId }` so a\n // transient failure between `orchestrator.start` and `markDelivered`\n // doesn't double-spawn the user job.\n const input = entry.map(event as never);\n const { runId } = await ctx.step<{ runId: string }>(\n SPAWN_USER_RUN_STEP,\n async () => {\n const run = await this.orchestrator.start(entry.jobType, input, {\n parentRunId: ctx.run.id,\n triggerSource: 'event',\n triggerRef: delivery.eventId,\n tenantId: delivery.tenantId,\n });\n return { runId: run.id };\n },\n );\n\n // Step 7 — ledger transition.\n await this.repo.markDelivered(delivery.id, runId);\n return { runId };\n }\n\n /**\n * Locate the registry entry for `(eventType, triggerId)`. Linear scan\n * over the per-event-type array — N is the number of triggers declared\n * for one event, typically 1–5; the table is not big enough to warrant\n * a secondary index.\n */\n private findRegistryEntry(\n eventType: string,\n triggerId: string,\n ): BridgeTriggerEntry | undefined {\n const candidates =\n this.registry[eventType as EventTypeName] ?? undefined;\n if (!candidates) return undefined;\n return candidates.find((c) => c.triggerId === triggerId) as\n | BridgeTriggerEntry\n | undefined;\n }\n}\n\n/**\n * Re-export for BRIDGE-7 facade Case B and BRIDGE-4 wrapper insert.\n * Single source of truth for the canonical type string keeps refactors\n * in one place.\n */\nexport { BRIDGE_DELIVERY_JOB_TYPE as BridgeDeliveryJobType };\n","/** Canonical package namespace for cross-boundary DI token keys. MUST be a hardcoded\n * constant (NOT derived from package.json) so a vendored copy — which lives inside the\n * CONSUMER's package — produces the identical key and the two copies share the symbol. */\nexport const PKG = '@pattern-stack/codegen';\n// TODO(token-version): if/when a runtime contract version is adopted, inject it HERE only\n// (e.g. `${PKG}#${ABI}.${area}.${name}`) — this helper is the single chokepoint.\nexport const tokenKey = (area: string, name: string): string => `${PKG}.${area}.${name}`;\n","/**\n * Injection tokens for the job orchestration domain layer (ADR-022, JOB-2).\n *\n * Consumer code injects these symbols via `@Inject(JOB_ORCHESTRATOR)` etc.;\n * concrete backends (JOB-3 Drizzle, JOB-4 Memory) provide the implementations\n * through `JobsDomainModule.forRoot({ backend })` in JOB-5.\n *\n * Each token is a namespaced `Symbol.for(...)` (ADR-037, via `tokenKey()`) —\n * distinct per key, so Nest's DI lookup is unambiguous, AND matching by VALUE\n * across import boundaries so the package and a (legacy) vendored runtime copy\n * resolve to the same symbol.\n */\nimport { tokenKey } from '../token-key';\n\nexport const JOB_ORCHESTRATOR = Symbol.for(tokenKey('jobs', 'orchestrator'));\nexport const JOB_RUN_SERVICE = Symbol.for(tokenKey('jobs', 'run-service'));\nexport const JOB_STEP_SERVICE = Symbol.for(tokenKey('jobs', 'step-service'));\n\n/**\n * Multi-tenancy opt-in flag (JOB-8). Bound to the boolean passed in via\n * `JobsDomainModule.forRoot({ multiTenant })`, defaulting to `false`.\n *\n * When `true`, the four service-layer backends (Drizzle + Memory orchestrator\n * and run-service) enforce `tenantId` on every mutating / targeted-read call:\n * `start`, `cancel`, `listForScope`, `cancelForScope`, `rescheduleForScope`.\n * Missing (`undefined`) `tenantId` throws `MissingTenantIdError`; explicit\n * `null` opts into cross-tenant background work and passes through.\n *\n * The JobWorker claim loop is **cross-tenant by design** — the worker has no\n * tenant context; `tenantId` is populated at write time and enforced on\n * targeted reads. See docs/specs/JOB-8.md.\n */\nexport const JOBS_MULTI_TENANT = Symbol.for(tokenKey('jobs', 'multi-tenant'));\n","/**\n * Handler base class, JobContext, @JobHandler decorator, and policy types\n * for the job orchestration domain (ADR-022, JOB-2).\n *\n * User-authored jobs subclass `JobHandlerBase<TInput, TOutput>` and decorate\n * the class with `@JobHandler<TInput>('job_type', meta)`. The decorator\n * 1. stores metadata via `Reflect.defineMetadata` so Nest's reflector can\n * pick it up at module boot, and\n * 2. populates `JOB_HANDLER_REGISTRY` — a module-singleton map consumed by\n * `JobWorkerModule` (JOB-5) to materialise `job` rows and resolve\n * handler classes during claim/execute.\n *\n * No runtime orchestration lives here; this file is a pure type + decorator\n * surface so downstream PRs (JOB-3..JOB-5) can implement against a stable\n * shape.\n */\n// TODO(logging-subsystem): swap to ILogger once ADR-028 lands\nimport type { Logger } from '@nestjs/common';\nimport { tokenKey } from '../token-key';\nimport type { EventOfType, EventTypeName } from '../events/generated/types';\nimport type { JobRun } from './job-orchestrator.protocol';\n\n// ─── ParentClosePolicy ──────────────────────────────────────────────────────\n\n/**\n * What happens to running child runs when a parent enters a terminal state.\n * Stored on the child at spawn; changes to the parent after spawn do NOT\n * retroactively rewrite children.\n */\nexport enum ParentClosePolicy {\n Terminate = 'terminate',\n Cancel = 'cancel',\n Abandon = 'abandon',\n}\n\n// ─── Policy types ───────────────────────────────────────────────────────────\n\nexport interface RetryPolicy {\n attempts: number;\n backoff: 'fixed' | 'exponential';\n baseMs: number;\n nonRetryableErrors?: string[];\n}\n\nexport interface ConcurrencyPolicy<TInput> {\n key: (input: TInput) => string;\n collisionMode: 'queue' | 'reject' | 'replace';\n}\n\nexport interface DedupePolicy<TInput> {\n key: (input: TInput) => string;\n windowMs: number;\n}\n\n/**\n * Declarative scope reference. `TScope` is parameterised so JOB-7 can narrow\n * `entity` to the generated `ScopeEntityType` union at the call site without\n * modifying this file (OQ-1 resolution, 2026-04-20).\n */\nexport interface ScopeRef<TInput, TScope extends string = string> {\n entity: TScope;\n from: (input: TInput) => string;\n}\n\n/**\n * Bridge trigger authoring shape (BRIDGE-6 follow-up — BRIDGE-6 shipped the\n * generator + runtime for `@JobHandler({ triggers })` but never added the\n * authoring field to this type; the generator's tests scan source as strings,\n * so a real decorator was never compiled and the gap went uncaught).\n *\n * Declared on `@JobHandler({ triggers })`; the codegen bridge-registry\n * generator (`src/cli/shared/bridge-registry-generator.ts`) scans these from\n * source and emits `bridge/generated/registry.ts`, validating each `event`\n * against the generated `eventRegistry` at `gen-all`. The distributed union\n * narrows `map`/`when` per `event`, so callbacks are typed against the event\n * payload (ADR-023, \"typed against PayloadOfType<T>\").\n *\n * Typed against events' generated types — the same `import type` coupling the\n * bridge already has (erased at runtime). `jobs` must NOT import `bridge`, so\n * the post-gen `BridgeTriggerEntry` is deliberately not referenced here;\n * `triggerId`/`jobType` are computed by the generator, not authored.\n */\nexport type JobTrigger<TInput> = {\n [T in EventTypeName]: {\n /** Event type that fires this trigger. Validated against `eventRegistry`. */\n event: T;\n /** Maps the event to the job input. Inlined verbatim into the registry. */\n map: (event: EventOfType<T>) => TInput;\n /** Optional guard; `false` → wrapper records `status='skipped'`. */\n when?: (event: EventOfType<T>) => boolean;\n };\n}[EventTypeName];\n\nexport interface JobHandlerMeta<TInput> {\n pool?: string;\n scope?: ScopeRef<TInput>;\n retry?: RetryPolicy;\n concurrency?: ConcurrencyPolicy<TInput>;\n dedupe?: DedupePolicy<TInput>;\n timeoutMs?: number;\n replayFrom?: 'scratch' | 'last_step' | 'last_checkpoint';\n /**\n * Bridge triggers (ADR-023 Tier 3). Codegen scans these into `bridgeRegistry`;\n * the framework `BridgeDeliveryHandler` starts this job per matched event.\n * Absent for jobs started directly or via `IEventFlow.publishAndStart`.\n */\n triggers?: readonly JobTrigger<TInput>[];\n}\n\n// ─── Runtime option shapes ──────────────────────────────────────────────────\n\nexport interface StepOptions {\n retry?: RetryPolicy;\n timeoutMs?: number;\n}\n\nexport interface SpawnChildOptions {\n closePolicy?: ParentClosePolicy;\n runAt?: Date;\n priority?: number;\n tags?: Record<string, string>;\n}\n\n// ─── JobContext ─────────────────────────────────────────────────────────────\n\nexport interface JobContext<TInput> {\n readonly input: TInput;\n readonly run: JobRun;\n step<TOutput>(\n stepId: string,\n fn: () => Promise<TOutput>,\n opts?: StepOptions,\n ): Promise<TOutput>;\n spawnChild(type: string, input: unknown, opts?: SpawnChildOptions): Promise<JobRun>;\n readonly logger: Logger;\n // NOT in Phase 1 — deferred to ADR-025:\n // waitFor(kind, token, opts)\n // signal(token, payload)\n // sleep(ms)\n}\n\n// ─── JobHandlerBase ─────────────────────────────────────────────────────────\n\nexport abstract class JobHandlerBase<TInput, TOutput = unknown> {\n abstract run(ctx: JobContext<TInput>): Promise<TOutput>;\n}\n\n// ─── Registry + decorator ───────────────────────────────────────────────────\n\n/**\n * Module-singleton map keyed by job type. Populated by the `@JobHandler`\n * decorator at class definition time; consumed by `JobWorkerModule` (JOB-5)\n * to upsert `job` rows and resolve handler classes during claim/execute.\n */\nexport const JOB_HANDLER_REGISTRY = new Map<\n string,\n {\n type: string;\n meta: JobHandlerMeta<unknown>;\n handlerClass: new (...args: unknown[]) => JobHandlerBase<unknown>;\n }\n>();\n\n// ADR-037: namespaced `Symbol.for(...)` (via `tokenKey()`) so the reflection-metadata\n// key matches by value across import boundaries (the @JobHandler decorator and the\n// reader may resolve different runtime copies). Distinct from the DI tokens but\n// subject to the same dual-package identity hazard.\nexport const JOB_HANDLER_METADATA_KEY = Symbol.for(tokenKey('jobs', 'handler-metadata'));\n\n/**\n * Class decorator that registers a handler with the job type, the full\n * metadata shape, and the target class constructor.\n *\n * Duplicate-type behaviour (OQ-3, resolved 2026-04-18):\n * - `NODE_ENV === 'production'` → throw; silent overwrite in prod is a\n * correctness bug.\n * - `NODE_ENV === 'test'` → silent overwrite (tests intentionally\n * re-register handlers).\n * - otherwise (dev) → `console.warn` + overwrite. `console`\n * is used intentionally instead of the Nest `Logger` — decorators run\n * at module-load time before any Nest container exists.\n */\nexport function JobHandler<TInput>(\n type: string,\n meta: JobHandlerMeta<TInput>,\n): ClassDecorator {\n return (target) => {\n if (JOB_HANDLER_REGISTRY.has(type)) {\n const env = process.env.NODE_ENV;\n if (env === 'production') {\n throw new Error(\n `[JobHandler] Duplicate registration for job type '${type}'. ` +\n `Each @JobHandler must declare a unique type.`,\n );\n }\n if (env !== 'test') {\n // eslint-disable-next-line no-console\n console.warn(\n `[JobHandler] Duplicate registration for job type '${type}'. ` +\n `Overwriting previous handler — this is almost certainly a bug.`,\n );\n }\n }\n\n Reflect.defineMetadata(JOB_HANDLER_METADATA_KEY, { type, meta }, target);\n JOB_HANDLER_REGISTRY.set(type, {\n type,\n meta: meta as JobHandlerMeta<unknown>,\n handlerClass: target as unknown as new (\n ...args: unknown[]\n ) => JobHandlerBase<unknown>,\n });\n };\n}\n\n// ─── HandlerRegistry — read helpers consumed by JobWorkerModule (JOB-5) ─────\n\n/**\n * Single entry shape returned by `HandlerRegistry.getAll()` / `.get()` and\n * exposed to `JobWorkerModule.onModuleInit` for boot-time upserts.\n *\n * Structurally compatible with `IJobOrchestrator.upsertJobRows`'s\n * `JobUpsertEntry` so the worker module can pass entries through verbatim\n * without re-mapping.\n */\nexport interface HandlerRegistryEntry {\n type: string;\n meta: JobHandlerMeta<unknown>;\n handlerClass: new (...args: unknown[]) => JobHandlerBase<unknown>;\n}\n\n/**\n * Read facade over `JOB_HANDLER_REGISTRY`. The decorator's write path is\n * unchanged; this namespace exists so consumers (the worker module, tests)\n * don't import the raw `Map` and accidentally mutate it.\n */\nexport namespace HandlerRegistry {\n /** All registered entries in insertion order. */\n export function getAll(): HandlerRegistryEntry[] {\n return Array.from(JOB_HANDLER_REGISTRY.values());\n }\n\n /** Lookup by job type, or `undefined` if no `@JobHandler` is registered. */\n export function get(type: string): HandlerRegistryEntry | undefined {\n return JOB_HANDLER_REGISTRY.get(type);\n }\n}\n","/**\n * Injection token for the event bus.\n *\n * String constant (not Symbol) so it matches by value across import boundaries.\n * Matches the token in runtime/constants/tokens.ts — both are 'EVENT_BUS'.\n *\n * Usage in use cases:\n * ```typescript\n * constructor(@Inject(EVENT_BUS) private readonly eventBus: IEventBus) {}\n * ```\n */\nimport { tokenKey } from '../token-key';\n\nexport const EVENT_BUS = 'EVENT_BUS' as const;\n\n/**\n * Injection token for the read-side `IEventReadPort` over `domain_events`\n * (OBS-LIST-1).\n *\n * Bound by `EventsModule.forRoot` to the same backend instance as\n * `EVENT_BUS` for the `drizzle` and `memory` backends (both implement\n * `IEventReadPort`). The `redis` backend retains no history and therefore\n * does NOT provide this token — consumers composing it (e.g. the\n * observability combiner) inject it `@Optional()` and degrade to empty\n * results.\n *\n * String constant (not Symbol) so it matches by value across import\n * boundaries — same convention as `EVENT_BUS`.\n */\nexport const EVENT_READ_PORT = 'EVENT_READ_PORT' as const;\n\n/**\n * Injection token for the generated `TypedEventBus` facade.\n *\n * `TypedEventBus` lives in `runtime/subsystems/events/generated/bus.ts` and\n * wraps `IEventBus` with project-specific `AppDomainEvent`-typed `publish<T>()`\n * and `subscribe<T>()`. Use cases inject this token in preference to\n * `EVENT_BUS` when they want compile-time type safety on event shapes.\n *\n * String constant (not Symbol) so it matches by value across import\n * boundaries — same convention as `EVENT_BUS`.\n *\n * Provider registration lands in EVT-6 (EventsModule wiring); the token is\n * declared here so generated code can import it without depending on the\n * still-being-formalised module.\n */\nexport const TYPED_EVENT_BUS = 'TYPED_EVENT_BUS' as const;\n\n/**\n * Injection token for the resolved multi-tenancy flag.\n *\n * Provided by `EventsModule.forRoot(...)` as `options.multiTenant ?? false`.\n * Consumed by `TypedEventBus` to enforce the tenantId-is-required rule at\n * publish time.\n *\n * String constant (not Symbol) so it matches by value across import\n * boundaries — same convention as the other events tokens. (The jobs\n * subsystem uses Symbols for the analogous token; events chose strings\n * from the start and we keep the file internally consistent.)\n */\nexport const EVENTS_MULTI_TENANT = 'EVENTS_MULTI_TENANT' as const;\n\n/**\n * Injection token for the Redis connection URL used by RedisEventBus.\n * Provided automatically by EventsModule.forRoot({ backend: 'redis' }).\n *\n * ADR-037: namespaced `Symbol.for(...)` (via `tokenKey()`) so it matches by value\n * across runtime copies (the sibling string tokens above are already value-stable).\n * Note the jobs subsystem defines its own `REDIS_URL`-equivalent; this is the\n * events one.\n */\nexport const REDIS_URL = Symbol.for(tokenKey('events', 'redis-url'));\n\n/**\n * Injection token for the resolved `EventsModuleOptions` object.\n *\n * Provided automatically by `EventsModule.forRoot(...)` /\n * `EventsModule.forRootAsync(...)`. Backends that need to observe module\n * configuration (e.g. `DrizzleEventBus` reading `opts.pools` for\n * pool-filtered drain) inject via this token.\n *\n * String-valued (not `Symbol`) so it matches by value across import\n * boundaries — same convention as `EVENT_BUS`.\n */\nexport const EVENTS_MODULE_OPTIONS = 'EVENTS_MODULE_OPTIONS' as const;\n","/**\n * Injection tokens for the bridge subsystem (ADR-023 Phase 2, BRIDGE-2).\n *\n * String constants (not Symbols) so they match by value across import\n * boundaries — same convention as `EVENT_BUS` / `EVENTS_MULTI_TENANT` in the\n * events subsystem (per EVT-6 §Implementation Notes). The jobs subsystem\n * uses Symbols for its analogous tokens; we keep the bridge file internally\n * consistent with the events convention because the bridge is conceptually\n * downstream of (and imports from) the events module.\n */\n\n/**\n * Token for the `IJobBridge` repo backend (memory in BRIDGE-3, Drizzle in\n * BRIDGE-4). Consumed by `BridgeDeliveryHandler` (BRIDGE-5), the outbox\n * drain (BRIDGE-4 modification), and `EventFlowService` (BRIDGE-7).\n */\nexport const BRIDGE_DELIVERY_REPO = 'BRIDGE_DELIVERY_REPO' as const;\n\n/**\n * Token for the `IEventFlow` facade implementation (BRIDGE-7). Use cases\n * inject this in preference to `EVENT_BUS` / `TYPED_EVENT_BUS` — calling\n * `eventFlow.publish(...)` / `eventFlow.publishAndStart(...)` is the\n * sanctioned authoring surface (ADR-023 §Decision 7).\n */\nexport const EVENT_FLOW = 'EVENT_FLOW' as const;\n\n/**\n * Token for the resolved multi-tenancy flag, provided by\n * `BridgeModule.forRoot({ multiTenant })` in BRIDGE-8. Consumed by\n * `EventFlowService.publishAndStart` (entry), `BridgeDeliveryHandler.handle`\n * (entry), and `DrizzleBridgeDeliveryRepo.insertDelivery` (pre-write) — the\n * three enforcement sites called out in ADR-023 §Multi-tenancy.\n */\nexport const BRIDGE_MULTI_TENANT = 'BRIDGE_MULTI_TENANT' as const;\n\n/**\n * Token for the resolved `BridgeModuleOptions` object. Provided by\n * `BridgeModule.forRoot(...)` / `forRootAsync(...)` in BRIDGE-8.\n * Mirrors `EVENTS_MODULE_OPTIONS` and `JOBS_DOMAIN_OPTIONS` shape — backends\n * inject this when they need to observe additional module configuration\n * (e.g. pool overrides) without each adding a dedicated token.\n */\nexport const BRIDGE_MODULE_OPTIONS = 'BRIDGE_MODULE_OPTIONS' as const;\n\n/**\n * Token for the codegen-emitted `bridgeRegistry` — the\n * `Record<EventTypeName, BridgeTriggerEntry[]>` map that drives\n * outbox-drain trigger lookup (BRIDGE-4) and `EventFlowService` Case B\n * dedup (BRIDGE-7). Provider registration lands in BRIDGE-8; the token is\n * declared here so generated code (BRIDGE-6) can import it without\n * depending on the still-being-formalised module.\n */\nexport const BRIDGE_REGISTRY = 'BRIDGE_REGISTRY' as const;\n\n\n/**\n * Token for the `IBridgeOutboxDrainHook` implementation (BRIDGE-4).\n * Injected `@Optional()` into `DrizzleEventBus` — when the bridge\n * subsystem is not installed the token is undefined and the events\n * outbox drain skips the bridge block entirely (preserves EVT-4\n * baseline behaviour).\n *\n * Resolution order: `BridgeModule.forRoot()` provides this token in\n * BRIDGE-8 alongside the rest of the bridge subsystem. `EventsModule`\n * itself never provides it; the events subsystem stays unaware of the\n * bridge unless the consumer wires it.\n */\nexport const BRIDGE_OUTBOX_DRAIN_HOOK = 'BRIDGE_OUTBOX_DRAIN_HOOK' as const;\n","/**\n * Typed errors for the bridge subsystem (ADR-023 Phase 2, BRIDGE-2).\n *\n * All thrown by the three enforcement sites named in ADR-023 §Multi-tenancy:\n * - `EventFlowService.publishAndStart` entry (BRIDGE-7)\n * - `BridgeDeliveryHandler.handle` entry (BRIDGE-5)\n * - `DrizzleBridgeDeliveryRepo.insertDelivery` pre-write (BRIDGE-4)\n *\n * Same shape as `runtime/subsystems/jobs/jobs-errors.ts` and\n * `runtime/subsystems/events/events-errors.ts` so consumers can catch them\n * with the same exception-filter pattern across all three subsystems.\n */\n\n/**\n * Thrown when `BridgeModule` was configured with `multiTenant: true` but\n * the caller did not pass a `tenantId` at one of the three enforcement\n * sites listed above.\n *\n * **Strict enforcement rationale (mirrors JOB-8 / SYNC-6 stance, locked\n * 2026-04-18 for jobs; same rationale applies here).** Cross-tenant data\n * leakage is the worst class of bug a multi-tenant system can ship;\n * surfacing the misuse loudly at the call site (rather than silently\n * defaulting to `null` or to \"the last tenant seen\") prevents both\n * accidental global writes and sneaky reads that return a union of tenants.\n *\n * - `undefined` `tenantId` → throw this error.\n * - Explicit `null` `tenantId` → passes; opts the call into cross-tenant\n * work (e.g. a system housekeeping event with no owning tenant). The\n * `bridge_delivery` row is persisted with `tenant_id = NULL`.\n *\n * The `callSite` constructor argument names which of the three enforcement\n * sites threw — review reports and ops dashboards rely on a stable site\n * name, so use the canonical strings: `'EventFlowService.publishAndStart'`,\n * `'BridgeDeliveryHandler.handle'`,\n * `'DrizzleBridgeDeliveryRepo.insertDelivery'`.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly callSite: string) {\n super(\n `MissingTenantIdError: BridgeModule was configured with ` +\n `multiTenant=true but ${callSite} was called without tenantId ` +\n `(undefined). Pass an explicit tenantId, or pass null for ` +\n `cross-tenant work.`,\n );\n }\n}\n\n/**\n * Synthetic error thrown by `MemoryBridgeDeliveryRepo.insertDelivery` when\n * a duplicate `(event_id, trigger_id)` insert hits the simulated UNIQUE\n * constraint (BRIDGE-3).\n *\n * Carries a `constraint` field equal to the Drizzle constraint name\n * declared in BRIDGE-1's schema (`uq_bridge_delivery_event_trigger`) so\n * call sites can branch on the same discriminator regardless of which\n * backend is wired up. This matters because ADR-023 explicitly leans on\n * the constraint as the dedup mechanism in two places — outbox replay\n * and `publishAndStart` Case B — and BRIDGE-4 / BRIDGE-7 will share a\n * type-check path with BRIDGE-3-driven tests.\n *\n * The Drizzle backend (BRIDGE-4) does NOT throw this error: it uses\n * `INSERT … ON CONFLICT (event_id, trigger_id) DO NOTHING RETURNING id`\n * per the BRIDGE-4 spec recommendation, so collisions surface as an empty\n * result set rather than an exception. The error exists so the memory\n * backend can faithfully model the \"duplicate raises\" behaviour for tests\n * that want to assert the constraint actually fires.\n */\n/**\n * Thrown by `BridgeModule.onModuleInit` when `JobWorkerModule` is wired\n * alongside the bridge but its active `pools` list does not include one\n * or more of the three reserved bridge pools (`events_inbound`,\n * `events_change`, `events_outbound`).\n *\n * Without a worker polling those pools, the wrapper `job_run` rows the\n * outbox drain inserts (BRIDGE-4) sit `pending` forever — a silent\n * footgun where `eventFlow.publish(...)` returns success but no user\n * job ever spawns. The boot-time check converts that into a fail-fast.\n *\n * Operators can either (a) add `...BRIDGE_RESERVED_POOLS` to their\n * `JobWorkerModule.forRoot({ pools })` configuration so the same\n * process polls the reserved pools, or (b) run a separate worker\n * process per reserved pool for lane isolation (ADR-022 §Pool\n * isolation).\n */\nexport class BridgeReservedPoolsNotPolledError extends Error {\n override readonly name = 'BridgeReservedPoolsNotPolledError';\n constructor(public readonly missingPools: readonly string[]) {\n super(\n `BridgeModule loaded but JobWorkerModule is not polling reserved ` +\n `pool '${missingPools[0]}'. Add ...BRIDGE_RESERVED_POOLS to your ` +\n `JobWorkerModule.forRoot({ pools }) configuration. Missing pools: ` +\n `${missingPools.join(', ')}. (Bridge-fanout wrappers will sit ` +\n `pending forever without these pollers.)`,\n );\n }\n}\n\nexport class UniqueConstraintError extends Error {\n override readonly name = 'UniqueConstraintError';\n constructor(\n public readonly constraint: string,\n public readonly eventId: string,\n public readonly triggerId: string,\n ) {\n super(\n `UniqueConstraintError: duplicate insert into bridge_delivery for ` +\n `(event_id='${eventId}', trigger_id='${triggerId}') — violates ` +\n `constraint '${constraint}'.`,\n );\n }\n}\n","/**\n * `assertTenantId` — shared multi-tenancy enforcement helper for the\n * bridge subsystem (BRIDGE-8, ADR-023 §Multi-tenancy null-tenantId).\n *\n * Single source of truth for the three enforcement sites named in\n * ADR-023 §Multi-tenancy and the BRIDGE-2 spec:\n *\n * (a) `EventFlowService.publishAndStart` — request-path entry\n * (b) `BridgeDeliveryHandler.run` — wrapper handler entry\n * (c) `DrizzleBridgeDeliveryRepo.insertDelivery` — last-line repo defense\n *\n * Contract (mirrors JOB-8 / SYNC-6 — locked 2026-04-18 for jobs and\n * carried into the bridge here):\n *\n * - `multiTenant === false` → no-op (always passes).\n * - `multiTenant === true`,\n * `tenantId === undefined` → throw `MissingTenantIdError(site)`.\n * - `multiTenant === true`,\n * `tenantId === null` → passes; opts the call into\n * cross-tenant work (system\n * housekeeping, framework events\n * with no owning tenant). Persists\n * to the DB as `tenant_id = NULL`.\n * - `multiTenant === true`,\n * `tenantId` is a string → passes.\n *\n * The strict `undefined`-vs-`null` discrimination is the entire point —\n * silent defaulting is exactly the failure mode that lets cross-tenant\n * leaks ship.\n */\nimport { MissingTenantIdError } from './bridge-errors';\n\n/**\n * Throws `MissingTenantIdError(site)` if `multiTenant === true` and\n * `tenantId === undefined`. Explicit `null` always passes.\n *\n * @param site Canonical site name — one of:\n * `'EventFlowService.publishAndStart'`,\n * `'BridgeDeliveryHandler.run'`,\n * `'DrizzleBridgeDeliveryRepo.insertDelivery'`.\n * Stable strings; ops dashboards / review reports key\n * on these. Use the same string the existing tests\n * and `MissingTenantIdError` JSDoc enumerate.\n * @param multiTenant Resolved `BRIDGE_MULTI_TENANT` flag (from\n * `BridgeModule.forRoot({ multiTenant })`).\n * @param tenantId The tenantId the caller supplied (or didn't).\n */\nexport function assertTenantId(\n site: string,\n multiTenant: boolean,\n tenantId: string | null | undefined,\n): void {\n if (multiTenant && tenantId === undefined) {\n throw new MissingTenantIdError(site);\n }\n // explicit null passes — opts into cross-tenant work\n}\n"],"mappings":"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","names":["HandlerRegistry"]}
|
|
1
|
+
{"version":3,"sources":["../../../../runtime/subsystems/bridge/bridge-delivery-handler.ts","../../../../runtime/subsystems/token-key.ts","../../../../runtime/subsystems/jobs/jobs-domain.tokens.ts","../../../../runtime/subsystems/jobs/job-handler.base.ts","../../../../runtime/subsystems/events/events.tokens.ts","../../../../runtime/subsystems/bridge/bridge.tokens.ts","../../../../runtime/subsystems/bridge/bridge-errors.ts","../../../../runtime/subsystems/bridge/assert-tenant-id.ts"],"sourcesContent":["/**\n * BridgeDeliveryHandler — the framework `@JobHandler` that runs every\n * bridge-fanout wrapper on the reserved `events_*` pools (BRIDGE-5,\n * ADR-023 §Decision 2 flow diagram).\n *\n * Role: when the outbox drain (BRIDGE-4) inserts a `bridge_delivery + wrapper\n * job_run` pair, the worker that polls the wrapper's pool claims that\n * wrapper and dispatches it to this handler. The handler:\n *\n * 1. Loads the `bridge_delivery` row by `ctx.input.deliveryId`.\n * 2. Looks up the trigger entry in the codegen-emitted `bridgeRegistry`\n * (`runtime/subsystems/bridge/generated/registry.ts`, BRIDGE-6).\n * A missing entry means the trigger was renamed or removed since the\n * delivery row was written; mark `skipped` with\n * `skip_reason='trigger_unregistered'` per ADR-023 §Trigger rename\n * or removal.\n * 3. Re-fetches the authoritative `domain_events` row (`IEventBus.findById`)\n * so `when:` / `map:` callbacks see the committed payload — never a\n * copy that drifted between drain and claim time.\n * 4. Evaluates `entry.when?.(event)`. False ⇒ mark `skipped` with\n * `skip_reason='predicate_false'`.\n * 5. Calls `IJobOrchestrator.start(entry.jobType, entry.map(event), …)`\n * INSIDE `ctx.step('spawn_user_run', …)`. The step memoization is\n * what makes wrapper retries (BRIDGE-1 ledger says no auto-retry past\n * the wrapper's own retry policy, but Phase 1 wrappers DO retry per\n * JOB-3) idempotent — a successful spawn followed by a transient\n * ledger-update failure would otherwise re-spawn on the next attempt.\n * 6. Marks `delivered` with the spawned `runId`.\n *\n * Pool registration: BRIDGE-5 ships ONE `@JobHandler` registration with\n * `pool: 'events_change'` (the default). The wrapper rows the drain\n * inserts carry `pool: events_<direction>` per row, so workers polling\n * `events_inbound` / `events_outbound` claim and dispatch to this same\n * handler class regardless of the metadata pool — the worker filter is on\n * `job_run.pool`, not on `@JobHandler.meta.pool`. BRIDGE-8 confirms the\n * three pools are active and registered at module init. The\n * `@framework/*` job-type prefix exempts this registration from the\n * reserved-pool validator (BRIDGE-5 added that exemption to\n * `job-worker.module.ts`).\n *\n * Tenant threading: when `BRIDGE_MULTI_TENANT=true`, the handler asserts\n * `delivery.tenantId !== undefined` before the spawn (the column is\n * nullable, so explicit `null` is allowed for cross-tenant work — same\n * contract as JOB-8). BRIDGE-8 wires the assertion via the\n * `BRIDGE_MULTI_TENANT` token.\n *\n * Failure path: any throw inside the handler propagates up; the worker's\n * normal retry policy (declared on the `@JobHandler` here as `attempts:\n * 3, backoff: exponential, baseMs: 250`) absorbs transient infra blips.\n * After exhaustion, the wrapper transitions to `failed`; the outer error\n * handler catches and calls `repo.markFailed(...)` so the delivery row\n * reflects the final state. Operators see `bridge_delivery.status='failed'`\n * surface via the `idx_bridge_delivery_status` partial index (BRIDGE-1).\n */\nimport { Inject, Injectable, Logger, Optional } from '@nestjs/common';\n\nimport { JOB_ORCHESTRATOR } from '../jobs/jobs-domain.tokens';\nimport type { IJobOrchestrator } from '../jobs/job-orchestrator.protocol';\nimport {\n JobHandler,\n JobHandlerBase,\n type JobContext,\n} from '../jobs/job-handler.base';\n\nimport { EVENT_BUS } from '../events/events.tokens';\nimport type { IEventBus, DomainEvent } from '../events/event-bus.protocol';\nimport type { EventTypeName } from '../events/event-registry';\n\nimport {\n BRIDGE_DELIVERY_REPO,\n BRIDGE_MULTI_TENANT,\n BRIDGE_REGISTRY,\n} from './bridge.tokens';\nimport type {\n BridgeRegistry,\n BridgeTriggerEntry,\n IJobBridge,\n} from './bridge.protocol';\nimport { assertTenantId } from './assert-tenant-id';\n\n/** Stable canonical job type — referenced by BRIDGE-4 wrapper inserts. */\nexport const BRIDGE_DELIVERY_JOB_TYPE = '@framework/bridge_delivery' as const;\n\n/** Stable canonical step id — referenced for memoization across attempts. */\nconst SPAWN_USER_RUN_STEP = 'spawn_user_run' as const;\n\nexport interface BridgeDeliveryInput {\n /** PK of the `bridge_delivery` row this wrapper services. */\n deliveryId: string;\n}\n\n@Injectable()\n@JobHandler<BridgeDeliveryInput>(BRIDGE_DELIVERY_JOB_TYPE, {\n pool: 'events_change',\n retry: { attempts: 3, backoff: 'exponential', baseMs: 250 },\n replayFrom: 'last_step',\n})\nexport class BridgeDeliveryHandler extends JobHandlerBase<\n BridgeDeliveryInput,\n { runId: string } | { skipped: true; reason: string }\n> {\n private readonly classLogger = new Logger(BridgeDeliveryHandler.name);\n\n constructor(\n @Inject(BRIDGE_DELIVERY_REPO) private readonly repo: IJobBridge,\n @Inject(JOB_ORCHESTRATOR) private readonly orchestrator: IJobOrchestrator,\n @Inject(EVENT_BUS) private readonly events: IEventBus,\n @Inject(BRIDGE_REGISTRY) private readonly registry: BridgeRegistry,\n @Optional()\n @Inject(BRIDGE_MULTI_TENANT)\n private readonly multiTenant: boolean = false,\n ) {\n super();\n }\n\n async run(\n ctx: JobContext<BridgeDeliveryInput>,\n ): Promise<{ runId: string } | { skipped: true; reason: string }> {\n const { deliveryId } = ctx.input;\n\n // Step 1 — locate the delivery row by primary key.\n const delivery = await this.repo.findDeliveryById(deliveryId);\n if (!delivery) {\n // The drain wrote a wrapper job_run but the delivery row is gone\n // (manual ops cleanup, or delete-cascade from the parent event).\n // No row → no work; return without throwing so the wrapper marks\n // completed cleanly.\n this.classLogger.warn(\n `bridge_delivery row '${deliveryId}' not found; wrapper completes ` +\n `without spawning a user job.`,\n );\n return { skipped: true, reason: 'delivery_row_missing' };\n }\n\n // Step 2 — multi-tenancy gate. Site (b) of the three ADR-023\n // §Multi-tenancy enforcement sites; shared helper from BRIDGE-8.\n // The DB always returns string|null, never undefined; this branch\n // exists for the in-memory backend's older test fixtures and to\n // pin the contract in shape-typed tests.\n assertTenantId(\n 'BridgeDeliveryHandler.run',\n this.multiTenant,\n delivery.tenantId,\n );\n\n // Step 3 — load the typed event row.\n const event = await this.events.findById(delivery.eventId);\n if (!event) {\n // FK from bridge_delivery.event_id → domain_events.id should make\n // this impossible at the DB layer, but defensive: if the row is\n // missing we mark skipped, not failed (no work the bridge can do).\n this.classLogger.warn(\n `domain_events row '${delivery.eventId}' missing for delivery ` +\n `'${deliveryId}'; marking skipped.`,\n );\n await this.repo.markSkipped(delivery.id, 'event_row_missing');\n return { skipped: true, reason: 'event_row_missing' };\n }\n\n // Step 4 — registry lookup. Handles trigger rename/removal cleanly.\n const entry = this.findRegistryEntry(event.type, delivery.triggerId);\n if (!entry) {\n await this.repo.markSkipped(delivery.id, 'trigger_unregistered');\n return { skipped: true, reason: 'trigger_unregistered' };\n }\n\n // Step 5 — `when:` predicate.\n if (entry.when && !entry.when(event as never)) {\n await this.repo.markSkipped(delivery.id, 'predicate_false');\n return { skipped: true, reason: 'predicate_false' };\n }\n\n // Step 6 — memoized spawn. `ctx.step` records the result in\n // `job_step` and on retry returns the cached `{ runId }` so a\n // transient failure between `orchestrator.start` and `markDelivered`\n // doesn't double-spawn the user job.\n const input = entry.map(event as never);\n const { runId } = await ctx.step<{ runId: string }>(\n SPAWN_USER_RUN_STEP,\n async () => {\n const run = await this.orchestrator.start(entry.jobType, input, {\n parentRunId: ctx.run.id,\n triggerSource: 'event',\n triggerRef: delivery.eventId,\n tenantId: delivery.tenantId,\n });\n return { runId: run.id };\n },\n );\n\n // Step 7 — ledger transition.\n await this.repo.markDelivered(delivery.id, runId);\n return { runId };\n }\n\n /**\n * Locate the registry entry for `(eventType, triggerId)`. Linear scan\n * over the per-event-type array — N is the number of triggers declared\n * for one event, typically 1–5; the table is not big enough to warrant\n * a secondary index.\n */\n private findRegistryEntry(\n eventType: string,\n triggerId: string,\n ): BridgeTriggerEntry | undefined {\n const candidates =\n this.registry[eventType as EventTypeName] ?? undefined;\n if (!candidates) return undefined;\n return candidates.find((c) => c.triggerId === triggerId) as\n | BridgeTriggerEntry\n | undefined;\n }\n}\n\n/**\n * Re-export for BRIDGE-7 facade Case B and BRIDGE-4 wrapper insert.\n * Single source of truth for the canonical type string keeps refactors\n * in one place.\n */\nexport { BRIDGE_DELIVERY_JOB_TYPE as BridgeDeliveryJobType };\n","/** Canonical package namespace for cross-boundary DI token keys. MUST be a hardcoded\n * constant (NOT derived from package.json) so a vendored copy — which lives inside the\n * CONSUMER's package — produces the identical key and the two copies share the symbol. */\nexport const PKG = '@pattern-stack/codegen';\n// TODO(token-version): if/when a runtime contract version is adopted, inject it HERE only\n// (e.g. `${PKG}#${ABI}.${area}.${name}`) — this helper is the single chokepoint.\nexport const tokenKey = (area: string, name: string): string => `${PKG}.${area}.${name}`;\n","/**\n * Injection tokens for the job orchestration domain layer (ADR-022, JOB-2).\n *\n * Consumer code injects these symbols via `@Inject(JOB_ORCHESTRATOR)` etc.;\n * concrete backends (JOB-3 Drizzle, JOB-4 Memory) provide the implementations\n * through `JobsDomainModule.forRoot({ backend })` in JOB-5.\n *\n * Each token is a namespaced `Symbol.for(...)` (ADR-037, via `tokenKey()`) —\n * distinct per key, so Nest's DI lookup is unambiguous, AND matching by VALUE\n * across import boundaries so the package and a (legacy) vendored runtime copy\n * resolve to the same symbol.\n */\nimport { tokenKey } from '../token-key';\n\nexport const JOB_ORCHESTRATOR = Symbol.for(tokenKey('jobs', 'orchestrator'));\nexport const JOB_RUN_SERVICE = Symbol.for(tokenKey('jobs', 'run-service'));\nexport const JOB_STEP_SERVICE = Symbol.for(tokenKey('jobs', 'step-service'));\n\n/**\n * Multi-tenancy opt-in flag (JOB-8). Bound to the boolean passed in via\n * `JobsDomainModule.forRoot({ multiTenant })`, defaulting to `false`.\n *\n * When `true`, the four service-layer backends (Drizzle + Memory orchestrator\n * and run-service) enforce `tenantId` on every mutating / targeted-read call:\n * `start`, `cancel`, `listForScope`, `cancelForScope`, `rescheduleForScope`.\n * Missing (`undefined`) `tenantId` throws `MissingTenantIdError`; explicit\n * `null` opts into cross-tenant background work and passes through.\n *\n * The JobWorker claim loop is **cross-tenant by design** — the worker has no\n * tenant context; `tenantId` is populated at write time and enforced on\n * targeted reads. See docs/specs/JOB-8.md.\n */\nexport const JOBS_MULTI_TENANT = Symbol.for(tokenKey('jobs', 'multi-tenant'));\n","/**\n * Handler base class, JobContext, @JobHandler decorator, and policy types\n * for the job orchestration domain (ADR-022, JOB-2).\n *\n * User-authored jobs subclass `JobHandlerBase<TInput, TOutput>` and decorate\n * the class with `@JobHandler<TInput>('job_type', meta)`. The decorator\n * 1. stores metadata via `Reflect.defineMetadata` so Nest's reflector can\n * pick it up at module boot, and\n * 2. populates `JOB_HANDLER_REGISTRY` — a module-singleton map consumed by\n * `JobWorkerModule` (JOB-5) to materialise `job` rows and resolve\n * handler classes during claim/execute.\n *\n * No runtime orchestration lives here; this file is a pure type + decorator\n * surface so downstream PRs (JOB-3..JOB-5) can implement against a stable\n * shape.\n */\n// TODO(logging-subsystem): swap to ILogger once ADR-028 lands\nimport type { Logger } from '@nestjs/common';\nimport { tokenKey } from '../token-key';\nimport type { EventOfType, EventTypeName } from '../events/event-registry';\nimport type { JobRun } from './job-orchestrator.protocol';\n\n// ─── ParentClosePolicy ──────────────────────────────────────────────────────\n\n/**\n * What happens to running child runs when a parent enters a terminal state.\n * Stored on the child at spawn; changes to the parent after spawn do NOT\n * retroactively rewrite children.\n */\nexport enum ParentClosePolicy {\n Terminate = 'terminate',\n Cancel = 'cancel',\n Abandon = 'abandon',\n}\n\n// ─── Policy types ───────────────────────────────────────────────────────────\n\nexport interface RetryPolicy {\n attempts: number;\n backoff: 'fixed' | 'exponential';\n baseMs: number;\n nonRetryableErrors?: string[];\n}\n\nexport interface ConcurrencyPolicy<TInput> {\n key: (input: TInput) => string;\n collisionMode: 'queue' | 'reject' | 'replace';\n}\n\nexport interface DedupePolicy<TInput> {\n key: (input: TInput) => string;\n windowMs: number;\n}\n\n/**\n * Declarative scope reference. `TScope` is parameterised so JOB-7 can narrow\n * `entity` to the generated `ScopeEntityType` union at the call site without\n * modifying this file (OQ-1 resolution, 2026-04-20).\n */\nexport interface ScopeRef<TInput, TScope extends string = string> {\n entity: TScope;\n from: (input: TInput) => string;\n}\n\n/**\n * Bridge trigger authoring shape (BRIDGE-6 follow-up — BRIDGE-6 shipped the\n * generator + runtime for `@JobHandler({ triggers })` but never added the\n * authoring field to this type; the generator's tests scan source as strings,\n * so a real decorator was never compiled and the gap went uncaught).\n *\n * Declared on `@JobHandler({ triggers })`; the codegen bridge-registry\n * generator (`src/cli/shared/bridge-registry-generator.ts`) scans these from\n * source and emits `bridge/generated/registry.ts`, validating each `event`\n * against the generated `eventRegistry` at `gen-all`. The distributed union\n * narrows `map`/`when` per `event`, so callbacks are typed against the event\n * payload (ADR-023, \"typed against PayloadOfType<T>\").\n *\n * Typed against events' generated types — the same `import type` coupling the\n * bridge already has (erased at runtime). `jobs` must NOT import `bridge`, so\n * the post-gen `BridgeTriggerEntry` is deliberately not referenced here;\n * `triggerId`/`jobType` are computed by the generator, not authored.\n */\nexport type JobTrigger<TInput> = {\n [T in EventTypeName]: {\n /** Event type that fires this trigger. Validated against `eventRegistry`. */\n event: T;\n /** Maps the event to the job input. Inlined verbatim into the registry. */\n map: (event: EventOfType<T>) => TInput;\n /** Optional guard; `false` → wrapper records `status='skipped'`. */\n when?: (event: EventOfType<T>) => boolean;\n };\n}[EventTypeName];\n\nexport interface JobHandlerMeta<TInput> {\n pool?: string;\n scope?: ScopeRef<TInput>;\n retry?: RetryPolicy;\n concurrency?: ConcurrencyPolicy<TInput>;\n dedupe?: DedupePolicy<TInput>;\n timeoutMs?: number;\n replayFrom?: 'scratch' | 'last_step' | 'last_checkpoint';\n /**\n * Bridge triggers (ADR-023 Tier 3). Codegen scans these into `bridgeRegistry`;\n * the framework `BridgeDeliveryHandler` starts this job per matched event.\n * Absent for jobs started directly or via `IEventFlow.publishAndStart`.\n */\n triggers?: readonly JobTrigger<TInput>[];\n}\n\n// ─── Runtime option shapes ──────────────────────────────────────────────────\n\nexport interface StepOptions {\n retry?: RetryPolicy;\n timeoutMs?: number;\n}\n\nexport interface SpawnChildOptions {\n closePolicy?: ParentClosePolicy;\n runAt?: Date;\n priority?: number;\n tags?: Record<string, string>;\n}\n\n// ─── JobContext ─────────────────────────────────────────────────────────────\n\nexport interface JobContext<TInput> {\n readonly input: TInput;\n readonly run: JobRun;\n step<TOutput>(\n stepId: string,\n fn: () => Promise<TOutput>,\n opts?: StepOptions,\n ): Promise<TOutput>;\n spawnChild(type: string, input: unknown, opts?: SpawnChildOptions): Promise<JobRun>;\n readonly logger: Logger;\n // NOT in Phase 1 — deferred to ADR-025:\n // waitFor(kind, token, opts)\n // signal(token, payload)\n // sleep(ms)\n}\n\n// ─── JobHandlerBase ─────────────────────────────────────────────────────────\n\nexport abstract class JobHandlerBase<TInput, TOutput = unknown> {\n abstract run(ctx: JobContext<TInput>): Promise<TOutput>;\n}\n\n// ─── Registry + decorator ───────────────────────────────────────────────────\n\n/**\n * Module-singleton map keyed by job type. Populated by the `@JobHandler`\n * decorator at class definition time; consumed by `JobWorkerModule` (JOB-5)\n * to upsert `job` rows and resolve handler classes during claim/execute.\n */\nexport const JOB_HANDLER_REGISTRY = new Map<\n string,\n {\n type: string;\n meta: JobHandlerMeta<unknown>;\n handlerClass: new (...args: unknown[]) => JobHandlerBase<unknown>;\n }\n>();\n\n// ADR-037: namespaced `Symbol.for(...)` (via `tokenKey()`) so the reflection-metadata\n// key matches by value across import boundaries (the @JobHandler decorator and the\n// reader may resolve different runtime copies). Distinct from the DI tokens but\n// subject to the same dual-package identity hazard.\nexport const JOB_HANDLER_METADATA_KEY = Symbol.for(tokenKey('jobs', 'handler-metadata'));\n\n/**\n * Class decorator that registers a handler with the job type, the full\n * metadata shape, and the target class constructor.\n *\n * Duplicate-type behaviour (OQ-3, resolved 2026-04-18):\n * - `NODE_ENV === 'production'` → throw; silent overwrite in prod is a\n * correctness bug.\n * - `NODE_ENV === 'test'` → silent overwrite (tests intentionally\n * re-register handlers).\n * - otherwise (dev) → `console.warn` + overwrite. `console`\n * is used intentionally instead of the Nest `Logger` — decorators run\n * at module-load time before any Nest container exists.\n */\nexport function JobHandler<TInput>(\n type: string,\n meta: JobHandlerMeta<TInput>,\n): ClassDecorator {\n return (target) => {\n if (JOB_HANDLER_REGISTRY.has(type)) {\n const env = process.env.NODE_ENV;\n if (env === 'production') {\n throw new Error(\n `[JobHandler] Duplicate registration for job type '${type}'. ` +\n `Each @JobHandler must declare a unique type.`,\n );\n }\n if (env !== 'test') {\n // eslint-disable-next-line no-console\n console.warn(\n `[JobHandler] Duplicate registration for job type '${type}'. ` +\n `Overwriting previous handler — this is almost certainly a bug.`,\n );\n }\n }\n\n Reflect.defineMetadata(JOB_HANDLER_METADATA_KEY, { type, meta }, target);\n JOB_HANDLER_REGISTRY.set(type, {\n type,\n meta: meta as JobHandlerMeta<unknown>,\n handlerClass: target as unknown as new (\n ...args: unknown[]\n ) => JobHandlerBase<unknown>,\n });\n };\n}\n\n// ─── HandlerRegistry — read helpers consumed by JobWorkerModule (JOB-5) ─────\n\n/**\n * Single entry shape returned by `HandlerRegistry.getAll()` / `.get()` and\n * exposed to `JobWorkerModule.onModuleInit` for boot-time upserts.\n *\n * Structurally compatible with `IJobOrchestrator.upsertJobRows`'s\n * `JobUpsertEntry` so the worker module can pass entries through verbatim\n * without re-mapping.\n */\nexport interface HandlerRegistryEntry {\n type: string;\n meta: JobHandlerMeta<unknown>;\n handlerClass: new (...args: unknown[]) => JobHandlerBase<unknown>;\n}\n\n/**\n * Read facade over `JOB_HANDLER_REGISTRY`. The decorator's write path is\n * unchanged; this namespace exists so consumers (the worker module, tests)\n * don't import the raw `Map` and accidentally mutate it.\n */\nexport namespace HandlerRegistry {\n /** All registered entries in insertion order. */\n export function getAll(): HandlerRegistryEntry[] {\n return Array.from(JOB_HANDLER_REGISTRY.values());\n }\n\n /** Lookup by job type, or `undefined` if no `@JobHandler` is registered. */\n export function get(type: string): HandlerRegistryEntry | undefined {\n return JOB_HANDLER_REGISTRY.get(type);\n }\n}\n","/**\n * Injection token for the event bus.\n *\n * String constant (not Symbol) so it matches by value across import boundaries.\n * Matches the token in runtime/constants/tokens.ts — both are 'EVENT_BUS'.\n *\n * Usage in use cases:\n * ```typescript\n * constructor(@Inject(EVENT_BUS) private readonly eventBus: IEventBus) {}\n * ```\n */\nimport { tokenKey } from '../token-key';\n\nexport const EVENT_BUS = 'EVENT_BUS' as const;\n\n/**\n * Injection token for the read-side `IEventReadPort` over `domain_events`\n * (OBS-LIST-1).\n *\n * Bound by `EventsModule.forRoot` to the same backend instance as\n * `EVENT_BUS` for the `drizzle` and `memory` backends (both implement\n * `IEventReadPort`). The `redis` backend retains no history and therefore\n * does NOT provide this token — consumers composing it (e.g. the\n * observability combiner) inject it `@Optional()` and degrade to empty\n * results.\n *\n * String constant (not Symbol) so it matches by value across import\n * boundaries — same convention as `EVENT_BUS`.\n */\nexport const EVENT_READ_PORT = 'EVENT_READ_PORT' as const;\n\n/**\n * Injection token for the generated `TypedEventBus` facade.\n *\n * `TypedEventBus` lives in `runtime/subsystems/events/generated/bus.ts` and\n * wraps `IEventBus` with project-specific `AppDomainEvent`-typed `publish<T>()`\n * and `subscribe<T>()`. Use cases inject this token in preference to\n * `EVENT_BUS` when they want compile-time type safety on event shapes.\n *\n * String constant (not Symbol) so it matches by value across import\n * boundaries — same convention as `EVENT_BUS`.\n *\n * Provider registration lands in EVT-6 (EventsModule wiring); the token is\n * declared here so generated code can import it without depending on the\n * still-being-formalised module.\n */\nexport const TYPED_EVENT_BUS = 'TYPED_EVENT_BUS' as const;\n\n/**\n * Injection token for the resolved multi-tenancy flag.\n *\n * Provided by `EventsModule.forRoot(...)` as `options.multiTenant ?? false`.\n * Consumed by `TypedEventBus` to enforce the tenantId-is-required rule at\n * publish time.\n *\n * String constant (not Symbol) so it matches by value across import\n * boundaries — same convention as the other events tokens. (The jobs\n * subsystem uses Symbols for the analogous token; events chose strings\n * from the start and we keep the file internally consistent.)\n */\nexport const EVENTS_MULTI_TENANT = 'EVENTS_MULTI_TENANT' as const;\n\n/**\n * Injection token for the Redis connection URL used by RedisEventBus.\n * Provided automatically by EventsModule.forRoot({ backend: 'redis' }).\n *\n * ADR-037: namespaced `Symbol.for(...)` (via `tokenKey()`) so it matches by value\n * across runtime copies (the sibling string tokens above are already value-stable).\n * Note the jobs subsystem defines its own `REDIS_URL`-equivalent; this is the\n * events one.\n */\nexport const REDIS_URL = Symbol.for(tokenKey('events', 'redis-url'));\n\n/**\n * Injection token for the resolved `EventsModuleOptions` object.\n *\n * Provided automatically by `EventsModule.forRoot(...)` /\n * `EventsModule.forRootAsync(...)`. Backends that need to observe module\n * configuration (e.g. `DrizzleEventBus` reading `opts.pools` for\n * pool-filtered drain) inject via this token.\n *\n * String-valued (not `Symbol`) so it matches by value across import\n * boundaries — same convention as `EVENT_BUS`.\n */\nexport const EVENTS_MODULE_OPTIONS = 'EVENTS_MODULE_OPTIONS' as const;\n","/**\n * Injection tokens for the bridge subsystem (ADR-023 Phase 2, BRIDGE-2).\n *\n * String constants (not Symbols) so they match by value across import\n * boundaries — same convention as `EVENT_BUS` / `EVENTS_MULTI_TENANT` in the\n * events subsystem (per EVT-6 §Implementation Notes). The jobs subsystem\n * uses Symbols for its analogous tokens; we keep the bridge file internally\n * consistent with the events convention because the bridge is conceptually\n * downstream of (and imports from) the events module.\n */\n\n/**\n * Token for the `IJobBridge` repo backend (memory in BRIDGE-3, Drizzle in\n * BRIDGE-4). Consumed by `BridgeDeliveryHandler` (BRIDGE-5), the outbox\n * drain (BRIDGE-4 modification), and `EventFlowService` (BRIDGE-7).\n */\nexport const BRIDGE_DELIVERY_REPO = 'BRIDGE_DELIVERY_REPO' as const;\n\n/**\n * Token for the `IEventFlow` facade implementation (BRIDGE-7). Use cases\n * inject this in preference to `EVENT_BUS` / `TYPED_EVENT_BUS` — calling\n * `eventFlow.publish(...)` / `eventFlow.publishAndStart(...)` is the\n * sanctioned authoring surface (ADR-023 §Decision 7).\n */\nexport const EVENT_FLOW = 'EVENT_FLOW' as const;\n\n/**\n * Token for the resolved multi-tenancy flag, provided by\n * `BridgeModule.forRoot({ multiTenant })` in BRIDGE-8. Consumed by\n * `EventFlowService.publishAndStart` (entry), `BridgeDeliveryHandler.handle`\n * (entry), and `DrizzleBridgeDeliveryRepo.insertDelivery` (pre-write) — the\n * three enforcement sites called out in ADR-023 §Multi-tenancy.\n */\nexport const BRIDGE_MULTI_TENANT = 'BRIDGE_MULTI_TENANT' as const;\n\n/**\n * Token for the resolved `BridgeModuleOptions` object. Provided by\n * `BridgeModule.forRoot(...)` / `forRootAsync(...)` in BRIDGE-8.\n * Mirrors `EVENTS_MODULE_OPTIONS` and `JOBS_DOMAIN_OPTIONS` shape — backends\n * inject this when they need to observe additional module configuration\n * (e.g. pool overrides) without each adding a dedicated token.\n */\nexport const BRIDGE_MODULE_OPTIONS = 'BRIDGE_MODULE_OPTIONS' as const;\n\n/**\n * Token for the codegen-emitted `bridgeRegistry` — the\n * `Record<EventTypeName, BridgeTriggerEntry[]>` map that drives\n * outbox-drain trigger lookup (BRIDGE-4) and `EventFlowService` Case B\n * dedup (BRIDGE-7). Provider registration lands in BRIDGE-8; the token is\n * declared here so generated code (BRIDGE-6) can import it without\n * depending on the still-being-formalised module.\n */\nexport const BRIDGE_REGISTRY = 'BRIDGE_REGISTRY' as const;\n\n\n/**\n * Token for the `IBridgeOutboxDrainHook` implementation (BRIDGE-4).\n * Injected `@Optional()` into `DrizzleEventBus` — when the bridge\n * subsystem is not installed the token is undefined and the events\n * outbox drain skips the bridge block entirely (preserves EVT-4\n * baseline behaviour).\n *\n * Resolution order: `BridgeModule.forRoot()` provides this token in\n * BRIDGE-8 alongside the rest of the bridge subsystem. `EventsModule`\n * itself never provides it; the events subsystem stays unaware of the\n * bridge unless the consumer wires it.\n */\nexport const BRIDGE_OUTBOX_DRAIN_HOOK = 'BRIDGE_OUTBOX_DRAIN_HOOK' as const;\n","/**\n * Typed errors for the bridge subsystem (ADR-023 Phase 2, BRIDGE-2).\n *\n * All thrown by the three enforcement sites named in ADR-023 §Multi-tenancy:\n * - `EventFlowService.publishAndStart` entry (BRIDGE-7)\n * - `BridgeDeliveryHandler.handle` entry (BRIDGE-5)\n * - `DrizzleBridgeDeliveryRepo.insertDelivery` pre-write (BRIDGE-4)\n *\n * Same shape as `runtime/subsystems/jobs/jobs-errors.ts` and\n * `runtime/subsystems/events/events-errors.ts` so consumers can catch them\n * with the same exception-filter pattern across all three subsystems.\n */\n\n/**\n * Thrown when `BridgeModule` was configured with `multiTenant: true` but\n * the caller did not pass a `tenantId` at one of the three enforcement\n * sites listed above.\n *\n * **Strict enforcement rationale (mirrors JOB-8 / SYNC-6 stance, locked\n * 2026-04-18 for jobs; same rationale applies here).** Cross-tenant data\n * leakage is the worst class of bug a multi-tenant system can ship;\n * surfacing the misuse loudly at the call site (rather than silently\n * defaulting to `null` or to \"the last tenant seen\") prevents both\n * accidental global writes and sneaky reads that return a union of tenants.\n *\n * - `undefined` `tenantId` → throw this error.\n * - Explicit `null` `tenantId` → passes; opts the call into cross-tenant\n * work (e.g. a system housekeeping event with no owning tenant). The\n * `bridge_delivery` row is persisted with `tenant_id = NULL`.\n *\n * The `callSite` constructor argument names which of the three enforcement\n * sites threw — review reports and ops dashboards rely on a stable site\n * name, so use the canonical strings: `'EventFlowService.publishAndStart'`,\n * `'BridgeDeliveryHandler.handle'`,\n * `'DrizzleBridgeDeliveryRepo.insertDelivery'`.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly callSite: string) {\n super(\n `MissingTenantIdError: BridgeModule was configured with ` +\n `multiTenant=true but ${callSite} was called without tenantId ` +\n `(undefined). Pass an explicit tenantId, or pass null for ` +\n `cross-tenant work.`,\n );\n }\n}\n\n/**\n * Synthetic error thrown by `MemoryBridgeDeliveryRepo.insertDelivery` when\n * a duplicate `(event_id, trigger_id)` insert hits the simulated UNIQUE\n * constraint (BRIDGE-3).\n *\n * Carries a `constraint` field equal to the Drizzle constraint name\n * declared in BRIDGE-1's schema (`uq_bridge_delivery_event_trigger`) so\n * call sites can branch on the same discriminator regardless of which\n * backend is wired up. This matters because ADR-023 explicitly leans on\n * the constraint as the dedup mechanism in two places — outbox replay\n * and `publishAndStart` Case B — and BRIDGE-4 / BRIDGE-7 will share a\n * type-check path with BRIDGE-3-driven tests.\n *\n * The Drizzle backend (BRIDGE-4) does NOT throw this error: it uses\n * `INSERT … ON CONFLICT (event_id, trigger_id) DO NOTHING RETURNING id`\n * per the BRIDGE-4 spec recommendation, so collisions surface as an empty\n * result set rather than an exception. The error exists so the memory\n * backend can faithfully model the \"duplicate raises\" behaviour for tests\n * that want to assert the constraint actually fires.\n */\n/**\n * Thrown by `BridgeModule.onModuleInit` when `JobWorkerModule` is wired\n * alongside the bridge but its active `pools` list does not include one\n * or more of the three reserved bridge pools (`events_inbound`,\n * `events_change`, `events_outbound`).\n *\n * Without a worker polling those pools, the wrapper `job_run` rows the\n * outbox drain inserts (BRIDGE-4) sit `pending` forever — a silent\n * footgun where `eventFlow.publish(...)` returns success but no user\n * job ever spawns. The boot-time check converts that into a fail-fast.\n *\n * Operators can either (a) add `...BRIDGE_RESERVED_POOLS` to their\n * `JobWorkerModule.forRoot({ pools })` configuration so the same\n * process polls the reserved pools, or (b) run a separate worker\n * process per reserved pool for lane isolation (ADR-022 §Pool\n * isolation).\n */\nexport class BridgeReservedPoolsNotPolledError extends Error {\n override readonly name = 'BridgeReservedPoolsNotPolledError';\n constructor(public readonly missingPools: readonly string[]) {\n super(\n `BridgeModule loaded but JobWorkerModule is not polling reserved ` +\n `pool '${missingPools[0]}'. Add ...BRIDGE_RESERVED_POOLS to your ` +\n `JobWorkerModule.forRoot({ pools }) configuration. Missing pools: ` +\n `${missingPools.join(', ')}. (Bridge-fanout wrappers will sit ` +\n `pending forever without these pollers.)`,\n );\n }\n}\n\nexport class UniqueConstraintError extends Error {\n override readonly name = 'UniqueConstraintError';\n constructor(\n public readonly constraint: string,\n public readonly eventId: string,\n public readonly triggerId: string,\n ) {\n super(\n `UniqueConstraintError: duplicate insert into bridge_delivery for ` +\n `(event_id='${eventId}', trigger_id='${triggerId}') — violates ` +\n `constraint '${constraint}'.`,\n );\n }\n}\n","/**\n * `assertTenantId` — shared multi-tenancy enforcement helper for the\n * bridge subsystem (BRIDGE-8, ADR-023 §Multi-tenancy null-tenantId).\n *\n * Single source of truth for the three enforcement sites named in\n * ADR-023 §Multi-tenancy and the BRIDGE-2 spec:\n *\n * (a) `EventFlowService.publishAndStart` — request-path entry\n * (b) `BridgeDeliveryHandler.run` — wrapper handler entry\n * (c) `DrizzleBridgeDeliveryRepo.insertDelivery` — last-line repo defense\n *\n * Contract (mirrors JOB-8 / SYNC-6 — locked 2026-04-18 for jobs and\n * carried into the bridge here):\n *\n * - `multiTenant === false` → no-op (always passes).\n * - `multiTenant === true`,\n * `tenantId === undefined` → throw `MissingTenantIdError(site)`.\n * - `multiTenant === true`,\n * `tenantId === null` → passes; opts the call into\n * cross-tenant work (system\n * housekeeping, framework events\n * with no owning tenant). Persists\n * to the DB as `tenant_id = NULL`.\n * - `multiTenant === true`,\n * `tenantId` is a string → passes.\n *\n * The strict `undefined`-vs-`null` discrimination is the entire point —\n * silent defaulting is exactly the failure mode that lets cross-tenant\n * leaks ship.\n */\nimport { MissingTenantIdError } from './bridge-errors';\n\n/**\n * Throws `MissingTenantIdError(site)` if `multiTenant === true` and\n * `tenantId === undefined`. Explicit `null` always passes.\n *\n * @param site Canonical site name — one of:\n * `'EventFlowService.publishAndStart'`,\n * `'BridgeDeliveryHandler.run'`,\n * `'DrizzleBridgeDeliveryRepo.insertDelivery'`.\n * Stable strings; ops dashboards / review reports key\n * on these. Use the same string the existing tests\n * and `MissingTenantIdError` JSDoc enumerate.\n * @param multiTenant Resolved `BRIDGE_MULTI_TENANT` flag (from\n * `BridgeModule.forRoot({ multiTenant })`).\n * @param tenantId The tenantId the caller supplied (or didn't).\n */\nexport function assertTenantId(\n site: string,\n multiTenant: boolean,\n tenantId: string | null | undefined,\n): void {\n if (multiTenant && tenantId === undefined) {\n throw new MissingTenantIdError(site);\n }\n // explicit null passes — opts into cross-tenant work\n}\n"],"mappings":"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","names":["HandlerRegistry"]}
|
|
@@ -5,7 +5,7 @@ import { IJobBridge, BridgeDeliveryInsert, StatusHistogram } from './bridge.prot
|
|
|
5
5
|
import 'drizzle-orm/node-postgres';
|
|
6
6
|
import 'drizzle-orm/pg-core';
|
|
7
7
|
import 'drizzle-orm';
|
|
8
|
-
import '../events/
|
|
8
|
+
import '../events/event-registry.js';
|
|
9
9
|
|
|
10
10
|
declare class DrizzleBridgeDeliveryRepo implements IJobBridge {
|
|
11
11
|
private readonly db;
|
|
@@ -4,7 +4,7 @@ import 'drizzle-orm';
|
|
|
4
4
|
import '../events/event-bus.protocol.js';
|
|
5
5
|
import '../../types/drizzle.js';
|
|
6
6
|
import 'drizzle-orm/node-postgres';
|
|
7
|
-
import '../events/
|
|
7
|
+
import '../events/event-registry.js';
|
|
8
8
|
import 'drizzle-orm/pg-core';
|
|
9
9
|
|
|
10
10
|
declare class MemoryBridgeDeliveryRepo implements IJobBridge {
|
|
@@ -3,7 +3,7 @@ import { IBridgeOutboxDrainHook, BridgeRegistry, BridgeOutboxDrainResult } from
|
|
|
3
3
|
import '../../types/drizzle.js';
|
|
4
4
|
import 'drizzle-orm/node-postgres';
|
|
5
5
|
import 'drizzle-orm';
|
|
6
|
-
import '../events/
|
|
6
|
+
import '../events/event-registry.js';
|
|
7
7
|
import './bridge-delivery.schema.js';
|
|
8
8
|
import 'drizzle-orm/pg-core';
|
|
9
9
|
|