@pattern-stack/codegen 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/{job-orchestrator.protocol-BwsBd37o.d.ts → job-orchestrator.protocol-CHOEqBDk.d.ts} +36 -1
  3. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.d.ts +2 -2
  4. package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js.map +1 -1
  5. package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js.map +1 -1
  6. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +5 -1
  7. package/dist/runtime/subsystems/bridge/bridge.module.js +930 -275
  8. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  9. package/dist/runtime/subsystems/bridge/event-flow.service.d.ts +1 -1
  10. package/dist/runtime/subsystems/bridge/event-flow.service.js.map +1 -1
  11. package/dist/runtime/subsystems/bridge/index.d.ts +4 -1
  12. package/dist/runtime/subsystems/bridge/index.js +837 -182
  13. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  14. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +3 -1
  15. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +92 -1
  16. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
  17. package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +3 -1
  18. package/dist/runtime/subsystems/events/event-bus.memory-backend.js +99 -0
  19. package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -1
  20. package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -1
  21. package/dist/runtime/subsystems/events/event-keyset-cursor.d.ts +32 -0
  22. package/dist/runtime/subsystems/events/event-keyset-cursor.js +38 -0
  23. package/dist/runtime/subsystems/events/event-keyset-cursor.js.map +1 -0
  24. package/dist/runtime/subsystems/events/event-read.protocol.d.ts +94 -0
  25. package/dist/runtime/subsystems/events/event-read.protocol.js +9 -0
  26. package/dist/runtime/subsystems/events/event-read.protocol.js.map +1 -0
  27. package/dist/runtime/subsystems/events/events.module.js +177 -3
  28. package/dist/runtime/subsystems/events/events.module.js.map +1 -1
  29. package/dist/runtime/subsystems/events/events.tokens.d.ts +16 -1
  30. package/dist/runtime/subsystems/events/events.tokens.js +2 -0
  31. package/dist/runtime/subsystems/events/events.tokens.js.map +1 -1
  32. package/dist/runtime/subsystems/events/generated/bus.js.map +1 -1
  33. package/dist/runtime/subsystems/events/generated/index.js.map +1 -1
  34. package/dist/runtime/subsystems/events/index.d.ts +2 -1
  35. package/dist/runtime/subsystems/events/index.js +178 -3
  36. package/dist/runtime/subsystems/events/index.js.map +1 -1
  37. package/dist/runtime/subsystems/index.d.ts +3 -2
  38. package/dist/runtime/subsystems/index.js +1194 -264
  39. package/dist/runtime/subsystems/index.js.map +1 -1
  40. package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +98 -0
  41. package/dist/runtime/subsystems/jobs/bullmq.config.js +143 -0
  42. package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -0
  43. package/dist/runtime/subsystems/jobs/index.d.ts +8 -3
  44. package/dist/runtime/subsystems/jobs/index.js +861 -201
  45. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  46. package/dist/runtime/subsystems/jobs/job-handler.base.d.ts +2 -1
  47. package/dist/runtime/subsystems/jobs/job-handler.base.js.map +1 -1
  48. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.d.ts +108 -0
  49. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +922 -0
  50. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -0
  51. package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.d.ts +2 -1
  52. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.d.ts +2 -1
  53. package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js.map +1 -1
  54. package/dist/runtime/subsystems/jobs/job-orchestrator.protocol.d.ts +2 -1
  55. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.d.ts +53 -0
  56. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js +57 -0
  57. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js.map +1 -0
  58. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.d.ts +4 -2
  59. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +81 -1
  60. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js.map +1 -1
  61. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.d.ts +4 -2
  62. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +81 -0
  63. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js.map +1 -1
  64. package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +76 -2
  65. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.d.ts +49 -0
  66. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +374 -0
  67. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -0
  68. package/dist/runtime/subsystems/jobs/job-worker.d.ts +2 -1
  69. package/dist/runtime/subsystems/jobs/job-worker.js.map +1 -1
  70. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +44 -5
  71. package/dist/runtime/subsystems/jobs/job-worker.module.js +832 -178
  72. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  73. package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +10 -1
  74. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +519 -20
  75. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  76. package/dist/runtime/subsystems/jobs/jobs-errors.d.ts +2 -1
  77. package/dist/runtime/subsystems/jobs/pool-config.loader.d.ts +9 -1
  78. package/dist/runtime/subsystems/jobs/pool-config.loader.js +4 -0
  79. package/dist/runtime/subsystems/jobs/pool-config.loader.js.map +1 -1
  80. package/dist/runtime/subsystems/observability/index.d.ts +4 -3
  81. package/dist/runtime/subsystems/observability/index.js +109 -2
  82. package/dist/runtime/subsystems/observability/index.js.map +1 -1
  83. package/dist/runtime/subsystems/observability/observability.module.js +109 -2
  84. package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
  85. package/dist/runtime/subsystems/observability/observability.protocol.d.ts +64 -3
  86. package/dist/runtime/subsystems/observability/observability.service.d.ts +22 -4
  87. package/dist/runtime/subsystems/observability/observability.service.js +109 -2
  88. package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
  89. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -2
  90. package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -2
  91. package/dist/src/cli/index.js +30 -6
  92. package/dist/src/cli/index.js.map +1 -1
  93. package/package.json +5 -1
  94. package/runtime/subsystems/bridge/bridge.module.ts +5 -0
  95. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +109 -3
  96. package/runtime/subsystems/events/event-bus.memory-backend.ts +103 -1
  97. package/runtime/subsystems/events/event-keyset-cursor.ts +59 -0
  98. package/runtime/subsystems/events/event-read.protocol.ts +97 -0
  99. package/runtime/subsystems/events/events.module.ts +18 -2
  100. package/runtime/subsystems/events/events.tokens.ts +16 -0
  101. package/runtime/subsystems/events/index.ts +7 -0
  102. package/runtime/subsystems/jobs/bullmq.config.ts +125 -0
  103. package/runtime/subsystems/jobs/index.ts +22 -0
  104. package/runtime/subsystems/jobs/job-handler.base.ts +36 -0
  105. package/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.ts +381 -0
  106. package/runtime/subsystems/jobs/job-run-keyset-cursor.ts +88 -0
  107. package/runtime/subsystems/jobs/job-run-service.drizzle-backend.ts +59 -1
  108. package/runtime/subsystems/jobs/job-run-service.memory-backend.ts +53 -0
  109. package/runtime/subsystems/jobs/job-run-service.protocol.ts +77 -0
  110. package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +311 -0
  111. package/runtime/subsystems/jobs/job-worker.module.ts +124 -10
  112. package/runtime/subsystems/jobs/jobs-domain.module.ts +40 -21
  113. package/runtime/subsystems/jobs/pool-config.loader.ts +11 -0
  114. package/runtime/subsystems/observability/index.ts +8 -0
  115. package/runtime/subsystems/observability/observability.protocol.ts +76 -0
  116. package/runtime/subsystems/observability/observability.service.ts +148 -1
  117. package/templates/entity/new/clean-lite-ps/prompt-extension.js +14 -12
  118. package/templates/relationship/new/prompt.js +8 -5
  119. package/templates/subsystem/jobs/worker.ejs.t +30 -7
  120. package/templates/subsystem/sync/sync-audit.schema.ejs.t +12 -16
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../runtime/subsystems/events/generated/bus.ts","../../../../../runtime/subsystems/events/events.tokens.ts","../../../../../runtime/subsystems/events/events-errors.ts","../../../../../runtime/subsystems/events/generated/schemas.ts","../../../../../runtime/subsystems/events/generated/registry.ts"],"sourcesContent":["// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\nimport { Injectable, Inject } from '@nestjs/common';\nimport { randomUUID } from 'crypto';\nimport { EVENT_BUS, EVENTS_MULTI_TENANT } from '../events.tokens';\nimport { MissingTenantIdError } from '../events-errors';\nimport type { IEventBus, DrizzleTransaction } from '../event-bus.protocol';\nimport { eventPayloadSchemas } from './schemas';\nimport { getEventMetadata } from './registry';\nimport type { EventTypeName, EventOfType, PayloadOfType } from './types';\n\n/**\n * Typed facade over IEventBus.\n *\n * Stamps `pool`, `direction`, `tier`, and `version` into `event.metadata`\n * from the generated `eventRegistry` before delegating to\n * `IEventBus.publish()`. Downstream backends (DrizzleEventBus) read those\n * values to populate the explicit `domain_events` columns.\n *\n * Tier stamping (AUDIT-3): every event carries `metadata.tier`, sourced\n * from the registry. For `tier: 'audit'` events, the bus FORCES\n * `metadata.pool = null` and `metadata.direction = null` regardless of\n * any caller-supplied values in `opts.metadata` — audit routing is\n * bus-stamped, not caller-controlled. Caller overrides are silently\n * dropped with a debug-level log (callers should not be specifying these\n * for audit events; see ai-docs/specs/issue-242/plan.md §AUDIT-3).\n *\n * Validation gating (EVT-Q5): `CODEGEN_EVENT_VALIDATE` env flag, default on.\n * Uses `safeParse` + `console.warn` — never throws, so a bad publish does\n * not crash a hot path.\n *\n * Multi-tenancy (EVT-6): when the EventsModule is configured with\n * `multiTenant: true`, every publish must supply `opts.metadata.tenantId`\n * — otherwise `publish()` throws `MissingTenantIdError`. When `multiTenant`\n * is `false` (default), no tenantId is required. If a tenantId IS supplied,\n * it is preserved on `event.metadata` and the Drizzle backend writes it to\n * `domain_events.tenant_id` (EVT-4).\n */\n@Injectable()\nexport class TypedEventBus {\n\tconstructor(\n\t\t@Inject(EVENT_BUS) private readonly bus: IEventBus,\n\t\t@Inject(EVENTS_MULTI_TENANT) private readonly multiTenant: boolean,\n\t) {}\n\n\tasync publish<T extends EventTypeName>(\n\t\ttype: T,\n\t\taggregateId: string,\n\t\tpayload: PayloadOfType<T>,\n\t\topts?: { tx?: DrizzleTransaction; metadata?: Record<string, unknown> },\n\t): Promise<void> {\n\t\tconst meta = getEventMetadata(type);\n\n\t\tconst flag = process.env['CODEGEN_EVENT_VALIDATE'];\n\t\tconst shouldValidate =\n\t\t\tflag === undefined ? true : flag !== 'false' && flag !== '0';\n\t\tif (shouldValidate) {\n\t\t\t// `eventPayloadSchemas` is typed as `Record<EventTypeName, z.ZodType>`,\n\t\t\t// so under `noUncheckedIndexedAccess` the indexed lookup widens\n\t\t\t// to `z.ZodType | undefined`. When no events are registered at\n\t\t\t// codegen time `EventTypeName` degrades to `string` and the\n\t\t\t// schemas object is literally `{}` — the guard below is the\n\t\t\t// honest handling of that empty-registry case (skip validation;\n\t\t\t// it's a warn-only best-effort check per the class docblock).\n\t\t\tconst schema = eventPayloadSchemas[type];\n\t\t\tif (schema) {\n\t\t\t\tconst check = schema.safeParse(payload);\n\t\t\t\tif (!check.success) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[TypedEventBus] payload validation failed for ${String(type)}:`,\n\t\t\t\t\t\tcheck.error.issues,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst tenantId = opts?.metadata?.['tenantId'];\n\t\tif (this.multiTenant && (tenantId === undefined || tenantId === null)) {\n\t\t\tthrow new MissingTenantIdError(type as string);\n\t\t}\n\n\t\tconst aggregateType =\n\t\t\tmeta.aggregate ?? meta.source ?? meta.destination ?? (type as string);\n\n\t\t// AUDIT-3: build metadata with tier-aware routing stamping. For\n\t\t// `tier: 'audit'` events the bus FORCES pool/direction to null,\n\t\t// even if the caller supplied them in opts.metadata. Audit routing\n\t\t// is bus-stamped, not caller-controlled (see plan §AUDIT-3).\n\t\tconst baseMetadata: Record<string, unknown> = { ...(opts?.metadata ?? {}) };\n\t\tif (meta.tier === 'audit') {\n\t\t\tif (\n\t\t\t\tbaseMetadata['pool'] !== undefined ||\n\t\t\t\tbaseMetadata['direction'] !== undefined\n\t\t\t) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[TypedEventBus] tier:audit event '${String(type)}' had pool/direction in opts.metadata; overriding to null.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbaseMetadata['pool'] = null;\n\t\t\tbaseMetadata['direction'] = null;\n\t\t\tbaseMetadata['tier'] = 'audit';\n\t\t} else {\n\t\t\tbaseMetadata['pool'] = meta.pool;\n\t\t\tbaseMetadata['direction'] = meta.direction;\n\t\t\tbaseMetadata['tier'] = 'domain';\n\t\t}\n\t\tbaseMetadata['version'] = meta.version;\n\n\t\tawait this.bus.publish(\n\t\t\t{\n\t\t\t\tid: randomUUID(),\n\t\t\t\ttype,\n\t\t\t\taggregateId,\n\t\t\t\taggregateType,\n\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t\toccurredAt: new Date(),\n\t\t\t\tmetadata: baseMetadata,\n\t\t\t},\n\t\t\topts?.tx,\n\t\t);\n\t}\n\n\tsubscribe<T extends EventTypeName>(\n\t\ttype: T,\n\t\thandler: (event: EventOfType<T>) => Promise<void>,\n\t): () => void {\n\t\treturn this.bus.subscribe<EventOfType<T>>(type, handler as never);\n\t}\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 */\nexport const EVENT_BUS = 'EVENT_BUS' 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 */\nexport const REDIS_URL = Symbol('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 * Typed errors for the events subsystem (ADR-024, EVT-6).\n *\n * All thrown from the publish path of `TypedEventBus`. They exist as\n * classes so consumers can `instanceof` them in catch blocks and\n * exception filters can map them to HTTP codes.\n */\n\n/**\n * Thrown by `TypedEventBus.publish()` when the EventsModule is configured\n * with `multiTenant: true` and the caller did not supply\n * `opts.metadata.tenantId`. Multi-tenant mode requires every outbox row to\n * be attributable to a tenant — the `domain_events.tenant_id` column is\n * populated from this value and the drain loop uses it for future\n * tenant-scoped filtering (deferred — see ADR-024 §Multi-tenancy).\n *\n * Disable multi-tenancy at the module level (`multiTenant: false`, the\n * default) to opt out of the requirement entirely.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly eventType: string) {\n super(\n `Missing tenantId for event '${eventType}'. EventsModule is configured ` +\n `with multiTenant: true — every publish must include ` +\n `opts.metadata.tenantId. Either pass the tenantId or disable ` +\n `multi-tenancy on the module.`,\n );\n }\n}\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport { z } from 'zod';\nimport type { EventTypeName } from './types';\n\nexport const contactCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid().nullable(),\n\tcontactId: z.string().uuid(),\n\tcreatedBy: z.string().uuid(),\n}).strict();\n\nexport const contactMarkedChampionPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\topportunityId: z.string().uuid(),\n}).strict();\n\nexport const contactMergedPayloadSchema = z.object({\n\tmergedBy: z.string().uuid(),\n\tsourceId: z.string().uuid(),\n\ttargetId: z.string().uuid(),\n}).strict();\n\nexport const crmSyncStartedPayloadSchema = z.object({\n\trunId: z.string().uuid(),\n\tsource: z.string(),\n}).strict();\n\nexport const dealCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid(),\n\tdealId: z.string().uuid(),\n\townerId: z.string().uuid(),\n}).strict();\n\nexport const dealStageChangedPayloadSchema = z.object({\n\tdealId: z.string().uuid(),\n\tnewStage: z.string(),\n\toldStage: z.string(),\n}).strict();\n\nexport const stripePaymentReceivedPayloadSchema = z.object({\n\tamountCents: z.number(),\n\tcurrency: z.string(),\n\tcustomerId: z.string(),\n\teventId: z.string(),\n\treceivedAt: z.coerce.date(),\n}).strict();\n\nexport const webhookOutboundContactSyncPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\toccurredAt: z.coerce.date(),\n\toperation: z.string(),\n}).strict();\n\nexport const eventPayloadSchemas = {\n\t'contact_created': contactCreatedPayloadSchema,\n\t'contact_marked_champion': contactMarkedChampionPayloadSchema,\n\t'contact_merged': contactMergedPayloadSchema,\n\t'crm_sync_started': crmSyncStartedPayloadSchema,\n\t'deal_created': dealCreatedPayloadSchema,\n\t'deal_stage_changed': dealStageChangedPayloadSchema,\n\t'stripe_payment_received': stripePaymentReceivedPayloadSchema,\n\t'webhook_outbound_contact_sync': webhookOutboundContactSyncPayloadSchema,\n} as const satisfies Record<EventTypeName, z.ZodType>;\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport type { EventTypeName } from './types';\n\nexport interface EventMetadata {\n\ttype: EventTypeName;\n\ttier: 'domain' | 'audit';\n\tdirection: 'inbound' | 'change' | 'outbound' | null;\n\tpool: 'events_inbound' | 'events_change' | 'events_outbound' | null;\n\taggregate?: string;\n\tsource?: string;\n\tdestination?: string;\n\tversion: number;\n\tretry: { attempts: number; backoff: 'linear' | 'exponential' };\n}\n\nexport const eventRegistry = {\n\t'contact_created': {\n\t\ttype: 'contact_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_marked_champion': {\n\t\ttype: 'contact_marked_champion',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_merged': {\n\t\ttype: 'contact_merged',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'crm_sync_started': {\n\t\ttype: 'crm_sync_started',\n\t\ttier: 'audit',\n\t\tdirection: null,\n\t\tpool: null,\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_created': {\n\t\ttype: 'deal_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_stage_changed': {\n\t\ttype: 'deal_stage_changed',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'stripe_payment_received': {\n\t\ttype: 'stripe_payment_received',\n\t\ttier: 'domain',\n\t\tdirection: 'inbound',\n\t\tpool: 'events_inbound',\n\t\tsource: 'stripe',\n\t\tversion: 1,\n\t\tretry: { attempts: 5, backoff: 'exponential' },\n\t},\n\t'webhook_outbound_contact_sync': {\n\t\ttype: 'webhook_outbound_contact_sync',\n\t\ttier: 'domain',\n\t\tdirection: 'outbound',\n\t\tpool: 'events_outbound',\n\t\taggregate: 'contact',\n\t\tdestination: 'crm',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n} as const satisfies Record<EventTypeName, EventMetadata>;\n\nexport function getEventMetadata<T extends EventTypeName>(type: T): EventMetadata {\n\tconst meta = eventRegistry[type];\n\tif (!meta) {\n\t\tthrow new Error(`No registry entry for event type '${String(type)}' — declare events under events/*.yaml and re-run \\`codegen entity new --all\\`.`);\n\t}\n\treturn meta;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../runtime/subsystems/events/generated/bus.ts","../../../../../runtime/subsystems/events/events.tokens.ts","../../../../../runtime/subsystems/events/events-errors.ts","../../../../../runtime/subsystems/events/generated/schemas.ts","../../../../../runtime/subsystems/events/generated/registry.ts"],"sourcesContent":["// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\nimport { Injectable, Inject } from '@nestjs/common';\nimport { randomUUID } from 'crypto';\nimport { EVENT_BUS, EVENTS_MULTI_TENANT } from '../events.tokens';\nimport { MissingTenantIdError } from '../events-errors';\nimport type { IEventBus, DrizzleTransaction } from '../event-bus.protocol';\nimport { eventPayloadSchemas } from './schemas';\nimport { getEventMetadata } from './registry';\nimport type { EventTypeName, EventOfType, PayloadOfType } from './types';\n\n/**\n * Typed facade over IEventBus.\n *\n * Stamps `pool`, `direction`, `tier`, and `version` into `event.metadata`\n * from the generated `eventRegistry` before delegating to\n * `IEventBus.publish()`. Downstream backends (DrizzleEventBus) read those\n * values to populate the explicit `domain_events` columns.\n *\n * Tier stamping (AUDIT-3): every event carries `metadata.tier`, sourced\n * from the registry. For `tier: 'audit'` events, the bus FORCES\n * `metadata.pool = null` and `metadata.direction = null` regardless of\n * any caller-supplied values in `opts.metadata` — audit routing is\n * bus-stamped, not caller-controlled. Caller overrides are silently\n * dropped with a debug-level log (callers should not be specifying these\n * for audit events; see ai-docs/specs/issue-242/plan.md §AUDIT-3).\n *\n * Validation gating (EVT-Q5): `CODEGEN_EVENT_VALIDATE` env flag, default on.\n * Uses `safeParse` + `console.warn` — never throws, so a bad publish does\n * not crash a hot path.\n *\n * Multi-tenancy (EVT-6): when the EventsModule is configured with\n * `multiTenant: true`, every publish must supply `opts.metadata.tenantId`\n * — otherwise `publish()` throws `MissingTenantIdError`. When `multiTenant`\n * is `false` (default), no tenantId is required. If a tenantId IS supplied,\n * it is preserved on `event.metadata` and the Drizzle backend writes it to\n * `domain_events.tenant_id` (EVT-4).\n */\n@Injectable()\nexport class TypedEventBus {\n\tconstructor(\n\t\t@Inject(EVENT_BUS) private readonly bus: IEventBus,\n\t\t@Inject(EVENTS_MULTI_TENANT) private readonly multiTenant: boolean,\n\t) {}\n\n\tasync publish<T extends EventTypeName>(\n\t\ttype: T,\n\t\taggregateId: string,\n\t\tpayload: PayloadOfType<T>,\n\t\topts?: { tx?: DrizzleTransaction; metadata?: Record<string, unknown> },\n\t): Promise<void> {\n\t\tconst meta = getEventMetadata(type);\n\n\t\tconst flag = process.env['CODEGEN_EVENT_VALIDATE'];\n\t\tconst shouldValidate =\n\t\t\tflag === undefined ? true : flag !== 'false' && flag !== '0';\n\t\tif (shouldValidate) {\n\t\t\t// `eventPayloadSchemas` is typed as `Record<EventTypeName, z.ZodType>`,\n\t\t\t// so under `noUncheckedIndexedAccess` the indexed lookup widens\n\t\t\t// to `z.ZodType | undefined`. When no events are registered at\n\t\t\t// codegen time `EventTypeName` degrades to `string` and the\n\t\t\t// schemas object is literally `{}` — the guard below is the\n\t\t\t// honest handling of that empty-registry case (skip validation;\n\t\t\t// it's a warn-only best-effort check per the class docblock).\n\t\t\tconst schema = eventPayloadSchemas[type];\n\t\t\tif (schema) {\n\t\t\t\tconst check = schema.safeParse(payload);\n\t\t\t\tif (!check.success) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[TypedEventBus] payload validation failed for ${String(type)}:`,\n\t\t\t\t\t\tcheck.error.issues,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst tenantId = opts?.metadata?.['tenantId'];\n\t\tif (this.multiTenant && (tenantId === undefined || tenantId === null)) {\n\t\t\tthrow new MissingTenantIdError(type as string);\n\t\t}\n\n\t\tconst aggregateType =\n\t\t\tmeta.aggregate ?? meta.source ?? meta.destination ?? (type as string);\n\n\t\t// AUDIT-3: build metadata with tier-aware routing stamping. For\n\t\t// `tier: 'audit'` events the bus FORCES pool/direction to null,\n\t\t// even if the caller supplied them in opts.metadata. Audit routing\n\t\t// is bus-stamped, not caller-controlled (see plan §AUDIT-3).\n\t\tconst baseMetadata: Record<string, unknown> = { ...(opts?.metadata ?? {}) };\n\t\tif (meta.tier === 'audit') {\n\t\t\tif (\n\t\t\t\tbaseMetadata['pool'] !== undefined ||\n\t\t\t\tbaseMetadata['direction'] !== undefined\n\t\t\t) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[TypedEventBus] tier:audit event '${String(type)}' had pool/direction in opts.metadata; overriding to null.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbaseMetadata['pool'] = null;\n\t\t\tbaseMetadata['direction'] = null;\n\t\t\tbaseMetadata['tier'] = 'audit';\n\t\t} else {\n\t\t\tbaseMetadata['pool'] = meta.pool;\n\t\t\tbaseMetadata['direction'] = meta.direction;\n\t\t\tbaseMetadata['tier'] = 'domain';\n\t\t}\n\t\tbaseMetadata['version'] = meta.version;\n\n\t\tawait this.bus.publish(\n\t\t\t{\n\t\t\t\tid: randomUUID(),\n\t\t\t\ttype,\n\t\t\t\taggregateId,\n\t\t\t\taggregateType,\n\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t\toccurredAt: new Date(),\n\t\t\t\tmetadata: baseMetadata,\n\t\t\t},\n\t\t\topts?.tx,\n\t\t);\n\t}\n\n\tsubscribe<T extends EventTypeName>(\n\t\ttype: T,\n\t\thandler: (event: EventOfType<T>) => Promise<void>,\n\t): () => void {\n\t\treturn this.bus.subscribe<EventOfType<T>>(type, handler as never);\n\t}\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 */\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 */\nexport const REDIS_URL = Symbol('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 * Typed errors for the events subsystem (ADR-024, EVT-6).\n *\n * All thrown from the publish path of `TypedEventBus`. They exist as\n * classes so consumers can `instanceof` them in catch blocks and\n * exception filters can map them to HTTP codes.\n */\n\n/**\n * Thrown by `TypedEventBus.publish()` when the EventsModule is configured\n * with `multiTenant: true` and the caller did not supply\n * `opts.metadata.tenantId`. Multi-tenant mode requires every outbox row to\n * be attributable to a tenant — the `domain_events.tenant_id` column is\n * populated from this value and the drain loop uses it for future\n * tenant-scoped filtering (deferred — see ADR-024 §Multi-tenancy).\n *\n * Disable multi-tenancy at the module level (`multiTenant: false`, the\n * default) to opt out of the requirement entirely.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly eventType: string) {\n super(\n `Missing tenantId for event '${eventType}'. EventsModule is configured ` +\n `with multiTenant: true — every publish must include ` +\n `opts.metadata.tenantId. Either pass the tenantId or disable ` +\n `multi-tenancy on the module.`,\n );\n }\n}\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport { z } from 'zod';\nimport type { EventTypeName } from './types';\n\nexport const contactCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid().nullable(),\n\tcontactId: z.string().uuid(),\n\tcreatedBy: z.string().uuid(),\n}).strict();\n\nexport const contactMarkedChampionPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\topportunityId: z.string().uuid(),\n}).strict();\n\nexport const contactMergedPayloadSchema = z.object({\n\tmergedBy: z.string().uuid(),\n\tsourceId: z.string().uuid(),\n\ttargetId: z.string().uuid(),\n}).strict();\n\nexport const crmSyncStartedPayloadSchema = z.object({\n\trunId: z.string().uuid(),\n\tsource: z.string(),\n}).strict();\n\nexport const dealCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid(),\n\tdealId: z.string().uuid(),\n\townerId: z.string().uuid(),\n}).strict();\n\nexport const dealStageChangedPayloadSchema = z.object({\n\tdealId: z.string().uuid(),\n\tnewStage: z.string(),\n\toldStage: z.string(),\n}).strict();\n\nexport const stripePaymentReceivedPayloadSchema = z.object({\n\tamountCents: z.number(),\n\tcurrency: z.string(),\n\tcustomerId: z.string(),\n\teventId: z.string(),\n\treceivedAt: z.coerce.date(),\n}).strict();\n\nexport const webhookOutboundContactSyncPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\toccurredAt: z.coerce.date(),\n\toperation: z.string(),\n}).strict();\n\nexport const eventPayloadSchemas = {\n\t'contact_created': contactCreatedPayloadSchema,\n\t'contact_marked_champion': contactMarkedChampionPayloadSchema,\n\t'contact_merged': contactMergedPayloadSchema,\n\t'crm_sync_started': crmSyncStartedPayloadSchema,\n\t'deal_created': dealCreatedPayloadSchema,\n\t'deal_stage_changed': dealStageChangedPayloadSchema,\n\t'stripe_payment_received': stripePaymentReceivedPayloadSchema,\n\t'webhook_outbound_contact_sync': webhookOutboundContactSyncPayloadSchema,\n} as const satisfies Record<EventTypeName, z.ZodType>;\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport type { EventTypeName } from './types';\n\nexport interface EventMetadata {\n\ttype: EventTypeName;\n\ttier: 'domain' | 'audit';\n\tdirection: 'inbound' | 'change' | 'outbound' | null;\n\tpool: 'events_inbound' | 'events_change' | 'events_outbound' | null;\n\taggregate?: string;\n\tsource?: string;\n\tdestination?: string;\n\tversion: number;\n\tretry: { attempts: number; backoff: 'linear' | 'exponential' };\n}\n\nexport const eventRegistry = {\n\t'contact_created': {\n\t\ttype: 'contact_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_marked_champion': {\n\t\ttype: 'contact_marked_champion',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_merged': {\n\t\ttype: 'contact_merged',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'crm_sync_started': {\n\t\ttype: 'crm_sync_started',\n\t\ttier: 'audit',\n\t\tdirection: null,\n\t\tpool: null,\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_created': {\n\t\ttype: 'deal_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_stage_changed': {\n\t\ttype: 'deal_stage_changed',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'stripe_payment_received': {\n\t\ttype: 'stripe_payment_received',\n\t\ttier: 'domain',\n\t\tdirection: 'inbound',\n\t\tpool: 'events_inbound',\n\t\tsource: 'stripe',\n\t\tversion: 1,\n\t\tretry: { attempts: 5, backoff: 'exponential' },\n\t},\n\t'webhook_outbound_contact_sync': {\n\t\ttype: 'webhook_outbound_contact_sync',\n\t\ttier: 'domain',\n\t\tdirection: 'outbound',\n\t\tpool: 'events_outbound',\n\t\taggregate: 'contact',\n\t\tdestination: 'crm',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n} as const satisfies Record<EventTypeName, EventMetadata>;\n\nexport function getEventMetadata<T extends EventTypeName>(type: T): EventMetadata {\n\tconst meta = eventRegistry[type];\n\tif (!meta) {\n\t\tthrow new Error(`No registry entry for event type '${String(type)}' — declare events under events/*.yaml and re-run \\`codegen entity new --all\\`.`);\n\t}\n\treturn meta;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../runtime/subsystems/events/generated/schemas.ts","../../../../../runtime/subsystems/events/generated/registry.ts","../../../../../runtime/subsystems/events/generated/bus.ts","../../../../../runtime/subsystems/events/events.tokens.ts","../../../../../runtime/subsystems/events/events-errors.ts"],"sourcesContent":["// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport { z } from 'zod';\nimport type { EventTypeName } from './types';\n\nexport const contactCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid().nullable(),\n\tcontactId: z.string().uuid(),\n\tcreatedBy: z.string().uuid(),\n}).strict();\n\nexport const contactMarkedChampionPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\topportunityId: z.string().uuid(),\n}).strict();\n\nexport const contactMergedPayloadSchema = z.object({\n\tmergedBy: z.string().uuid(),\n\tsourceId: z.string().uuid(),\n\ttargetId: z.string().uuid(),\n}).strict();\n\nexport const crmSyncStartedPayloadSchema = z.object({\n\trunId: z.string().uuid(),\n\tsource: z.string(),\n}).strict();\n\nexport const dealCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid(),\n\tdealId: z.string().uuid(),\n\townerId: z.string().uuid(),\n}).strict();\n\nexport const dealStageChangedPayloadSchema = z.object({\n\tdealId: z.string().uuid(),\n\tnewStage: z.string(),\n\toldStage: z.string(),\n}).strict();\n\nexport const stripePaymentReceivedPayloadSchema = z.object({\n\tamountCents: z.number(),\n\tcurrency: z.string(),\n\tcustomerId: z.string(),\n\teventId: z.string(),\n\treceivedAt: z.coerce.date(),\n}).strict();\n\nexport const webhookOutboundContactSyncPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\toccurredAt: z.coerce.date(),\n\toperation: z.string(),\n}).strict();\n\nexport const eventPayloadSchemas = {\n\t'contact_created': contactCreatedPayloadSchema,\n\t'contact_marked_champion': contactMarkedChampionPayloadSchema,\n\t'contact_merged': contactMergedPayloadSchema,\n\t'crm_sync_started': crmSyncStartedPayloadSchema,\n\t'deal_created': dealCreatedPayloadSchema,\n\t'deal_stage_changed': dealStageChangedPayloadSchema,\n\t'stripe_payment_received': stripePaymentReceivedPayloadSchema,\n\t'webhook_outbound_contact_sync': webhookOutboundContactSyncPayloadSchema,\n} as const satisfies Record<EventTypeName, z.ZodType>;\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport type { EventTypeName } from './types';\n\nexport interface EventMetadata {\n\ttype: EventTypeName;\n\ttier: 'domain' | 'audit';\n\tdirection: 'inbound' | 'change' | 'outbound' | null;\n\tpool: 'events_inbound' | 'events_change' | 'events_outbound' | null;\n\taggregate?: string;\n\tsource?: string;\n\tdestination?: string;\n\tversion: number;\n\tretry: { attempts: number; backoff: 'linear' | 'exponential' };\n}\n\nexport const eventRegistry = {\n\t'contact_created': {\n\t\ttype: 'contact_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_marked_champion': {\n\t\ttype: 'contact_marked_champion',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_merged': {\n\t\ttype: 'contact_merged',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'crm_sync_started': {\n\t\ttype: 'crm_sync_started',\n\t\ttier: 'audit',\n\t\tdirection: null,\n\t\tpool: null,\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_created': {\n\t\ttype: 'deal_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_stage_changed': {\n\t\ttype: 'deal_stage_changed',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'stripe_payment_received': {\n\t\ttype: 'stripe_payment_received',\n\t\ttier: 'domain',\n\t\tdirection: 'inbound',\n\t\tpool: 'events_inbound',\n\t\tsource: 'stripe',\n\t\tversion: 1,\n\t\tretry: { attempts: 5, backoff: 'exponential' },\n\t},\n\t'webhook_outbound_contact_sync': {\n\t\ttype: 'webhook_outbound_contact_sync',\n\t\ttier: 'domain',\n\t\tdirection: 'outbound',\n\t\tpool: 'events_outbound',\n\t\taggregate: 'contact',\n\t\tdestination: 'crm',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n} as const satisfies Record<EventTypeName, EventMetadata>;\n\nexport function getEventMetadata<T extends EventTypeName>(type: T): EventMetadata {\n\tconst meta = eventRegistry[type];\n\tif (!meta) {\n\t\tthrow new Error(`No registry entry for event type '${String(type)}' — declare events under events/*.yaml and re-run \\`codegen entity new --all\\`.`);\n\t}\n\treturn meta;\n}\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\nimport { Injectable, Inject } from '@nestjs/common';\nimport { randomUUID } from 'crypto';\nimport { EVENT_BUS, EVENTS_MULTI_TENANT } from '../events.tokens';\nimport { MissingTenantIdError } from '../events-errors';\nimport type { IEventBus, DrizzleTransaction } from '../event-bus.protocol';\nimport { eventPayloadSchemas } from './schemas';\nimport { getEventMetadata } from './registry';\nimport type { EventTypeName, EventOfType, PayloadOfType } from './types';\n\n/**\n * Typed facade over IEventBus.\n *\n * Stamps `pool`, `direction`, `tier`, and `version` into `event.metadata`\n * from the generated `eventRegistry` before delegating to\n * `IEventBus.publish()`. Downstream backends (DrizzleEventBus) read those\n * values to populate the explicit `domain_events` columns.\n *\n * Tier stamping (AUDIT-3): every event carries `metadata.tier`, sourced\n * from the registry. For `tier: 'audit'` events, the bus FORCES\n * `metadata.pool = null` and `metadata.direction = null` regardless of\n * any caller-supplied values in `opts.metadata` — audit routing is\n * bus-stamped, not caller-controlled. Caller overrides are silently\n * dropped with a debug-level log (callers should not be specifying these\n * for audit events; see ai-docs/specs/issue-242/plan.md §AUDIT-3).\n *\n * Validation gating (EVT-Q5): `CODEGEN_EVENT_VALIDATE` env flag, default on.\n * Uses `safeParse` + `console.warn` — never throws, so a bad publish does\n * not crash a hot path.\n *\n * Multi-tenancy (EVT-6): when the EventsModule is configured with\n * `multiTenant: true`, every publish must supply `opts.metadata.tenantId`\n * — otherwise `publish()` throws `MissingTenantIdError`. When `multiTenant`\n * is `false` (default), no tenantId is required. If a tenantId IS supplied,\n * it is preserved on `event.metadata` and the Drizzle backend writes it to\n * `domain_events.tenant_id` (EVT-4).\n */\n@Injectable()\nexport class TypedEventBus {\n\tconstructor(\n\t\t@Inject(EVENT_BUS) private readonly bus: IEventBus,\n\t\t@Inject(EVENTS_MULTI_TENANT) private readonly multiTenant: boolean,\n\t) {}\n\n\tasync publish<T extends EventTypeName>(\n\t\ttype: T,\n\t\taggregateId: string,\n\t\tpayload: PayloadOfType<T>,\n\t\topts?: { tx?: DrizzleTransaction; metadata?: Record<string, unknown> },\n\t): Promise<void> {\n\t\tconst meta = getEventMetadata(type);\n\n\t\tconst flag = process.env['CODEGEN_EVENT_VALIDATE'];\n\t\tconst shouldValidate =\n\t\t\tflag === undefined ? true : flag !== 'false' && flag !== '0';\n\t\tif (shouldValidate) {\n\t\t\t// `eventPayloadSchemas` is typed as `Record<EventTypeName, z.ZodType>`,\n\t\t\t// so under `noUncheckedIndexedAccess` the indexed lookup widens\n\t\t\t// to `z.ZodType | undefined`. When no events are registered at\n\t\t\t// codegen time `EventTypeName` degrades to `string` and the\n\t\t\t// schemas object is literally `{}` — the guard below is the\n\t\t\t// honest handling of that empty-registry case (skip validation;\n\t\t\t// it's a warn-only best-effort check per the class docblock).\n\t\t\tconst schema = eventPayloadSchemas[type];\n\t\t\tif (schema) {\n\t\t\t\tconst check = schema.safeParse(payload);\n\t\t\t\tif (!check.success) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[TypedEventBus] payload validation failed for ${String(type)}:`,\n\t\t\t\t\t\tcheck.error.issues,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst tenantId = opts?.metadata?.['tenantId'];\n\t\tif (this.multiTenant && (tenantId === undefined || tenantId === null)) {\n\t\t\tthrow new MissingTenantIdError(type as string);\n\t\t}\n\n\t\tconst aggregateType =\n\t\t\tmeta.aggregate ?? meta.source ?? meta.destination ?? (type as string);\n\n\t\t// AUDIT-3: build metadata with tier-aware routing stamping. For\n\t\t// `tier: 'audit'` events the bus FORCES pool/direction to null,\n\t\t// even if the caller supplied them in opts.metadata. Audit routing\n\t\t// is bus-stamped, not caller-controlled (see plan §AUDIT-3).\n\t\tconst baseMetadata: Record<string, unknown> = { ...(opts?.metadata ?? {}) };\n\t\tif (meta.tier === 'audit') {\n\t\t\tif (\n\t\t\t\tbaseMetadata['pool'] !== undefined ||\n\t\t\t\tbaseMetadata['direction'] !== undefined\n\t\t\t) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[TypedEventBus] tier:audit event '${String(type)}' had pool/direction in opts.metadata; overriding to null.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbaseMetadata['pool'] = null;\n\t\t\tbaseMetadata['direction'] = null;\n\t\t\tbaseMetadata['tier'] = 'audit';\n\t\t} else {\n\t\t\tbaseMetadata['pool'] = meta.pool;\n\t\t\tbaseMetadata['direction'] = meta.direction;\n\t\t\tbaseMetadata['tier'] = 'domain';\n\t\t}\n\t\tbaseMetadata['version'] = meta.version;\n\n\t\tawait this.bus.publish(\n\t\t\t{\n\t\t\t\tid: randomUUID(),\n\t\t\t\ttype,\n\t\t\t\taggregateId,\n\t\t\t\taggregateType,\n\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t\toccurredAt: new Date(),\n\t\t\t\tmetadata: baseMetadata,\n\t\t\t},\n\t\t\topts?.tx,\n\t\t);\n\t}\n\n\tsubscribe<T extends EventTypeName>(\n\t\ttype: T,\n\t\thandler: (event: EventOfType<T>) => Promise<void>,\n\t): () => void {\n\t\treturn this.bus.subscribe<EventOfType<T>>(type, handler as never);\n\t}\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 */\nexport const EVENT_BUS = 'EVENT_BUS' 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 */\nexport const REDIS_URL = Symbol('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 * Typed errors for the events subsystem (ADR-024, EVT-6).\n *\n * All thrown from the publish path of `TypedEventBus`. They exist as\n * classes so consumers can `instanceof` them in catch blocks and\n * exception filters can map them to HTTP codes.\n */\n\n/**\n * Thrown by `TypedEventBus.publish()` when the EventsModule is configured\n * with `multiTenant: true` and the caller did not supply\n * `opts.metadata.tenantId`. Multi-tenant mode requires every outbox row to\n * be attributable to a tenant — the `domain_events.tenant_id` column is\n * populated from this value and the drain loop uses it for future\n * tenant-scoped filtering (deferred — see ADR-024 §Multi-tenancy).\n *\n * Disable multi-tenancy at the module level (`multiTenant: false`, the\n * default) to opt out of the requirement entirely.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly eventType: string) {\n super(\n `Missing tenantId for event '${eventType}'. EventsModule is configured ` +\n `with multiTenant: true — every publish must include ` +\n `opts.metadata.tenantId. Either pass the tenantId or disable ` +\n `multi-tenancy on the module.`,\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../runtime/subsystems/events/generated/schemas.ts","../../../../../runtime/subsystems/events/generated/registry.ts","../../../../../runtime/subsystems/events/generated/bus.ts","../../../../../runtime/subsystems/events/events.tokens.ts","../../../../../runtime/subsystems/events/events-errors.ts"],"sourcesContent":["// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport { z } from 'zod';\nimport type { EventTypeName } from './types';\n\nexport const contactCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid().nullable(),\n\tcontactId: z.string().uuid(),\n\tcreatedBy: z.string().uuid(),\n}).strict();\n\nexport const contactMarkedChampionPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\topportunityId: z.string().uuid(),\n}).strict();\n\nexport const contactMergedPayloadSchema = z.object({\n\tmergedBy: z.string().uuid(),\n\tsourceId: z.string().uuid(),\n\ttargetId: z.string().uuid(),\n}).strict();\n\nexport const crmSyncStartedPayloadSchema = z.object({\n\trunId: z.string().uuid(),\n\tsource: z.string(),\n}).strict();\n\nexport const dealCreatedPayloadSchema = z.object({\n\taccountId: z.string().uuid(),\n\tdealId: z.string().uuid(),\n\townerId: z.string().uuid(),\n}).strict();\n\nexport const dealStageChangedPayloadSchema = z.object({\n\tdealId: z.string().uuid(),\n\tnewStage: z.string(),\n\toldStage: z.string(),\n}).strict();\n\nexport const stripePaymentReceivedPayloadSchema = z.object({\n\tamountCents: z.number(),\n\tcurrency: z.string(),\n\tcustomerId: z.string(),\n\teventId: z.string(),\n\treceivedAt: z.coerce.date(),\n}).strict();\n\nexport const webhookOutboundContactSyncPayloadSchema = z.object({\n\tcontactId: z.string().uuid(),\n\toccurredAt: z.coerce.date(),\n\toperation: z.string(),\n}).strict();\n\nexport const eventPayloadSchemas = {\n\t'contact_created': contactCreatedPayloadSchema,\n\t'contact_marked_champion': contactMarkedChampionPayloadSchema,\n\t'contact_merged': contactMergedPayloadSchema,\n\t'crm_sync_started': crmSyncStartedPayloadSchema,\n\t'deal_created': dealCreatedPayloadSchema,\n\t'deal_stage_changed': dealStageChangedPayloadSchema,\n\t'stripe_payment_received': stripePaymentReceivedPayloadSchema,\n\t'webhook_outbound_contact_sync': webhookOutboundContactSyncPayloadSchema,\n} as const satisfies Record<EventTypeName, z.ZodType>;\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\n\nimport type { EventTypeName } from './types';\n\nexport interface EventMetadata {\n\ttype: EventTypeName;\n\ttier: 'domain' | 'audit';\n\tdirection: 'inbound' | 'change' | 'outbound' | null;\n\tpool: 'events_inbound' | 'events_change' | 'events_outbound' | null;\n\taggregate?: string;\n\tsource?: string;\n\tdestination?: string;\n\tversion: number;\n\tretry: { attempts: number; backoff: 'linear' | 'exponential' };\n}\n\nexport const eventRegistry = {\n\t'contact_created': {\n\t\ttype: 'contact_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_marked_champion': {\n\t\ttype: 'contact_marked_champion',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'contact_merged': {\n\t\ttype: 'contact_merged',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'contact',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'crm_sync_started': {\n\t\ttype: 'crm_sync_started',\n\t\ttier: 'audit',\n\t\tdirection: null,\n\t\tpool: null,\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_created': {\n\t\ttype: 'deal_created',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'deal_stage_changed': {\n\t\ttype: 'deal_stage_changed',\n\t\ttier: 'domain',\n\t\tdirection: 'change',\n\t\tpool: 'events_change',\n\t\taggregate: 'deal',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n\t'stripe_payment_received': {\n\t\ttype: 'stripe_payment_received',\n\t\ttier: 'domain',\n\t\tdirection: 'inbound',\n\t\tpool: 'events_inbound',\n\t\tsource: 'stripe',\n\t\tversion: 1,\n\t\tretry: { attempts: 5, backoff: 'exponential' },\n\t},\n\t'webhook_outbound_contact_sync': {\n\t\ttype: 'webhook_outbound_contact_sync',\n\t\ttier: 'domain',\n\t\tdirection: 'outbound',\n\t\tpool: 'events_outbound',\n\t\taggregate: 'contact',\n\t\tdestination: 'crm',\n\t\tversion: 1,\n\t\tretry: { attempts: 3, backoff: 'exponential' },\n\t},\n} as const satisfies Record<EventTypeName, EventMetadata>;\n\nexport function getEventMetadata<T extends EventTypeName>(type: T): EventMetadata {\n\tconst meta = eventRegistry[type];\n\tif (!meta) {\n\t\tthrow new Error(`No registry entry for event type '${String(type)}' — declare events under events/*.yaml and re-run \\`codegen entity new --all\\`.`);\n\t}\n\treturn meta;\n}\n","// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.\n// Run `codegen entity new --all` to refresh.\n\nimport { Injectable, Inject } from '@nestjs/common';\nimport { randomUUID } from 'crypto';\nimport { EVENT_BUS, EVENTS_MULTI_TENANT } from '../events.tokens';\nimport { MissingTenantIdError } from '../events-errors';\nimport type { IEventBus, DrizzleTransaction } from '../event-bus.protocol';\nimport { eventPayloadSchemas } from './schemas';\nimport { getEventMetadata } from './registry';\nimport type { EventTypeName, EventOfType, PayloadOfType } from './types';\n\n/**\n * Typed facade over IEventBus.\n *\n * Stamps `pool`, `direction`, `tier`, and `version` into `event.metadata`\n * from the generated `eventRegistry` before delegating to\n * `IEventBus.publish()`. Downstream backends (DrizzleEventBus) read those\n * values to populate the explicit `domain_events` columns.\n *\n * Tier stamping (AUDIT-3): every event carries `metadata.tier`, sourced\n * from the registry. For `tier: 'audit'` events, the bus FORCES\n * `metadata.pool = null` and `metadata.direction = null` regardless of\n * any caller-supplied values in `opts.metadata` — audit routing is\n * bus-stamped, not caller-controlled. Caller overrides are silently\n * dropped with a debug-level log (callers should not be specifying these\n * for audit events; see ai-docs/specs/issue-242/plan.md §AUDIT-3).\n *\n * Validation gating (EVT-Q5): `CODEGEN_EVENT_VALIDATE` env flag, default on.\n * Uses `safeParse` + `console.warn` — never throws, so a bad publish does\n * not crash a hot path.\n *\n * Multi-tenancy (EVT-6): when the EventsModule is configured with\n * `multiTenant: true`, every publish must supply `opts.metadata.tenantId`\n * — otherwise `publish()` throws `MissingTenantIdError`. When `multiTenant`\n * is `false` (default), no tenantId is required. If a tenantId IS supplied,\n * it is preserved on `event.metadata` and the Drizzle backend writes it to\n * `domain_events.tenant_id` (EVT-4).\n */\n@Injectable()\nexport class TypedEventBus {\n\tconstructor(\n\t\t@Inject(EVENT_BUS) private readonly bus: IEventBus,\n\t\t@Inject(EVENTS_MULTI_TENANT) private readonly multiTenant: boolean,\n\t) {}\n\n\tasync publish<T extends EventTypeName>(\n\t\ttype: T,\n\t\taggregateId: string,\n\t\tpayload: PayloadOfType<T>,\n\t\topts?: { tx?: DrizzleTransaction; metadata?: Record<string, unknown> },\n\t): Promise<void> {\n\t\tconst meta = getEventMetadata(type);\n\n\t\tconst flag = process.env['CODEGEN_EVENT_VALIDATE'];\n\t\tconst shouldValidate =\n\t\t\tflag === undefined ? true : flag !== 'false' && flag !== '0';\n\t\tif (shouldValidate) {\n\t\t\t// `eventPayloadSchemas` is typed as `Record<EventTypeName, z.ZodType>`,\n\t\t\t// so under `noUncheckedIndexedAccess` the indexed lookup widens\n\t\t\t// to `z.ZodType | undefined`. When no events are registered at\n\t\t\t// codegen time `EventTypeName` degrades to `string` and the\n\t\t\t// schemas object is literally `{}` — the guard below is the\n\t\t\t// honest handling of that empty-registry case (skip validation;\n\t\t\t// it's a warn-only best-effort check per the class docblock).\n\t\t\tconst schema = eventPayloadSchemas[type];\n\t\t\tif (schema) {\n\t\t\t\tconst check = schema.safeParse(payload);\n\t\t\t\tif (!check.success) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[TypedEventBus] payload validation failed for ${String(type)}:`,\n\t\t\t\t\t\tcheck.error.issues,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst tenantId = opts?.metadata?.['tenantId'];\n\t\tif (this.multiTenant && (tenantId === undefined || tenantId === null)) {\n\t\t\tthrow new MissingTenantIdError(type as string);\n\t\t}\n\n\t\tconst aggregateType =\n\t\t\tmeta.aggregate ?? meta.source ?? meta.destination ?? (type as string);\n\n\t\t// AUDIT-3: build metadata with tier-aware routing stamping. For\n\t\t// `tier: 'audit'` events the bus FORCES pool/direction to null,\n\t\t// even if the caller supplied them in opts.metadata. Audit routing\n\t\t// is bus-stamped, not caller-controlled (see plan §AUDIT-3).\n\t\tconst baseMetadata: Record<string, unknown> = { ...(opts?.metadata ?? {}) };\n\t\tif (meta.tier === 'audit') {\n\t\t\tif (\n\t\t\t\tbaseMetadata['pool'] !== undefined ||\n\t\t\t\tbaseMetadata['direction'] !== undefined\n\t\t\t) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[TypedEventBus] tier:audit event '${String(type)}' had pool/direction in opts.metadata; overriding to null.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbaseMetadata['pool'] = null;\n\t\t\tbaseMetadata['direction'] = null;\n\t\t\tbaseMetadata['tier'] = 'audit';\n\t\t} else {\n\t\t\tbaseMetadata['pool'] = meta.pool;\n\t\t\tbaseMetadata['direction'] = meta.direction;\n\t\t\tbaseMetadata['tier'] = 'domain';\n\t\t}\n\t\tbaseMetadata['version'] = meta.version;\n\n\t\tawait this.bus.publish(\n\t\t\t{\n\t\t\t\tid: randomUUID(),\n\t\t\t\ttype,\n\t\t\t\taggregateId,\n\t\t\t\taggregateType,\n\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t\toccurredAt: new Date(),\n\t\t\t\tmetadata: baseMetadata,\n\t\t\t},\n\t\t\topts?.tx,\n\t\t);\n\t}\n\n\tsubscribe<T extends EventTypeName>(\n\t\ttype: T,\n\t\thandler: (event: EventOfType<T>) => Promise<void>,\n\t): () => void {\n\t\treturn this.bus.subscribe<EventOfType<T>>(type, handler as never);\n\t}\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 */\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 */\nexport const REDIS_URL = Symbol('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 * Typed errors for the events subsystem (ADR-024, EVT-6).\n *\n * All thrown from the publish path of `TypedEventBus`. They exist as\n * classes so consumers can `instanceof` them in catch blocks and\n * exception filters can map them to HTTP codes.\n */\n\n/**\n * Thrown by `TypedEventBus.publish()` when the EventsModule is configured\n * with `multiTenant: true` and the caller did not supply\n * `opts.metadata.tenantId`. Multi-tenant mode requires every outbox row to\n * be attributable to a tenant — the `domain_events.tenant_id` column is\n * populated from this value and the drain loop uses it for future\n * tenant-scoped filtering (deferred — see ADR-024 §Multi-tenancy).\n *\n * Disable multi-tenancy at the module level (`multiTenant: false`, the\n * default) to opt out of the requirement entirely.\n */\nexport class MissingTenantIdError extends Error {\n override readonly name = 'MissingTenantIdError';\n constructor(public readonly eventType: string) {\n super(\n `Missing tenantId for event '${eventType}'. EventsModule is configured ` +\n `with multiTenant: true — every publish must include ` +\n `opts.metadata.tenantId. Either pass the tenantId or disable ` +\n `multi-tenancy on the module.`,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAIA,SAAS,SAAS;AAGX,IAAM,8BAA8B,EAAE,OAAO;AAAA,EACnD,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACtC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,WAAW,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC,EAAE,OAAO;AAEH,IAAM,qCAAqC,EAAE,OAAO;AAAA,EAC1D,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO,EAAE,KAAK;AAChC,CAAC,EAAE,OAAO;AAEH,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAClD,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,KAAK;AAC3B,CAAC,EAAE,OAAO;AAEH,IAAM,8BAA8B,EAAE,OAAO;AAAA,EACnD,OAAO,EAAE,OAAO,EAAE,KAAK;AAAA,EACvB,QAAQ,EAAE,OAAO;AAClB,CAAC,EAAE,OAAO;AAEH,IAAM,2BAA2B,EAAE,OAAO;AAAA,EAChD,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,QAAQ,EAAE,OAAO,EAAE,KAAK;AAAA,EACxB,SAAS,EAAE,OAAO,EAAE,KAAK;AAC1B,CAAC,EAAE,OAAO;AAEH,IAAM,gCAAgC,EAAE,OAAO;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,KAAK;AAAA,EACxB,UAAU,EAAE,OAAO;AAAA,EACnB,UAAU,EAAE,OAAO;AACpB,CAAC,EAAE,OAAO;AAEH,IAAM,qCAAqC,EAAE,OAAO;AAAA,EAC1D,aAAa,EAAE,OAAO;AAAA,EACtB,UAAU,EAAE,OAAO;AAAA,EACnB,YAAY,EAAE,OAAO;AAAA,EACrB,SAAS,EAAE,OAAO;AAAA,EAClB,YAAY,EAAE,OAAO,KAAK;AAC3B,CAAC,EAAE,OAAO;AAEH,IAAM,0CAA0C,EAAE,OAAO;AAAA,EAC/D,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,YAAY,EAAE,OAAO,KAAK;AAAA,EAC1B,WAAW,EAAE,OAAO;AACrB,CAAC,EAAE,OAAO;AAEH,IAAM,sBAAsB;AAAA,EAClC,mBAAmB;AAAA,EACnB,2BAA2B;AAAA,EAC3B,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA,EAC3B,iCAAiC;AAClC;;;AC9CO,IAAM,gBAAgB;AAAA,EAC5B,mBAAmB;AAAA,IAClB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,2BAA2B;AAAA,IAC1B,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,kBAAkB;AAAA,IACjB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,oBAAoB;AAAA,IACnB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,gBAAgB;AAAA,IACf,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,sBAAsB;AAAA,IACrB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,2BAA2B;AAAA,IAC1B,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AAAA,EACA,iCAAiC;AAAA,IAChC,MAAM;AAAA,IACN,MAAM;AAAA,IACN,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,IACX,aAAa;AAAA,IACb,SAAS;AAAA,IACT,OAAO,EAAE,UAAU,GAAG,SAAS,cAAc;AAAA,EAC9C;AACD;AAEO,SAAS,iBAA0C,MAAwB;AACjF,QAAM,OAAO,cAAc,IAAI;AAC/B,MAAI,CAAC,MAAM;AACV,UAAM,IAAI,MAAM,qCAAqC,OAAO,IAAI,CAAC,sFAAiF;AAAA,EACnJ;AACA,SAAO;AACR;;;AChGA,SAAS,YAAY,cAAc;AACnC,SAAS,kBAAkB;;;ACOpB,IAAM,YAAY;AA+ClB,IAAM,sBAAsB;;;ACvC5B,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAE9C,YAA4B,WAAmB;AAC7C;AAAA,MACE,+BAA+B,SAAS;AAAA,IAI1C;AAN0B;AAAA,EAO5B;AAAA,EAP4B;AAAA,EADV,OAAO;AAS3B;;;AFWO,IAAM,gBAAN,MAAoB;AAAA,EAC1B,YACqC,KACU,aAC7C;AAFmC;AACU;AAAA,EAC5C;AAAA,EAFkC;AAAA,EACU;AAAA,EAG/C,MAAM,QACL,MACA,aACA,SACA,MACgB;AAChB,UAAM,OAAO,iBAAiB,IAAI;AAElC,UAAM,OAAO,QAAQ,IAAI,wBAAwB;AACjD,UAAM,iBACL,SAAS,SAAY,OAAO,SAAS,WAAW,SAAS;AAC1D,QAAI,gBAAgB;AAQnB,YAAM,SAAS,oBAAoB,IAAI;AACvC,UAAI,QAAQ;AACX,cAAM,QAAQ,OAAO,UAAU,OAAO;AACtC,YAAI,CAAC,MAAM,SAAS;AACnB,kBAAQ;AAAA,YACP,iDAAiD,OAAO,IAAI,CAAC;AAAA,YAC7D,MAAM,MAAM;AAAA,UACb;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,UAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAI,KAAK,gBAAgB,aAAa,UAAa,aAAa,OAAO;AACtE,YAAM,IAAI,qBAAqB,IAAc;AAAA,IAC9C;AAEA,UAAM,gBACL,KAAK,aAAa,KAAK,UAAU,KAAK,eAAgB;AAMvD,UAAM,eAAwC,EAAE,GAAI,MAAM,YAAY,CAAC,EAAG;AAC1E,QAAI,KAAK,SAAS,SAAS;AAC1B,UACC,aAAa,MAAM,MAAM,UACzB,aAAa,WAAW,MAAM,QAC7B;AACD,gBAAQ;AAAA,UACP,qCAAqC,OAAO,IAAI,CAAC;AAAA,QAClD;AAAA,MACD;AACA,mBAAa,MAAM,IAAI;AACvB,mBAAa,WAAW,IAAI;AAC5B,mBAAa,MAAM,IAAI;AAAA,IACxB,OAAO;AACN,mBAAa,MAAM,IAAI,KAAK;AAC5B,mBAAa,WAAW,IAAI,KAAK;AACjC,mBAAa,MAAM,IAAI;AAAA,IACxB;AACA,iBAAa,SAAS,IAAI,KAAK;AAE/B,UAAM,KAAK,IAAI;AAAA,MACd;AAAA,QACC,IAAI,WAAW;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY,oBAAI,KAAK;AAAA,QACrB,UAAU;AAAA,MACX;AAAA,MACA,MAAM;AAAA,IACP;AAAA,EACD;AAAA,EAEA,UACC,MACA,SACa;AACb,WAAO,KAAK,IAAI,UAA0B,MAAM,OAAgB;AAAA,EACjE;AACD;AAzFa,gBAAN;AAAA,EADN,WAAW;AAAA,EAGT,0BAAO,SAAS;AAAA,EAChB,0BAAO,mBAAmB;AAAA,GAHhB;","names":[]}
@@ -1,5 +1,6 @@
1
1
  export { DomainEvent, DrizzleTransaction, IEventBus } from './event-bus.protocol.js';
