diff --git a/apps/api/src/lib/outboxTrigger.ts b/apps/api/src/lib/outboxTrigger.ts index ea007a2..b790d66 100644 --- a/apps/api/src/lib/outboxTrigger.ts +++ b/apps/api/src/lib/outboxTrigger.ts @@ -23,10 +23,18 @@ const emitter = new EventEmitter(); /** * Fire a drain signal after a successful outbox enqueue commit. - * Synchronous and fire-and-forget — never awaited (D-04). + * Fire-and-forget — never awaited (D-04). + * + * WR-01: the emit is deferred to a microtask so the registered listener + * (scheduleOutboxDrain) never runs on the enqueue caller's stack. EventEmitter.emit + * dispatches listeners synchronously; without this defer, any synchronous throw in + * the listener chain would propagate back into the route handler's try/catch and + * surface a misleading 503 for an enqueue that actually committed. Deferring keeps + * the documented decoupling honest: a misbehaving listener can never corrupt the + * route response. */ export function signalOutboxDrain(): void { - emitter.emit('drain'); + queueMicrotask(() => emitter.emit('drain')); } /**