New @boocode/ion package (v0.0.1) for inference optimization network. .codesight/ wiki artifacts for codebase documentation. .omo/ work plans for openspec cleanup and enhanced file panel.
214 lines
5.8 KiB
TypeScript
214 lines
5.8 KiB
TypeScript
/**
|
|
* Typed event emitter for the Ion workflow engine.
|
|
*
|
|
* Provides a singleton event bus for workflow lifecycle events.
|
|
* Supports both global and run-scoped subscriptions.
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Event types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export type WorkflowEventType =
|
|
| 'workflow_started'
|
|
| 'workflow_completed'
|
|
| 'workflow_failed'
|
|
| 'workflow_cancelled'
|
|
| 'node_started'
|
|
| 'node_completed'
|
|
| 'node_failed'
|
|
| 'node_skipped'
|
|
| 'loop_iteration_started'
|
|
| 'loop_iteration_completed'
|
|
| 'approval_pending';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Event shapes
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface WorkflowEventBase {
|
|
/** Discriminator for the event type. */
|
|
type: WorkflowEventType;
|
|
/** The workflow run id. */
|
|
runId: string;
|
|
/** The node id (when applicable). */
|
|
nodeId?: string;
|
|
/** The workflow name. */
|
|
workflowName?: string;
|
|
/** Error message (for failure events). */
|
|
error?: string;
|
|
/** Human-readable step name. */
|
|
stepName?: string;
|
|
/** Arbitrary metadata attached to the event. */
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
/** Specific shapes for strongly-typed event handling. */
|
|
export interface WorkflowStartedEvent extends WorkflowEventBase {
|
|
type: 'workflow_started';
|
|
workflowName: string;
|
|
}
|
|
|
|
export interface WorkflowCompletedEvent extends WorkflowEventBase {
|
|
type: 'workflow_completed';
|
|
workflowName: string;
|
|
}
|
|
|
|
export interface WorkflowFailedEvent extends WorkflowEventBase {
|
|
type: 'workflow_failed';
|
|
error: string;
|
|
}
|
|
|
|
export interface WorkflowCancelledEvent extends WorkflowEventBase {
|
|
type: 'workflow_cancelled';
|
|
}
|
|
|
|
export interface NodeStartedEvent extends WorkflowEventBase {
|
|
type: 'node_started';
|
|
nodeId: string;
|
|
stepName?: string;
|
|
}
|
|
|
|
export interface NodeCompletedEvent extends WorkflowEventBase {
|
|
type: 'node_completed';
|
|
nodeId: string;
|
|
stepName?: string;
|
|
}
|
|
|
|
export interface NodeFailedEvent extends WorkflowEventBase {
|
|
type: 'node_failed';
|
|
nodeId: string;
|
|
error: string;
|
|
}
|
|
|
|
export interface NodeSkippedEvent extends WorkflowEventBase {
|
|
type: 'node_skipped';
|
|
nodeId: string;
|
|
stepName?: string;
|
|
}
|
|
|
|
export interface LoopIterationStartedEvent extends WorkflowEventBase {
|
|
type: 'loop_iteration_started';
|
|
nodeId: string;
|
|
metadata?: { iteration: number };
|
|
}
|
|
|
|
export interface LoopIterationCompletedEvent extends WorkflowEventBase {
|
|
type: 'loop_iteration_completed';
|
|
nodeId: string;
|
|
metadata?: { iteration: number };
|
|
}
|
|
|
|
export interface ApprovalPendingEvent extends WorkflowEventBase {
|
|
type: 'approval_pending';
|
|
nodeId: string;
|
|
metadata?: { approver?: string; reason?: string };
|
|
}
|
|
|
|
export type WorkflowEvent =
|
|
| WorkflowStartedEvent
|
|
| WorkflowCompletedEvent
|
|
| WorkflowFailedEvent
|
|
| WorkflowCancelledEvent
|
|
| NodeStartedEvent
|
|
| NodeCompletedEvent
|
|
| NodeFailedEvent
|
|
| NodeSkippedEvent
|
|
| LoopIterationStartedEvent
|
|
| LoopIterationCompletedEvent
|
|
| ApprovalPendingEvent;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Event handler type
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export type WorkflowEventHandler = (event: WorkflowEvent) => void;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// WorkflowEventEmitter — singleton event bus
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export class WorkflowEventEmitter {
|
|
private listeners: Set<WorkflowEventHandler> = new Set();
|
|
private runListeners: Map<string, Set<WorkflowEventHandler>> = new Map();
|
|
|
|
/** Subscribe to all workflow events. */
|
|
subscribe(handler: WorkflowEventHandler): () => void {
|
|
this.listeners.add(handler);
|
|
return () => {
|
|
this.listeners.delete(handler);
|
|
};
|
|
}
|
|
|
|
/** Unsubscribe a handler from all events. */
|
|
unsubscribe(handler: WorkflowEventHandler): void {
|
|
this.listeners.delete(handler);
|
|
}
|
|
|
|
/** Emit an event to all subscribers (global + run-scoped). */
|
|
emit(event: WorkflowEvent): void {
|
|
for (const handler of this.listeners) {
|
|
try {
|
|
handler(event);
|
|
} catch {
|
|
// Swallow handler errors to prevent cascading failures.
|
|
}
|
|
}
|
|
|
|
// Also notify run-scoped listeners.
|
|
const runHandlers = this.runListeners.get(event.runId);
|
|
if (runHandlers) {
|
|
for (const handler of runHandlers) {
|
|
try {
|
|
handler(event);
|
|
} catch {
|
|
// Swallow handler errors to prevent cascading failures.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Remove all global listeners and run-scoped listeners. */
|
|
clear(): void {
|
|
this.listeners.clear();
|
|
this.runListeners.clear();
|
|
}
|
|
|
|
// -- Run-scoped subscriptions ------------------------------------------------
|
|
|
|
/** Register a run-scoped event listener. */
|
|
registerRun(runId: string, handler: WorkflowEventHandler): () => void {
|
|
let handlers = this.runListeners.get(runId);
|
|
if (!handlers) {
|
|
handlers = new Set();
|
|
this.runListeners.set(runId, handlers);
|
|
}
|
|
handlers.add(handler);
|
|
|
|
return () => {
|
|
handlers!.delete(handler);
|
|
if (handlers!.size === 0) {
|
|
this.runListeners.delete(runId);
|
|
}
|
|
};
|
|
}
|
|
|
|
/** Unregister all listeners for a specific run. */
|
|
unregisterRun(runId: string): void {
|
|
this.runListeners.delete(runId);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Singleton factory
|
|
// ---------------------------------------------------------------------------
|
|
|
|
let instance: WorkflowEventEmitter | undefined;
|
|
|
|
/** Get the singleton WorkflowEventEmitter instance. */
|
|
export function getWorkflowEventEmitter(): WorkflowEventEmitter {
|
|
if (!instance) {
|
|
instance = new WorkflowEventEmitter();
|
|
}
|
|
return instance;
|
|
} |