@pattern-stack/codegen 0.14.2 → 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.
Files changed (66) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/dist/{job-orchestrator.protocol-CARhMLCO.d.ts → job-orchestrator.protocol-DubMVbm9.d.ts} +1 -1
  3. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.d.ts +2 -2
  4. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js.map +1 -1
  5. package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.d.ts +1 -1
  6. package/dist/runtime/subsystems/bridge/bridge-delivery.memory-backend.d.ts +1 -1
  7. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.d.ts +1 -1
  8. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js.map +1 -1
  9. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +2 -2
  10. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  11. package/dist/runtime/subsystems/bridge/bridge.protocol.d.ts +1 -1
  12. package/dist/runtime/subsystems/bridge/event-flow.service.d.ts +2 -2
  13. package/dist/runtime/subsystems/bridge/event-flow.service.js.map +1 -1
  14. package/dist/runtime/subsystems/bridge/generated/registry.d.ts +1 -1
  15. package/dist/runtime/subsystems/bridge/index.d.ts +2 -2
  16. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  17. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +1 -1
  18. package/dist/runtime/subsystems/events/event-registry.d.ts +77 -0
  19. package/dist/runtime/subsystems/events/event-registry.js +1 -0
  20. package/dist/runtime/subsystems/events/event-registry.js.map +1 -0
  21. package/dist/runtime/subsystems/events/events.module.d.ts +26 -1
  22. package/dist/runtime/subsystems/events/events.module.js +6 -5
  23. package/dist/runtime/subsystems/events/events.module.js.map +1 -1
  24. package/dist/runtime/subsystems/events/index.d.ts +1 -0
  25. package/dist/runtime/subsystems/events/index.js +6 -5
  26. package/dist/runtime/subsystems/events/index.js.map +1 -1
  27. package/dist/runtime/subsystems/index.d.ts +2 -2
  28. package/dist/runtime/subsystems/index.js +6 -5
  29. package/dist/runtime/subsystems/index.js.map +1 -1
  30. package/dist/runtime/subsystems/jobs/index.d.ts +2 -2
  31. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  32. package/dist/runtime/subsystems/jobs/job-handler.base.d.ts +2 -2
  33. package/dist/runtime/subsystems/jobs/job-handler.base.js.map +1 -1
  34. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +2 -2
  35. package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.d.ts +2 -2
  36. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +2 -2
  37. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js.map +1 -1
  38. package/dist/runtime/subsystems/jobs/job-orchestrator.protocol.d.ts +2 -2
  39. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +2 -2
  40. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +2 -2
  41. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +2 -2
  42. package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +2 -2
  43. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +2 -2
  44. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -1
  45. package/dist/runtime/subsystems/jobs/job-worker.d.ts +2 -2
  46. package/dist/runtime/subsystems/jobs/job-worker.js.map +1 -1
  47. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +2 -2
  48. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  49. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  50. package/dist/runtime/subsystems/jobs/jobs-errors.d.ts +2 -2
  51. package/dist/runtime/subsystems/observability/index.d.ts +2 -2
  52. package/dist/runtime/subsystems/observability/observability.protocol.d.ts +2 -2
  53. package/dist/runtime/subsystems/observability/observability.service.d.ts +2 -2
  54. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +2 -2
  55. package/dist/runtime/subsystems/observability/reporters/index.d.ts +2 -2
  56. package/dist/src/cli/index.js +1332 -1274
  57. package/dist/src/cli/index.js.map +1 -1
  58. package/package.json +1 -1
  59. package/runtime/subsystems/bridge/bridge-delivery-handler.ts +1 -1
  60. package/runtime/subsystems/bridge/bridge-outbox-drain-hook.ts +1 -1
  61. package/runtime/subsystems/bridge/bridge.protocol.ts +1 -1
  62. package/runtime/subsystems/bridge/event-flow.service.ts +1 -1
  63. package/runtime/subsystems/events/event-registry.ts +77 -0
  64. package/runtime/subsystems/events/events.module.ts +39 -6
  65. package/runtime/subsystems/events/index.ts +12 -0
  66. package/runtime/subsystems/jobs/job-handler.base.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import { InferInsertModel } from 'drizzle-orm';