2
- export { EVENTS_MODULE_OPTIONS, EVENTS_MULTI_TENANT, EVENT_BUS, TYPED_EVENT_BUS } from './events.tokens.js';
2
+ export { EventPage, EventSummary, IEventReadPort, ListEventsQuery } from './event-read.protocol.js';
3
+ export { EVENTS_MODULE_OPTIONS, EVENTS_MULTI_TENANT, EVENT_BUS, EVENT_READ_PORT, TYPED_EVENT_BUS } from './events.tokens.js';
3
4
  export { TypedEventBus } from './generated/bus.js';
4
5
  export { MissingTenantIdError } from './events-errors.js';
5
6
  export { EventsModule, EventsModuleOptions } from './events.module.js';
@@ -12,6 +12,7 @@ var __decorateParam = (index2, decorator) => (target, key) => decorator(target,
12
12
 
13
13
  // runtime/subsystems/events/events.tokens.ts
14
14
  var EVENT_BUS = "EVENT_BUS";
15
+ var EVENT_READ_PORT = "EVENT_READ_PORT";
15
16
  var TYPED_EVENT_BUS = "TYPED_EVENT_BUS";
16
17
  var EVENTS_MULTI_TENANT = "EVENTS_MULTI_TENANT";
17
18
  var REDIS_URL = /* @__PURE__ */ Symbol("REDIS_URL");
@@ -245,7 +246,38 @@ var DRIZZLE = "DRIZZLE";
245
246
 
246
247
  // runtime/subsystems/events/event-bus.drizzle-backend.ts
247
248
  import { Injectable as Injectable2, Inject as Inject2, Logger, Optional } from "@nestjs/common";
248
- import { eq, and, inArray, asc } from "drizzle-orm";
249
+ import { eq, and, inArray, asc, desc, gte, lt, or, sql as sql2 } from "drizzle-orm";
250
+
251
+ // runtime/subsystems/events/event-keyset-cursor.ts
252
+ var DEFAULT_EVENT_LIST_LIMIT = 50;
253
+ var MAX_EVENT_LIST_LIMIT = 200;
254
+ function clampEventLimit(limit) {
255
+ if (typeof limit !== "number" || !Number.isFinite(limit)) {
256
+ return DEFAULT_EVENT_LIST_LIMIT;
257
+ }
258
+ const floored = Math.floor(limit);
259
+ if (floored < 1) return 1;
260
+ if (floored > MAX_EVENT_LIST_LIMIT) return MAX_EVENT_LIST_LIMIT;
261
+ return floored;
262
+ }
263
+ function encodeEventCursor(keyset) {
264
+ const tuple = [keyset.occurredAt.toISOString(), keyset.id];
265
+ return Buffer.from(JSON.stringify(tuple), "utf8").toString("base64url");
266
+ }
267
+ function decodeEventCursor(cursor) {
268
+ try {
269
+ const json = Buffer.from(cursor, "base64url").toString("utf8");
270
+ const parsed = JSON.parse(json);
271
+ if (!Array.isArray(parsed) || parsed.length !== 2) return null;
272
+ const [iso, id] = parsed;
273
+ if (typeof iso !== "string" || typeof id !== "string") return null;
274
+ const occurredAt = new Date(iso);
275
+ if (Number.isNaN(occurredAt.getTime())) return null;
276
+ return { occurredAt, id };
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
249
281
 
250
282
  // runtime/subsystems/events/domain-events.schema.ts
251
283
  import {
@@ -349,6 +381,24 @@ function toInsertValues(event) {
349
381
  tenantId
350
382
  };
351
383
  }
384
+ function toEventSummary(r) {
385
+ const metadata = r.metadata ?? void 0;
386
+ const rootRunId = metadata?.["rootRunId"];
387
+ return {
388
+ id: r.id,
389
+ type: r.type,
390
+ aggregateId: r.aggregateId,
391
+ aggregateType: r.aggregateType,
392
+ status: r.status,
393
+ pool: r.pool,
394
+ direction: r.direction,
395
+ tier: r.tier,
396
+ rootRunId: typeof rootRunId === "string" ? rootRunId : null,
397
+ tenantId: r.tenantId,
398
+ occurredAt: r.occurredAt instanceof Date ? r.occurredAt : new Date(r.occurredAt),
399
+ processedAt: r.processedAt == null ? null : r.processedAt instanceof Date ? r.processedAt : new Date(r.processedAt)
400
+ };
401
+ }
352
402
  var DrizzleEventBus = class {
353
403
  constructor(db, opts, bridgeHook = null) {
354
404
  this.db = db;
@@ -414,6 +464,48 @@ var DrizzleEventBus = class {
414
464
  };
415
465
  }
416
466
  // ============================================================================
467
+ // IEventReadPort (OBS-LIST-1)
468
+ // ============================================================================
469
+ async listEvents(query = {}) {
470
+ const limit = clampEventLimit(query.limit);
471
+ const conditions = [];
472
+ if (query.poolId) conditions.push(eq(domainEvents.pool, query.poolId));
473
+ if (query.direction)
474
+ conditions.push(eq(domainEvents.direction, query.direction));
475
+ if (query.since) conditions.push(gte(domainEvents.occurredAt, query.since));
476
+ if (query.rootRunId) {
477
+ conditions.push(
478
+ sql2`${domainEvents.metadata}->>'rootRunId' = ${query.rootRunId}`
479
+ );
480
+ }
481
+ if (query.tenantId !== void 0) {
482
+ conditions.push(
483
+ query.tenantId === null ? sql2`${domainEvents.tenantId} is null` : eq(domainEvents.tenantId, query.tenantId)
484
+ );
485
+ }
486
+ if (query.cursor) {
487
+ const keyset = decodeEventCursor(query.cursor);
488
+ if (keyset) {
489
+ conditions.push(
490
+ or(
491
+ lt(domainEvents.occurredAt, keyset.occurredAt),
492
+ and(
493
+ eq(domainEvents.occurredAt, keyset.occurredAt),
494
+ lt(domainEvents.id, keyset.id)
495
+ )
496
+ )
497
+ );
498
+ }
499
+ }
500
+ const rows = await this.db.select().from(domainEvents).where(conditions.length > 0 ? and(...conditions) : void 0).orderBy(desc(domainEvents.occurredAt), desc(domainEvents.id)).limit(limit + 1);
501
+ const hasMore = rows.length > limit;
502
+ const page = hasMore ? rows.slice(0, limit) : rows;
503
+ const items = page.map(toEventSummary);
504
+ const last = page[page.length - 1];
505
+ const nextCursor = hasMore && last ? encodeEventCursor({ occurredAt: last.occurredAt, id: last.id }) : null;
506
+ return { items, nextCursor };
507
+ }
508
+ // ============================================================================
417
509
  // Polling
418
510
  // ============================================================================
419
511
  /**
@@ -538,6 +630,27 @@ DrizzleEventBus = __decorateClass([
538
630
 
539
631
  // runtime/subsystems/events/event-bus.memory-backend.ts
540
632
  import { Inject as Inject3, Injectable as Injectable3, Logger as Logger2, Optional as Optional2 } from "@nestjs/common";
633
+ function toEventSummary2(event) {
634
+ const metadata = event.metadata;
635
+ const str = (key) => {
636
+ const v = metadata?.[key];
637
+ return typeof v === "string" ? v : null;
638
+ };
639
+ return {
640
+ id: event.id,
641
+ type: event.type,
642
+ aggregateId: event.aggregateId,
643
+ aggregateType: event.aggregateType,
644
+ status: "processed",
645
+ pool: str("pool"),
646
+ direction: str("direction"),
647
+ tier: str("tier") ?? "domain",
648
+ rootRunId: str("rootRunId"),
649
+ tenantId: str("tenantId"),
650
+ occurredAt: event.occurredAt,
651
+ processedAt: event.occurredAt
652
+ };
653
+ }
541
654
  var MemoryEventBus = class {
542
655
  logger = new Logger2(MemoryEventBus.name);
543
656
  /** All events published since construction (or last clear). */
@@ -573,6 +686,53 @@ var MemoryEventBus = class {
573
686
  set.delete(h);
574
687
  };
575
688
  }
689
+ // ============================================================================
690
+ // IEventReadPort (OBS-LIST-1)
691
+ // ============================================================================
692
+ async listEvents(query = {}) {
693
+ const limit = clampEventLimit(query.limit);
694
+ const keyset = query.cursor ? decodeEventCursor(query.cursor) : null;
695
+ const str = (e, key) => {
696
+ const v = e.metadata?.[key];
697
+ return typeof v === "string" ? v : null;
698
+ };
699
+ const matched = this.publishedEvents.filter((e) => {
700
+ if (query.poolId && str(e, "pool") !== query.poolId) return false;
701
+ if (query.direction && str(e, "direction") !== query.direction)
702
+ return false;
703
+ if (query.rootRunId && str(e, "rootRunId") !== query.rootRunId)
704
+ return false;
705
+ if (query.since && e.occurredAt.getTime() < query.since.getTime())
706
+ return false;
707
+ if (query.tenantId !== void 0) {
708
+ const t = str(e, "tenantId");
709
+ if (query.tenantId === null) {
710
+ if (t !== null) return false;
711
+ } else if (t !== query.tenantId) {
712
+ return false;
713
+ }
714
+ }
715
+ return true;
716
+ });
717
+ matched.sort((a, b) => {
718
+ const dt = b.occurredAt.getTime() - a.occurredAt.getTime();
719
+ if (dt !== 0) return dt;
720
+ return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
721
+ });
722
+ const seeked = keyset ? matched.filter((e) => {
723
+ const ct = e.occurredAt.getTime();
724
+ const kt = keyset.occurredAt.getTime();
725
+ if (ct < kt) return true;
726
+ if (ct > kt) return false;
727
+ return e.id < keyset.id;
728
+ }) : matched;
729
+ const hasMore = seeked.length > limit;
730
+ const page = hasMore ? seeked.slice(0, limit) : seeked;
731
+ const items = page.map(toEventSummary2);
732
+ const last = page[page.length - 1];
733
+ const nextCursor = hasMore && last ? encodeEventCursor({ occurredAt: last.occurredAt, id: last.id }) : null;
734
+ return { items, nextCursor };
735
+ }
576
736
  /** Remove all published events and subscriptions. Useful in beforeEach. */
577
737
  clear() {
578
738
  this.publishedEvents.length = 0;
@@ -930,10 +1090,20 @@ var EventsModule = class {
930
1090
  REDIS_URL
931
1091
  ]
932
1092
  },
1093
+ {
1094
+ // Read port (OBS-LIST-1). Drizzle + memory backends implement
1095
+ // IEventReadPort on the EVENT_BUS instance; the redis backend
1096
+ // retains no history, so EVENT_READ_PORT resolves to `null` and
1097
+ // optional consumers (the observability combiner) degrade to
1098
+ // empty results.
1099
+ provide: EVENT_READ_PORT,
1100
+ useFactory: (options, bus) => options.backend === "redis" ? null : bus,
1101
+ inject: [EVENTS_MODULE_OPTIONS, EVENT_BUS]
1102
+ },
933
1103
  TypedEventBus,
934
1104
  { provide: TYPED_EVENT_BUS, useExisting: TypedEventBus }
935
1105
  ],
936
- exports: [EVENT_BUS, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT]
1106
+ exports: [EVENT_BUS, EVENT_READ_PORT, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT]
937
1107
  };
938
1108
  }
