import type { RequestHeaders, RouteSendOptions, RouteStream } from '../routes/types.ts'; import type { BaseStream } from './stream/base-stream.ts'; import { HTTP_STATUS_CODE_NO_CONTENT, HTTP_STATUS_CODE_SUCCESS } from '../constants.ts'; /** Shared immutable value used when a request supplies no headers. */ const EMPTY_REQUEST_HEADERS: RequestHeaders = Object.freeze({}); /** * Binds one application request to a connection-level stream. * * Request correlation remains immutable even when multiple WebSocket handlers * execute concurrently. The underlying connection remains available to * connection-level services such as the broadcaster. */ export class ApplicationRouteStream implements RouteStream { readonly headers: RequestHeaders; /** * @param connection - Shared transport stream backing this request. * @param body - Transport-decoded application payload for the route handler. * @param path - Canonical application route selected for this request. * @param requestId - Optional correlation ID for multiplexed transports. * @param headers - Transport-normalized request headers for this dispatch. */ constructor( readonly connection: BaseStream, readonly body: unknown, readonly path: string, private readonly requestId?: string, headers: RequestHeaders = EMPTY_REQUEST_HEADERS, ) { this.headers = headers === EMPTY_REQUEST_HEADERS ? headers : Object.freeze({ ...headers }); } /** Whether the underlying connection can deliver server-pushed events. */ get streaming(): boolean { return this.connection.streaming; } /** Whether the underlying connection supports later unsubscribe requests. */ get bidirectional(): boolean { return this.connection.bidirectional; } /** * Send a response or event envelope through the shared connection. * * @param data - Response body or event payload. * @param options - Controls message type and HTTP status for responses. */ async send(data: unknown, options: RouteSendOptions = {}): Promise { const type = options.type ?? 'response'; // Route responses carry an HTTP status and optional correlation ID. if (type === 'response') { await this.connection.send({ ...(this.requestId === undefined ? {} : { id: this.requestId }), type, statusCode: options.statusCode ?? (data === undefined ? HTTP_STATUS_CODE_NO_CONTENT : HTTP_STATUS_CODE_SUCCESS), body: data ?? null, }); return; } // Non-response messages are application events with a neutral envelope. await this.connection.send({ ...(this.requestId === undefined ? {} : { id: this.requestId }), type, data, }); } }