@cleverbrush/server 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ActionResult.ts","../src/ProblemDetails.ts","../src/HttpError.ts","../src/RequestContext.ts","../src/safeJson.ts","../src/Server.ts","../src/ContentNegotiator.ts","../src/MiddlewarePipeline.ts","../src/ParameterResolver.ts","../src/Router.ts","../src/VirtualHttp.ts","../src/WebSocketProtocol.ts","../src/Webhook.ts"],"sourcesContent":["import type * as http from 'node:http';\nimport type { Readable } from 'node:stream';\nimport type { ContentNegotiator } from './ContentNegotiator.js';\n\n// ---------------------------------------------------------------------------\n// Base\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract base for all HTTP action results.\n *\n * Instead of writing directly to `res`, handlers return an `ActionResult`\n * instance. The server calls `executeAsync()` after the middleware pipeline\n * completes, ensuring consistent error handling and content negotiation.\n *\n * Use the static factory methods (`ActionResult.ok()`, `.created()`, etc.)\n * rather than constructing subclasses directly.\n *\n * @example\n * ```ts\n * server.handle(GetUser, ({ params }) => {\n * const user = db.find(params.id);\n * if (!user) throw new NotFoundError();\n * return ActionResult.ok(user);\n * });\n * ```\n */\nexport abstract class ActionResult {\n abstract executeAsync(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n contentNegotiator: ContentNegotiator\n ): Promise<void>;\n\n // -----------------------------------------------------------------------\n // Factory methods\n // -----------------------------------------------------------------------\n\n /** 200 OK — serializes value using content negotiation. */\n static ok<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<200, T> {\n return new JsonResult(body, 200, headers) as JsonResult<200, T>;\n }\n\n /** 201 Created — serializes value using content negotiation. */\n static created<T>(\n body: T,\n location?: string,\n headers?: Record<string, string>\n ): JsonResult<201, T> {\n const h: Record<string, string> = { ...headers };\n if (location) h['location'] = location;\n return new JsonResult(body, 201, h) as JsonResult<201, T>;\n }\n\n /** 202 Accepted — serializes value using content negotiation. */\n static accepted<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<202, T> {\n return new JsonResult(body, 202, headers) as JsonResult<202, T>;\n }\n\n /** 204 No Content. */\n static noContent(): NoContentResult {\n return new NoContentResult();\n }\n\n /** 400 Bad Request — serializes value as JSON. */\n static badRequest<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<400, T> {\n return new JsonResult(body, 400, headers) as JsonResult<400, T>;\n }\n\n /** 401 Unauthorized — serializes value as JSON. */\n static unauthorized<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<401, T> {\n return new JsonResult(body, 401, headers) as JsonResult<401, T>;\n }\n\n /** 403 Forbidden — serializes value as JSON. */\n static forbidden<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<403, T> {\n return new JsonResult(body, 403, headers) as JsonResult<403, T>;\n }\n\n /** 404 Not Found — serializes value as JSON. */\n static notFound<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<404, T> {\n return new JsonResult(body, 404, headers) as JsonResult<404, T>;\n }\n\n /** 409 Conflict — serializes value as JSON. */\n static conflict<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<409, T> {\n return new JsonResult(body, 409, headers) as JsonResult<409, T>;\n }\n\n /** Temporary (302) or permanent (301) redirect. */\n static redirect(url: string, permanent = false): RedirectResult {\n return new RedirectResult(url, permanent);\n }\n\n /**\n * Explicit JSON response with a specific status code.\n * Use the named factories (`ok`, `notFound`, etc.) for common codes.\n * This overload is an escape hatch for uncommon status codes.\n */\n static json<T>(body: T): JsonResult<200, T>;\n static json<S extends number, T>(\n body: T,\n status: S,\n headers?: Record<string, string>\n ): JsonResult<S, T>;\n static json(\n body: unknown,\n status: number = 200,\n headers?: Record<string, string>\n ): JsonResult {\n return new JsonResult(body, status, headers);\n }\n\n /** Send a file buffer as a download attachment. */\n static file(\n content: Buffer | Uint8Array,\n fileName: string,\n contentType = 'application/octet-stream'\n ): FileResult {\n return new FileResult(content, fileName, contentType);\n }\n\n /** Arbitrary string body with an explicit content type. */\n static content(\n body: string,\n contentType: string,\n status = 200\n ): ContentResult {\n return new ContentResult(body, contentType, status);\n }\n\n /** Pipe a Readable stream to the response. */\n static stream(\n readable: Readable,\n contentType: string,\n fileName?: string\n ): StreamResult {\n return new StreamResult(readable, contentType, fileName);\n }\n\n /** Bare status code with no body. */\n static status<S extends number>(\n status: S,\n headers?: Record<string, string>\n ): StatusCodeResult<S> {\n return new StatusCodeResult(status, headers) as StatusCodeResult<S>;\n }\n}\n\n// ---------------------------------------------------------------------------\n// JsonResult\n// ---------------------------------------------------------------------------\n\n/**\n * Serializes a value and writes it as JSON with `content-type: application/json`,\n * bypassing content negotiation entirely.\n *\n * Created by `ActionResult.json()`.\n * `ActionResult.ok()` and `ActionResult.created()` produce a {@link JsonResult}\n * that goes through content negotiation instead.\n */\nexport class JsonResult<\n TStatus extends number = number,\n TBody = unknown\n> extends ActionResult {\n readonly body: TBody;\n readonly status: TStatus;\n readonly headers: Record<string, string>;\n\n constructor(\n body: TBody,\n status: TStatus | number = 200,\n headers?: Record<string, string>\n ) {\n super();\n this.body = body;\n this.status = status as TStatus;\n this.headers = headers ?? {};\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n for (const [key, value] of Object.entries(this.headers)) {\n res.setHeader(key, value);\n }\n\n if (this.body === null || this.body === undefined) {\n res.writeHead(this.status);\n res.end();\n return;\n }\n\n res.writeHead(this.status, { 'content-type': 'application/json' });\n res.end(JSON.stringify(this.body));\n }\n}\n\n// ---------------------------------------------------------------------------\n// FileResult\n// ---------------------------------------------------------------------------\n\n/**\n * Sends a binary buffer as a file download attachment.\n * Created by `ActionResult.file()`.\n */\nexport class FileResult extends ActionResult {\n readonly content: Buffer | Uint8Array;\n readonly fileName: string;\n readonly contentType: string;\n\n constructor(\n content: Buffer | Uint8Array,\n fileName: string,\n contentType = 'application/octet-stream'\n ) {\n super();\n this.content = content;\n this.fileName = fileName;\n this.contentType = contentType;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(200, {\n 'content-type': this.contentType,\n 'content-disposition': `attachment; filename=\"${this.fileName}\"`,\n 'content-length': String(this.content.byteLength)\n });\n res.end(this.content);\n }\n}\n\n// ---------------------------------------------------------------------------\n// ContentResult\n// ---------------------------------------------------------------------------\n\n/**\n * Writes an arbitrary string body with a specific content type and status.\n * Created by `ActionResult.content()`.\n */\nexport class ContentResult extends ActionResult {\n readonly body: string;\n readonly contentType: string;\n readonly status: number;\n\n constructor(body: string, contentType: string, status = 200) {\n super();\n this.body = body;\n this.contentType = contentType;\n this.status = status;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(this.status, { 'content-type': this.contentType });\n res.end(this.body);\n }\n}\n\n// ---------------------------------------------------------------------------\n// StreamResult\n// ---------------------------------------------------------------------------\n\n/**\n * Pipes a `Readable` stream to the HTTP response.\n * Created by `ActionResult.stream()`.\n */\nexport class StreamResult extends ActionResult {\n readonly readable: Readable;\n readonly contentType: string;\n readonly fileName: string | undefined;\n\n constructor(readable: Readable, contentType: string, fileName?: string) {\n super();\n this.readable = readable;\n this.contentType = contentType;\n this.fileName = fileName;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n const headers: Record<string, string> = {\n 'content-type': this.contentType\n };\n if (this.fileName) {\n headers['content-disposition'] =\n `attachment; filename=\"${this.fileName}\"`;\n }\n res.writeHead(200, headers);\n\n await new Promise<void>((resolve, reject) => {\n this.readable.on('error', reject);\n res.on('error', reject);\n this.readable.on('end', resolve);\n this.readable.pipe(res, { end: true });\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// StatusCodeResult\n// ---------------------------------------------------------------------------\n\n/**\n * Responds with a bare HTTP status code and no body.\n * Created by `ActionResult.status()`.\n */\nexport class StatusCodeResult<\n TStatus extends number = number\n> extends ActionResult {\n readonly status: TStatus;\n readonly headers: Record<string, string>;\n\n constructor(status: TStatus | number, headers?: Record<string, string>) {\n super();\n this.status = status as TStatus;\n this.headers = headers ?? {};\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(this.status, this.headers);\n res.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// RedirectResult\n// ---------------------------------------------------------------------------\n\n/**\n * Redirects the client to a new URL.\n * Uses 302 (temporary) by default; pass `permanent = true` for 301.\n * Created by `ActionResult.redirect()`.\n */\nexport class RedirectResult extends ActionResult {\n readonly url: string;\n readonly permanent: boolean;\n\n constructor(url: string, permanent = false) {\n super();\n this.url = url;\n this.permanent = permanent;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(this.permanent ? 301 : 302, { location: this.url });\n res.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// NoContentResult\n// ---------------------------------------------------------------------------\n\n/**\n * Responds with 204 No Content and no body.\n * Created by `ActionResult.noContent()`.\n */\nexport class NoContentResult extends ActionResult {\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(204);\n res.end();\n }\n}\n","// ---------------------------------------------------------------------------\n// RFC 9457 Problem Details\n// ---------------------------------------------------------------------------\n\n/**\n * An RFC 9457 (formerly RFC 7807) Problem Details object.\n *\n * Provides machine-readable error information in HTTP API responses.\n * Serialized as `application/problem+json`.\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc9457 RFC 9457}\n */\nexport interface ProblemDetails {\n readonly type: string;\n readonly status: number;\n readonly title: string;\n readonly detail?: string;\n readonly instance?: string;\n readonly [extension: string]: unknown;\n}\n\nconst STATUS_TITLES: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 405: 'Method Not Allowed',\n 409: 'Conflict',\n 415: 'Unsupported Media Type',\n 422: 'Unprocessable Content',\n 500: 'Internal Server Error',\n 503: 'Service Unavailable'\n};\n\n/**\n * Create a {@link ProblemDetails} object for the given HTTP status code.\n *\n * @param status - HTTP status code (e.g. 400, 404, 500).\n * @param title - Short, human-readable summary. Defaults to a standard phrase\n * for common status codes.\n * @param detail - Longer explanation specific to this occurrence.\n * @param extensions - Extra fields merged into the object (RFC 9457 §3.1).\n */\nexport function createProblemDetails(\n status: number,\n title?: string,\n detail?: string,\n extensions?: Record<string, unknown>\n): ProblemDetails {\n return {\n type: `https://httpstatuses.com/${status}`,\n status,\n title: title ?? STATUS_TITLES[status] ?? 'Error',\n ...(detail !== undefined ? { detail } : {}),\n ...extensions\n };\n}\n\n/**\n * A single field-level validation error, used in validation Problem Details\n * responses. `pointer` follows JSON Pointer syntax (RFC 6901).\n *\n * @example `{ pointer: '/body/email', detail: 'Must be a valid email address' }`\n */\nexport interface ValidationErrorItem {\n readonly pointer: string;\n readonly detail: string;\n}\n\n/**\n * Create a 400 Bad Request Problem Details object listing all validation\n * field errors.\n *\n * @param errors - Array of per-field errors with JSON Pointer paths.\n */\nexport function createValidationProblemDetails(\n errors: readonly ValidationErrorItem[]\n): ProblemDetails {\n return createProblemDetails(\n 400,\n 'Bad Request',\n 'One or more validation errors occurred.',\n { errors }\n );\n}\n\n/**\n * Serialize a {@link ProblemDetails} object to a JSON string.\n */\nexport function serializeProblemDetails(pd: ProblemDetails): string {\n return JSON.stringify(pd);\n}\n\n/** The MIME type for Problem Details JSON responses (`application/problem+json`). */\nexport const PROBLEM_JSON_CONTENT_TYPE = 'application/problem+json';\n","import { createProblemDetails, type ProblemDetails } from './ProblemDetails.js';\n\n/**\n * Base class for HTTP errors thrown from endpoint handlers.\n *\n * Instances are automatically caught by the server and serialized as\n * RFC 9457 Problem Details (`application/problem+json`) responses.\n *\n * @example\n * ```ts\n * throw new HttpError(429, 'Too Many Requests', 'Rate limit exceeded.');\n * ```\n */\nexport class HttpError extends Error {\n readonly status: number;\n readonly title: string;\n readonly detail?: string;\n readonly extensions?: Record<string, unknown>;\n\n constructor(\n status: number,\n title?: string,\n detail?: string,\n extensions?: Record<string, unknown>\n ) {\n super(detail ?? title ?? `HTTP ${status}`);\n this.name = 'HttpError';\n this.status = status;\n this.title = title ?? `HTTP ${status}`;\n this.detail = detail;\n this.extensions = extensions;\n }\n\n /** Converts this error into an RFC 9457 {@link ProblemDetails} object. */\n toProblemDetails(): ProblemDetails {\n return createProblemDetails(\n this.status,\n this.title,\n this.detail,\n this.extensions\n );\n }\n}\n\n/** Thrown when a requested resource cannot be found. Produces a 404 response. */\nexport class NotFoundError extends HttpError {\n constructor(detail?: string) {\n super(404, 'Not Found', detail);\n this.name = 'NotFoundError';\n }\n}\n\n/** Thrown when the request is malformed or fails validation. Produces a 400 response. */\nexport class BadRequestError extends HttpError {\n constructor(detail?: string) {\n super(400, 'Bad Request', detail);\n this.name = 'BadRequestError';\n }\n}\n\n/** Thrown when the request lacks valid authentication credentials. Produces a 401 response. */\nexport class UnauthorizedError extends HttpError {\n constructor(detail?: string) {\n super(401, 'Unauthorized', detail);\n this.name = 'UnauthorizedError';\n }\n}\n\n/** Thrown when the authenticated principal lacks permission. Produces a 403 response. */\nexport class ForbiddenError extends HttpError {\n constructor(detail?: string) {\n super(403, 'Forbidden', detail);\n this.name = 'ForbiddenError';\n }\n}\n\n/** Thrown when the request conflicts with the current state of the resource. Produces a 409 response. */\nexport class ConflictError extends HttpError {\n constructor(detail?: string) {\n super(409, 'Conflict', detail);\n this.name = 'ConflictError';\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport type { IServiceProvider } from '@cleverbrush/di';\nimport {\n any,\n boolean,\n func,\n object,\n promise,\n record,\n string\n} from '@cleverbrush/schema';\nimport { HttpError } from './HttpError.js';\nimport { checkJsonDepth, safeJsonParse } from './safeJson.js';\n\n/**\n * IRequestContext — the schema definition for the request context.\n * Serves as both a DI key and a type definition.\n */\nexport const IRequestContext = object({\n method: string(),\n url: string(),\n pathParams: record(string(), string()),\n queryParams: record(string(), string()),\n headers: record(string(), string()),\n items: any(),\n body: func().hasReturnType(promise(any())),\n json: func().hasReturnType(promise(any())),\n responded: boolean()\n});\n\n/**\n * Per-request context object passed to every middleware and endpoint handler.\n *\n * Provides typed access to path/query parameters, headers, the request body,\n * and the DI service provider for the current request scope.\n *\n * @example\n * ```ts\n * const middleware: Middleware = async (ctx, next) => {\n * ctx.items.set('startTime', Date.now());\n * await next();\n * };\n * ```\n */\n/** Default maximum request body size: 5 MB. */\nexport const DEFAULT_MAX_BODY_SIZE = 5 * 1024 * 1024;\n\nexport class RequestContext {\n readonly request: IncomingMessage;\n readonly response: ServerResponse;\n readonly url: URL;\n readonly method: string;\n readonly headers: Record<string, string>;\n readonly items: Map<string, unknown> = new Map();\n readonly maxBodySize: number;\n\n #pathParams: Record<string, string> = {};\n /** @internal — overridable for testing */\n _queryParams?: Record<string, string>;\n #services?: IServiceProvider;\n #bodyBuffer: Buffer | null = null;\n #bodyRead = false;\n #jsonCache: unknown = undefined;\n #jsonParsed = false;\n responded = false;\n\n /**\n * The authenticated principal for this request.\n * Set by authentication middleware; typed as `unknown` at the\n * RequestContext level — handlers receive a fully typed version\n * via `ActionContext.principal`.\n */\n principal: unknown = undefined;\n\n constructor(\n request: IncomingMessage,\n response: ServerResponse,\n maxBodySize?: number\n ) {\n this.request = request;\n this.response = response;\n this.method = (request.method ?? 'GET').toUpperCase();\n this.maxBodySize = maxBodySize ?? DEFAULT_MAX_BODY_SIZE;\n\n // Parse URL — use a placeholder host for relative URLs\n const rawUrl = request.url ?? '/';\n this.url = new URL(\n rawUrl,\n `http://${request.headers.host ?? 'localhost'}`\n );\n\n // Build headers record (lowercased keys, string values)\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(request.headers)) {\n if (typeof value === 'string') {\n headers[key] = value;\n } else if (Array.isArray(value)) {\n headers[key] = value.join(', ');\n }\n }\n this.headers = headers;\n }\n\n /** Path parameters extracted from the matched route template. */\n get pathParams(): Record<string, string> {\n return this.#pathParams;\n }\n\n set pathParams(value: Record<string, string>) {\n this.#pathParams = value;\n }\n\n /** Parsed query string parameters from the request URL. */\n get queryParams(): Record<string, string> {\n if (this._queryParams) return this._queryParams;\n const params: Record<string, string> = {};\n for (const [key, value] of this.url.searchParams) {\n params[key] = value;\n }\n return params;\n }\n\n /** The DI service provider scoped to this request. Set by the server before invoking the handler. */\n get services(): IServiceProvider | undefined {\n return this.#services;\n }\n\n set services(value: IServiceProvider) {\n this.#services = value;\n }\n\n /** Read and buffer the raw request body. Result is cached after the first call. */\n async body(): Promise<Buffer> {\n if (this.#bodyRead) return this.#bodyBuffer!;\n\n this.#bodyBuffer = await new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n this.request.on('data', (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > this.maxBodySize) {\n this.request.destroy();\n reject(new HttpError(413, 'Payload Too Large'));\n return;\n }\n chunks.push(chunk);\n });\n this.request.on('end', () => resolve(Buffer.concat(chunks)));\n this.request.on('error', reject);\n });\n this.#bodyRead = true;\n return this.#bodyBuffer;\n }\n\n /** Read, buffer, and JSON-parse the request body. Result is cached after the first call. */\n async json(): Promise<unknown> {\n if (this.#jsonParsed) return this.#jsonCache;\n\n const buf = await this.body();\n const text = buf.toString('utf-8');\n if (text.length > 0) {\n this.#jsonCache = safeJsonParse(text);\n checkJsonDepth(this.#jsonCache);\n }\n this.#jsonParsed = true;\n return this.#jsonCache;\n }\n}\n","/**\n * Safe JSON utilities to prevent prototype pollution and excessive nesting.\n *\n * @module\n * @internal\n */\n\n/** Default maximum nesting depth for parsed JSON objects. */\nexport const MAX_JSON_DEPTH = 64;\n\n/**\n * Parse a JSON string while stripping dangerous keys (`__proto__`,\n * `constructor`) that could lead to prototype pollution.\n *\n * Uses a `JSON.parse` reviver to remove polluting keys during parsing,\n * which is more efficient than a post-parse walk.\n *\n * @throws {SyntaxError} If `raw` is not valid JSON.\n */\nexport function safeJsonParse(raw: string): unknown {\n return JSON.parse(raw, (key, value) => {\n if (key === '__proto__' || key === 'constructor') {\n return undefined;\n }\n return value;\n });\n}\n\n/**\n * Walk a parsed JSON value and throw if the nesting depth exceeds\n * `maxDepth`. Must be called after parsing.\n *\n * Only objects and arrays contribute to depth; primitives do not.\n *\n * @throws {Error} When nesting exceeds `maxDepth`.\n */\nexport function checkJsonDepth(\n value: unknown,\n maxDepth: number = MAX_JSON_DEPTH\n): void {\n walk(value, 0, maxDepth);\n}\n\nfunction walk(value: unknown, current: number, max: number): void {\n if (value === null || typeof value !== 'object') return;\n if (current >= max) {\n throw new Error(`JSON nesting depth exceeds maximum of ${max}`);\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n walk(item, current + 1, max);\n }\n } else {\n for (const v of Object.values(value as Record<string, unknown>)) {\n walk(v, current + 1, max);\n }\n }\n}\n","import * as http from 'node:http';\nimport * as https from 'node:https';\nimport type { Duplex } from 'node:stream';\nimport type {\n AuthenticationContext,\n AuthenticationScheme,\n AuthorizationPolicy\n} from '@cleverbrush/auth';\nimport {\n AuthorizationService,\n PolicyBuilder,\n Principal,\n parseCookies,\n requireRole\n} from '@cleverbrush/auth';\nimport { ServiceCollection, type ServiceProvider } from '@cleverbrush/di';\nimport { type WebSocket, WebSocketServer } from 'ws';\nimport { ActionResult, JsonResult } from './ActionResult.js';\nimport { ContentNegotiator } from './ContentNegotiator.js';\nimport type { EndpointBuilder, Handler, HandlerMapping } from './Endpoint.js';\nimport { HttpError } from './HttpError.js';\nimport { MiddlewarePipeline } from './MiddlewarePipeline.js';\nimport { needsBody, resolveArgs } from './ParameterResolver.js';\nimport {\n createProblemDetails,\n PROBLEM_JSON_CONTENT_TYPE,\n serializeProblemDetails\n} from './ProblemDetails.js';\nimport { DEFAULT_MAX_BODY_SIZE, RequestContext } from './RequestContext.js';\nimport { Router } from './Router.js';\nimport type { SubscriptionMetadata } from './Subscription.js';\nimport { isTrackedEvent } from './Subscription.js';\nimport { checkJsonDepth, safeJsonParse } from './safeJson.js';\nimport type {\n ContentTypeHandler,\n EndpointRegistration,\n Middleware,\n ServerBatchingOptions,\n ServerOptions,\n SubscriptionRegistration\n} from './types.js';\nimport {\n VirtualIncomingMessage,\n VirtualServerResponse\n} from './VirtualHttp.js';\nimport type { WebhookDefinition } from './Webhook.js';\nimport {\n errorFrame,\n messageFrame,\n parseClientFrame,\n pongFrame,\n trackedFrame\n} from './WebSocketProtocol.js';\n\n// ---------------------------------------------------------------------------\n// Authentication / Authorization Config Types\n// ---------------------------------------------------------------------------\n\n/**\n * Authentication configuration passed to `ServerBuilder.useAuthentication()`.\n *\n * At least one scheme must be listed. The `defaultScheme` name must match\n * one of the registered scheme `name` values — it is used when no specific\n * scheme is requested.\n */\nexport interface AuthenticationConfig {\n /** Name of the default scheme to use (must match a scheme's `name`). */\n defaultScheme: string;\n /** Registered authentication schemes. */\n schemes: AuthenticationScheme<any>[];\n}\n\n/**\n * Authorization configuration passed to `ServerBuilder.useAuthorization()`.\n *\n * Named policies can be referenced by string in future `authorize('policy-name')`\n * calls (currently resolved at startup time).\n */\nexport interface AuthorizationConfig {\n /** Named policies (looked up by `authorize('policy-name')` — future use). */\n policies?: Record<string, (builder: PolicyBuilder) => void>;\n}\n\n/**\n * Fluent builder for constructing and starting an HTTP server.\n *\n * @example\n * ```ts\n * const server = new ServerBuilder();\n *\n * server\n * .services(svc => svc.addSingleton(IDb, () => new Db()))\n * .use(loggingMiddleware)\n * .handle(GetUser, ({ params }) => db.find(params.id));\n *\n * await server.listen(3000);\n * ```\n */\nexport class ServerBuilder {\n readonly #serviceCollection = new ServiceCollection();\n readonly #registrations: EndpointRegistration[] = [];\n readonly #subscriptionRegistrations: SubscriptionRegistration[] = [];\n readonly #webhooks: WebhookDefinition[] = [];\n readonly #globalMiddlewares: Middleware[] = [];\n readonly #contentNegotiator = new ContentNegotiator();\n #options: ServerOptions = {};\n #authConfig: AuthenticationConfig | null = null;\n #authzConfig: AuthorizationConfig | null = null;\n #healthcheck = false;\n #batchConfig: ServerBatchingOptions | null = null;\n\n /**\n * Configure the DI service collection.\n *\n * @param configureFn - Receives the `ServiceCollection` for registrations.\n */\n services(configureFn: (svc: ServiceCollection) => void): this {\n configureFn(this.#serviceCollection);\n return this;\n }\n\n /**\n * Add a global middleware that runs for every request.\n * Middleware is executed in the order it is added.\n */\n use(middleware: Middleware): this {\n this.#globalMiddlewares.push(middleware);\n return this;\n }\n\n /**\n * Register an additional content type handler for content negotiation.\n * JSON is registered by default.\n */\n contentType(handler: ContentTypeHandler): this {\n this.#contentNegotiator.register(handler);\n return this;\n }\n\n /**\n * Enable authentication with one or more schemes.\n * Registers a global middleware that authenticates every request and\n * sets `ctx.principal`.\n */\n useAuthentication(config: AuthenticationConfig): this {\n this.#authConfig = config;\n return this;\n }\n\n /**\n * Enable authorization enforcement.\n * Registers a global middleware that checks endpoint `authorize()`\n * metadata against the authenticated principal.\n * Must be called after `useAuthentication()`.\n */\n useAuthorization(config?: AuthorizationConfig): this {\n this.#authzConfig = config ?? {};\n return this;\n }\n\n /**\n * Enable the `GET /health` endpoint that returns `{ ok: true }` (200).\n * Useful for load balancer and container readiness probes.\n */\n withHealthcheck(): this {\n this.#healthcheck = true;\n return this;\n }\n\n /**\n * Enable the server-side request batching endpoint.\n *\n * Once enabled, the server accepts `POST <path>` (default `/__batch`)\n * containing an array of sub-requests and processes each one through the\n * full middleware and handler pipeline, returning an array of\n * sub-responses in a single HTTP reply.\n *\n * Pair this with the `batching()` middleware from `@cleverbrush/client/batching`\n * on the client side.\n *\n * @param options - {@link ServerBatchingOptions} (all fields optional).\n *\n * @example\n * ```ts\n * new ServerBuilder()\n * .useBatching()\n * .handleAll(mapping)\n * .listen(3000);\n * ```\n */\n useBatching(options: ServerBatchingOptions = {}): this {\n this.#batchConfig = options;\n return this;\n }\n\n /**\n * Register an endpoint and its handler.\n *\n * @param endpointDef - An `EndpointBuilder` instance (e.g. from `endpoint.get(...)`).\n * @param handler - The typed handler function.\n * @param options - Optional per-endpoint middleware.\n */\n handle<\n E extends EndpointBuilder<any, any, any, any, any, any, any, any, any>\n >(\n endpointDef: E,\n handler: Handler<E>,\n options?: { middlewares?: Middleware[] }\n ): this {\n this.#registrations.push({\n endpoint: endpointDef.introspect(),\n handler,\n middlewares: options?.middlewares\n });\n return this;\n }\n\n /**\n * Register all endpoints from a {@link HandlerMapping} created by\n * {@link mapHandlers}. This is the bulk equivalent of calling\n * `.handle()` for each endpoint individually.\n *\n * @param mapping - The mapping produced by `mapHandlers(endpoints, handlers)`.\n */\n handleAll(mapping: HandlerMapping): this {\n for (const entry of mapping._entries) {\n this.#registrations.push({\n endpoint: entry.endpoint.introspect(),\n handler: entry.handler,\n middlewares: entry.middlewares\n });\n }\n for (const entry of mapping._subscriptions) {\n this.#subscriptionRegistrations.push({\n endpoint: entry.endpoint.introspect(),\n handler: entry.handler,\n middlewares: entry.middlewares\n });\n }\n return this;\n }\n\n /**\n * Returns a snapshot of all registered endpoints.\n * Useful for generating OpenAPI specs or other documentation.\n */\n getRegistrations(): readonly EndpointRegistration[] {\n return [...this.#registrations];\n }\n\n /**\n * Returns a snapshot of all registered WebSocket subscription endpoints.\n * Consumed by `@cleverbrush/server-openapi` to emit the AsyncAPI spec.\n */\n getSubscriptionRegistrations(): readonly SubscriptionRegistration[] {\n return [...this.#subscriptionRegistrations];\n }\n\n /**\n * Register a webhook definition.\n *\n * Webhooks are recorded for OpenAPI spec generation only — they are not\n * served as HTTP routes by the runtime server.\n *\n * @param def - A {@link WebhookDefinition} created with {@link defineWebhook}.\n */\n webhook(def: WebhookDefinition): this {\n this.#webhooks.push(def);\n return this;\n }\n\n /**\n * Returns a snapshot of all registered webhook definitions.\n * Consumed by `@cleverbrush/server-openapi` to emit the `webhooks` map.\n */\n getWebhooks(): readonly WebhookDefinition[] {\n return [...this.#webhooks];\n }\n\n /**\n * Returns the authentication configuration, or `null` if\n * `useAuthentication()` has not been called.\n */\n getAuthenticationConfig(): AuthenticationConfig | null {\n return this.#authConfig;\n }\n\n /**\n * Start listening on the given port and host. Resolves with the running\n * {@link Server} instance.\n *\n * @param port - TCP port (default: `ServerOptions.port ?? 3000`).\n * @param host - Bind address (default: `ServerOptions.host ?? '0.0.0.0'`).\n */\n async listen(port?: number, host?: string): Promise<Server> {\n const router = new Router();\n\n for (const reg of this.#registrations) {\n router.addRoute(reg);\n }\n for (const reg of this.#subscriptionRegistrations) {\n router.addSubscriptionRoute(reg);\n }\n\n const serviceProvider = this.#serviceCollection.buildServiceProvider({\n validateScopes: false\n });\n\n // Build auth middleware stack\n const authMiddlewares: Middleware[] = [];\n\n if (this.#authConfig) {\n authMiddlewares.push(\n createAuthenticationMiddleware(this.#authConfig)\n );\n }\n\n if (this.#authzConfig !== null) {\n const policies = new Map<string, AuthorizationPolicy>();\n if (this.#authzConfig.policies) {\n for (const [name, configureFn] of Object.entries(\n this.#authzConfig.policies\n )) {\n const builder = new PolicyBuilder();\n configureFn(builder);\n policies.set(name, builder.build(name));\n }\n }\n const authzService = new AuthorizationService(policies);\n authMiddlewares.push(\n createAuthorizationMiddleware(authzService, this.#authConfig)\n );\n }\n\n // Auth middlewares go before user-registered global middlewares\n const allMiddlewares = [...authMiddlewares, ...this.#globalMiddlewares];\n\n const server = new Server(\n router,\n serviceProvider,\n this.#contentNegotiator,\n allMiddlewares,\n this.#healthcheck,\n this.#subscriptionRegistrations.length > 0,\n this.#batchConfig,\n this.#options.maxBodySize\n );\n\n const listenPort = port ?? this.#options.port ?? 3000;\n const listenHost = host ?? this.#options.host ?? '0.0.0.0';\n\n await server.start(listenPort, listenHost, this.#options);\n return server;\n }\n}\n\n/**\n * The running HTTP/HTTPS server instance returned by `ServerBuilder.listen()`.\n *\n * Use `close()` to gracefully shut down the server.\n */\n/** Maximum number of queued incoming WebSocket messages before the connection is closed. */\nconst MAX_WS_QUEUE_SIZE = 1024;\n\nexport class Server {\n readonly #router: Router;\n readonly #serviceProvider: ServiceProvider;\n readonly #contentNegotiator: ContentNegotiator;\n readonly #globalMiddlewares: Middleware[];\n readonly #healthcheck: boolean;\n readonly #hasSubscriptions: boolean;\n readonly #batchConfig: ServerBatchingOptions | null;\n readonly #maxBodySize: number;\n #httpServer: http.Server | https.Server | null = null;\n #wss: WebSocketServer | null = null;\n readonly #activeConnections: Set<WebSocket> = new Set();\n\n constructor(\n router: Router,\n serviceProvider: ServiceProvider,\n contentNegotiator: ContentNegotiator,\n globalMiddlewares: Middleware[],\n healthcheck = false,\n hasSubscriptions = false,\n batchConfig: ServerBatchingOptions | null = null,\n maxBodySize: number = DEFAULT_MAX_BODY_SIZE\n ) {\n this.#router = router;\n this.#serviceProvider = serviceProvider;\n this.#contentNegotiator = contentNegotiator;\n this.#globalMiddlewares = globalMiddlewares;\n this.#healthcheck = healthcheck;\n this.#hasSubscriptions = hasSubscriptions;\n this.#batchConfig = batchConfig;\n this.#maxBodySize = maxBodySize;\n }\n\n /**\n * Start listening. Called internally by `ServerBuilder.listen()` after\n * the server is fully configured.\n */\n async start(\n port: number,\n host: string,\n options: ServerOptions\n ): Promise<void> {\n const handler = (\n req: http.IncomingMessage,\n res: http.ServerResponse\n ) => {\n this.#handleRequest(req, res).catch((_err: unknown) => {\n if (!res.headersSent) {\n res.writeHead(500, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(createProblemDetails(500)));\n }\n });\n };\n\n if (options.https) {\n this.#httpServer = https.createServer(\n { key: options.https.key, cert: options.https.cert },\n handler\n );\n } else {\n this.#httpServer = http.createServer(handler);\n }\n\n await new Promise<void>(resolve => {\n this.#httpServer!.listen(port, host, resolve);\n });\n\n // Set up WebSocket server if subscriptions are registered\n if (this.#hasSubscriptions) {\n this.#wss = new WebSocketServer({\n noServer: true,\n maxPayload: this.#maxBodySize\n });\n\n this.#httpServer!.on(\n 'upgrade',\n (req: http.IncomingMessage, socket: Duplex, head: Buffer) => {\n const urlPath = new URL(\n req.url ?? '/',\n `http://${req.headers.host ?? 'localhost'}`\n ).pathname;\n\n const result = this.#router.matchSubscription(urlPath);\n if (!result) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n this.#wss!.handleUpgrade(req, socket, head, ws => {\n this.#handleWebSocket(\n ws,\n req,\n result.registration,\n result.parsedPath\n );\n });\n }\n );\n }\n }\n\n /** Gracefully stop the server and free the TCP port. */\n async close(): Promise<void> {\n // Close all active WebSocket connections\n for (const ws of this.#activeConnections) {\n ws.close(1001, 'Server shutting down');\n }\n this.#activeConnections.clear();\n\n // Close the WebSocket server\n if (this.#wss) {\n await new Promise<void>((resolve, reject) => {\n this.#wss!.close((err?: Error) => {\n if (err) reject(err);\n else resolve();\n });\n });\n this.#wss = null;\n }\n\n if (!this.#httpServer) return;\n await new Promise<void>((resolve, reject) => {\n this.#httpServer!.close((err: Error | undefined) => {\n if (err) reject(err);\n else resolve();\n });\n });\n this.#httpServer = null;\n }\n\n /**\n * The bound address after `listen()` resolves.\n * Returns `null` if the server has been closed or not yet started.\n */\n get address(): { port: number; host: string } | null {\n const addr = this.#httpServer?.address();\n if (!addr || typeof addr === 'string') return null;\n return { port: addr.port, host: addr.address };\n }\n\n async #handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse\n ): Promise<void> {\n const scope = this.#serviceProvider.createScope();\n\n try {\n const ctx = new RequestContext(req, res, this.#maxBodySize);\n const urlPath = ctx.url.pathname;\n const method = ctx.method;\n\n if (\n this.#healthcheck &&\n method === 'GET' &&\n urlPath === '/health'\n ) {\n res.writeHead(200);\n res.end();\n return;\n }\n\n // Batch endpoint — handled before routing and auth.\n if (\n this.#batchConfig !== null &&\n method === 'POST' &&\n urlPath === (this.#batchConfig.path ?? '/__batch')\n ) {\n await this.#handleBatchRequest(req, res);\n return;\n }\n\n const routeResult = this.#router.match(method, urlPath);\n\n if (!routeResult.match) {\n if (routeResult.badRequest) {\n const pd = createProblemDetails(400);\n res.writeHead(400, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n if (routeResult.methodNotAllowed) {\n const pd = createProblemDetails(405, 'Method Not Allowed');\n res.writeHead(405, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE,\n allow: routeResult.allowedMethods!.join(', ')\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n const pd = createProblemDetails(404);\n res.writeHead(404, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n const { registration, parsedPath } = routeResult.match;\n const meta = registration.endpoint;\n\n // Set path params on context (raw string form for middleware)\n if (parsedPath) {\n const rawParams: Record<string, string> = {};\n flattenToStrings(parsedPath, '', rawParams);\n ctx.pathParams = rawParams;\n }\n\n ctx.services = scope.serviceProvider;\n\n // Store endpoint metadata for authorization middleware\n ctx.items.set('__endpoint_meta', meta);\n\n // Build middleware pipeline\n const pipeline = new MiddlewarePipeline();\n for (const mw of this.#globalMiddlewares) {\n pipeline.add(mw);\n }\n if (registration.middlewares) {\n for (const mw of registration.middlewares) {\n pipeline.add(mw);\n }\n }\n\n await pipeline.execute(ctx, async () => {\n if (ctx.responded) return;\n\n // Parse body if needed\n let parsedBody: unknown;\n if (needsBody(meta)) {\n const contentType = req.headers['content-type'];\n const ctHandler =\n this.#contentNegotiator.selectRequestHandler(\n contentType\n );\n if (ctHandler) {\n const rawBody = await ctx.body();\n const bodyText = rawBody.toString('utf-8');\n if (bodyText.length > 0) {\n try {\n parsedBody = ctHandler.deserialize(bodyText);\n } catch {\n const pd = createProblemDetails(\n 400,\n 'Malformed request body'\n );\n res.writeHead(400, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n }\n } else if (contentType) {\n const pd = createProblemDetails(415);\n res.writeHead(415, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n }\n\n // Resolve parameters\n const resolveResult = await resolveArgs(\n meta,\n parsedPath,\n ctx,\n parsedBody\n );\n if (!resolveResult.valid) {\n res.writeHead(400, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(\n serializeProblemDetails(resolveResult.problemDetails)\n );\n ctx.responded = true;\n return;\n }\n\n // Call handler\n let result = registration.handler(...resolveResult.args);\n if (result instanceof Promise) {\n result = await result;\n }\n\n if (ctx.responded) return;\n await this.#sendResult(req, res, result);\n ctx.responded = true;\n });\n } catch (err) {\n if (res.headersSent) return;\n\n if (err instanceof HttpError) {\n const pd = err.toProblemDetails();\n res.writeHead(pd.status, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n } else {\n console.error('[server] Unhandled error:', err);\n const pd = createProblemDetails(500);\n res.writeHead(500, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n }\n } finally {\n try {\n await scope.asyncDispose();\n } catch {\n // Swallow disposal errors\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Batch request handler\n // -----------------------------------------------------------------------\n\n async #handleBatchRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse\n ): Promise<void> {\n const config = this.#batchConfig!;\n const maxSize = config.maxSize ?? 20;\n const parallel = config.parallel ?? true;\n\n // Read the outer body.\n let outerBody: { requests: Array<BatchSubRequest> };\n try {\n const raw = await readBuffer(req, this.#maxBodySize);\n const parsed = safeJsonParse(raw.toString('utf-8'));\n checkJsonDepth(parsed);\n outerBody = parsed as {\n requests: Array<BatchSubRequest>;\n };\n if (!Array.isArray(outerBody?.requests)) {\n throw new Error('requests must be an array');\n }\n } catch (err) {\n if (err instanceof HttpError && err.status === 413) {\n const pd = createProblemDetails(413, 'Payload Too Large');\n res.writeHead(413, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n const pd = createProblemDetails(400, 'Invalid batch request body');\n res.writeHead(400, { 'content-type': PROBLEM_JSON_CONTENT_TYPE });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n if (outerBody.requests.length > maxSize) {\n const pd = createProblemDetails(\n 400,\n `Batch size ${outerBody.requests.length} exceeds maximum of ${maxSize}`\n );\n res.writeHead(400, { 'content-type': PROBLEM_JSON_CONTENT_TYPE });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n const execute = async (\n item: BatchSubRequest\n ): Promise<BatchSubResponse> => {\n const virtualReq = new VirtualIncomingMessage({\n method: (item.method ?? 'GET').toUpperCase(),\n url: item.url,\n headers: item.headers ?? {},\n body: item.body\n });\n const virtualRes = new VirtualServerResponse();\n\n await this.#handleRequest(\n virtualReq as unknown as http.IncomingMessage,\n virtualRes as unknown as http.ServerResponse\n );\n\n return virtualRes.toResult();\n };\n\n let results: BatchSubResponse[];\n if (parallel) {\n results = await Promise.all(outerBody.requests.map(execute));\n } else {\n results = [];\n for (const item of outerBody.requests) {\n results.push(await execute(item));\n }\n }\n\n res.writeHead(200, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ responses: results }));\n }\n\n async #sendResult(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n result: unknown\n ): Promise<void> {\n if (result instanceof ActionResult) {\n await result.executeAsync(req, res, this.#contentNegotiator);\n } else if (result === null || result === undefined) {\n res.writeHead(204);\n res.end();\n } else {\n await new JsonResult(result, 200).executeAsync(\n req,\n res,\n this.#contentNegotiator\n );\n }\n }\n\n // -----------------------------------------------------------------------\n // WebSocket subscription handler\n // -----------------------------------------------------------------------\n\n #handleWebSocket(\n ws: WebSocket,\n req: http.IncomingMessage,\n registration: SubscriptionRegistration,\n parsedPath: Record<string, any> | null\n ): void {\n this.#activeConnections.add(ws);\n const scope = this.#serviceProvider.createScope();\n const abortController = new AbortController();\n const meta = registration.endpoint;\n\n // Build RequestContext for middleware / auth\n const dummyRes = new http.ServerResponse(req);\n const ctx = new RequestContext(req, dummyRes);\n if (parsedPath) {\n const rawParams: Record<string, string> = {};\n flattenToStrings(parsedPath, '', rawParams);\n ctx.pathParams = rawParams;\n }\n ctx.services = scope.serviceProvider;\n ctx.items.set('__endpoint_meta', meta);\n\n // Authenticate via the same auth middleware pipeline\n const authPipeline = new MiddlewarePipeline();\n for (const mw of this.#globalMiddlewares) {\n authPipeline.add(mw);\n }\n if (registration.middlewares) {\n for (const mw of registration.middlewares) {\n authPipeline.add(mw);\n }\n }\n\n authPipeline\n .execute(ctx, async () => {\n if (ctx.responded) {\n // Middleware rejected the request\n ws.close(1008, 'Unauthorized');\n return;\n }\n\n this.#runSubscription(\n ws,\n ctx,\n meta,\n registration.handler,\n parsedPath,\n scope,\n abortController\n );\n })\n .catch(() => {\n ws.close(1011, 'Internal Server Error');\n });\n\n ws.on('close', () => {\n this.#activeConnections.delete(ws);\n abortController.abort();\n scope.asyncDispose().catch(() => {});\n });\n\n ws.on('error', () => {\n this.#activeConnections.delete(ws);\n abortController.abort();\n scope.asyncDispose().catch(() => {});\n });\n }\n\n #runSubscription(\n ws: WebSocket,\n ctx: RequestContext,\n meta: SubscriptionMetadata,\n handler: (...args: any[]) => any,\n parsedPath: Record<string, any> | null,\n scope: {\n serviceProvider: import('@cleverbrush/di').IServiceProvider;\n asyncDispose(): Promise<void>;\n },\n abortController: AbortController\n ): void {\n // Build incoming async iterable from client messages\n const incomingQueue: unknown[] = [];\n let incomingResolve: (() => void) | null = null;\n let incomingDone = false;\n\n const incoming: AsyncIterable<unknown> = {\n [Symbol.asyncIterator]() {\n return {\n next(): Promise<IteratorResult<unknown>> {\n if (incomingQueue.length > 0) {\n return Promise.resolve({\n value: incomingQueue.shift()!,\n done: false\n });\n }\n if (incomingDone) {\n return Promise.resolve({\n value: undefined,\n done: true\n });\n }\n return new Promise(resolve => {\n incomingResolve = () => {\n incomingResolve = null;\n if (incomingQueue.length > 0) {\n resolve({\n value: incomingQueue.shift()!,\n done: false\n });\n } else {\n resolve({ value: undefined, done: true });\n }\n };\n });\n },\n return(): Promise<IteratorResult<unknown>> {\n incomingDone = true;\n return Promise.resolve({\n value: undefined,\n done: true\n });\n }\n };\n }\n };\n\n // Handle incoming WebSocket messages\n ws.on('message', (raw: Buffer | string) => {\n const text = typeof raw === 'string' ? raw : raw.toString('utf-8');\n const frame = parseClientFrame(text);\n\n if (!frame) {\n ws.send(\n JSON.stringify(errorFrame(400, 'Invalid frame format'))\n );\n return;\n }\n\n if (frame.type === 'ping') {\n ws.send(JSON.stringify(pongFrame()));\n return;\n }\n\n // Enforce queue size limit to prevent memory exhaustion\n if (incomingQueue.length >= MAX_WS_QUEUE_SIZE) {\n ws.send(\n JSON.stringify(\n errorFrame(\n 429,\n 'Message queue full — slow down or reconnect'\n )\n )\n );\n ws.close(1008, 'Message queue overflow');\n return;\n }\n\n // frame.type === 'message'\n if (meta.incomingSchema) {\n const result = meta.incomingSchema.validate(frame.data);\n if (!result.valid) {\n const errors = (result.errors ?? [])\n .map((e: { message: string }) => e.message)\n .join('; ');\n ws.send(\n JSON.stringify(\n errorFrame(422, `Validation failed: ${errors}`)\n )\n );\n return;\n }\n incomingQueue.push(result.object);\n } else {\n incomingQueue.push(frame.data);\n }\n\n if (incomingResolve) incomingResolve();\n });\n\n // On close, finish the incoming stream\n ws.on('close', () => {\n incomingDone = true;\n if (incomingResolve) incomingResolve();\n });\n\n // Build subscription context\n const subscriptionCtx: Record<string, unknown> = {\n context: ctx,\n signal: abortController.signal\n };\n\n if (parsedPath && Object.keys(parsedPath).length > 0) {\n // Validate path params through the path schema if present\n subscriptionCtx.params = parsedPath;\n }\n\n // Parse query params\n if (meta.querySchema) {\n const queryObj: Record<string, string> = {};\n for (const [k, v] of ctx.url.searchParams.entries()) {\n queryObj[k] = v;\n }\n const result = meta.querySchema.validate(queryObj);\n if (!result.valid) {\n const errors = (result.errors ?? [])\n .map((e: { message: string }) => e.message)\n .join('; ');\n ws.close(1002, `Query validation failed: ${errors}`);\n return;\n }\n subscriptionCtx.query = result.object;\n }\n\n // Parse headers\n if (meta.headerSchema) {\n const result = meta.headerSchema.validate(ctx.headers);\n if (!result.valid) {\n const errors = (result.errors ?? [])\n .map((e: { message: string }) => e.message)\n .join('; ');\n ws.close(1002, `Header validation failed: ${errors}`);\n return;\n }\n subscriptionCtx.headers = result.object;\n }\n\n // Set principal if auth was performed\n if (ctx.principal !== undefined) {\n subscriptionCtx.principal = ctx.principal;\n }\n\n // Add incoming iterable if there's an incoming schema (or just the raw iterable)\n if (meta.incomingSchema) {\n subscriptionCtx.incoming = incoming;\n } else {\n subscriptionCtx.incoming = incoming;\n }\n\n // Resolve DI services\n const handlerArgs: unknown[] = [subscriptionCtx];\n if (meta.serviceSchemas) {\n const services: Record<string, unknown> = {};\n for (const [key, schema] of Object.entries(meta.serviceSchemas)) {\n services[key] = scope.serviceProvider.get(schema);\n }\n handlerArgs.push(services);\n }\n\n // Run the async generator\n (async () => {\n try {\n const generator = handler(...handlerArgs);\n for await (const value of generator) {\n if (ws.readyState !== ws.OPEN) break;\n\n let frameToSend: string;\n if (isTrackedEvent(value)) {\n const outData = meta.outgoingSchema\n ? meta.outgoingSchema.validate(value.data)\n : { valid: true, object: value.data };\n\n if (!(outData as any).valid) {\n ws.send(\n JSON.stringify(\n errorFrame(\n 500,\n 'Outgoing validation failed'\n )\n )\n );\n continue;\n }\n frameToSend = JSON.stringify(\n trackedFrame(value.id, (outData as any).object)\n );\n } else {\n const outData = meta.outgoingSchema\n ? meta.outgoingSchema.validate(value)\n : { valid: true, object: value };\n\n if (!(outData as any).valid) {\n ws.send(\n JSON.stringify(\n errorFrame(\n 500,\n 'Outgoing validation failed'\n )\n )\n );\n continue;\n }\n frameToSend = JSON.stringify(\n messageFrame((outData as any).object)\n );\n }\n\n ws.send(frameToSend);\n }\n } catch (err) {\n if (ws.readyState === ws.OPEN) {\n // Never leak raw error messages to clients\n if (err instanceof Error) {\n console.error(\n '[server] Subscription handler error:',\n err\n );\n }\n ws.send(JSON.stringify(errorFrame(500, 'Internal error')));\n ws.close(1011, 'Handler error');\n }\n }\n })();\n }\n}\n\nexport function createServer(options?: ServerOptions): ServerBuilder {\n const builder = new ServerBuilder();\n if (options) {\n (builder as any).__options = options;\n }\n return builder;\n}\n\n// ---------------------------------------------------------------------------\n// Batch helpers\n// ---------------------------------------------------------------------------\n\n/** A single sub-request within a batch body. */\ninterface BatchSubRequest {\n method: string;\n /** Path + query string, e.g. `/api/todos?page=1`. */\n url: string;\n headers?: Record<string, string>;\n /** Raw JSON-serialised body string. Absent for GET/HEAD/DELETE. */\n body?: string;\n}\n\n/** A single sub-response within the batch reply. */\ninterface BatchSubResponse {\n status: number;\n headers: Record<string, string>;\n body: string;\n}\n\n/** Reads the entire body of an `IncomingMessage` into a `Buffer`. */\nfunction readBuffer(\n req: http.IncomingMessage,\n maxSize: number = DEFAULT_MAX_BODY_SIZE\n): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n req.on('data', (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > maxSize) {\n req.destroy();\n reject(new HttpError(413, 'Payload Too Large'));\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => resolve(Buffer.concat(chunks)));\n req.on('error', reject);\n });\n}\n\n/** Flatten a nested object to a flat Record<string, string> for raw pathParams */\nfunction flattenToStrings(\n obj: Record<string, any>,\n prefix: string,\n result: Record<string, string>\n): void {\n for (const [key, value] of Object.entries(obj)) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n flattenToStrings(value, fullKey, result);\n } else {\n result[fullKey] = String(value);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Authentication Middleware\n// ---------------------------------------------------------------------------\n\nfunction createAuthenticationMiddleware(\n config: AuthenticationConfig\n): Middleware {\n const schemeMap = new Map<string, AuthenticationScheme<any>>();\n for (const scheme of config.schemes) {\n schemeMap.set(scheme.name, scheme);\n }\n\n return async (ctx, next) => {\n const scheme = schemeMap.get(config.defaultScheme);\n if (!scheme) {\n // No matching scheme — leave principal as anonymous\n ctx.principal = Principal.anonymous();\n await next();\n return;\n }\n\n // Build transport-agnostic auth context\n const authCtx: AuthenticationContext = {\n headers: ctx.headers,\n cookies: parseCookies(ctx.headers['cookie'] ?? ''),\n items: ctx.items\n };\n\n const result = await scheme.authenticate(authCtx);\n\n if (result.succeeded) {\n ctx.principal = result.principal;\n } else {\n ctx.principal = Principal.anonymous();\n }\n\n await next();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Authorization Middleware\n// ---------------------------------------------------------------------------\n\nfunction createAuthorizationMiddleware(\n authzService: AuthorizationService,\n authConfig: AuthenticationConfig | null\n): Middleware {\n // Collect challenge headers from schemes for 401 responses\n const challengeHeaders: Record<string, string> = {};\n if (authConfig) {\n for (const scheme of authConfig.schemes) {\n if (scheme.challenge) {\n const ch = scheme.challenge();\n challengeHeaders[ch.headerName.toLowerCase()] = ch.headerValue;\n }\n }\n }\n\n return async (ctx, next) => {\n const meta = ctx.items.get('__endpoint_meta') as\n | import('./Endpoint.js').EndpointMetadata\n | undefined;\n\n // No auth metadata or authRoles is null → public endpoint\n if (!meta || meta.authRoles === null) {\n await next();\n return;\n }\n\n // Endpoint requires auth — check principal\n const principal = ctx.principal;\n\n if (\n !principal ||\n !(principal instanceof Principal) ||\n !principal.isAuthenticated\n ) {\n // 401 Unauthorized\n const pd = createProblemDetails(401, 'Unauthorized');\n const headers: Record<string, string> = {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE,\n ...challengeHeaders\n };\n ctx.response.writeHead(401, headers);\n ctx.response.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n\n // If roles are specified, check them\n if (meta.authRoles.length > 0) {\n const result = await authzService.authorize(principal, [\n requireRole(...meta.authRoles)\n ]);\n if (!result.allowed) {\n const pd = createProblemDetails(403, 'Forbidden');\n ctx.response.writeHead(403, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n ctx.response.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n }\n\n // For typed handler access — set the principal value\n if (principal instanceof Principal) {\n ctx.principal = principal.value;\n }\n\n await next();\n };\n}\n","import { checkJsonDepth, safeJsonParse } from './safeJson.js';\nimport type { ContentTypeHandler } from './types.js';\n\nconst JSON_HANDLER: ContentTypeHandler = {\n mimeType: 'application/json',\n serialize(value: unknown): string {\n return JSON.stringify(value);\n },\n deserialize(raw: string): unknown {\n const parsed = safeJsonParse(raw);\n checkJsonDepth(parsed);\n return parsed;\n }\n};\n\ninterface ParsedAccept {\n mimeType: string;\n quality: number;\n}\n\nfunction parseAcceptHeader(accept: string): ParsedAccept[] {\n return accept\n .split(',')\n .map(part => {\n const trimmed = part.trim();\n const [mimeType, ...params] = trimmed.split(';').map(s => s.trim());\n let quality = 1;\n for (const p of params) {\n const [key, val] = p.split('=');\n if (key?.trim() === 'q' && val) {\n quality = parseFloat(val);\n if (Number.isNaN(quality)) quality = 1;\n }\n }\n return { mimeType: mimeType.toLowerCase(), quality };\n })\n .sort((a, b) => b.quality - a.quality);\n}\n\n/**\n * Selects the appropriate serializer/deserializer for a request or response\n * based on the `Accept` / `Content-Type` HTTP headers.\n *\n * JSON is registered by default. Additional handlers can be added with\n * `register()` or via `ServerBuilder.contentType()`.\n */\nexport class ContentNegotiator {\n readonly #handlers: Map<string, ContentTypeHandler> = new Map();\n\n constructor() {\n this.register(JSON_HANDLER);\n }\n\n /**\n * Register a new content type handler.\n * If a handler for the same MIME type was already registered it is replaced.\n */\n register(handler: ContentTypeHandler): void {\n this.#handlers.set(handler.mimeType.toLowerCase(), handler);\n }\n\n /**\n * Select the best response serializer for the given `Accept` header value.\n *\n * Returns `null` if no registered handler can satisfy the request;\n * the server will respond with 406 Not Acceptable in that case.\n */\n selectResponseHandler(acceptHeader?: string): ContentTypeHandler | null {\n if (!acceptHeader)\n return this.#handlers.get('application/json') ?? null;\n\n const parsed = parseAcceptHeader(acceptHeader);\n for (const { mimeType } of parsed) {\n if (mimeType === '*/*') {\n return this.#handlers.get('application/json') ?? null;\n }\n const handler = this.#handlers.get(mimeType);\n if (handler) return handler;\n }\n\n return null;\n }\n\n /**\n * Select the deserializer for an incoming `Content-Type` header.\n *\n * Returns `null` if the content type is not recognised; the server will\n * respond with 415 Unsupported Media Type in that case.\n */\n selectRequestHandler(\n contentTypeHeader?: string\n ): ContentTypeHandler | null {\n if (!contentTypeHeader) return null;\n\n // Extract mime type (ignore charset, boundary, etc.)\n const mimeType = contentTypeHeader.split(';')[0].trim().toLowerCase();\n return this.#handlers.get(mimeType) ?? null;\n }\n}\n","import type { RequestContext } from './RequestContext.js';\nimport type { Middleware } from './types.js';\n\n/**\n * Executes a chain of {@link Middleware} functions in order, then invokes\n * a final handler when `next()` is called by every middleware in the chain.\n *\n * Middleware can short-circuit the chain by not calling `next()`.\n */\nexport class MiddlewarePipeline {\n readonly #middlewares: Middleware[] = [];\n\n /** Append a middleware to the end of the pipeline. */\n add(middleware: Middleware): void {\n this.#middlewares.push(middleware);\n }\n\n /**\n * Execute the pipeline with the given `context`, calling each middleware\n * in order and finally invoking `finalHandler`.\n */\n async execute(\n context: RequestContext,\n finalHandler: () => Promise<void>\n ): Promise<void> {\n let index = 0;\n\n const next = async (): Promise<void> => {\n if (index < this.#middlewares.length) {\n const middleware = this.#middlewares[index++];\n await middleware(context, next);\n } else {\n await finalHandler();\n }\n };\n\n await next();\n }\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport type { EndpointMetadata } from './Endpoint.js';\nimport type { ProblemDetails, ValidationErrorItem } from './ProblemDetails.js';\nimport { createValidationProblemDetails } from './ProblemDetails.js';\nimport type { RequestContext } from './RequestContext.js';\n\n/**\n * Result returned by `resolveArgs()`. When `valid` is `false` the\n * `problemDetails` payload should be sent as a 400 response.\n */\nexport type ResolveResult =\n | { valid: true; args: unknown[] }\n | { valid: false; problemDetails: ProblemDetails };\n\n/**\n * Returns true if the endpoint declares a body schema.\n */\nexport function needsBody(meta: EndpointMetadata): boolean {\n return meta.bodySchema != null;\n}\n\n/**\n * Resolve the action context object for an endpoint-based handler.\n *\n * Builds `{ context, params?, body?, query?, headers? }` based on\n * what the endpoint declares.\n */\nexport async function resolveArgs(\n meta: EndpointMetadata,\n parsedPath: Record<string, any> | null,\n context: RequestContext,\n parsedBody: unknown\n): Promise<ResolveResult> {\n const errors: ValidationErrorItem[] = [];\n const contextObj: Record<string, unknown> = {};\n\n // Always provide context\n contextObj.context = context;\n\n // Principal — from authentication middleware (if endpoint requires auth)\n if (meta.authRoles !== null && context.principal !== undefined) {\n contextObj.principal = context.principal;\n }\n\n // Params — from parsed path (already validated by ParseStringSchemaBuilder)\n if (parsedPath && Object.keys(parsedPath).length > 0) {\n contextObj.params = parsedPath;\n }\n\n // Body — validate against endpoint's body schema\n if (meta.bodySchema) {\n const result = await meta.bodySchema.validateAsync(parsedBody, {\n doNotStopOnFirstError: true\n });\n if (result.valid) {\n contextObj.body = result.object;\n } else {\n const getInvalidProperties =\n typeof (result as any).getInvalidProperties === 'function'\n ? ((result as any)\n .getInvalidProperties as () => ReadonlyArray<{\n errors: ReadonlyArray<string>;\n descriptor: { toJsonPointer: () => string };\n }>)\n : null;\n\n let errorsAdded = false;\n if (getInvalidProperties) {\n for (const prop of getInvalidProperties()) {\n const pointer = prop.descriptor.toJsonPointer();\n for (const msg of prop.errors) {\n errors.push({\n pointer: `/body${pointer}`,\n detail: msg\n });\n errorsAdded = true;\n }\n }\n }\n // Fallback: required-check failures surface in result.errors\n // but not in the property descriptor map (e.g. null body)\n if (!errorsAdded) {\n for (const err of result.errors ?? []) {\n errors.push({ pointer: '/body', detail: err.message });\n }\n }\n }\n }\n\n // Query — validate against endpoint's query schema\n if (meta.querySchema) {\n const queryIntro = meta.querySchema.introspect() as any;\n if (queryIntro.type !== 'object' || !queryIntro.properties) {\n throw new Error(\n 'Endpoint query schema must be an object schema whose properties map to query parameter names.'\n );\n }\n const queryProps = queryIntro.properties as Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n >;\n const queryObj: Record<string, unknown> = {};\n for (const [qName, qSchema] of Object.entries(queryProps)) {\n const raw = context.queryParams[qName];\n const result = await qSchema.validateAsync(raw, {\n doNotStopOnFirstError: true\n });\n if (result.valid) {\n queryObj[qName] = result.object;\n } else {\n for (const err of result.errors ?? []) {\n errors.push({\n pointer: `/query/${qName}`,\n detail: err.message\n });\n }\n }\n }\n contextObj.query = queryObj;\n }\n\n // Headers — validate against endpoint's header schema\n if (meta.headerSchema) {\n const headersIntro = meta.headerSchema.introspect() as any;\n if (headersIntro.type !== 'object' || !headersIntro.properties) {\n throw new Error(\n 'Endpoint headers schema must be an object schema whose properties map to header names.'\n );\n }\n const headerProps = headersIntro.properties as Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n >;\n const headersObj: Record<string, unknown> = {};\n for (const [hName, hSchema] of Object.entries(headerProps)) {\n const raw = context.headers[hName.toLowerCase()];\n const result = await hSchema.validateAsync(raw, {\n doNotStopOnFirstError: true\n });\n if (result.valid) {\n headersObj[hName] = result.object;\n } else {\n for (const err of result.errors ?? []) {\n errors.push({\n pointer: `/headers/${hName}`,\n detail: err.message\n });\n }\n }\n }\n contextObj.headers = headersObj;\n }\n\n if (errors.length > 0) {\n return {\n valid: false,\n problemDetails: createValidationProblemDetails(errors)\n };\n }\n\n // Services — resolve declared dependencies from the DI container\n if (meta.serviceSchemas) {\n if (!context.services) {\n throw new Error(\n 'Endpoint declares .inject() dependencies but no service provider is available. ' +\n 'Register services via createServer().services() before handling this endpoint.'\n );\n }\n const servicesObj: Record<string, unknown> = {};\n for (const [name, schema] of Object.entries(meta.serviceSchemas)) {\n servicesObj[name] = context.services.get(schema);\n }\n return { valid: true, args: [contextObj, servicesObj] };\n }\n\n return { valid: true, args: [contextObj] };\n}\n","import type { ParseStringSchemaBuilder } from '@cleverbrush/schema';\nimport type {\n EndpointRegistration,\n RouteMatch,\n SubscriptionRegistration\n} from './types.js';\n\ninterface RegisteredRoute {\n readonly basePath: string;\n readonly routePath:\n | string\n | ParseStringSchemaBuilder<any, any, any, any, any>;\n readonly registration: EndpointRegistration;\n}\n\ninterface RegisteredSubscriptionRoute {\n readonly basePath: string;\n readonly routePath:\n | string\n | ParseStringSchemaBuilder<any, any, any, any, any>;\n readonly registration: SubscriptionRegistration;\n}\n\nfunction normalizePath(p: string): string {\n // Use decodeURI (not decodeURIComponent) so that reserved characters such\n // as %2F (encoded slash) are kept encoded and do not alter path segmentation.\n // Throws URIError on malformed percent-encoding – callers that process\n // untrusted input (e.g. match()) must catch that and return a 400.\n const decoded = decodeURI(p);\n if (decoded.length > 1 && decoded.endsWith('/')) {\n return decoded.slice(0, -1);\n }\n return decoded;\n}\n\nfunction isParseStringSchema(\n p: string | ParseStringSchemaBuilder<any, any, any, any, any>\n): p is ParseStringSchemaBuilder<any, any, any, any, any> {\n return typeof p !== 'string' && typeof (p as any).validate === 'function';\n}\n\n/**\n * Radix-style HTTP router that maps method + path to endpoint registrations.\n *\n * Both static string paths (exact-match only) and `ParseStringSchemaBuilder`\n * typed path templates are supported. For dynamic path parameters use\n * `route()` / `parseString()` templates rather than colon-param strings.\n */\nexport class Router {\n readonly #routes: Map<string, RegisteredRoute[]> = new Map();\n readonly #subscriptionRoutes: RegisteredSubscriptionRoute[] = [];\n\n /**\n * Register an endpoint with the router.\n */\n addRoute(registration: EndpointRegistration): void {\n const { method, basePath, pathTemplate } = registration.endpoint;\n const upperMethod = method.toUpperCase();\n const normalizedBase = normalizePath(basePath);\n\n const route: RegisteredRoute = {\n basePath: normalizedBase,\n routePath: pathTemplate,\n registration\n };\n\n if (!this.#routes.has(upperMethod)) {\n this.#routes.set(upperMethod, []);\n }\n this.#routes.get(upperMethod)!.push(route);\n }\n\n /**\n * Match an incoming HTTP method and URL to a registered endpoint.\n *\n * Returns:\n * - `{ match }` — a successful match with parsed path parameters.\n * - `{ match: null, methodNotAllowed: true, allowedMethods }` — path matches\n * but the method does not (405 Method Not Allowed).\n * - `{ match: null, methodNotAllowed: false }` — no match at all (404).\n * - `{ match: null, methodNotAllowed: false, badRequest: true }` — the URL\n * contains malformed percent-encoding (caller should respond with 400).\n */\n match(\n method: string,\n url: string\n ): {\n match: RouteMatch | null;\n methodNotAllowed: boolean;\n badRequest?: boolean;\n allowedMethods?: string[];\n } {\n let normalized: string;\n try {\n normalized = normalizePath(url);\n } catch {\n // URIError from decodeURI – malformed percent-encoding in the URL\n return { match: null, methodNotAllowed: false, badRequest: true };\n }\n const upperMethod = method.toUpperCase();\n\n // Try exact method match first\n const methodRoutes = this.#routes.get(upperMethod);\n if (methodRoutes) {\n for (const route of methodRoutes) {\n const result = this.#tryMatch(route, normalized);\n if (result) return { match: result, methodNotAllowed: false };\n }\n }\n\n // Check if any other method matches this path (405 detection)\n const allowedMethods: string[] = [];\n for (const [m, routes] of this.#routes) {\n if (m === upperMethod) continue;\n for (const route of routes) {\n if (this.#tryMatch(route, normalized)) {\n allowedMethods.push(m);\n break;\n }\n }\n }\n\n if (allowedMethods.length > 0) {\n return { match: null, methodNotAllowed: true, allowedMethods };\n }\n\n return { match: null, methodNotAllowed: false };\n }\n\n #tryMatch(\n route: RegisteredRoute,\n normalizedUrl: string\n ): RouteMatch | null {\n const { basePath, routePath } = route;\n\n // Check basePath prefix\n if (basePath && !normalizedUrl.startsWith(basePath)) {\n return null;\n }\n\n const remainder = basePath\n ? normalizedUrl.slice(basePath.length)\n : normalizedUrl;\n\n if (isParseStringSchema(routePath)) {\n // Dynamic route: validate remainder via parseString schema\n const result = routePath.validate(remainder);\n if (result.valid) {\n return {\n registration: route.registration,\n parsedPath: result.object as Record<string, any>\n };\n }\n return null;\n }\n\n // Static route: exact match\n const normalizedRoutePath = normalizePath(routePath);\n const normalizedRemainder = remainder.length === 0 ? '/' : remainder;\n\n if (normalizedRemainder === normalizedRoutePath) {\n return {\n registration: route.registration,\n parsedPath: null\n };\n }\n\n return null;\n }\n\n // -----------------------------------------------------------------------\n // Subscription routing\n // -----------------------------------------------------------------------\n\n /**\n * Register a subscription endpoint with the router.\n */\n addSubscriptionRoute(registration: SubscriptionRegistration): void {\n const { basePath, pathTemplate } = registration.endpoint;\n const normalizedBase = normalizePath(basePath);\n\n this.#subscriptionRoutes.push({\n basePath: normalizedBase,\n routePath: pathTemplate,\n registration\n });\n }\n\n /**\n * Match an incoming WebSocket upgrade URL to a registered subscription.\n *\n * Returns the matched registration and parsed path params, or `null`.\n */\n matchSubscription(url: string): {\n registration: SubscriptionRegistration;\n parsedPath: Record<string, any> | null;\n } | null {\n let normalized: string;\n try {\n normalized = normalizePath(url);\n } catch {\n return null;\n }\n\n for (const route of this.#subscriptionRoutes) {\n const { basePath, routePath } = route;\n\n if (basePath && !normalized.startsWith(basePath)) {\n continue;\n }\n\n const remainder = basePath\n ? normalized.slice(basePath.length)\n : normalized;\n\n if (isParseStringSchema(routePath)) {\n const result = routePath.validate(remainder);\n if (result.valid) {\n return {\n registration: route.registration,\n parsedPath: result.object as Record<string, any>\n };\n }\n continue;\n }\n\n const normalizedRoutePath = normalizePath(routePath);\n const normalizedRemainder =\n remainder.length === 0 ? '/' : remainder;\n\n if (normalizedRemainder === normalizedRoutePath) {\n return {\n registration: route.registration,\n parsedPath: null\n };\n }\n }\n\n return null;\n }\n}\n","/**\n * Lightweight virtual HTTP request/response objects used by the batch\n * endpoint handler to process sub-requests through the normal server pipeline\n * without spawning additional HTTP connections.\n *\n * @internal\n */\n\nimport { Readable, Writable } from 'node:stream';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Initialisation options for a {@link VirtualIncomingMessage}.\n */\nexport interface VirtualRequestInit {\n /** HTTP method, e.g. `'GET'` or `'POST'`. */\n method: string;\n /**\n * URL path and optional query string, e.g. `/api/todos?page=1`.\n * The value is passed verbatim to `RequestContext` as `request.url`.\n */\n url: string;\n /** Request headers, typically forwarded from the outer batch request. */\n headers?: Record<string, string>;\n /**\n * Raw body string (the JSON-serialised body that would have been sent as\n * the HTTP body). Absent for methods that carry no body.\n */\n body?: string;\n}\n\n/**\n * The captured result of a virtualised HTTP response.\n */\nexport interface VirtualResult {\n status: number;\n headers: Record<string, string>;\n /** Raw response body (JSON string or plain text). */\n body: string;\n}\n\n// ---------------------------------------------------------------------------\n// VirtualIncomingMessage\n// ---------------------------------------------------------------------------\n\n/**\n * A `Readable` that mimics the subset of `http.IncomingMessage` consumed\n * by `RequestContext` and `Server.#handleRequest()`.\n *\n * When pushed to, it emits the body buffer and then signals EOF.\n */\nexport class VirtualIncomingMessage extends Readable {\n readonly method: string;\n readonly url: string;\n readonly headers: Record<string, string>;\n // Satisfy the `socket` property that IncomingMessage exposes.\n readonly socket: null = null;\n\n readonly #body: Buffer;\n #pushed = false;\n\n constructor(init: VirtualRequestInit) {\n super();\n this.method = init.method;\n this.url = init.url;\n // Ensure a `host` header is present so that RequestContext can parse\n // the URL correctly (it uses `http://${req.headers.host}` as the base).\n // Lowercase all keys to match Node.js http.IncomingMessage behaviour,\n // which normalises header names to lower-case before exposing them.\n const lowercased: Record<string, string> = {};\n for (const [key, value] of Object.entries(init.headers ?? {})) {\n lowercased[key.toLowerCase()] = value;\n }\n this.headers = { host: 'localhost', ...lowercased };\n this.#body =\n init.body != null && init.body.length > 0\n ? Buffer.from(init.body, 'utf-8')\n : Buffer.alloc(0);\n }\n\n override _read(): void {\n if (!this.#pushed) {\n this.#pushed = true;\n if (this.#body.length > 0) {\n this.push(this.#body);\n }\n this.push(null); // EOF\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// VirtualServerResponse\n// ---------------------------------------------------------------------------\n\n/**\n * A `Writable` that captures the subset of `http.ServerResponse` calls made\n * by `Server.#handleRequest()` and `ActionResult.executeAsync()`.\n *\n * After the handler finishes, call {@link toResult} to retrieve the status\n * code, headers, and body as plain values.\n */\nexport class VirtualServerResponse extends Writable {\n statusCode = 200;\n headersSent = false;\n\n readonly #chunks: Buffer[] = [];\n readonly #customHeaders: Record<string, string> = {};\n #customStatus = 200;\n\n // -----------------------------------------------------------------------\n // Writable interface — captures data written via readable.pipe(res)\n // -----------------------------------------------------------------------\n\n override _write(\n chunk: Buffer | string,\n _encoding: BufferEncoding,\n callback: (err?: Error | null) => void\n ): void {\n this.#chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n callback();\n }\n\n // -----------------------------------------------------------------------\n // http.ServerResponse surface\n // -----------------------------------------------------------------------\n\n /**\n * Sets the status code and optional response headers.\n * Mirrors `ServerResponse.writeHead()`.\n */\n writeHead(\n status: number,\n headers?: Record<string, string | string[] | number> | string | string[]\n ): this {\n this.#customStatus = status;\n this.statusCode = status;\n if (\n headers != null &&\n typeof headers === 'object' &&\n !Array.isArray(headers)\n ) {\n for (const [k, v] of Object.entries(\n headers as Record<string, string | string[] | number>\n )) {\n this.#customHeaders[k.toLowerCase()] = Array.isArray(v)\n ? v.join(', ')\n : String(v);\n }\n }\n this.headersSent = true;\n return this;\n }\n\n /**\n * Sets a single response header.\n * Mirrors `ServerResponse.setHeader()`.\n */\n setHeader(name: string, value: string | number | string[]): this {\n this.#customHeaders[name.toLowerCase()] = Array.isArray(value)\n ? value.join(', ')\n : String(value);\n return this;\n }\n\n /**\n * Returns a previously set response header.\n * Mirrors `ServerResponse.getHeader()`.\n */\n getHeader(name: string): string | undefined {\n return this.#customHeaders[name.toLowerCase()];\n }\n\n /**\n * Captures the body and signals the end of the response.\n *\n * Handles the three call forms used by `#handleRequest` and\n * `ActionResult.executeAsync()`:\n * - `end()` — no body\n * - `end(data)` — body is a `string`, `Buffer`, or `Uint8Array`\n * - `end(callback)` — end with callback (body already captured via pipe)\n */\n override end(chunk?: unknown, ...rest: unknown[]): this {\n if (chunk != null && typeof chunk !== 'function') {\n const buf =\n Buffer.isBuffer(chunk) || chunk instanceof Uint8Array\n ? Buffer.from(chunk as Uint8Array)\n : Buffer.from(String(chunk), 'utf-8');\n this.#chunks.push(buf);\n // Call super.end() without the chunk (we already captured it).\n super.end(...(rest as []));\n } else if (typeof chunk === 'function') {\n super.end(chunk);\n } else {\n super.end(...(rest as []));\n }\n return this;\n }\n\n // -----------------------------------------------------------------------\n // Result extraction\n // -----------------------------------------------------------------------\n\n /**\n * Returns the captured status, headers, and body as a plain object\n * suitable for embedding in a batch response.\n */\n toResult(): VirtualResult {\n return {\n status: this.#customStatus,\n headers: { ...this.#customHeaders },\n body: Buffer.concat(this.#chunks).toString('utf-8')\n };\n }\n}\n","/**\n * WebSocket framing protocol for subscription endpoints.\n *\n * Client→Server:\n * ```json\n * { \"type\": \"message\", \"data\": <incoming payload> }\n * { \"type\": \"ping\" }\n * ```\n *\n * Server→Client:\n * ```json\n * { \"type\": \"message\", \"data\": <outgoing payload> }\n * { \"type\": \"tracked\", \"id\": \"<string>\", \"data\": <outgoing payload> }\n * { \"type\": \"pong\" }\n * { \"type\": \"error\", \"code\": <number>, \"message\": \"<string>\" }\n * ```\n *\n * @module\n * @internal\n */\n\nimport { checkJsonDepth, safeJsonParse } from './safeJson.js';\n\n// ---------------------------------------------------------------------------\n// Client → Server frame types\n// ---------------------------------------------------------------------------\n\nexport interface ClientMessageFrame {\n readonly type: 'message';\n readonly data: unknown;\n}\n\nexport interface ClientPingFrame {\n readonly type: 'ping';\n}\n\nexport type ClientFrame = ClientMessageFrame | ClientPingFrame;\n\n// ---------------------------------------------------------------------------\n// Server → Client frame types\n// ---------------------------------------------------------------------------\n\nexport interface ServerMessageFrame {\n readonly type: 'message';\n readonly data: unknown;\n}\n\nexport interface ServerTrackedFrame {\n readonly type: 'tracked';\n readonly id: string;\n readonly data: unknown;\n}\n\nexport interface ServerPongFrame {\n readonly type: 'pong';\n}\n\nexport interface ServerErrorFrame {\n readonly type: 'error';\n readonly code: number;\n readonly message: string;\n}\n\nexport type ServerFrame =\n | ServerMessageFrame\n | ServerTrackedFrame\n | ServerPongFrame\n | ServerErrorFrame;\n\n// ---------------------------------------------------------------------------\n// Frame constructors\n// ---------------------------------------------------------------------------\n\nexport function messageFrame(data: unknown): ServerMessageFrame {\n return { type: 'message', data };\n}\n\nexport function trackedFrame(id: string, data: unknown): ServerTrackedFrame {\n return { type: 'tracked', id, data };\n}\n\nexport function pongFrame(): ServerPongFrame {\n return { type: 'pong' };\n}\n\nexport function errorFrame(code: number, message: string): ServerErrorFrame {\n return { type: 'error', code, message };\n}\n\n// ---------------------------------------------------------------------------\n// Client frame parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a raw WebSocket text message into a typed client frame.\n * Returns `null` if the message is not valid JSON or not a known frame type.\n */\nexport function parseClientFrame(raw: string): ClientFrame | null {\n let parsed: unknown;\n try {\n parsed = safeJsonParse(raw);\n checkJsonDepth(parsed);\n } catch {\n return null;\n }\n\n if (typeof parsed !== 'object' || parsed === null) return null;\n\n const obj = parsed as Record<string, unknown>;\n if (obj.type === 'ping') return { type: 'ping' };\n if (obj.type === 'message' && 'data' in obj) {\n return { type: 'message', data: obj.data };\n }\n\n return null;\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\n\n/**\n * Describes an out-of-band webhook that your API can send to consumers.\n *\n * Pass instances to `ServerBuilder.webhook()` so that\n * `@cleverbrush/server-openapi` can emit them inside the `webhooks` map of\n * the generated OpenAPI document.\n *\n * @example\n * ```ts\n * const userCreatedWebhook = defineWebhook('userCreated', {\n * method: 'POST',\n * summary: 'Fired when a new user is created',\n * body: object({ id: number(), email: string() }),\n * });\n * ```\n */\nexport interface WebhookDefinition {\n /** Unique webhook name used as the key in the `webhooks` map. */\n readonly name: string;\n /** HTTP method sent to the consumer endpoint (default: `'POST'`). */\n readonly method?: string;\n /** Short summary for OpenAPI documentation. */\n readonly summary?: string;\n /** Longer description for OpenAPI documentation. Supports Markdown. */\n readonly description?: string;\n /** Tags to group this webhook in generated documentation. */\n readonly tags?: readonly string[];\n /** Schema describing the webhook request payload. */\n readonly body?: SchemaBuilder<any, any, any, any, any>;\n /** Schema describing the expected response from the consumer. */\n readonly response?: SchemaBuilder<any, any, any, any, any>;\n}\n\n/**\n * Convenience factory for creating {@link WebhookDefinition} objects.\n *\n * @param name - Unique key for this webhook in the `webhooks` map.\n * @param options - Webhook configuration (all fields except `name`).\n */\nexport function defineWebhook(\n name: string,\n options: Omit<WebhookDefinition, 'name'>\n): WebhookDefinition {\n return { name, ...options };\n}\n"],"mappings":"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","names":["ActionResult","body","headers","JsonResult","location","h","NoContentResult","url","permanent","RedirectResult","status","content","fileName","contentType","FileResult","ContentResult","readable","StreamResult","StatusCodeResult","_req","res","_contentNegotiator","key","value","resolve","reject","STATUS_TITLES","createProblemDetails","status","title","detail","extensions","createValidationProblemDetails","errors","serializeProblemDetails","pd","PROBLEM_JSON_CONTENT_TYPE","HttpError","status","title","detail","extensions","createProblemDetails","NotFoundError","BadRequestError","UnauthorizedError","ForbiddenError","ConflictError","URL","any","boolean","func","object","promise","record","string","safeJsonParse","raw","key","value","checkJsonDepth","maxDepth","walk","current","max","item","v","IRequestContext","object","string","record","any","func","promise","boolean","DEFAULT_MAX_BODY_SIZE","RequestContext","#pathParams","#services","#bodyBuffer","#bodyRead","#jsonCache","#jsonParsed","request","response","maxBodySize","rawUrl","URL","headers","key","value","params","resolve","reject","chunks","totalSize","chunk","HttpError","text","safeJsonParse","checkJsonDepth","http","https","AuthorizationService","PolicyBuilder","Principal","parseCookies","requireRole","ServiceCollection","WebSocketServer","JSON_HANDLER","value","raw","parsed","safeJsonParse","checkJsonDepth","parseAcceptHeader","accept","part","trimmed","mimeType","params","s","quality","p","key","val","a","b","ContentNegotiator","#handlers","handler","acceptHeader","contentTypeHeader","MiddlewarePipeline","#middlewares","middleware","context","finalHandler","index","next","needsBody","meta","resolveArgs","parsedPath","context","parsedBody","errors","contextObj","result","getInvalidProperties","errorsAdded","prop","pointer","msg","err","queryIntro","queryProps","queryObj","qName","qSchema","raw","headersIntro","headerProps","headersObj","hName","hSchema","createValidationProblemDetails","servicesObj","name","schema","normalizePath","p","decoded","isParseStringSchema","Router","#routes","#subscriptionRoutes","registration","method","basePath","pathTemplate","upperMethod","route","url","normalized","methodRoutes","result","#tryMatch","allowedMethods","m","routes","normalizedUrl","routePath","remainder","normalizedRoutePath","normalizedBase","Readable","Writable","VirtualIncomingMessage","#body","#pushed","init","lowercased","key","value","VirtualServerResponse","#chunks","#customHeaders","#customStatus","chunk","_encoding","callback","status","headers","k","v","name","rest","buf","messageFrame","data","trackedFrame","id","pongFrame","errorFrame","code","message","parseClientFrame","raw","parsed","safeJsonParse","checkJsonDepth","obj","ServerBuilder","#serviceCollection","ServiceCollection","#registrations","#subscriptionRegistrations","#webhooks","#globalMiddlewares","#contentNegotiator","ContentNegotiator","#options","#authConfig","#authzConfig","#healthcheck","#batchConfig","configureFn","middleware","handler","config","options","endpointDef","mapping","entry","def","port","host","router","Router","reg","serviceProvider","authMiddlewares","createAuthenticationMiddleware","policies","name","builder","PolicyBuilder","authzService","AuthorizationService","createAuthorizationMiddleware","allMiddlewares","server","Server","listenPort","listenHost","MAX_WS_QUEUE_SIZE","#router","#serviceProvider","#hasSubscriptions","#maxBodySize","#httpServer","#wss","#activeConnections","contentNegotiator","globalMiddlewares","healthcheck","hasSubscriptions","batchConfig","maxBodySize","DEFAULT_MAX_BODY_SIZE","req","res","#handleRequest","_err","PROBLEM_JSON_CONTENT_TYPE","serializeProblemDetails","createProblemDetails","resolve","WebSocketServer","socket","head","urlPath","result","ws","#handleWebSocket","reject","err","addr","scope","ctx","RequestContext","method","#handleBatchRequest","routeResult","pd","registration","parsedPath","meta","rawParams","flattenToStrings","pipeline","MiddlewarePipeline","mw","parsedBody","needsBody","contentType","ctHandler","bodyText","resolveResult","resolveArgs","#sendResult","HttpError","maxSize","parallel","outerBody","raw","readBuffer","parsed","safeJsonParse","checkJsonDepth","execute","item","virtualReq","VirtualIncomingMessage","virtualRes","VirtualServerResponse","results","ActionResult","JsonResult","abortController","dummyRes","authPipeline","#runSubscription","incomingQueue","incomingResolve","incomingDone","incoming","text","frame","parseClientFrame","errorFrame","pongFrame","errors","e","subscriptionCtx","queryObj","k","v","handlerArgs","services","key","schema","generator","value","frameToSend","isTrackedEvent","outData","trackedFrame","messageFrame","createServer","chunks","totalSize","chunk","obj","prefix","fullKey","schemeMap","scheme","next","Principal","authCtx","parseCookies","authConfig","challengeHeaders","ch","principal","headers","requireRole","defineWebhook","name","options"]}
1
+ {"version":3,"sources":["../src/ActionResult.ts","../src/ProblemDetails.ts","../src/HttpError.ts","../src/RequestContext.ts","../src/safeJson.ts","../src/Server.ts","../src/ContentNegotiator.ts","../src/MiddlewarePipeline.ts","../src/ParameterResolver.ts","../src/Router.ts","../src/VirtualHttp.ts","../src/WebSocketProtocol.ts","../src/Webhook.ts"],"sourcesContent":["import type * as http from 'node:http';\nimport type { Readable } from 'node:stream';\nimport type { ContentNegotiator } from './ContentNegotiator.js';\n\n// ---------------------------------------------------------------------------\n// Base\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract base for all HTTP action results.\n *\n * Instead of writing directly to `res`, handlers return an `ActionResult`\n * instance. The server calls `executeAsync()` after the middleware pipeline\n * completes, ensuring consistent error handling and content negotiation.\n *\n * Use the static factory methods (`ActionResult.ok()`, `.created()`, etc.)\n * rather than constructing subclasses directly.\n *\n * @example\n * ```ts\n * server.handle(GetUser, ({ params }) => {\n * const user = db.find(params.id);\n * if (!user) throw new NotFoundError();\n * return ActionResult.ok(user);\n * });\n * ```\n */\nexport abstract class ActionResult {\n abstract executeAsync(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n contentNegotiator: ContentNegotiator\n ): Promise<void>;\n\n // -----------------------------------------------------------------------\n // Factory methods\n // -----------------------------------------------------------------------\n\n /** 200 OK — serializes value using content negotiation. */\n static ok<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<200, T> {\n return new JsonResult(body, 200, headers) as JsonResult<200, T>;\n }\n\n /** 201 Created — serializes value using content negotiation. */\n static created<T>(\n body: T,\n location?: string,\n headers?: Record<string, string>\n ): JsonResult<201, T> {\n const h: Record<string, string> = { ...headers };\n if (location) h['location'] = location;\n return new JsonResult(body, 201, h) as JsonResult<201, T>;\n }\n\n /** 202 Accepted — serializes value using content negotiation. */\n static accepted<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<202, T> {\n return new JsonResult(body, 202, headers) as JsonResult<202, T>;\n }\n\n /** 204 No Content. */\n static noContent(): NoContentResult {\n return new NoContentResult();\n }\n\n /** 400 Bad Request — serializes value as JSON. */\n static badRequest<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<400, T> {\n return new JsonResult(body, 400, headers) as JsonResult<400, T>;\n }\n\n /** 401 Unauthorized — serializes value as JSON. */\n static unauthorized<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<401, T> {\n return new JsonResult(body, 401, headers) as JsonResult<401, T>;\n }\n\n /** 403 Forbidden — serializes value as JSON. */\n static forbidden<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<403, T> {\n return new JsonResult(body, 403, headers) as JsonResult<403, T>;\n }\n\n /** 404 Not Found — serializes value as JSON. */\n static notFound<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<404, T> {\n return new JsonResult(body, 404, headers) as JsonResult<404, T>;\n }\n\n /** 409 Conflict — serializes value as JSON. */\n static conflict<T>(\n body: T,\n headers?: Record<string, string>\n ): JsonResult<409, T> {\n return new JsonResult(body, 409, headers) as JsonResult<409, T>;\n }\n\n /** Temporary (302) or permanent (301) redirect. */\n static redirect(url: string, permanent = false): RedirectResult {\n return new RedirectResult(url, permanent);\n }\n\n /**\n * Explicit JSON response with a specific status code.\n * Use the named factories (`ok`, `notFound`, etc.) for common codes.\n * This overload is an escape hatch for uncommon status codes.\n */\n static json<T>(body: T): JsonResult<200, T>;\n static json<S extends number, T>(\n body: T,\n status: S,\n headers?: Record<string, string>\n ): JsonResult<S, T>;\n static json(\n body: unknown,\n status: number = 200,\n headers?: Record<string, string>\n ): JsonResult {\n return new JsonResult(body, status, headers);\n }\n\n /** Send a file buffer as a download attachment. */\n static file(\n content: Buffer | Uint8Array,\n fileName: string,\n contentType = 'application/octet-stream'\n ): FileResult {\n return new FileResult(content, fileName, contentType);\n }\n\n /** Arbitrary string body with an explicit content type. */\n static content(\n body: string,\n contentType: string,\n status = 200\n ): ContentResult {\n return new ContentResult(body, contentType, status);\n }\n\n /** Pipe a Readable stream to the response. */\n static stream(\n readable: Readable,\n contentType: string,\n fileName?: string\n ): StreamResult {\n return new StreamResult(readable, contentType, fileName);\n }\n\n /** Bare status code with no body. */\n static status<S extends number>(\n status: S,\n headers?: Record<string, string>\n ): StatusCodeResult<S> {\n return new StatusCodeResult(status, headers) as StatusCodeResult<S>;\n }\n}\n\n// ---------------------------------------------------------------------------\n// JsonResult\n// ---------------------------------------------------------------------------\n\n/**\n * Serializes a value and writes it as JSON with `content-type: application/json`,\n * bypassing content negotiation entirely.\n *\n * Created by `ActionResult.json()`.\n * `ActionResult.ok()` and `ActionResult.created()` produce a {@link JsonResult}\n * that goes through content negotiation instead.\n */\nexport class JsonResult<\n TStatus extends number = number,\n TBody = unknown\n> extends ActionResult {\n readonly body: TBody;\n readonly status: TStatus;\n readonly headers: Record<string, string>;\n\n constructor(\n body: TBody,\n status: TStatus | number = 200,\n headers?: Record<string, string>\n ) {\n super();\n this.body = body;\n this.status = status as TStatus;\n this.headers = headers ?? {};\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n for (const [key, value] of Object.entries(this.headers)) {\n res.setHeader(key, value);\n }\n\n if (this.body === null || this.body === undefined) {\n res.writeHead(this.status);\n res.end();\n return;\n }\n\n res.writeHead(this.status, { 'content-type': 'application/json' });\n res.end(JSON.stringify(this.body));\n }\n}\n\n// ---------------------------------------------------------------------------\n// FileResult\n// ---------------------------------------------------------------------------\n\n/**\n * Sends a binary buffer as a file download attachment.\n * Created by `ActionResult.file()`.\n */\nexport class FileResult extends ActionResult {\n readonly content: Buffer | Uint8Array;\n readonly fileName: string;\n readonly contentType: string;\n\n constructor(\n content: Buffer | Uint8Array,\n fileName: string,\n contentType = 'application/octet-stream'\n ) {\n super();\n this.content = content;\n this.fileName = fileName;\n this.contentType = contentType;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(200, {\n 'content-type': this.contentType,\n 'content-disposition': `attachment; filename=\"${this.fileName}\"`,\n 'content-length': String(this.content.byteLength)\n });\n res.end(this.content);\n }\n}\n\n// ---------------------------------------------------------------------------\n// ContentResult\n// ---------------------------------------------------------------------------\n\n/**\n * Writes an arbitrary string body with a specific content type and status.\n * Created by `ActionResult.content()`.\n */\nexport class ContentResult extends ActionResult {\n readonly body: string;\n readonly contentType: string;\n readonly status: number;\n\n constructor(body: string, contentType: string, status = 200) {\n super();\n this.body = body;\n this.contentType = contentType;\n this.status = status;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(this.status, { 'content-type': this.contentType });\n res.end(this.body);\n }\n}\n\n// ---------------------------------------------------------------------------\n// StreamResult\n// ---------------------------------------------------------------------------\n\n/**\n * Pipes a `Readable` stream to the HTTP response.\n * Created by `ActionResult.stream()`.\n */\nexport class StreamResult extends ActionResult {\n readonly readable: Readable;\n readonly contentType: string;\n readonly fileName: string | undefined;\n\n constructor(readable: Readable, contentType: string, fileName?: string) {\n super();\n this.readable = readable;\n this.contentType = contentType;\n this.fileName = fileName;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n const headers: Record<string, string> = {\n 'content-type': this.contentType\n };\n if (this.fileName) {\n headers['content-disposition'] =\n `attachment; filename=\"${this.fileName}\"`;\n }\n res.writeHead(200, headers);\n\n await new Promise<void>((resolve, reject) => {\n this.readable.on('error', reject);\n res.on('error', reject);\n this.readable.on('end', resolve);\n this.readable.pipe(res, { end: true });\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// StatusCodeResult\n// ---------------------------------------------------------------------------\n\n/**\n * Responds with a bare HTTP status code and no body.\n * Created by `ActionResult.status()`.\n */\nexport class StatusCodeResult<\n TStatus extends number = number\n> extends ActionResult {\n readonly status: TStatus;\n readonly headers: Record<string, string>;\n\n constructor(status: TStatus | number, headers?: Record<string, string>) {\n super();\n this.status = status as TStatus;\n this.headers = headers ?? {};\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(this.status, this.headers);\n res.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// RedirectResult\n// ---------------------------------------------------------------------------\n\n/**\n * Redirects the client to a new URL.\n * Uses 302 (temporary) by default; pass `permanent = true` for 301.\n * Created by `ActionResult.redirect()`.\n */\nexport class RedirectResult extends ActionResult {\n readonly url: string;\n readonly permanent: boolean;\n\n constructor(url: string, permanent = false) {\n super();\n this.url = url;\n this.permanent = permanent;\n }\n\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(this.permanent ? 301 : 302, { location: this.url });\n res.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// NoContentResult\n// ---------------------------------------------------------------------------\n\n/**\n * Responds with 204 No Content and no body.\n * Created by `ActionResult.noContent()`.\n */\nexport class NoContentResult extends ActionResult {\n async executeAsync(\n _req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n res.writeHead(204);\n res.end();\n }\n}\n","// ---------------------------------------------------------------------------\n// RFC 9457 Problem Details\n// ---------------------------------------------------------------------------\n\n/**\n * An RFC 9457 (formerly RFC 7807) Problem Details object.\n *\n * Provides machine-readable error information in HTTP API responses.\n * Serialized as `application/problem+json`.\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc9457 RFC 9457}\n */\nexport interface ProblemDetails {\n readonly type: string;\n readonly status: number;\n readonly title: string;\n readonly detail?: string;\n readonly instance?: string;\n readonly [extension: string]: unknown;\n}\n\nconst STATUS_TITLES: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 405: 'Method Not Allowed',\n 409: 'Conflict',\n 415: 'Unsupported Media Type',\n 422: 'Unprocessable Content',\n 500: 'Internal Server Error',\n 503: 'Service Unavailable'\n};\n\n/**\n * Create a {@link ProblemDetails} object for the given HTTP status code.\n *\n * @param status - HTTP status code (e.g. 400, 404, 500).\n * @param title - Short, human-readable summary. Defaults to a standard phrase\n * for common status codes.\n * @param detail - Longer explanation specific to this occurrence.\n * @param extensions - Extra fields merged into the object (RFC 9457 §3.1).\n */\nexport function createProblemDetails(\n status: number,\n title?: string,\n detail?: string,\n extensions?: Record<string, unknown>\n): ProblemDetails {\n return {\n type: `https://httpstatuses.com/${status}`,\n status,\n title: title ?? STATUS_TITLES[status] ?? 'Error',\n ...(detail !== undefined ? { detail } : {}),\n ...extensions\n };\n}\n\n/**\n * A single field-level validation error, used in validation Problem Details\n * responses. `pointer` follows JSON Pointer syntax (RFC 6901).\n *\n * @example `{ pointer: '/body/email', detail: 'Must be a valid email address' }`\n */\nexport interface ValidationErrorItem {\n readonly pointer: string;\n readonly detail: string;\n}\n\n/**\n * Create a 400 Bad Request Problem Details object listing all validation\n * field errors.\n *\n * @param errors - Array of per-field errors with JSON Pointer paths.\n */\nexport function createValidationProblemDetails(\n errors: readonly ValidationErrorItem[]\n): ProblemDetails {\n return createProblemDetails(\n 400,\n 'Bad Request',\n 'One or more validation errors occurred.',\n { errors }\n );\n}\n\n/**\n * Serialize a {@link ProblemDetails} object to a JSON string.\n */\nexport function serializeProblemDetails(pd: ProblemDetails): string {\n return JSON.stringify(pd);\n}\n\n/** The MIME type for Problem Details JSON responses (`application/problem+json`). */\nexport const PROBLEM_JSON_CONTENT_TYPE = 'application/problem+json';\n","import { createProblemDetails, type ProblemDetails } from './ProblemDetails.js';\n\n/**\n * Base class for HTTP errors thrown from endpoint handlers.\n *\n * Instances are automatically caught by the server and serialized as\n * RFC 9457 Problem Details (`application/problem+json`) responses.\n *\n * @example\n * ```ts\n * throw new HttpError(429, 'Too Many Requests', 'Rate limit exceeded.');\n * ```\n */\nexport class HttpError extends Error {\n readonly status: number;\n readonly title: string;\n readonly detail?: string;\n readonly extensions?: Record<string, unknown>;\n\n constructor(\n status: number,\n title?: string,\n detail?: string,\n extensions?: Record<string, unknown>\n ) {\n super(detail ?? title ?? `HTTP ${status}`);\n this.name = 'HttpError';\n this.status = status;\n this.title = title ?? `HTTP ${status}`;\n this.detail = detail;\n this.extensions = extensions;\n }\n\n /** Converts this error into an RFC 9457 {@link ProblemDetails} object. */\n toProblemDetails(): ProblemDetails {\n return createProblemDetails(\n this.status,\n this.title,\n this.detail,\n this.extensions\n );\n }\n}\n\n/** Thrown when a requested resource cannot be found. Produces a 404 response. */\nexport class NotFoundError extends HttpError {\n constructor(detail?: string) {\n super(404, 'Not Found', detail);\n this.name = 'NotFoundError';\n }\n}\n\n/** Thrown when the request is malformed or fails validation. Produces a 400 response. */\nexport class BadRequestError extends HttpError {\n constructor(detail?: string) {\n super(400, 'Bad Request', detail);\n this.name = 'BadRequestError';\n }\n}\n\n/** Thrown when the request lacks valid authentication credentials. Produces a 401 response. */\nexport class UnauthorizedError extends HttpError {\n constructor(detail?: string) {\n super(401, 'Unauthorized', detail);\n this.name = 'UnauthorizedError';\n }\n}\n\n/** Thrown when the authenticated principal lacks permission. Produces a 403 response. */\nexport class ForbiddenError extends HttpError {\n constructor(detail?: string) {\n super(403, 'Forbidden', detail);\n this.name = 'ForbiddenError';\n }\n}\n\n/** Thrown when the request conflicts with the current state of the resource. Produces a 409 response. */\nexport class ConflictError extends HttpError {\n constructor(detail?: string) {\n super(409, 'Conflict', detail);\n this.name = 'ConflictError';\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport type { IServiceProvider } from '@cleverbrush/di';\nimport {\n any,\n boolean,\n func,\n object,\n promise,\n record,\n string\n} from '@cleverbrush/schema';\nimport { HttpError } from './HttpError.js';\nimport { checkJsonDepth, safeJsonParse } from './safeJson.js';\n\n/**\n * IRequestContext — the schema definition for the request context.\n * Serves as both a DI key and a type definition.\n */\nexport const IRequestContext = object({\n method: string(),\n url: string(),\n pathParams: record(string(), string()),\n queryParams: record(string(), string()),\n headers: record(string(), string()),\n items: any(),\n body: func().hasReturnType(promise(any())),\n json: func().hasReturnType(promise(any())),\n responded: boolean()\n});\n\n/**\n * Per-request context object passed to every middleware and endpoint handler.\n *\n * Provides typed access to path/query parameters, headers, the request body,\n * and the DI service provider for the current request scope.\n *\n * @example\n * ```ts\n * const middleware: Middleware = async (ctx, next) => {\n * ctx.items.set('startTime', Date.now());\n * await next();\n * };\n * ```\n */\n/** Default maximum request body size: 5 MB. */\nexport const DEFAULT_MAX_BODY_SIZE = 5 * 1024 * 1024;\n\nexport class RequestContext {\n readonly request: IncomingMessage;\n readonly response: ServerResponse;\n readonly url: URL;\n readonly method: string;\n readonly headers: Record<string, string>;\n readonly items: Map<string, unknown> = new Map();\n readonly maxBodySize: number;\n\n #pathParams: Record<string, string> = {};\n /** @internal — overridable for testing */\n _queryParams?: Record<string, string>;\n #services?: IServiceProvider;\n #bodyBuffer: Buffer | null = null;\n #bodyRead = false;\n #jsonCache: unknown = undefined;\n #jsonParsed = false;\n responded = false;\n\n /**\n * The authenticated principal for this request.\n * Set by authentication middleware; typed as `unknown` at the\n * RequestContext level — handlers receive a fully typed version\n * via `ActionContext.principal`.\n */\n principal: unknown = undefined;\n\n constructor(\n request: IncomingMessage,\n response: ServerResponse,\n maxBodySize?: number\n ) {\n this.request = request;\n this.response = response;\n this.method = (request.method ?? 'GET').toUpperCase();\n this.maxBodySize = maxBodySize ?? DEFAULT_MAX_BODY_SIZE;\n\n // Parse URL — use a placeholder host for relative URLs\n const rawUrl = request.url ?? '/';\n this.url = new URL(\n rawUrl,\n `http://${request.headers.host ?? 'localhost'}`\n );\n\n // Build headers record (lowercased keys, string values)\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(request.headers)) {\n if (typeof value === 'string') {\n headers[key] = value;\n } else if (Array.isArray(value)) {\n headers[key] = value.join(', ');\n }\n }\n this.headers = headers;\n }\n\n /** Path parameters extracted from the matched route template. */\n get pathParams(): Record<string, string> {\n return this.#pathParams;\n }\n\n set pathParams(value: Record<string, string>) {\n this.#pathParams = value;\n }\n\n /** Parsed query string parameters from the request URL. */\n get queryParams(): Record<string, string> {\n if (this._queryParams) return this._queryParams;\n const params: Record<string, string> = {};\n for (const [key, value] of this.url.searchParams) {\n params[key] = value;\n }\n return params;\n }\n\n /** The DI service provider scoped to this request. Set by the server before invoking the handler. */\n get services(): IServiceProvider | undefined {\n return this.#services;\n }\n\n set services(value: IServiceProvider) {\n this.#services = value;\n }\n\n /** Read and buffer the raw request body. Result is cached after the first call. */\n async body(): Promise<Buffer> {\n if (this.#bodyRead) return this.#bodyBuffer!;\n\n this.#bodyBuffer = await new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n this.request.on('data', (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > this.maxBodySize) {\n this.request.destroy();\n reject(new HttpError(413, 'Payload Too Large'));\n return;\n }\n chunks.push(chunk);\n });\n this.request.on('end', () => resolve(Buffer.concat(chunks)));\n this.request.on('error', reject);\n });\n this.#bodyRead = true;\n return this.#bodyBuffer;\n }\n\n /** Read, buffer, and JSON-parse the request body. Result is cached after the first call. */\n async json(): Promise<unknown> {\n if (this.#jsonParsed) return this.#jsonCache;\n\n const buf = await this.body();\n const text = buf.toString('utf-8');\n if (text.length > 0) {\n this.#jsonCache = safeJsonParse(text);\n checkJsonDepth(this.#jsonCache);\n }\n this.#jsonParsed = true;\n return this.#jsonCache;\n }\n}\n","/**\n * Safe JSON utilities to prevent prototype pollution and excessive nesting.\n *\n * @module\n * @internal\n */\n\n/** Default maximum nesting depth for parsed JSON objects. */\nexport const MAX_JSON_DEPTH = 64;\n\n/**\n * Parse a JSON string while stripping dangerous keys (`__proto__`,\n * `constructor`) that could lead to prototype pollution.\n *\n * Uses a `JSON.parse` reviver to remove polluting keys during parsing,\n * which is more efficient than a post-parse walk.\n *\n * @throws {SyntaxError} If `raw` is not valid JSON.\n */\nexport function safeJsonParse(raw: string): unknown {\n return JSON.parse(raw, (key, value) => {\n if (key === '__proto__' || key === 'constructor') {\n return undefined;\n }\n return value;\n });\n}\n\n/**\n * Walk a parsed JSON value and throw if the nesting depth exceeds\n * `maxDepth`. Must be called after parsing.\n *\n * Only objects and arrays contribute to depth; primitives do not.\n *\n * @throws {Error} When nesting exceeds `maxDepth`.\n */\nexport function checkJsonDepth(\n value: unknown,\n maxDepth: number = MAX_JSON_DEPTH\n): void {\n walk(value, 0, maxDepth);\n}\n\nfunction walk(value: unknown, current: number, max: number): void {\n if (value === null || typeof value !== 'object') return;\n if (current >= max) {\n throw new Error(`JSON nesting depth exceeds maximum of ${max}`);\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n walk(item, current + 1, max);\n }\n } else {\n for (const v of Object.values(value as Record<string, unknown>)) {\n walk(v, current + 1, max);\n }\n }\n}\n","import * as http from 'node:http';\nimport * as https from 'node:https';\nimport type { Duplex } from 'node:stream';\nimport type {\n AuthenticationContext,\n AuthenticationScheme,\n AuthorizationPolicy\n} from '@cleverbrush/auth';\nimport {\n AuthorizationService,\n PolicyBuilder,\n Principal,\n parseCookies,\n requireRole\n} from '@cleverbrush/auth';\nimport { ServiceCollection, type ServiceProvider } from '@cleverbrush/di';\nimport { type WebSocket, WebSocketServer } from 'ws';\nimport { ActionResult, JsonResult } from './ActionResult.js';\nimport { ContentNegotiator } from './ContentNegotiator.js';\nimport type { EndpointBuilder, Handler, HandlerMapping } from './Endpoint.js';\nimport { HttpError } from './HttpError.js';\nimport { MiddlewarePipeline } from './MiddlewarePipeline.js';\nimport { needsBody, resolveArgs } from './ParameterResolver.js';\nimport {\n createProblemDetails,\n PROBLEM_JSON_CONTENT_TYPE,\n serializeProblemDetails\n} from './ProblemDetails.js';\nimport { DEFAULT_MAX_BODY_SIZE, RequestContext } from './RequestContext.js';\nimport { Router } from './Router.js';\nimport type { SubscriptionMetadata } from './Subscription.js';\nimport { isTrackedEvent } from './Subscription.js';\nimport { checkJsonDepth, safeJsonParse } from './safeJson.js';\nimport type {\n ContentTypeHandler,\n EndpointRegistration,\n Middleware,\n ServerBatchingOptions,\n ServerOptions,\n SubscriptionRegistration\n} from './types.js';\nimport {\n VirtualIncomingMessage,\n VirtualServerResponse\n} from './VirtualHttp.js';\nimport type { WebhookDefinition } from './Webhook.js';\nimport {\n errorFrame,\n messageFrame,\n parseClientFrame,\n pongFrame,\n trackedFrame\n} from './WebSocketProtocol.js';\n\n// ---------------------------------------------------------------------------\n// Authentication / Authorization Config Types\n// ---------------------------------------------------------------------------\n\n/**\n * Authentication configuration passed to `ServerBuilder.useAuthentication()`.\n *\n * At least one scheme must be listed. The `defaultScheme` name must match\n * one of the registered scheme `name` values — it is used when no specific\n * scheme is requested.\n */\nexport interface AuthenticationConfig {\n /** Name of the default scheme to use (must match a scheme's `name`). */\n defaultScheme: string;\n /** Registered authentication schemes. */\n schemes: AuthenticationScheme<any>[];\n}\n\n/**\n * Authorization configuration passed to `ServerBuilder.useAuthorization()`.\n *\n * Named policies can be referenced by string in future `authorize('policy-name')`\n * calls (currently resolved at startup time).\n */\nexport interface AuthorizationConfig {\n /** Named policies (looked up by `authorize('policy-name')` — future use). */\n policies?: Record<string, (builder: PolicyBuilder) => void>;\n}\n\n/**\n * Fluent builder for constructing and starting an HTTP server.\n *\n * @example\n * ```ts\n * const server = new ServerBuilder();\n *\n * server\n * .services(svc => svc.addSingleton(IDb, () => new Db()))\n * .use(loggingMiddleware)\n * .handle(GetUser, ({ params }) => db.find(params.id));\n *\n * await server.listen(3000);\n * ```\n */\n\nexport class ServerBuilder {\n readonly #serviceCollection = new ServiceCollection();\n readonly #registrations: EndpointRegistration[] = [];\n readonly #subscriptionRegistrations: SubscriptionRegistration[] = [];\n readonly #webhooks: WebhookDefinition[] = [];\n readonly #globalMiddlewares: Middleware[] = [];\n readonly #contentNegotiator = new ContentNegotiator();\n #options: ServerOptions = {};\n #authConfig: AuthenticationConfig | null = null;\n #authzConfig: AuthorizationConfig | null = null;\n #healthcheck = false;\n #batchConfig: ServerBatchingOptions | null = null;\n\n /**\n * Configure the DI service collection.\n *\n * @param configureFn - Receives the `ServiceCollection` for registrations.\n */\n services(configureFn: (svc: ServiceCollection) => void): this {\n configureFn(this.#serviceCollection);\n return this;\n }\n\n /**\n * Add a global middleware that runs for every request.\n * Middleware is executed in the order it is added.\n */\n use(middleware: Middleware): this {\n this.#globalMiddlewares.push(middleware);\n return this;\n }\n\n /**\n * Register an additional content type handler for content negotiation.\n * JSON is registered by default.\n */\n contentType(handler: ContentTypeHandler): this {\n this.#contentNegotiator.register(handler);\n return this;\n }\n\n /**\n * Enable authentication with one or more schemes.\n * Registers a global middleware that authenticates every request and\n * sets `ctx.principal`.\n */\n useAuthentication(config: AuthenticationConfig): this {\n this.#authConfig = config;\n return this;\n }\n\n /**\n * Enable authorization enforcement.\n * Registers a global middleware that checks endpoint `authorize()`\n * metadata against the authenticated principal.\n * Must be called after `useAuthentication()`.\n */\n useAuthorization(config?: AuthorizationConfig): this {\n this.#authzConfig = config ?? {};\n return this;\n }\n\n /**\n * Enable the `GET /health` endpoint that returns `{ ok: true }` (200).\n * Useful for load balancer and container readiness probes.\n *\n * The path is available as {@link HEALTHCHECK_PATH}.\n */\n withHealthcheck(): this {\n this.#healthcheck = true;\n return this;\n }\n\n /**\n * Enable the server-side request batching endpoint.\n *\n * Once enabled, the server accepts `POST <path>` (default `/__batch`)\n * containing an array of sub-requests and processes each one through the\n * full middleware and handler pipeline, returning an array of\n * sub-responses in a single HTTP reply.\n *\n * Pair this with the `batching()` middleware from `@cleverbrush/client/batching`\n * on the client side.\n *\n * @param options - {@link ServerBatchingOptions} (all fields optional).\n *\n * @example\n * ```ts\n * new ServerBuilder()\n * .useBatching()\n * .handleAll(mapping)\n * .listen(3000);\n * ```\n */\n useBatching(options: ServerBatchingOptions = {}): this {\n this.#batchConfig = options;\n return this;\n }\n\n /**\n * Register an endpoint and its handler.\n *\n * @param endpointDef - An `EndpointBuilder` instance (e.g. from `endpoint.get(...)`).\n * @param handler - The typed handler function.\n * @param options - Optional per-endpoint middleware.\n */\n handle<\n E extends EndpointBuilder<any, any, any, any, any, any, any, any, any>\n >(\n endpointDef: E,\n handler: Handler<E>,\n options?: { middlewares?: Middleware[] }\n ): this {\n this.#registrations.push({\n endpoint: endpointDef.introspect(),\n handler,\n middlewares: options?.middlewares\n });\n return this;\n }\n\n /**\n * Register all endpoints from a {@link HandlerMapping} created by\n * {@link mapHandlers}. This is the bulk equivalent of calling\n * `.handle()` for each endpoint individually.\n *\n * @param mapping - The mapping produced by `mapHandlers(endpoints, handlers)`.\n */\n handleAll(mapping: HandlerMapping): this {\n for (const entry of mapping._entries) {\n this.#registrations.push({\n endpoint: entry.endpoint.introspect(),\n handler: entry.handler,\n middlewares: entry.middlewares\n });\n }\n for (const entry of mapping._subscriptions) {\n this.#subscriptionRegistrations.push({\n endpoint: entry.endpoint.introspect(),\n handler: entry.handler,\n middlewares: entry.middlewares\n });\n }\n return this;\n }\n\n /**\n * Returns a snapshot of all registered endpoints.\n * Useful for generating OpenAPI specs or other documentation.\n */\n getRegistrations(): readonly EndpointRegistration[] {\n return [...this.#registrations];\n }\n\n /**\n * Returns a snapshot of all registered WebSocket subscription endpoints.\n * Consumed by `@cleverbrush/server-openapi` to emit the AsyncAPI spec.\n */\n getSubscriptionRegistrations(): readonly SubscriptionRegistration[] {\n return [...this.#subscriptionRegistrations];\n }\n\n /**\n * Register a webhook definition.\n *\n * Webhooks are recorded for OpenAPI spec generation only — they are not\n * served as HTTP routes by the runtime server.\n *\n * @param def - A {@link WebhookDefinition} created with {@link defineWebhook}.\n */\n webhook(def: WebhookDefinition): this {\n this.#webhooks.push(def);\n return this;\n }\n\n /**\n * Returns a snapshot of all registered webhook definitions.\n * Consumed by `@cleverbrush/server-openapi` to emit the `webhooks` map.\n */\n getWebhooks(): readonly WebhookDefinition[] {\n return [...this.#webhooks];\n }\n\n /**\n * Returns the authentication configuration, or `null` if\n * `useAuthentication()` has not been called.\n */\n getAuthenticationConfig(): AuthenticationConfig | null {\n return this.#authConfig;\n }\n\n /**\n * Start listening on the given port and host. Resolves with the running\n * {@link Server} instance.\n *\n * @param port - TCP port (default: `ServerOptions.port ?? 3000`).\n * @param host - Bind address (default: `ServerOptions.host ?? '0.0.0.0'`).\n */\n async listen(port?: number, host?: string): Promise<Server> {\n const router = new Router();\n\n for (const reg of this.#registrations) {\n router.addRoute(reg);\n }\n for (const reg of this.#subscriptionRegistrations) {\n router.addSubscriptionRoute(reg);\n }\n\n const serviceProvider = this.#serviceCollection.buildServiceProvider({\n validateScopes: false\n });\n\n // Build auth middleware stack\n const authMiddlewares: Middleware[] = [];\n\n if (this.#authConfig) {\n authMiddlewares.push(\n createAuthenticationMiddleware(this.#authConfig)\n );\n }\n\n if (this.#authzConfig !== null) {\n const policies = new Map<string, AuthorizationPolicy>();\n if (this.#authzConfig.policies) {\n for (const [name, configureFn] of Object.entries(\n this.#authzConfig.policies\n )) {\n const builder = new PolicyBuilder();\n configureFn(builder);\n policies.set(name, builder.build(name));\n }\n }\n const authzService = new AuthorizationService(policies);\n authMiddlewares.push(\n createAuthorizationMiddleware(authzService, this.#authConfig)\n );\n }\n\n // Auth middlewares go before user-registered global middlewares\n const allMiddlewares = [...authMiddlewares, ...this.#globalMiddlewares];\n\n const server = new Server(\n router,\n serviceProvider,\n this.#contentNegotiator,\n allMiddlewares,\n this.#healthcheck,\n this.#subscriptionRegistrations.length > 0,\n this.#batchConfig,\n this.#options.maxBodySize\n );\n\n const listenPort = port ?? this.#options.port ?? 3000;\n const listenHost = host ?? this.#options.host ?? '0.0.0.0';\n\n await server.start(listenPort, listenHost, this.#options);\n return server;\n }\n}\n\n/**\n * The running HTTP/HTTPS server instance returned by `ServerBuilder.listen()`.\n *\n * Use `close()` to gracefully shut down the server.\n */\n/** Maximum number of queued incoming WebSocket messages before the connection is closed. */\nconst MAX_WS_QUEUE_SIZE = 1024;\n\nexport class Server {\n readonly #router: Router;\n readonly #serviceProvider: ServiceProvider;\n readonly #contentNegotiator: ContentNegotiator;\n readonly #globalMiddlewares: Middleware[];\n readonly #healthcheck: boolean;\n readonly #hasSubscriptions: boolean;\n readonly #batchConfig: ServerBatchingOptions | null;\n readonly #maxBodySize: number;\n #httpServer: http.Server | https.Server | null = null;\n #wss: WebSocketServer | null = null;\n readonly #activeConnections: Set<WebSocket> = new Set();\n\n constructor(\n router: Router,\n serviceProvider: ServiceProvider,\n contentNegotiator: ContentNegotiator,\n globalMiddlewares: Middleware[],\n healthcheck = false,\n hasSubscriptions = false,\n batchConfig: ServerBatchingOptions | null = null,\n maxBodySize: number = DEFAULT_MAX_BODY_SIZE\n ) {\n this.#router = router;\n this.#serviceProvider = serviceProvider;\n this.#contentNegotiator = contentNegotiator;\n this.#globalMiddlewares = globalMiddlewares;\n this.#healthcheck = healthcheck;\n this.#hasSubscriptions = hasSubscriptions;\n this.#batchConfig = batchConfig;\n this.#maxBodySize = maxBodySize;\n }\n\n /**\n * Start listening. Called internally by `ServerBuilder.listen()` after\n * the server is fully configured.\n */\n async start(\n port: number,\n host: string,\n options: ServerOptions\n ): Promise<void> {\n const handler = (\n req: http.IncomingMessage,\n res: http.ServerResponse\n ) => {\n this.#handleRequest(req, res).catch((_err: unknown) => {\n if (!res.headersSent) {\n res.writeHead(500, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(createProblemDetails(500)));\n }\n });\n };\n\n if (options.https) {\n this.#httpServer = https.createServer(\n { key: options.https.key, cert: options.https.cert },\n handler\n );\n } else {\n this.#httpServer = http.createServer(handler);\n }\n\n await new Promise<void>(resolve => {\n this.#httpServer!.listen(port, host, resolve);\n });\n\n // Set up WebSocket server if subscriptions are registered\n if (this.#hasSubscriptions) {\n this.#wss = new WebSocketServer({\n noServer: true,\n maxPayload: this.#maxBodySize\n });\n\n this.#httpServer!.on(\n 'upgrade',\n (req: http.IncomingMessage, socket: Duplex, head: Buffer) => {\n const urlPath = new URL(\n req.url ?? '/',\n `http://${req.headers.host ?? 'localhost'}`\n ).pathname;\n\n const result = this.#router.matchSubscription(urlPath);\n if (!result) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n this.#wss!.handleUpgrade(req, socket, head, ws => {\n this.#handleWebSocket(\n ws,\n req,\n result.registration,\n result.parsedPath\n );\n });\n }\n );\n }\n }\n\n /** Gracefully stop the server and free the TCP port. */\n async close(): Promise<void> {\n // Close all active WebSocket connections\n for (const ws of this.#activeConnections) {\n ws.close(1001, 'Server shutting down');\n }\n this.#activeConnections.clear();\n\n // Close the WebSocket server\n if (this.#wss) {\n await new Promise<void>((resolve, reject) => {\n this.#wss!.close((err?: Error) => {\n if (err) reject(err);\n else resolve();\n });\n });\n this.#wss = null;\n }\n\n if (!this.#httpServer) return;\n await new Promise<void>((resolve, reject) => {\n this.#httpServer!.close((err: Error | undefined) => {\n if (err) reject(err);\n else resolve();\n });\n });\n this.#httpServer = null;\n }\n\n /**\n * The bound address after `listen()` resolves.\n * Returns `null` if the server has been closed or not yet started.\n */\n get address(): { port: number; host: string } | null {\n const addr = this.#httpServer?.address();\n if (!addr || typeof addr === 'string') return null;\n return { port: addr.port, host: addr.address };\n }\n\n async #handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse\n ): Promise<void> {\n const scope = this.#serviceProvider.createScope();\n\n try {\n const ctx = new RequestContext(req, res, this.#maxBodySize);\n const urlPath = ctx.url.pathname;\n const method = ctx.method;\n\n if (\n this.#healthcheck &&\n method === 'GET' &&\n urlPath === '/health'\n ) {\n res.writeHead(200);\n res.end();\n return;\n }\n\n // Batch endpoint — handled before routing and auth.\n if (\n this.#batchConfig !== null &&\n method === 'POST' &&\n urlPath === (this.#batchConfig.path ?? '/__batch')\n ) {\n await this.#handleBatchRequest(req, res);\n return;\n }\n\n const routeResult = this.#router.match(method, urlPath);\n\n if (!routeResult.match) {\n if (routeResult.badRequest) {\n const pd = createProblemDetails(400);\n res.writeHead(400, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n if (routeResult.methodNotAllowed) {\n const pd = createProblemDetails(405, 'Method Not Allowed');\n res.writeHead(405, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE,\n allow: routeResult.allowedMethods!.join(', ')\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n const pd = createProblemDetails(404);\n res.writeHead(404, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n const { registration, parsedPath } = routeResult.match;\n const meta = registration.endpoint;\n\n // Set path params on context (raw string form for middleware)\n if (parsedPath) {\n const rawParams: Record<string, string> = {};\n flattenToStrings(parsedPath, '', rawParams);\n ctx.pathParams = rawParams;\n }\n\n ctx.services = scope.serviceProvider;\n\n // Store endpoint metadata for authorization middleware\n ctx.items.set('__endpoint_meta', meta);\n\n // Build middleware pipeline\n const pipeline = new MiddlewarePipeline();\n for (const mw of this.#globalMiddlewares) {\n pipeline.add(mw);\n }\n if (registration.middlewares) {\n for (const mw of registration.middlewares) {\n pipeline.add(mw);\n }\n }\n\n await pipeline.execute(ctx, async () => {\n if (ctx.responded) return;\n\n // Parse body if needed\n let parsedBody: unknown;\n if (needsBody(meta)) {\n const contentType = req.headers['content-type'];\n const ctHandler =\n this.#contentNegotiator.selectRequestHandler(\n contentType\n );\n if (ctHandler) {\n const rawBody = await ctx.body();\n const bodyText = rawBody.toString('utf-8');\n if (bodyText.length > 0) {\n try {\n parsedBody = ctHandler.deserialize(bodyText);\n } catch {\n const pd = createProblemDetails(\n 400,\n 'Malformed request body'\n );\n res.writeHead(400, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n }\n } else if (contentType) {\n const pd = createProblemDetails(415);\n res.writeHead(415, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n }\n\n // Resolve parameters\n const resolveResult = await resolveArgs(\n meta,\n parsedPath,\n ctx,\n parsedBody\n );\n if (!resolveResult.valid) {\n res.writeHead(400, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(\n serializeProblemDetails(resolveResult.problemDetails)\n );\n ctx.responded = true;\n return;\n }\n\n // Call handler\n let result = registration.handler(...resolveResult.args);\n if (result instanceof Promise) {\n result = await result;\n }\n\n if (ctx.responded) return;\n await this.#sendResult(req, res, result);\n ctx.responded = true;\n });\n } catch (err) {\n if (res.headersSent) return;\n\n if (err instanceof HttpError) {\n const pd = err.toProblemDetails();\n res.writeHead(pd.status, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n } else {\n console.error('[server] Unhandled error:', err);\n const pd = createProblemDetails(500);\n res.writeHead(500, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n }\n } finally {\n try {\n await scope.asyncDispose();\n } catch {\n // Swallow disposal errors\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Batch request handler\n // -----------------------------------------------------------------------\n\n async #handleBatchRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse\n ): Promise<void> {\n const config = this.#batchConfig!;\n const maxSize = config.maxSize ?? 20;\n const parallel = config.parallel ?? true;\n\n // Read the outer body.\n let outerBody: { requests: Array<BatchSubRequest> };\n try {\n const raw = await readBuffer(req, this.#maxBodySize);\n const parsed = safeJsonParse(raw.toString('utf-8'));\n checkJsonDepth(parsed);\n outerBody = parsed as {\n requests: Array<BatchSubRequest>;\n };\n if (!Array.isArray(outerBody?.requests)) {\n throw new Error('requests must be an array');\n }\n } catch (err) {\n if (err instanceof HttpError && err.status === 413) {\n const pd = createProblemDetails(413, 'Payload Too Large');\n res.writeHead(413, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n res.end(serializeProblemDetails(pd));\n return;\n }\n const pd = createProblemDetails(400, 'Invalid batch request body');\n res.writeHead(400, { 'content-type': PROBLEM_JSON_CONTENT_TYPE });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n if (outerBody.requests.length > maxSize) {\n const pd = createProblemDetails(\n 400,\n `Batch size ${outerBody.requests.length} exceeds maximum of ${maxSize}`\n );\n res.writeHead(400, { 'content-type': PROBLEM_JSON_CONTENT_TYPE });\n res.end(serializeProblemDetails(pd));\n return;\n }\n\n const execute = async (\n item: BatchSubRequest\n ): Promise<BatchSubResponse> => {\n const virtualReq = new VirtualIncomingMessage({\n method: (item.method ?? 'GET').toUpperCase(),\n url: item.url,\n headers: item.headers ?? {},\n body: item.body\n });\n const virtualRes = new VirtualServerResponse();\n\n await this.#handleRequest(\n virtualReq as unknown as http.IncomingMessage,\n virtualRes as unknown as http.ServerResponse\n );\n\n return virtualRes.toResult();\n };\n\n let results: BatchSubResponse[];\n if (parallel) {\n results = await Promise.all(outerBody.requests.map(execute));\n } else {\n results = [];\n for (const item of outerBody.requests) {\n results.push(await execute(item));\n }\n }\n\n res.writeHead(200, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ responses: results }));\n }\n\n async #sendResult(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n result: unknown\n ): Promise<void> {\n if (result instanceof ActionResult) {\n await result.executeAsync(req, res, this.#contentNegotiator);\n } else if (result === null || result === undefined) {\n res.writeHead(204);\n res.end();\n } else {\n await new JsonResult(result, 200).executeAsync(\n req,\n res,\n this.#contentNegotiator\n );\n }\n }\n\n // -----------------------------------------------------------------------\n // WebSocket subscription handler\n // -----------------------------------------------------------------------\n\n #handleWebSocket(\n ws: WebSocket,\n req: http.IncomingMessage,\n registration: SubscriptionRegistration,\n parsedPath: Record<string, any> | null\n ): void {\n this.#activeConnections.add(ws);\n const scope = this.#serviceProvider.createScope();\n const abortController = new AbortController();\n const meta = registration.endpoint;\n\n // Build RequestContext for middleware / auth\n const dummyRes = new http.ServerResponse(req);\n const ctx = new RequestContext(req, dummyRes);\n if (parsedPath) {\n const rawParams: Record<string, string> = {};\n flattenToStrings(parsedPath, '', rawParams);\n ctx.pathParams = rawParams;\n }\n ctx.services = scope.serviceProvider;\n ctx.items.set('__endpoint_meta', meta);\n\n // Authenticate via the same auth middleware pipeline\n const authPipeline = new MiddlewarePipeline();\n for (const mw of this.#globalMiddlewares) {\n authPipeline.add(mw);\n }\n if (registration.middlewares) {\n for (const mw of registration.middlewares) {\n authPipeline.add(mw);\n }\n }\n\n authPipeline\n .execute(ctx, async () => {\n if (ctx.responded) {\n // Middleware rejected the request\n ws.close(1008, 'Unauthorized');\n return;\n }\n\n this.#runSubscription(\n ws,\n ctx,\n meta,\n registration.handler,\n parsedPath,\n scope,\n abortController\n );\n })\n .catch(() => {\n ws.close(1011, 'Internal Server Error');\n });\n\n ws.on('close', () => {\n this.#activeConnections.delete(ws);\n abortController.abort();\n scope.asyncDispose().catch(() => {});\n });\n\n ws.on('error', () => {\n this.#activeConnections.delete(ws);\n abortController.abort();\n scope.asyncDispose().catch(() => {});\n });\n }\n\n #runSubscription(\n ws: WebSocket,\n ctx: RequestContext,\n meta: SubscriptionMetadata,\n handler: (...args: any[]) => any,\n parsedPath: Record<string, any> | null,\n scope: {\n serviceProvider: import('@cleverbrush/di').IServiceProvider;\n asyncDispose(): Promise<void>;\n },\n abortController: AbortController\n ): void {\n // Build incoming async iterable from client messages\n const incomingQueue: unknown[] = [];\n let incomingResolve: (() => void) | null = null;\n let incomingDone = false;\n\n const incoming: AsyncIterable<unknown> = {\n [Symbol.asyncIterator]() {\n return {\n next(): Promise<IteratorResult<unknown>> {\n if (incomingQueue.length > 0) {\n return Promise.resolve({\n value: incomingQueue.shift()!,\n done: false\n });\n }\n if (incomingDone) {\n return Promise.resolve({\n value: undefined,\n done: true\n });\n }\n return new Promise(resolve => {\n incomingResolve = () => {\n incomingResolve = null;\n if (incomingQueue.length > 0) {\n resolve({\n value: incomingQueue.shift()!,\n done: false\n });\n } else {\n resolve({ value: undefined, done: true });\n }\n };\n });\n },\n return(): Promise<IteratorResult<unknown>> {\n incomingDone = true;\n return Promise.resolve({\n value: undefined,\n done: true\n });\n }\n };\n }\n };\n\n // Handle incoming WebSocket messages\n ws.on('message', (raw: Buffer | string) => {\n const text = typeof raw === 'string' ? raw : raw.toString('utf-8');\n const frame = parseClientFrame(text);\n\n if (!frame) {\n ws.send(\n JSON.stringify(errorFrame(400, 'Invalid frame format'))\n );\n return;\n }\n\n if (frame.type === 'ping') {\n ws.send(JSON.stringify(pongFrame()));\n return;\n }\n\n // Enforce queue size limit to prevent memory exhaustion\n if (incomingQueue.length >= MAX_WS_QUEUE_SIZE) {\n ws.send(\n JSON.stringify(\n errorFrame(\n 429,\n 'Message queue full — slow down or reconnect'\n )\n )\n );\n ws.close(1008, 'Message queue overflow');\n return;\n }\n\n // frame.type === 'message'\n if (meta.incomingSchema) {\n const result = meta.incomingSchema.validate(frame.data);\n if (!result.valid) {\n const errors = (result.errors ?? [])\n .map((e: { message: string }) => e.message)\n .join('; ');\n ws.send(\n JSON.stringify(\n errorFrame(422, `Validation failed: ${errors}`)\n )\n );\n return;\n }\n incomingQueue.push(result.object);\n } else {\n incomingQueue.push(frame.data);\n }\n\n if (incomingResolve) incomingResolve();\n });\n\n // On close, finish the incoming stream\n ws.on('close', () => {\n incomingDone = true;\n if (incomingResolve) incomingResolve();\n });\n\n // Build subscription context\n const subscriptionCtx: Record<string, unknown> = {\n context: ctx,\n signal: abortController.signal\n };\n\n if (parsedPath && Object.keys(parsedPath).length > 0) {\n // Validate path params through the path schema if present\n subscriptionCtx.params = parsedPath;\n }\n\n // Parse query params\n if (meta.querySchema) {\n const queryObj: Record<string, string> = {};\n for (const [k, v] of ctx.url.searchParams.entries()) {\n queryObj[k] = v;\n }\n const result = meta.querySchema.validate(queryObj);\n if (!result.valid) {\n const errors = (result.errors ?? [])\n .map((e: { message: string }) => e.message)\n .join('; ');\n ws.close(1002, `Query validation failed: ${errors}`);\n return;\n }\n subscriptionCtx.query = result.object;\n }\n\n // Parse headers\n if (meta.headerSchema) {\n const result = meta.headerSchema.validate(ctx.headers);\n if (!result.valid) {\n const errors = (result.errors ?? [])\n .map((e: { message: string }) => e.message)\n .join('; ');\n ws.close(1002, `Header validation failed: ${errors}`);\n return;\n }\n subscriptionCtx.headers = result.object;\n }\n\n // Set principal if auth was performed\n if (ctx.principal !== undefined) {\n subscriptionCtx.principal = ctx.principal;\n }\n\n // Add incoming iterable if there's an incoming schema (or just the raw iterable)\n if (meta.incomingSchema) {\n subscriptionCtx.incoming = incoming;\n } else {\n subscriptionCtx.incoming = incoming;\n }\n\n // Resolve DI services\n const handlerArgs: unknown[] = [subscriptionCtx];\n if (meta.serviceSchemas) {\n const services: Record<string, unknown> = {};\n for (const [key, schema] of Object.entries(meta.serviceSchemas)) {\n services[key] = scope.serviceProvider.get(schema);\n }\n handlerArgs.push(services);\n }\n\n // Run the async generator\n (async () => {\n try {\n const generator = handler(...handlerArgs);\n for await (const value of generator) {\n if (ws.readyState !== ws.OPEN) break;\n\n let frameToSend: string;\n if (isTrackedEvent(value)) {\n const outData = meta.outgoingSchema\n ? meta.outgoingSchema.validate(value.data)\n : { valid: true, object: value.data };\n\n if (!(outData as any).valid) {\n ws.send(\n JSON.stringify(\n errorFrame(\n 500,\n 'Outgoing validation failed'\n )\n )\n );\n continue;\n }\n frameToSend = JSON.stringify(\n trackedFrame(value.id, (outData as any).object)\n );\n } else {\n const outData = meta.outgoingSchema\n ? meta.outgoingSchema.validate(value)\n : { valid: true, object: value };\n\n if (!(outData as any).valid) {\n ws.send(\n JSON.stringify(\n errorFrame(\n 500,\n 'Outgoing validation failed'\n )\n )\n );\n continue;\n }\n frameToSend = JSON.stringify(\n messageFrame((outData as any).object)\n );\n }\n\n ws.send(frameToSend);\n }\n } catch (err) {\n if (ws.readyState === ws.OPEN) {\n // Never leak raw error messages to clients\n if (err instanceof Error) {\n console.error(\n '[server] Subscription handler error:',\n err\n );\n }\n ws.send(JSON.stringify(errorFrame(500, 'Internal error')));\n ws.close(1011, 'Handler error');\n }\n }\n })();\n }\n}\n\nexport function createServer(options?: ServerOptions): ServerBuilder {\n const builder = new ServerBuilder();\n if (options) {\n (builder as any).__options = options;\n }\n return builder;\n}\n\n// ---------------------------------------------------------------------------\n// Batch helpers\n// ---------------------------------------------------------------------------\n\n/** A single sub-request within a batch body. */\ninterface BatchSubRequest {\n method: string;\n /** Path + query string, e.g. `/api/todos?page=1`. */\n url: string;\n headers?: Record<string, string>;\n /** Raw JSON-serialised body string. Absent for GET/HEAD/DELETE. */\n body?: string;\n}\n\n/** A single sub-response within the batch reply. */\ninterface BatchSubResponse {\n status: number;\n headers: Record<string, string>;\n body: string;\n}\n\n/** Reads the entire body of an `IncomingMessage` into a `Buffer`. */\nfunction readBuffer(\n req: http.IncomingMessage,\n maxSize: number = DEFAULT_MAX_BODY_SIZE\n): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n req.on('data', (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > maxSize) {\n req.destroy();\n reject(new HttpError(413, 'Payload Too Large'));\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => resolve(Buffer.concat(chunks)));\n req.on('error', reject);\n });\n}\n\n/** Flatten a nested object to a flat Record<string, string> for raw pathParams */\nfunction flattenToStrings(\n obj: Record<string, any>,\n prefix: string,\n result: Record<string, string>\n): void {\n for (const [key, value] of Object.entries(obj)) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n flattenToStrings(value, fullKey, result);\n } else {\n result[fullKey] = String(value);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Authentication Middleware\n// ---------------------------------------------------------------------------\n\nfunction createAuthenticationMiddleware(\n config: AuthenticationConfig\n): Middleware {\n const schemeMap = new Map<string, AuthenticationScheme<any>>();\n for (const scheme of config.schemes) {\n schemeMap.set(scheme.name, scheme);\n }\n\n return async (ctx, next) => {\n const scheme = schemeMap.get(config.defaultScheme);\n if (!scheme) {\n // No matching scheme — leave principal as anonymous\n ctx.principal = Principal.anonymous();\n await next();\n return;\n }\n\n // Build transport-agnostic auth context\n const authCtx: AuthenticationContext = {\n headers: ctx.headers,\n cookies: parseCookies(ctx.headers['cookie'] ?? ''),\n items: ctx.items\n };\n\n const result = await scheme.authenticate(authCtx);\n\n if (result.succeeded) {\n ctx.principal = result.principal;\n } else {\n ctx.principal = Principal.anonymous();\n }\n\n await next();\n };\n}\n\n// ---------------------------------------------------------------------------\n// Authorization Middleware\n// ---------------------------------------------------------------------------\n\nfunction createAuthorizationMiddleware(\n authzService: AuthorizationService,\n authConfig: AuthenticationConfig | null\n): Middleware {\n // Collect challenge headers from schemes for 401 responses\n const challengeHeaders: Record<string, string> = {};\n if (authConfig) {\n for (const scheme of authConfig.schemes) {\n if (scheme.challenge) {\n const ch = scheme.challenge();\n challengeHeaders[ch.headerName.toLowerCase()] = ch.headerValue;\n }\n }\n }\n\n return async (ctx, next) => {\n const meta = ctx.items.get('__endpoint_meta') as\n | import('./Endpoint.js').EndpointMetadata\n | undefined;\n\n // No auth metadata or authRoles is null → public endpoint\n if (!meta || meta.authRoles === null) {\n await next();\n return;\n }\n\n // Endpoint requires auth — check principal\n const principal = ctx.principal;\n\n if (\n !principal ||\n !(principal instanceof Principal) ||\n !principal.isAuthenticated\n ) {\n // 401 Unauthorized\n const pd = createProblemDetails(401, 'Unauthorized');\n const headers: Record<string, string> = {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE,\n ...challengeHeaders\n };\n ctx.response.writeHead(401, headers);\n ctx.response.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n\n // If roles are specified, check them\n if (meta.authRoles.length > 0) {\n const result = await authzService.authorize(principal, [\n requireRole(...meta.authRoles)\n ]);\n if (!result.allowed) {\n const pd = createProblemDetails(403, 'Forbidden');\n ctx.response.writeHead(403, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE\n });\n ctx.response.end(serializeProblemDetails(pd));\n ctx.responded = true;\n return;\n }\n }\n\n // For typed handler access — set the principal value\n if (principal instanceof Principal) {\n ctx.principal = principal.value;\n }\n\n await next();\n };\n}\n","import { checkJsonDepth, safeJsonParse } from './safeJson.js';\nimport type { ContentTypeHandler } from './types.js';\n\nconst JSON_HANDLER: ContentTypeHandler = {\n mimeType: 'application/json',\n serialize(value: unknown): string {\n return JSON.stringify(value);\n },\n deserialize(raw: string): unknown {\n const parsed = safeJsonParse(raw);\n checkJsonDepth(parsed);\n return parsed;\n }\n};\n\ninterface ParsedAccept {\n mimeType: string;\n quality: number;\n}\n\nfunction parseAcceptHeader(accept: string): ParsedAccept[] {\n return accept\n .split(',')\n .map(part => {\n const trimmed = part.trim();\n const [mimeType, ...params] = trimmed.split(';').map(s => s.trim());\n let quality = 1;\n for (const p of params) {\n const [key, val] = p.split('=');\n if (key?.trim() === 'q' && val) {\n quality = parseFloat(val);\n if (Number.isNaN(quality)) quality = 1;\n }\n }\n return { mimeType: mimeType.toLowerCase(), quality };\n })\n .sort((a, b) => b.quality - a.quality);\n}\n\n/**\n * Selects the appropriate serializer/deserializer for a request or response\n * based on the `Accept` / `Content-Type` HTTP headers.\n *\n * JSON is registered by default. Additional handlers can be added with\n * `register()` or via `ServerBuilder.contentType()`.\n */\nexport class ContentNegotiator {\n readonly #handlers: Map<string, ContentTypeHandler> = new Map();\n\n constructor() {\n this.register(JSON_HANDLER);\n }\n\n /**\n * Register a new content type handler.\n * If a handler for the same MIME type was already registered it is replaced.\n */\n register(handler: ContentTypeHandler): void {\n this.#handlers.set(handler.mimeType.toLowerCase(), handler);\n }\n\n /**\n * Select the best response serializer for the given `Accept` header value.\n *\n * Returns `null` if no registered handler can satisfy the request;\n * the server will respond with 406 Not Acceptable in that case.\n */\n selectResponseHandler(acceptHeader?: string): ContentTypeHandler | null {\n if (!acceptHeader)\n return this.#handlers.get('application/json') ?? null;\n\n const parsed = parseAcceptHeader(acceptHeader);\n for (const { mimeType } of parsed) {\n if (mimeType === '*/*') {\n return this.#handlers.get('application/json') ?? null;\n }\n const handler = this.#handlers.get(mimeType);\n if (handler) return handler;\n }\n\n return null;\n }\n\n /**\n * Select the deserializer for an incoming `Content-Type` header.\n *\n * Returns `null` if the content type is not recognised; the server will\n * respond with 415 Unsupported Media Type in that case.\n */\n selectRequestHandler(\n contentTypeHeader?: string\n ): ContentTypeHandler | null {\n if (!contentTypeHeader) return null;\n\n // Extract mime type (ignore charset, boundary, etc.)\n const mimeType = contentTypeHeader.split(';')[0].trim().toLowerCase();\n return this.#handlers.get(mimeType) ?? null;\n }\n}\n","import type { RequestContext } from './RequestContext.js';\nimport type { Middleware } from './types.js';\n\n/**\n * Executes a chain of {@link Middleware} functions in order, then invokes\n * a final handler when `next()` is called by every middleware in the chain.\n *\n * Middleware can short-circuit the chain by not calling `next()`.\n */\nexport class MiddlewarePipeline {\n readonly #middlewares: Middleware[] = [];\n\n /** Append a middleware to the end of the pipeline. */\n add(middleware: Middleware): void {\n this.#middlewares.push(middleware);\n }\n\n /**\n * Execute the pipeline with the given `context`, calling each middleware\n * in order and finally invoking `finalHandler`.\n */\n async execute(\n context: RequestContext,\n finalHandler: () => Promise<void>\n ): Promise<void> {\n let index = 0;\n\n const next = async (): Promise<void> => {\n if (index < this.#middlewares.length) {\n const middleware = this.#middlewares[index++];\n await middleware(context, next);\n } else {\n await finalHandler();\n }\n };\n\n await next();\n }\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport type { EndpointMetadata } from './Endpoint.js';\nimport type { ProblemDetails, ValidationErrorItem } from './ProblemDetails.js';\nimport { createValidationProblemDetails } from './ProblemDetails.js';\nimport type { RequestContext } from './RequestContext.js';\n\n/**\n * Result returned by `resolveArgs()`. When `valid` is `false` the\n * `problemDetails` payload should be sent as a 400 response.\n */\nexport type ResolveResult =\n | { valid: true; args: unknown[] }\n | { valid: false; problemDetails: ProblemDetails };\n\n/**\n * Returns true if the endpoint declares a body schema.\n */\nexport function needsBody(meta: EndpointMetadata): boolean {\n return meta.bodySchema != null;\n}\n\n/**\n * Resolve the action context object for an endpoint-based handler.\n *\n * Builds `{ context, params?, body?, query?, headers? }` based on\n * what the endpoint declares.\n */\nexport async function resolveArgs(\n meta: EndpointMetadata,\n parsedPath: Record<string, any> | null,\n context: RequestContext,\n parsedBody: unknown\n): Promise<ResolveResult> {\n const errors: ValidationErrorItem[] = [];\n const contextObj: Record<string, unknown> = {};\n\n // Always provide context\n contextObj.context = context;\n\n // Principal — from authentication middleware (if endpoint requires auth)\n if (meta.authRoles !== null && context.principal !== undefined) {\n contextObj.principal = context.principal;\n }\n\n // Params — from parsed path (already validated by ParseStringSchemaBuilder)\n if (parsedPath && Object.keys(parsedPath).length > 0) {\n contextObj.params = parsedPath;\n }\n\n // Body — validate against endpoint's body schema\n if (meta.bodySchema) {\n const result = await meta.bodySchema.validateAsync(parsedBody, {\n doNotStopOnFirstError: true\n });\n if (result.valid) {\n contextObj.body = result.object;\n } else {\n const getInvalidProperties =\n typeof (result as any).getInvalidProperties === 'function'\n ? ((result as any)\n .getInvalidProperties as () => ReadonlyArray<{\n errors: ReadonlyArray<string>;\n descriptor: { toJsonPointer: () => string };\n }>)\n : null;\n\n let errorsAdded = false;\n if (getInvalidProperties) {\n for (const prop of getInvalidProperties()) {\n const pointer = prop.descriptor.toJsonPointer();\n for (const msg of prop.errors) {\n errors.push({\n pointer: `/body${pointer}`,\n detail: msg\n });\n errorsAdded = true;\n }\n }\n }\n // Fallback: required-check failures surface in result.errors\n // but not in the property descriptor map (e.g. null body)\n if (!errorsAdded) {\n for (const err of result.errors ?? []) {\n errors.push({ pointer: '/body', detail: err.message });\n }\n }\n }\n }\n\n // Query — validate against endpoint's query schema\n if (meta.querySchema) {\n const queryIntro = meta.querySchema.introspect() as any;\n if (queryIntro.type !== 'object' || !queryIntro.properties) {\n throw new Error(\n 'Endpoint query schema must be an object schema whose properties map to query parameter names.'\n );\n }\n const queryProps = queryIntro.properties as Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n >;\n const queryObj: Record<string, unknown> = {};\n for (const [qName, qSchema] of Object.entries(queryProps)) {\n const raw = context.queryParams[qName];\n const result = await qSchema.validateAsync(raw, {\n doNotStopOnFirstError: true\n });\n if (result.valid) {\n queryObj[qName] = result.object;\n } else {\n for (const err of result.errors ?? []) {\n errors.push({\n pointer: `/query/${qName}`,\n detail: err.message\n });\n }\n }\n }\n contextObj.query = queryObj;\n }\n\n // Headers — validate against endpoint's header schema\n if (meta.headerSchema) {\n const headersIntro = meta.headerSchema.introspect() as any;\n if (headersIntro.type !== 'object' || !headersIntro.properties) {\n throw new Error(\n 'Endpoint headers schema must be an object schema whose properties map to header names.'\n );\n }\n const headerProps = headersIntro.properties as Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n >;\n const headersObj: Record<string, unknown> = {};\n for (const [hName, hSchema] of Object.entries(headerProps)) {\n const raw = context.headers[hName.toLowerCase()];\n const result = await hSchema.validateAsync(raw, {\n doNotStopOnFirstError: true\n });\n if (result.valid) {\n headersObj[hName] = result.object;\n } else {\n for (const err of result.errors ?? []) {\n errors.push({\n pointer: `/headers/${hName}`,\n detail: err.message\n });\n }\n }\n }\n contextObj.headers = headersObj;\n }\n\n if (errors.length > 0) {\n return {\n valid: false,\n problemDetails: createValidationProblemDetails(errors)\n };\n }\n\n // Services — resolve declared dependencies from the DI container\n if (meta.serviceSchemas) {\n if (!context.services) {\n throw new Error(\n 'Endpoint declares .inject() dependencies but no service provider is available. ' +\n 'Register services via createServer().services() before handling this endpoint.'\n );\n }\n const servicesObj: Record<string, unknown> = {};\n for (const [name, schema] of Object.entries(meta.serviceSchemas)) {\n servicesObj[name] = context.services.get(schema);\n }\n return { valid: true, args: [contextObj, servicesObj] };\n }\n\n return { valid: true, args: [contextObj] };\n}\n","import type { ParseStringSchemaBuilder } from '@cleverbrush/schema';\nimport type {\n EndpointRegistration,\n RouteMatch,\n SubscriptionRegistration\n} from './types.js';\n\ninterface RegisteredRoute {\n readonly basePath: string;\n readonly routePath:\n | string\n | ParseStringSchemaBuilder<any, any, any, any, any>;\n readonly registration: EndpointRegistration;\n}\n\ninterface RegisteredSubscriptionRoute {\n readonly basePath: string;\n readonly routePath:\n | string\n | ParseStringSchemaBuilder<any, any, any, any, any>;\n readonly registration: SubscriptionRegistration;\n}\n\nfunction normalizePath(p: string): string {\n // Use decodeURI (not decodeURIComponent) so that reserved characters such\n // as %2F (encoded slash) are kept encoded and do not alter path segmentation.\n // Throws URIError on malformed percent-encoding – callers that process\n // untrusted input (e.g. match()) must catch that and return a 400.\n const decoded = decodeURI(p);\n if (decoded.length > 1 && decoded.endsWith('/')) {\n return decoded.slice(0, -1);\n }\n return decoded;\n}\n\nfunction isParseStringSchema(\n p: string | ParseStringSchemaBuilder<any, any, any, any, any>\n): p is ParseStringSchemaBuilder<any, any, any, any, any> {\n return typeof p !== 'string' && typeof (p as any).validate === 'function';\n}\n\n/**\n * Radix-style HTTP router that maps method + path to endpoint registrations.\n *\n * Both static string paths (exact-match only) and `ParseStringSchemaBuilder`\n * typed path templates are supported. For dynamic path parameters use\n * `route()` / `parseString()` templates rather than colon-param strings.\n */\nexport class Router {\n readonly #routes: Map<string, RegisteredRoute[]> = new Map();\n readonly #subscriptionRoutes: RegisteredSubscriptionRoute[] = [];\n\n /**\n * Register an endpoint with the router.\n */\n addRoute(registration: EndpointRegistration): void {\n const { method, basePath, pathTemplate } = registration.endpoint;\n const upperMethod = method.toUpperCase();\n const normalizedBase = normalizePath(basePath);\n\n const route: RegisteredRoute = {\n basePath: normalizedBase,\n routePath: pathTemplate,\n registration\n };\n\n if (!this.#routes.has(upperMethod)) {\n this.#routes.set(upperMethod, []);\n }\n this.#routes.get(upperMethod)!.push(route);\n }\n\n /**\n * Match an incoming HTTP method and URL to a registered endpoint.\n *\n * Returns:\n * - `{ match }` — a successful match with parsed path parameters.\n * - `{ match: null, methodNotAllowed: true, allowedMethods }` — path matches\n * but the method does not (405 Method Not Allowed).\n * - `{ match: null, methodNotAllowed: false }` — no match at all (404).\n * - `{ match: null, methodNotAllowed: false, badRequest: true }` — the URL\n * contains malformed percent-encoding (caller should respond with 400).\n */\n match(\n method: string,\n url: string\n ): {\n match: RouteMatch | null;\n methodNotAllowed: boolean;\n badRequest?: boolean;\n allowedMethods?: string[];\n } {\n let normalized: string;\n try {\n normalized = normalizePath(url);\n } catch {\n // URIError from decodeURI – malformed percent-encoding in the URL\n return { match: null, methodNotAllowed: false, badRequest: true };\n }\n const upperMethod = method.toUpperCase();\n\n // Try exact method match first\n const methodRoutes = this.#routes.get(upperMethod);\n if (methodRoutes) {\n for (const route of methodRoutes) {\n const result = this.#tryMatch(route, normalized);\n if (result) return { match: result, methodNotAllowed: false };\n }\n }\n\n // Check if any other method matches this path (405 detection)\n const allowedMethods: string[] = [];\n for (const [m, routes] of this.#routes) {\n if (m === upperMethod) continue;\n for (const route of routes) {\n if (this.#tryMatch(route, normalized)) {\n allowedMethods.push(m);\n break;\n }\n }\n }\n\n if (allowedMethods.length > 0) {\n return { match: null, methodNotAllowed: true, allowedMethods };\n }\n\n return { match: null, methodNotAllowed: false };\n }\n\n #tryMatch(\n route: RegisteredRoute,\n normalizedUrl: string\n ): RouteMatch | null {\n const { basePath, routePath } = route;\n\n // Check basePath prefix\n if (basePath && !normalizedUrl.startsWith(basePath)) {\n return null;\n }\n\n const remainder = basePath\n ? normalizedUrl.slice(basePath.length)\n : normalizedUrl;\n\n if (isParseStringSchema(routePath)) {\n // Dynamic route: validate remainder via parseString schema\n const result = routePath.validate(remainder);\n if (result.valid) {\n return {\n registration: route.registration,\n parsedPath: result.object as Record<string, any>\n };\n }\n return null;\n }\n\n // Static route: exact match\n const normalizedRoutePath = normalizePath(routePath);\n const normalizedRemainder = remainder.length === 0 ? '/' : remainder;\n\n if (normalizedRemainder === normalizedRoutePath) {\n return {\n registration: route.registration,\n parsedPath: null\n };\n }\n\n return null;\n }\n\n // -----------------------------------------------------------------------\n // Subscription routing\n // -----------------------------------------------------------------------\n\n /**\n * Register a subscription endpoint with the router.\n */\n addSubscriptionRoute(registration: SubscriptionRegistration): void {\n const { basePath, pathTemplate } = registration.endpoint;\n const normalizedBase = normalizePath(basePath);\n\n this.#subscriptionRoutes.push({\n basePath: normalizedBase,\n routePath: pathTemplate,\n registration\n });\n }\n\n /**\n * Match an incoming WebSocket upgrade URL to a registered subscription.\n *\n * Returns the matched registration and parsed path params, or `null`.\n */\n matchSubscription(url: string): {\n registration: SubscriptionRegistration;\n parsedPath: Record<string, any> | null;\n } | null {\n let normalized: string;\n try {\n normalized = normalizePath(url);\n } catch {\n return null;\n }\n\n for (const route of this.#subscriptionRoutes) {\n const { basePath, routePath } = route;\n\n if (basePath && !normalized.startsWith(basePath)) {\n continue;\n }\n\n const remainder = basePath\n ? normalized.slice(basePath.length)\n : normalized;\n\n if (isParseStringSchema(routePath)) {\n const result = routePath.validate(remainder);\n if (result.valid) {\n return {\n registration: route.registration,\n parsedPath: result.object as Record<string, any>\n };\n }\n continue;\n }\n\n const normalizedRoutePath = normalizePath(routePath);\n const normalizedRemainder =\n remainder.length === 0 ? '/' : remainder;\n\n if (normalizedRemainder === normalizedRoutePath) {\n return {\n registration: route.registration,\n parsedPath: null\n };\n }\n }\n\n return null;\n }\n}\n","/**\n * Lightweight virtual HTTP request/response objects used by the batch\n * endpoint handler to process sub-requests through the normal server pipeline\n * without spawning additional HTTP connections.\n *\n * @internal\n */\n\nimport { Readable, Writable } from 'node:stream';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Initialisation options for a {@link VirtualIncomingMessage}.\n */\nexport interface VirtualRequestInit {\n /** HTTP method, e.g. `'GET'` or `'POST'`. */\n method: string;\n /**\n * URL path and optional query string, e.g. `/api/todos?page=1`.\n * The value is passed verbatim to `RequestContext` as `request.url`.\n */\n url: string;\n /** Request headers, typically forwarded from the outer batch request. */\n headers?: Record<string, string>;\n /**\n * Raw body string (the JSON-serialised body that would have been sent as\n * the HTTP body). Absent for methods that carry no body.\n */\n body?: string;\n}\n\n/**\n * The captured result of a virtualised HTTP response.\n */\nexport interface VirtualResult {\n status: number;\n headers: Record<string, string>;\n /** Raw response body (JSON string or plain text). */\n body: string;\n}\n\n// ---------------------------------------------------------------------------\n// VirtualIncomingMessage\n// ---------------------------------------------------------------------------\n\n/**\n * A `Readable` that mimics the subset of `http.IncomingMessage` consumed\n * by `RequestContext` and `Server.#handleRequest()`.\n *\n * When pushed to, it emits the body buffer and then signals EOF.\n */\nexport class VirtualIncomingMessage extends Readable {\n readonly method: string;\n readonly url: string;\n readonly headers: Record<string, string>;\n // Satisfy the `socket` property that IncomingMessage exposes.\n readonly socket: null = null;\n\n readonly #body: Buffer;\n #pushed = false;\n\n constructor(init: VirtualRequestInit) {\n super();\n this.method = init.method;\n this.url = init.url;\n // Ensure a `host` header is present so that RequestContext can parse\n // the URL correctly (it uses `http://${req.headers.host}` as the base).\n // Lowercase all keys to match Node.js http.IncomingMessage behaviour,\n // which normalises header names to lower-case before exposing them.\n const lowercased: Record<string, string> = {};\n for (const [key, value] of Object.entries(init.headers ?? {})) {\n lowercased[key.toLowerCase()] = value;\n }\n this.headers = { host: 'localhost', ...lowercased };\n this.#body =\n init.body != null && init.body.length > 0\n ? Buffer.from(init.body, 'utf-8')\n : Buffer.alloc(0);\n }\n\n override _read(): void {\n if (!this.#pushed) {\n this.#pushed = true;\n if (this.#body.length > 0) {\n this.push(this.#body);\n }\n this.push(null); // EOF\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// VirtualServerResponse\n// ---------------------------------------------------------------------------\n\n/**\n * A `Writable` that captures the subset of `http.ServerResponse` calls made\n * by `Server.#handleRequest()` and `ActionResult.executeAsync()`.\n *\n * After the handler finishes, call {@link toResult} to retrieve the status\n * code, headers, and body as plain values.\n */\nexport class VirtualServerResponse extends Writable {\n statusCode = 200;\n headersSent = false;\n\n readonly #chunks: Buffer[] = [];\n readonly #customHeaders: Record<string, string> = {};\n #customStatus = 200;\n\n // -----------------------------------------------------------------------\n // Writable interface — captures data written via readable.pipe(res)\n // -----------------------------------------------------------------------\n\n override _write(\n chunk: Buffer | string,\n _encoding: BufferEncoding,\n callback: (err?: Error | null) => void\n ): void {\n this.#chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n callback();\n }\n\n // -----------------------------------------------------------------------\n // http.ServerResponse surface\n // -----------------------------------------------------------------------\n\n /**\n * Sets the status code and optional response headers.\n * Mirrors `ServerResponse.writeHead()`.\n */\n writeHead(\n status: number,\n headers?: Record<string, string | string[] | number> | string | string[]\n ): this {\n this.#customStatus = status;\n this.statusCode = status;\n if (\n headers != null &&\n typeof headers === 'object' &&\n !Array.isArray(headers)\n ) {\n for (const [k, v] of Object.entries(\n headers as Record<string, string | string[] | number>\n )) {\n this.#customHeaders[k.toLowerCase()] = Array.isArray(v)\n ? v.join(', ')\n : String(v);\n }\n }\n this.headersSent = true;\n return this;\n }\n\n /**\n * Sets a single response header.\n * Mirrors `ServerResponse.setHeader()`.\n */\n setHeader(name: string, value: string | number | string[]): this {\n this.#customHeaders[name.toLowerCase()] = Array.isArray(value)\n ? value.join(', ')\n : String(value);\n return this;\n }\n\n /**\n * Returns a previously set response header.\n * Mirrors `ServerResponse.getHeader()`.\n */\n getHeader(name: string): string | undefined {\n return this.#customHeaders[name.toLowerCase()];\n }\n\n /**\n * Captures the body and signals the end of the response.\n *\n * Handles the three call forms used by `#handleRequest` and\n * `ActionResult.executeAsync()`:\n * - `end()` — no body\n * - `end(data)` — body is a `string`, `Buffer`, or `Uint8Array`\n * - `end(callback)` — end with callback (body already captured via pipe)\n */\n override end(chunk?: unknown, ...rest: unknown[]): this {\n if (chunk != null && typeof chunk !== 'function') {\n const buf =\n Buffer.isBuffer(chunk) || chunk instanceof Uint8Array\n ? Buffer.from(chunk as Uint8Array)\n : Buffer.from(String(chunk), 'utf-8');\n this.#chunks.push(buf);\n // Call super.end() without the chunk (we already captured it).\n super.end(...(rest as []));\n } else if (typeof chunk === 'function') {\n super.end(chunk);\n } else {\n super.end(...(rest as []));\n }\n return this;\n }\n\n // -----------------------------------------------------------------------\n // Result extraction\n // -----------------------------------------------------------------------\n\n /**\n * Returns the captured status, headers, and body as a plain object\n * suitable for embedding in a batch response.\n */\n toResult(): VirtualResult {\n return {\n status: this.#customStatus,\n headers: { ...this.#customHeaders },\n body: Buffer.concat(this.#chunks).toString('utf-8')\n };\n }\n}\n","/**\n * WebSocket framing protocol for subscription endpoints.\n *\n * Client→Server:\n * ```json\n * { \"type\": \"message\", \"data\": <incoming payload> }\n * { \"type\": \"ping\" }\n * ```\n *\n * Server→Client:\n * ```json\n * { \"type\": \"message\", \"data\": <outgoing payload> }\n * { \"type\": \"tracked\", \"id\": \"<string>\", \"data\": <outgoing payload> }\n * { \"type\": \"pong\" }\n * { \"type\": \"error\", \"code\": <number>, \"message\": \"<string>\" }\n * ```\n *\n * @module\n * @internal\n */\n\nimport { checkJsonDepth, safeJsonParse } from './safeJson.js';\n\n// ---------------------------------------------------------------------------\n// Client → Server frame types\n// ---------------------------------------------------------------------------\n\nexport interface ClientMessageFrame {\n readonly type: 'message';\n readonly data: unknown;\n}\n\nexport interface ClientPingFrame {\n readonly type: 'ping';\n}\n\nexport type ClientFrame = ClientMessageFrame | ClientPingFrame;\n\n// ---------------------------------------------------------------------------\n// Server → Client frame types\n// ---------------------------------------------------------------------------\n\nexport interface ServerMessageFrame {\n readonly type: 'message';\n readonly data: unknown;\n}\n\nexport interface ServerTrackedFrame {\n readonly type: 'tracked';\n readonly id: string;\n readonly data: unknown;\n}\n\nexport interface ServerPongFrame {\n readonly type: 'pong';\n}\n\nexport interface ServerErrorFrame {\n readonly type: 'error';\n readonly code: number;\n readonly message: string;\n}\n\nexport type ServerFrame =\n | ServerMessageFrame\n | ServerTrackedFrame\n | ServerPongFrame\n | ServerErrorFrame;\n\n// ---------------------------------------------------------------------------\n// Frame constructors\n// ---------------------------------------------------------------------------\n\nexport function messageFrame(data: unknown): ServerMessageFrame {\n return { type: 'message', data };\n}\n\nexport function trackedFrame(id: string, data: unknown): ServerTrackedFrame {\n return { type: 'tracked', id, data };\n}\n\nexport function pongFrame(): ServerPongFrame {\n return { type: 'pong' };\n}\n\nexport function errorFrame(code: number, message: string): ServerErrorFrame {\n return { type: 'error', code, message };\n}\n\n// ---------------------------------------------------------------------------\n// Client frame parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a raw WebSocket text message into a typed client frame.\n * Returns `null` if the message is not valid JSON or not a known frame type.\n */\nexport function parseClientFrame(raw: string): ClientFrame | null {\n let parsed: unknown;\n try {\n parsed = safeJsonParse(raw);\n checkJsonDepth(parsed);\n } catch {\n return null;\n }\n\n if (typeof parsed !== 'object' || parsed === null) return null;\n\n const obj = parsed as Record<string, unknown>;\n if (obj.type === 'ping') return { type: 'ping' };\n if (obj.type === 'message' && 'data' in obj) {\n return { type: 'message', data: obj.data };\n }\n\n return null;\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\n\n/**\n * Describes an out-of-band webhook that your API can send to consumers.\n *\n * Pass instances to `ServerBuilder.webhook()` so that\n * `@cleverbrush/server-openapi` can emit them inside the `webhooks` map of\n * the generated OpenAPI document.\n *\n * @example\n * ```ts\n * const userCreatedWebhook = defineWebhook('userCreated', {\n * method: 'POST',\n * summary: 'Fired when a new user is created',\n * body: object({ id: number(), email: string() }),\n * });\n * ```\n */\nexport interface WebhookDefinition {\n /** Unique webhook name used as the key in the `webhooks` map. */\n readonly name: string;\n /** HTTP method sent to the consumer endpoint (default: `'POST'`). */\n readonly method?: string;\n /** Short summary for OpenAPI documentation. */\n readonly summary?: string;\n /** Longer description for OpenAPI documentation. Supports Markdown. */\n readonly description?: string;\n /** Tags to group this webhook in generated documentation. */\n readonly tags?: readonly string[];\n /** Schema describing the webhook request payload. */\n readonly body?: SchemaBuilder<any, any, any, any, any>;\n /** Schema describing the expected response from the consumer. */\n readonly response?: SchemaBuilder<any, any, any, any, any>;\n}\n\n/**\n * Convenience factory for creating {@link WebhookDefinition} objects.\n *\n * @param name - Unique key for this webhook in the `webhooks` map.\n * @param options - Webhook configuration (all fields except `name`).\n */\nexport function defineWebhook(\n name: string,\n options: Omit<WebhookDefinition, 'name'>\n): WebhookDefinition {\n return { name, ...options };\n}\n"],"mappings":"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","names":["ActionResult","body","headers","JsonResult","location","h","NoContentResult","url","permanent","RedirectResult","status","content","fileName","contentType","FileResult","ContentResult","readable","StreamResult","StatusCodeResult","_req","res","_contentNegotiator","key","value","resolve","reject","STATUS_TITLES","createProblemDetails","status","title","detail","extensions","createValidationProblemDetails","errors","serializeProblemDetails","pd","PROBLEM_JSON_CONTENT_TYPE","HttpError","status","title","detail","extensions","createProblemDetails","NotFoundError","BadRequestError","UnauthorizedError","ForbiddenError","ConflictError","URL","any","boolean","func","object","promise","record","string","safeJsonParse","raw","key","value","checkJsonDepth","maxDepth","walk","current","max","item","v","IRequestContext","object","string","record","any","func","promise","boolean","DEFAULT_MAX_BODY_SIZE","RequestContext","#pathParams","#services","#bodyBuffer","#bodyRead","#jsonCache","#jsonParsed","request","response","maxBodySize","rawUrl","URL","headers","key","value","params","resolve","reject","chunks","totalSize","chunk","HttpError","text","safeJsonParse","checkJsonDepth","http","https","AuthorizationService","PolicyBuilder","Principal","parseCookies","requireRole","ServiceCollection","WebSocketServer","JSON_HANDLER","value","raw","parsed","safeJsonParse","checkJsonDepth","parseAcceptHeader","accept","part","trimmed","mimeType","params","s","quality","p","key","val","a","b","ContentNegotiator","#handlers","handler","acceptHeader","contentTypeHeader","MiddlewarePipeline","#middlewares","middleware","context","finalHandler","index","next","needsBody","meta","resolveArgs","parsedPath","context","parsedBody","errors","contextObj","result","getInvalidProperties","errorsAdded","prop","pointer","msg","err","queryIntro","queryProps","queryObj","qName","qSchema","raw","headersIntro","headerProps","headersObj","hName","hSchema","createValidationProblemDetails","servicesObj","name","schema","normalizePath","p","decoded","isParseStringSchema","Router","#routes","#subscriptionRoutes","registration","method","basePath","pathTemplate","upperMethod","route","url","normalized","methodRoutes","result","#tryMatch","allowedMethods","m","routes","normalizedUrl","routePath","remainder","normalizedRoutePath","normalizedBase","Readable","Writable","VirtualIncomingMessage","#body","#pushed","init","lowercased","key","value","VirtualServerResponse","#chunks","#customHeaders","#customStatus","chunk","_encoding","callback","status","headers","k","v","name","rest","buf","messageFrame","data","trackedFrame","id","pongFrame","errorFrame","code","message","parseClientFrame","raw","parsed","safeJsonParse","checkJsonDepth","obj","ServerBuilder","#serviceCollection","ServiceCollection","#registrations","#subscriptionRegistrations","#webhooks","#globalMiddlewares","#contentNegotiator","ContentNegotiator","#options","#authConfig","#authzConfig","#healthcheck","#batchConfig","configureFn","middleware","handler","config","options","endpointDef","mapping","entry","def","port","host","router","Router","reg","serviceProvider","authMiddlewares","createAuthenticationMiddleware","policies","name","builder","PolicyBuilder","authzService","AuthorizationService","createAuthorizationMiddleware","allMiddlewares","server","Server","listenPort","listenHost","MAX_WS_QUEUE_SIZE","#router","#serviceProvider","#hasSubscriptions","#maxBodySize","#httpServer","#wss","#activeConnections","contentNegotiator","globalMiddlewares","healthcheck","hasSubscriptions","batchConfig","maxBodySize","DEFAULT_MAX_BODY_SIZE","req","res","#handleRequest","_err","PROBLEM_JSON_CONTENT_TYPE","serializeProblemDetails","createProblemDetails","resolve","WebSocketServer","socket","head","urlPath","result","ws","#handleWebSocket","reject","err","addr","scope","ctx","RequestContext","method","#handleBatchRequest","routeResult","pd","registration","parsedPath","meta","rawParams","flattenToStrings","pipeline","MiddlewarePipeline","mw","parsedBody","needsBody","contentType","ctHandler","bodyText","resolveResult","resolveArgs","#sendResult","HttpError","maxSize","parallel","outerBody","raw","readBuffer","parsed","safeJsonParse","checkJsonDepth","execute","item","virtualReq","VirtualIncomingMessage","virtualRes","VirtualServerResponse","results","ActionResult","JsonResult","abortController","dummyRes","authPipeline","#runSubscription","incomingQueue","incomingResolve","incomingDone","incoming","text","frame","parseClientFrame","errorFrame","pongFrame","errors","e","subscriptionCtx","queryObj","k","v","handlerArgs","services","key","schema","generator","value","frameToSend","isTrackedEvent","outData","trackedFrame","messageFrame","createServer","chunks","totalSize","chunk","obj","prefix","fullKey","schemeMap","scheme","next","Principal","authCtx","parseCookies","authConfig","challengeHeaders","ch","principal","headers","requireRole","defineWebhook","name","options"]}