2
2
  import { DrizzleTransaction, DomainEvent } from '../events/event-bus.protocol.js';
3
- import { EventTypeName, EventOfType } from '../events/generated/types.js';
3
+ import { EventTypeName, EventOfType } from '../events/event-registry.js';
4
4
  import { bridgeDelivery, BridgeDeliveryRecord } from './bridge-delivery.schema.js';
5
5
  import '../../types/drizzle.js';
6
6
  import 'drizzle-orm/node-postgres';
@@ -1,7 +1,7 @@
1
1
  import { DrizzleClient } from '../../types/drizzle.js';
2
2
  import { IEventBus, DrizzleTransaction } from '../events/event-bus.protocol.js';
3
- import { EventTypeName, EventOfType } from '../events/generated/types.js';
4
- import { I as IJobOrchestrator } from '../../../job-orchestrator.protocol-CARhMLCO.js';
3
+ import { EventTypeName, EventOfType } from '../events/event-registry.js';
4
+ import { I as IJobOrchestrator } from '../../../job-orchestrator.protocol-DubMVbm9.js';
5
5
  import { IEventFlow, IJobBridge, BridgeRegistry, PublishAndStartOptions, PublishAndStartResult } from './bridge.protocol.js';
6
6
  import 'drizzle-orm/node-postgres';
