import { randomUUID } from "node:crypto"; import type { JsonObject } from "./types.js"; export type PlanningDomainEventType = "requirements_defined" | "tasks_planned"; export type ExecutionDomainEventType = "code_committed" | "task_blocked"; export type ValidationDomainEventType = "validation_passed" | "validation_failed"; export type IntegrationDomainEventType = "branch_merged"; export type DomainEventType = | PlanningDomainEventType | ExecutionDomainEventType | ValidationDomainEventType | IntegrationDomainEventType; export type DomainEventSource = "pipeline" | "actor"; export type DomainEventPayload = { summary?: string; details?: JsonObject; errorCode?: string; artifactPointer?: string; }; export type DomainEvent = { id: string; type: TType; source: DomainEventSource; sessionId: string; nodeId: string; attempt: number; timestamp: string; payload: DomainEventPayload; }; export type DomainEventEmission = { type: TType; payload?: DomainEventPayload; }; export type DomainEventHandler = ( event: DomainEvent, ) => Promise | void; const DOMAIN_EVENT_TYPES = new Set([ "requirements_defined", "tasks_planned", "code_committed", "task_blocked", "validation_passed", "validation_failed", "branch_merged", ]); export function isDomainEventType(value: string): value is DomainEventType { return DOMAIN_EVENT_TYPES.has(value as DomainEventType); } export function createDomainEvent(input: { type: DomainEventType; source: DomainEventSource; sessionId: string; nodeId: string; attempt: number; payload?: DomainEventPayload; }): DomainEvent { return { id: randomUUID(), type: input.type, source: input.source, sessionId: input.sessionId, nodeId: input.nodeId, attempt: input.attempt, timestamp: new Date().toISOString(), payload: input.payload ?? {}, }; } export class DomainEventBus { private readonly handlers = new Map>(); subscribe( type: TType, handler: DomainEventHandler, ): () => void { const typedHandler = handler as DomainEventHandler; const set = this.handlers.get(type); if (set) { set.add(typedHandler); } else { this.handlers.set(type, new Set([typedHandler])); } return () => { const current = this.handlers.get(type); current?.delete(typedHandler); if (current && current.size === 0) { this.handlers.delete(type); } }; } async publish(event: DomainEvent): Promise { const set = this.handlers.get(event.type); if (!set || set.size === 0) { return; } for (const handler of set) { await handler(event); } } }