939
1109
  static forRoot(options = { backend: "drizzle" }) {
@@ -961,9 +1131,13 @@ var EventsModule = class {
961
1131
  providers: [
962
1132
  { provide: EVENTS_MODULE_OPTIONS, useValue: options },
963
1133
  provider,
1134
+ // Read port (OBS-LIST-1): drizzle + memory backends implement
1135
+ // IEventReadPort on the same instance as EVENT_BUS. The redis
1136
+ // backend retains no history and does not provide this token.
1137
+ { provide: EVENT_READ_PORT, useExisting: EVENT_BUS },
964
1138
  ...buildTypedBusProviders(multiTenant)
965
1139
  ],
966
- exports: [EVENT_BUS, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT]
1140
+ exports: [EVENT_BUS, EVENT_READ_PORT, TYPED_EVENT_BUS, EVENTS_MULTI_TENANT]
967
1141
  };
968
1142
  }
969
1143
  };
@@ -975,6 +1149,7 @@ export {
975
1149
  EVENTS_MODULE_OPTIONS,
976
1150
  EVENTS_MULTI_TENANT,
977
1151
  EVENT_BUS,
1152
+ EVENT_READ_PORT,
978
1153
  EventsModule,
979
1154
  MemoryEventBus,
980
1155
  MissingTenantIdError,