7
7
  import '../jobs/job-orchestration.schema.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../runtime/subsystems/bridge/event-flow.service.ts","../../../../runtime/constants/tokens.ts","../../../../runtime/subsystems/token-key.ts","../../../../runtime/subsystems/events/events.tokens.ts","../../../../runtime/subsystems/jobs/jobs-domain.tokens.ts","../../../../runtime/subsystems/bridge/bridge.tokens.ts","../../../../runtime/subsystems/bridge/bridge-errors.ts","../../../../runtime/subsystems/bridge/assert-tenant-id.ts"],"sourcesContent":["/**\n * EventFlowService — `IEventFlow` facade implementation (BRIDGE-7,\n * ADR-023 §Decision 7 + §`publishAndStart` + existing `triggers:` collision).\n *\n * Two verbs:\n *\n * - `publish(event, tx?)` — thin delegate to `IEventBus.publish(event, tx)`.\n * Subscribers + bridge triggers fire as normal. Caller owns transaction.\n *\n * - `publishAndStart(event, jobType, input, opts?)` — the load-bearing\n * verb. Opens a transaction and performs THREE writes inside it:\n * 1. Outbox insert via `eventBus.publish(event, tx)`.\n * 2. Eager `orchestrator.start(jobType, input, opts, tx)`.\n * 3. **Case B only** — for every `bridgeRegistry[event.type]` entry\n * whose `jobType` matches the argument, pre-write a\n * `bridge_delivery(status='delivered', wrapper_run_id=null,\n * user_run_id=<eagerRunId>)` row via `bridgeRepo.insertDelivery(\n * row, tx)`. The `UNIQUE (event_id, trigger_id)` constraint then\n * dedups the drain's later attempt for that trigger; sibling\n * triggers (different trigger_id) still spawn normally.\n *\n * Returns `{ runId }` from the eager start. All three writes share\n * one tx — a crash anywhere rolls back all of them; the drain\n * re-claims the event on the next cycle and the bridge UNIQUE makes\n * the retry idempotent.\n *\n * **Pre-write ALL matching triggerIds** (lead decision 2026-04-22): the\n * facade uses `filter()` not `find()`. If a project has two triggers in\n * the registry for the same `(event, jobType)` pair (rare; codegen-time\n * `DuplicateTriggerError` from BRIDGE-7's BRIDGE-6 follow-up patch\n * prevents new occurrences), each gets its own pre-write — otherwise\n * the un-pre-written sibling would spawn a wrapper that re-runs the user\n * job, producing a double-spawn.\n *\n * **Multi-tenancy gate** at `publishAndStart` entry: when\n * `BRIDGE_MULTI_TENANT=true` and `opts?.tenantId === undefined`, throw\n * `MissingTenantIdError('EventFlowService.publishAndStart')`. Site (a)\n * of the three ADR-023 §Multi-tenancy enforcement sites (BRIDGE-5\n * handler is (b); BRIDGE-4 drizzle repo is (c)).\n */\nimport { Inject, Injectable, Optional } from '@nestjs/common';\n\nimport { DRIZZLE } from '../../constants/tokens';\nimport type { DrizzleClient } from '../../types/drizzle';\n\nimport { EVENT_BUS } from '../events/events.tokens';\nimport type {\n DomainEvent,\n DrizzleTransaction,\n IEventBus,\n} from '../events/event-bus.protocol';\nimport type {\n EventOfType,\n EventTypeName,\n} from '../events/generated/types';\n\nimport { JOB_ORCHESTRATOR } from '../jobs/jobs-domain.tokens';\nimport type { IJobOrchestrator } from '../jobs/job-orchestrator.protocol';\n\nimport {\n BRIDGE_DELIVERY_REPO,\n BRIDGE_MULTI_TENANT,\n BRIDGE_REGISTRY,\n} from './bridge.tokens';\nimport type {\n BridgeRegistry,\n BridgeTriggerEntry,\n IEventFlow,\n IJobBridge,\n PublishAndStartOptions,\n PublishAndStartResult,\n} from './bridge.protocol';\nimport { assertTenantId } from './assert-tenant-id';\n\n@Injectable()\nexport class EventFlowService implements IEventFlow {\n constructor(\n @Inject(DRIZZLE) private readonly db: DrizzleClient,\n @Inject(EVENT_BUS) private readonly eventBus: IEventBus,\n @Inject(JOB_ORCHESTRATOR) private readonly orchestrator: IJobOrchestrator,\n @Inject(BRIDGE_DELIVERY_REPO) private readonly bridgeRepo: IJobBridge,\n @Optional()\n @Inject(BRIDGE_REGISTRY)\n private readonly registry: BridgeRegistry = {},\n @Optional()\n @Inject(BRIDGE_MULTI_TENANT)\n private readonly multiTenant: boolean = false,\n ) {}\n\n async publish<T extends EventTypeName>(\n event: EventOfType<T>,\n tx?: DrizzleTransaction,\n ): Promise<void> {\n // Thin delegate. Subscribers + bridge triggers fire as normal via the\n // outbox drain (BRIDGE-4) and `IEventBus.subscribe` (Tier 1).\n await this.eventBus.publish(event as DomainEvent, tx);\n }\n\n async publishAndStart<T extends EventTypeName>(\n event: EventOfType<T>,\n jobType: string,\n input: unknown,\n opts: PublishAndStartOptions = {},\n ): Promise<PublishAndStartResult> {\n // Multi-tenancy gate — throw before any DB write so failures surface\n // at the call site, not from inside an aborted tx. Site (a) of the\n // three ADR-023 §Multi-tenancy enforcement sites; shared helper from\n // BRIDGE-8 keeps all three sites in lock-step.\n assertTenantId(\n 'EventFlowService.publishAndStart',\n this.multiTenant,\n opts.tenantId,\n );\n // Resolve null → null (cross-tenant work) once, so the same value\n // flows to both the eager start AND the bridge_delivery row.\n const tenantId: string | null = opts.tenantId ?? null;\n\n // Identify Case B — every registry entry whose jobType matches.\n // Lead decision 2026-04-22: pre-write ALL matches (filter, not find)\n // so duplicate triggers in the registry don't double-spawn.\n const matchingTriggers = this.matchingTriggers(event.type as EventTypeName, jobType);\n\n return this.db.transaction(async (tx) => {\n // 1. Outbox insert.\n await this.eventBus.publish(event as DomainEvent, tx);\n\n // 2. Eager start. Threads tx through (BRIDGE-7 protocol extension\n // on IJobOrchestrator.start, JOB-3 backend uses `tx ?? this.db`).\n const run = await this.orchestrator.start(\n jobType,\n input,\n {\n parentRunId: opts.parentRunId,\n tenantId,\n triggerSource: 'event',\n triggerRef: event.id,\n },\n tx,\n );\n\n // 3. Case B pre-writes — one per matching trigger.\n const now = new Date();\n for (const trigger of matchingTriggers) {\n await this.bridgeRepo.insertDelivery(\n {\n eventId: event.id,\n triggerId: trigger.triggerId,\n wrapperRunId: null, // facade never writes a wrapper\n userRunId: run.id,\n status: 'delivered',\n tenantId,\n attemptedAt: now,\n deliveredAt: now,\n },\n tx,\n );\n }\n\n return { runId: run.id };\n });\n }\n\n /**\n * Linear scan of the per-event-type trigger list for entries whose\n * `jobType` matches. Typical N is 1–5; the table is not big enough to\n * warrant a secondary index. Returns an empty array for Case A.\n */\n private matchingTriggers(\n eventType: EventTypeName,\n jobType: string,\n ): BridgeTriggerEntry[] {\n const triggers = this.registry[eventType] ?? [];\n return triggers.filter((t) => t.jobType === jobType) as BridgeTriggerEntry[];\n }\n}\n","/**\n * NestJS injection tokens\n *\n * Used with @Inject() decorator in concrete repository constructors.\n */\n\n/**\n * Injection token for the Drizzle ORM database client.\n *\n * Usage in concrete repositories:\n * ```typescript\n * constructor(@Inject(DRIZZLE) db: DrizzleClient) { super(db); }\n * ```\n */\nexport const DRIZZLE = 'DRIZZLE' as const;\n\n/**\n * Injection token for the event bus (IEventBus).\n *\n * Optional — only resolved when EventsModule.forRoot() is registered.\n * BaseService uses this with @Optional() to emit lifecycle events\n * without requiring the events subsystem to be installed.\n *\n * Usage in services/use cases:\n * ```typescript\n * @Optional() @Inject(EVENT_BUS) eventBus?: IEventBus\n * ```\n */\nexport const EVENT_BUS = 'EVENT_BUS' as const;\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 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 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 * 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":[]}
1
+ {"version":3,"sources":["../../../../runtime/subsystems/bridge/event-flow.service.ts","../../../../runtime/constants/tokens.ts","../../../../runtime/subsystems/token-key.ts","../../../../runtime/subsystems/events/events.tokens.ts","../../../../runtime/subsystems/jobs/jobs-domain.tokens.ts","../../../../runtime/subsystems/bridge/bridge.tokens.ts","../../../../runtime/subsystems/bridge/bridge-errors.ts","../../../../runtime/subsystems/bridge/assert-tenant-id.ts"],"sourcesContent":["/**\n * EventFlowService — `IEventFlow` facade implementation (BRIDGE-7,\n * ADR-023 §Decision 7 + §`publishAndStart` + existing `triggers:` collision).\n *\n * Two verbs:\n *\n * - `publish(event, tx?)` — thin delegate to `IEventBus.publish(event, tx)`.\n * Subscribers + bridge triggers fire as normal. Caller owns transaction.\n *\n * - `publishAndStart(event, jobType, input, opts?)` — the load-bearing\n * verb. Opens a transaction and performs THREE writes inside it:\n * 1. Outbox insert via `eventBus.publish(event, tx)`.\n * 2. Eager `orchestrator.start(jobType, input, opts, tx)`.\n * 3. **Case B only** — for every `bridgeRegistry[event.type]` entry\n * whose `jobType` matches the argument, pre-write a\n * `bridge_delivery(status='delivered', wrapper_run_id=null,\n * user_run_id=<eagerRunId>)` row via `bridgeRepo.insertDelivery(\n * row, tx)`. The `UNIQUE (event_id, trigger_id)` constraint then\n * dedups the drain's later attempt for that trigger; sibling\n * triggers (different trigger_id) still spawn normally.\n *\n * Returns `{ runId }` from the eager start. All three writes share\n * one tx — a crash anywhere rolls back all of them; the drain\n * re-claims the event on the next cycle and the bridge UNIQUE makes\n * the retry idempotent.\n *\n * **Pre-write ALL matching triggerIds** (lead decision 2026-04-22): the\n * facade uses `filter()` not `find()`. If a project has two triggers in\n * the registry for the same `(event, jobType)` pair (rare; codegen-time\n * `DuplicateTriggerError` from BRIDGE-7's BRIDGE-6 follow-up patch\n * prevents new occurrences), each gets its own pre-write — otherwise\n * the un-pre-written sibling would spawn a wrapper that re-runs the user\n * job, producing a double-spawn.\n *\n * **Multi-tenancy gate** at `publishAndStart` entry: when\n * `BRIDGE_MULTI_TENANT=true` and `opts?.tenantId === undefined`, throw\n * `MissingTenantIdError('EventFlowService.publishAndStart')`. Site (a)\n * of the three ADR-023 §Multi-tenancy enforcement sites (BRIDGE-5\n * handler is (b); BRIDGE-4 drizzle repo is (c)).\n */\nimport { Inject, Injectable, Optional } from '@nestjs/common';\n\nimport { DRIZZLE } from '../../constants/tokens';\nimport type { DrizzleClient } from '../../types/drizzle';\n\nimport { EVENT_BUS } from '../events/events.tokens';\nimport type {\n DomainEvent,\n DrizzleTransaction,\n IEventBus,\n} from '../events/event-bus.protocol';\nimport type {\n EventOfType,\n EventTypeName,\n} from '../events/event-registry';\n\nimport { JOB_ORCHESTRATOR } from '../jobs/jobs-domain.tokens';\nimport type { IJobOrchestrator } from '../jobs/job-orchestrator.protocol';\n\nimport {\n BRIDGE_DELIVERY_REPO,\n BRIDGE_MULTI_TENANT,\n BRIDGE_REGISTRY,\n} from './bridge.tokens';\nimport type {\n BridgeRegistry,\n BridgeTriggerEntry,\n IEventFlow,\n IJobBridge,\n PublishAndStartOptions,\n PublishAndStartResult,\n} from './bridge.protocol';\nimport { assertTenantId } from './assert-tenant-id';\n\n@Injectable()\nexport class EventFlowService implements IEventFlow {\n constructor(\n @Inject(DRIZZLE) private readonly db: DrizzleClient,\n @Inject(EVENT_BUS) private readonly eventBus: IEventBus,\n @Inject(JOB_ORCHESTRATOR) private readonly orchestrator: IJobOrchestrator,\n @Inject(BRIDGE_DELIVERY_REPO) private readonly bridgeRepo: IJobBridge,\n @Optional()\n @Inject(BRIDGE_REGISTRY)\n private readonly registry: BridgeRegistry = {},\n @Optional()\n @Inject(BRIDGE_MULTI_TENANT)\n private readonly multiTenant: boolean = false,\n ) {}\n\n async publish<T extends EventTypeName>(\n event: EventOfType<T>,\n tx?: DrizzleTransaction,\n ): Promise<void> {\n // Thin delegate. Subscribers + bridge triggers fire as normal via the\n // outbox drain (BRIDGE-4) and `IEventBus.subscribe` (Tier 1).\n await this.eventBus.publish(event as DomainEvent, tx);\n }\n\n async publishAndStart<T extends EventTypeName>(\n event: EventOfType<T>,\n jobType: string,\n input: unknown,\n opts: PublishAndStartOptions = {},\n ): Promise<PublishAndStartResult> {\n // Multi-tenancy gate — throw before any DB write so failures surface\n // at the call site, not from inside an aborted tx. Site (a) of the\n // three ADR-023 §Multi-tenancy enforcement sites; shared helper from\n // BRIDGE-8 keeps all three sites in lock-step.\n assertTenantId(\n 'EventFlowService.publishAndStart',\n this.multiTenant,\n opts.tenantId,\n );\n // Resolve null → null (cross-tenant work) once, so the same value\n // flows to both the eager start AND the bridge_delivery row.\n const tenantId: string | null = opts.tenantId ?? null;\n\n // Identify Case B — every registry entry whose jobType matches.\n // Lead decision 2026-04-22: pre-write ALL matches (filter, not find)\n // so duplicate triggers in the registry don't double-spawn.\n const matchingTriggers = this.matchingTriggers(event.type as EventTypeName, jobType);\n\n return this.db.transaction(async (tx) => {\n // 1. Outbox insert.\n await this.eventBus.publish(event as DomainEvent, tx);\n\n // 2. Eager start. Threads tx through (BRIDGE-7 protocol extension\n // on IJobOrchestrator.start, JOB-3 backend uses `tx ?? this.db`).\n const run = await this.orchestrator.start(\n jobType,\n input,\n {\n parentRunId: opts.parentRunId,\n tenantId,\n triggerSource: 'event',\n triggerRef: event.id,\n },\n tx,\n );\n\n // 3. Case B pre-writes — one per matching trigger.\n const now = new Date();\n for (const trigger of matchingTriggers) {\n await this.bridgeRepo.insertDelivery(\n {\n eventId: event.id,\n triggerId: trigger.triggerId,\n wrapperRunId: null, // facade never writes a wrapper\n userRunId: run.id,\n status: 'delivered',\n tenantId,\n attemptedAt: now,\n deliveredAt: now,\n },\n tx,\n );\n }\n\n return { runId: run.id };\n });\n }\n\n /**\n * Linear scan of the per-event-type trigger list for entries whose\n * `jobType` matches. Typical N is 1–5; the table is not big enough to\n * warrant a secondary index. Returns an empty array for Case A.\n */\n private matchingTriggers(\n eventType: EventTypeName,\n jobType: string,\n ): BridgeTriggerEntry[] {\n const triggers = this.registry[eventType] ?? [];\n return triggers.filter((t) => t.jobType === jobType) as BridgeTriggerEntry[];\n }\n}\n","/**\n * NestJS injection tokens\n *\n * Used with @Inject() decorator in concrete repository constructors.\n */\n\n/**\n * Injection token for the Drizzle ORM database client.\n *\n * Usage in concrete repositories:\n * ```typescript\n * constructor(@Inject(DRIZZLE) db: DrizzleClient) { super(db); }\n * ```\n */\nexport const DRIZZLE = 'DRIZZLE' as const;\n\n/**\n * Injection token for the event bus (IEventBus).\n *\n * Optional — only resolved when EventsModule.forRoot() is registered.\n * BaseService uses this with @Optional() to emit lifecycle events\n * without requiring the events subsystem to be installed.\n *\n * Usage in services/use cases:\n * ```typescript\n * @Optional() @Inject(EVENT_BUS) eventBus?: IEventBus\n * ```\n */\nexport const EVENT_BUS = 'EVENT_BUS' as const;\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 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 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 * 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":[]}
@@ -3,7 +3,7 @@ import 'drizzle-orm';
3
3
  import '../../events/event-bus.protocol.js';
4
4
  import '../../../types/drizzle.js';
5
5
  import 'drizzle-orm/node-postgres';
6
- import '../../events/generated/types.js';
6
+ import '../../events/event-registry.js';
7
7
  import '../bridge-delivery.schema.js';
8
8
  import 'drizzle-orm/pg-core';
9
9
 
@@ -15,8 +15,8 @@ import 'drizzle-orm';
15
15
  import '../events/event-bus.protocol.js';
16
16
  import '../../types/drizzle.js';
17
17
  import 'drizzle-orm/node-postgres';
18
- import '../events/generated/types.js';
19
- import '../../../job-orchestrator.protocol-CARhMLCO.js';
18
+ import '../events/event-registry.js';
19
+ import '../../../job-orchestrator.protocol-DubMVbm9.js';
20
20
  import '../jobs/job-orchestration.schema.js';
21
21
  import '@nestjs/common';
22
22
  import '../jobs/job-worker.module.js';