Files

74 lines
2.8 KiB
TypeScript

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<void> {
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,
});
}
}