/** * Transport plumbing for the {@link WorkflowEventSubscription}: the minimal * socket surface it drives, the watch-channel URL builder, the default * `WebSocket` factory (Bun vs. browser), and the frame parsing / overlap-dedup * helpers. Kept separate from the subscription state machine so each file stays * small and the pure helpers are independently testable. * * @module client/event-stream-transport */ import type { WorkflowEvent } from '../core/types.ts'; /** Minimal socket surface the subscription drives. Matches `WebSocket`. */ export type StreamSocket = { close(code?: number, reason?: string): void; addEventListener(type: 'open', listener: () => void): void; addEventListener(type: 'message', listener: (event: { data: unknown; }) => void): void; addEventListener(type: 'close', listener: () => void): void; addEventListener(type: 'error', listener: (event: unknown) => void): void; }; /** Builds a {@link StreamSocket} for a `ws(s)://…/watch` URL with headers. */ export type WebSocketFactory = (url: string, headers: Record) => StreamSocket; /** * Build the absolute `ws(s)://…/v1/workflows/:id/watch` URL for a workflow's * live event channel from a client base URL. * * An absolute `http(s)://…` base URL has its scheme swapped to `ws(s):`. A * relative base URL (`''`, `'/'`, `'/weft'` — the forms browser and * service-worker deployments use, where REST `fetch` resolves against the page * origin) is resolved against `globalThis.location` so the result is always the * absolute URL the `WebSocket` constructor requires. When no `location` is * available (e.g. a non-browser runtime given a relative base), the relative * base is returned unchanged — the same input REST `fetch` would also reject. */ export declare function workflowWatchWebSocketUrl(baseUrl: string, workflowId: string): string; /** * The default factory. Under Bun the second `WebSocket` argument is an options * bag that accepts custom upgrade headers, so auth headers ride the handshake. * Elsewhere the second argument is `protocols` (a string or string array) and * the platform `WebSocket` cannot send custom headers at all, so we construct * with the URL alone: * * - In a browser / service worker that is expected — cross-origin auth rides a * cookie or query param the server accepts, not a WebSocket header. * - In Node/edge the global `WebSocket` (Node 22+, undici) also cannot send * headers, so silently dropping a configured `Authorization`/`token` would * produce an unauthenticated socket that fails or reconnects forever against * an auth-enabled server. That is surfaced as an explicit configuration * error pointing at `HttpClientOptions.webSocketFactory` (e.g. backed by the * `ws` package, which supports headers) rather than failing silently. * * Tests that replace the factory bypass this entirely. */ export declare const defaultWebSocketFactory: WebSocketFactory; /** * Structural equality for deduping the catch-up/live overlap window. Events * carry no stable id, so a `type` + serialized-`data` comparison is the * pragmatic identity. `timestamp` is excluded because the live frame and the * persisted history record are stamped independently and can differ by a few * milliseconds for the same logical event. */ export declare function eventsEqual(a: WorkflowEvent, b: WorkflowEvent): boolean; /** * Drop the `buffered` live frames that the replayed `history` already covered * (the overlap window), returning the genuinely new frames in order. The dedup * is *consuming*: each history entry can cancel at most one live frame, so two * structurally identical events (e.g. two rapid signals with the same name and * payload) where only one is a true overlap duplicate keep the genuinely new one * instead of both being dropped. Shared by both client transports' catch-up. */ export declare function dropOverlappingLiveFrames(history: readonly WorkflowEvent[], buffered: readonly WorkflowEvent[]): WorkflowEvent[]; /** Parse a watch-channel frame into a {@link WorkflowEvent}, or null if malformed. */ export declare function parseWatchFrame(raw: unknown): WorkflowEvent | null;