// SSE stand-in for the fast profile. MSW 2.x does not have first-class // EventSource support (see AZ-456 Risk 3); jsdom does not ship one either. // `simulateSseStream` returns a fake EventSource that tests inject in place // of the global where production code allows it (e2e Playwright covers // SSE end-to-end where the real EventSource is exercised against the suite). export interface SseEvent { event?: string data: T id?: string } export interface FakeEventSource extends EventTarget { readyState: 0 | 1 | 2 url: string close(): void emit(e: SseEvent): void emitError(err?: Event): void } const READY_CONNECTING = 0 as const const READY_OPEN = 1 as const const READY_CLOSED = 2 as const export function createFakeEventSource(url = 'about:blank'): FakeEventSource { const target = new EventTarget() as FakeEventSource ;(target as { readyState: 0 | 1 | 2 }).readyState = READY_CONNECTING ;(target as { url: string }).url = url // Move to "open" on the next microtask so listeners attached synchronously // after construction still see the open event (matches the production // EventSource handshake behavior closely enough for assertions). queueMicrotask(() => { ;(target as { readyState: 0 | 1 | 2 }).readyState = READY_OPEN target.dispatchEvent(new Event('open')) }) target.close = () => { ;(target as { readyState: 0 | 1 | 2 }).readyState = READY_CLOSED } target.emit = (e: SseEvent) => { if (target.readyState !== READY_OPEN) return const payload = typeof e.data === 'string' ? e.data : JSON.stringify(e.data) const message = new MessageEvent(e.event ?? 'message', { data: payload, lastEventId: e.id }) target.dispatchEvent(message) } target.emitError = (err?: Event) => { target.dispatchEvent(err ?? new Event('error')) } return target } export function simulateSseStream(events: Array>): FakeEventSource { const source = createFakeEventSource() queueMicrotask(() => { for (const e of events) source.emit(e) }) return source }