@cleverbrush/server 4.5.0 → 5.1.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/safeJson.ts","../src/ContentNegotiator.ts","../src/ProblemDetails.ts","../src/HttpError.ts","../src/middlewares/Idempotency.ts","../src/middlewares/ResponseCache.ts","../src/RequestContext.ts","../src/Server.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 * Handler used by {@link ActionResult.raw}.\n *\n * The handler receives the native Node request and response and is responsible\n * for writing headers/body as needed.\n */\nexport type RawResultHandler = (\n req: http.IncomingMessage,\n res: http.ServerResponse\n) => void | Promise<void>;\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 /**\n * Execute a raw Node request/response handler.\n *\n * Use this for integrations that already know how to write to\n * `http.ServerResponse`, such as webhook libraries or compatibility\n * adapters. The callback is responsible for ending the response when it\n * writes a body.\n */\n static raw(handler: RawResultHandler): RawResult {\n return new RawResult(handler);\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// RawResult\n// ---------------------------------------------------------------------------\n\n/**\n * Runs a native Node request/response callback.\n * Created by `ActionResult.raw()`.\n */\nexport class RawResult extends ActionResult {\n readonly handler: RawResultHandler;\n\n constructor(handler: RawResultHandler) {\n super();\n this.handler = handler;\n }\n\n async executeAsync(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n await this.handler(req, res);\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 const filename = this.fileName;\n const sanitized = filename.replace(/[^\\x20-\\x7E]/g, '_');\n const disposition =\n sanitized === filename\n ? `attachment; filename=\"${filename}\"`\n : `attachment; filename=\"${sanitized}\"; filename*=UTF-8''${encodeURIComponent(filename)}`;\n res.writeHead(200, {\n 'content-type': this.contentType,\n 'content-disposition': disposition,\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 * 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 { checkJsonDepth, safeJsonParse } from './safeJson.js';\nimport type { ContentTypeHandler } from './types.js';\n\n/**\n * Built-in JSON content type handler.\n */\nexport const jsonContentTypeHandler: 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\n/**\n * Built-in `application/x-www-form-urlencoded` content type handler.\n *\n * Repeated fields are deserialized as arrays in insertion order:\n * `tag=a&tag=b` becomes `{ tag: ['a', 'b'] }`.\n */\nexport const formUrlEncodedContentTypeHandler: ContentTypeHandler = {\n mimeType: 'application/x-www-form-urlencoded',\n serialize(value: unknown): string {\n const params = new URLSearchParams();\n if (value && typeof value === 'object') {\n for (const [key, item] of Object.entries(\n value as Record<string, unknown>\n )) {\n if (item === undefined || item === null) continue;\n if (Array.isArray(item)) {\n for (const nested of item) {\n params.append(key, String(nested));\n }\n } else {\n params.append(key, String(item));\n }\n }\n }\n return params.toString();\n },\n deserialize(raw: string): unknown {\n const result: Record<string, string | string[]> = {};\n const params = new URLSearchParams(raw);\n\n for (const [key, value] of params) {\n if (isUnsafeFormKey(key)) continue;\n\n const existing = result[key];\n if (existing === undefined) {\n result[key] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n result[key] = [existing, value];\n }\n }\n\n return result;\n }\n};\n\nfunction isUnsafeFormKey(key: string): boolean {\n return key === '__proto__' || key === 'constructor' || key === 'prototype';\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(jsonContentTypeHandler);\n this.register(formUrlEncodedContentTypeHandler);\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","// ---------------------------------------------------------------------------\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","/**\n * Server-side idempotency middleware.\n *\n * Ensures mutating requests with the same idempotency key produce the\n * same result exactly once — subsequent replays return the stored\n * response without re-executing the handler.\n *\n * @module\n */\n\nimport type { ServerResponse } from 'node:http';\nimport type { RequestContext } from '../RequestContext.js';\nimport type { Middleware } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for {@link idempotency}.\n */\nexport interface ServerIdempotencyOptions {\n /**\n * TTL in milliseconds for stored responses.\n * Defaults to `86_400_000` (24 hours).\n */\n ttl?: number;\n\n /**\n * Header name to read the idempotency key from.\n * Defaults to `\"x-idempotency-key\"`.\n */\n headerName?: string;\n\n /**\n * Predicate that decides whether a request should be skipped.\n * Defaults to skipping non-mutating requests (GET, HEAD, OPTIONS).\n */\n skip?: (ctx: RequestContext) => boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ninterface StoredResponse {\n status: number;\n headers: Record<string, string | string[] | undefined>;\n body: Buffer;\n expiresAt: number;\n}\n\nfunction isMutating(method: string): boolean {\n return ['POST', 'PUT', 'DELETE', 'PATCH'].includes(method.toUpperCase());\n}\n\nconst CLEANUP_INTERVAL = 60_000;\n\n// ---------------------------------------------------------------------------\n// Middleware\n// ---------------------------------------------------------------------------\n\n/**\n * Server-side idempotency middleware.\n *\n * Reads the `x-idempotency-key` header from mutating requests. If a\n * response has already been stored for that key, it is returned\n * immediately — the handler is never called. Otherwise the handler\n * executes and its response is stored for future replays.\n *\n * GET, HEAD, and OPTIONS requests pass through without checking.\n *\n * @param options - Configuration.\n * @returns A server-side {@link Middleware}.\n *\n * @example\n * ```ts\n * server.handle(CreateTodo, createHandler, {\n * middlewares: [idempotency({ ttl: 86_400_000 })]\n * });\n * ```\n */\nexport function idempotency(\n options: ServerIdempotencyOptions = {}\n): Middleware {\n const {\n ttl = 86_400_000,\n headerName = 'x-idempotency-key',\n skip = (ctx: RequestContext) => !isMutating(ctx.method)\n } = options;\n\n const store = new Map<string, StoredResponse>();\n\n // Periodic cleanup of expired entries\n const cleanupTimer = setInterval(() => {\n const now = Date.now();\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) {\n store.delete(key);\n }\n }\n }, CLEANUP_INTERVAL);\n\n if (cleanupTimer.unref) {\n cleanupTimer.unref();\n }\n\n return async (ctx: RequestContext, next: () => Promise<void>) => {\n if (skip(ctx)) {\n return next();\n }\n\n const key =\n ctx.headers[headerName] ?? ctx.headers[headerName.toLowerCase()];\n if (!key || typeof key !== 'string' || key.length === 0) {\n return next();\n }\n\n // Check if we already have a stored response for this key\n const stored = store.get(key);\n if (stored) {\n if (stored.expiresAt <= Date.now()) {\n store.delete(key);\n // Expired — fall through to handler\n } else {\n // Replay stored response\n const res = ctx.response as ServerResponse;\n res.writeHead(stored.status, stored.headers);\n res.end(stored.body);\n ctx.responded = true;\n return;\n }\n }\n\n // Capture the handler's response for future replays\n const originalWriteHead = (\n ctx.response as ServerResponse\n ).writeHead.bind(ctx.response);\n const originalEnd = (ctx.response as ServerResponse).end.bind(\n ctx.response\n );\n\n let capturedStatus = 200;\n let capturedHeaders: Record<string, string | string[] | undefined> = {};\n const chunks: Buffer[] = [];\n\n (ctx.response as ServerResponse).writeHead = function (\n this: ServerResponse,\n statusCode: number,\n ...args: any[]\n ) {\n capturedStatus = statusCode;\n if (args.length > 0) {\n capturedHeaders = args[0];\n }\n return originalWriteHead(statusCode, ...args) as ServerResponse;\n } as any;\n\n (ctx.response as ServerResponse).end = function (\n this: ServerResponse,\n chunk?: any,\n ...args: any[]\n ) {\n if (chunk) {\n chunks.push(\n Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n );\n }\n return originalEnd(chunk, ...args) as ServerResponse;\n } as any;\n\n await next();\n\n // Store the response for future replays\n if (capturedStatus >= 200 && capturedStatus < 500) {\n const body = Buffer.concat(chunks);\n store.set(key, {\n status: capturedStatus,\n headers: capturedHeaders,\n body,\n expiresAt: Date.now() + ttl\n });\n }\n };\n}\n","/**\n * Server-side cache response middleware.\n *\n * Caches successful handler responses keyed by endpoint-defined cache tags.\n * On cache hit, the response is served directly — the handler never runs.\n * Mutating requests invalidate matching cache entries after the handler\n * completes successfully.\n *\n * @module\n */\n\nimport type { ServerResponse } from 'node:http';\nimport type { CacheTagDefinition } from '../CacheTag.js';\nimport { computeCacheKey } from '../cacheKey.js';\nimport type { RequestContext } from '../RequestContext.js';\nimport type { Middleware } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for {@link cacheResponse}.\n */\nexport interface ServerCacheOptions {\n /**\n * Default TTL in milliseconds for tags without an explicit TTL.\n * Defaults to `60000` (60 seconds).\n */\n defaultTtl?: number;\n\n /**\n * Per-tag TTL overrides: `{ [tagName]: ttlMs }`.\n */\n ttlByTag?: Record<string, number>;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry {\n status: number;\n headers: Record<string, string | string[] | undefined>;\n body: Buffer;\n expiresAt: number;\n generations: ReadonlyArray<readonly [string, number]>;\n}\n\nfunction isMutating(method: string): boolean {\n return ['POST', 'PUT', 'DELETE', 'PATCH'].includes(method.toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Middleware\n// ---------------------------------------------------------------------------\n\n/**\n * Server-side cache response middleware.\n *\n * Uses cache-tag definitions from the matched endpoint (already available\n * on `ctx.items.__endpoint_meta.cacheTags`) to compute deterministic cache\n * keys from request data (params, query, body, headers).\n *\n * - **GET**: Computes cache key → serves cached response if valid →\n * handler never executes. On cache miss, runs the handler and caches\n * the response.\n * - **Mutation (POST/PUT/PATCH/DELETE)**: Lets the handler run, then\n * invalidates all cache entries whose tag names start with any of the\n * endpoint's cache tag names. Older in-flight reads cannot refill them.\n *\n * @param options - Cache configuration.\n * @returns A server-side {@link Middleware}.\n *\n * @example\n * ```ts\n * server.handle(ListTodos, listHandler, {\n * middlewares: [cacheResponse({ defaultTtl: 30_000 })]\n * });\n * ```\n */\nexport function cacheResponse(options: ServerCacheOptions = {}): Middleware {\n const { ttlByTag = {}, defaultTtl = 60_000 } = options;\n\n const cache = new Map<string, CacheEntry>();\n const generations = new Map<string, number>();\n const isCurrent = (snapshot: CacheEntry['generations']) =>\n snapshot.every(\n ([name, generation]) => generations.get(name) === generation\n );\n\n return async (ctx: RequestContext, next: () => Promise<void>) => {\n const meta = ctx.items.get('__endpoint_meta') as any;\n const tags: readonly CacheTagDefinition[] = meta?.cacheTags ?? [];\n\n if (tags.length === 0) {\n return next();\n }\n\n if (isMutating(ctx.method)) {\n const names = tags.map(tag => tag.name);\n // Run handler first (so cache is invalidated only on success)\n await next();\n\n if (\n (ctx.response as ServerResponse).statusCode >= 200 &&\n (ctx.response as ServerResponse).statusCode < 300\n ) {\n for (const [name, generation] of generations) {\n if (names.some(prefix => name.startsWith(prefix))) {\n generations.set(name, generation + 1);\n }\n }\n for (const [key, entry] of cache) {\n if (!isCurrent(entry.generations)) cache.delete(key);\n }\n }\n return;\n }\n\n if (ctx.method === 'GET') {\n // Build root for key computation\n const root = {\n params: ctx.pathParams ?? {},\n body: undefined,\n query: ctx.queryParams ?? {},\n headers: ctx.headers ?? {}\n };\n\n const keys = tags.map(tag => computeCacheKey(tag, root));\n const snapshot = tags.map(({ name }) => {\n if (!generations.has(name)) generations.set(name, 0);\n return [name, generations.get(name)!] as const;\n });\n\n // Check all keys — first valid cache hit wins\n for (const key of keys) {\n const entry = cache.get(key);\n if (\n entry &&\n entry.expiresAt > Date.now() &&\n isCurrent(entry.generations)\n ) {\n // Serve from cache\n const res = ctx.response as ServerResponse;\n res.writeHead(entry.status, entry.headers);\n res.end(entry.body);\n ctx.responded = true;\n return;\n }\n if (entry) {\n cache.delete(key);\n }\n }\n\n // Cache miss — run handler, then capture the response\n const originalWriteHead = (\n ctx.response as ServerResponse\n ).writeHead.bind(ctx.response);\n const originalEnd = (ctx.response as ServerResponse).end.bind(\n ctx.response\n );\n\n let capturedStatus = 200;\n let capturedHeaders: Record<string, string | string[] | undefined> =\n {};\n const chunks: Buffer[] = [];\n\n (ctx.response as ServerResponse).writeHead = function (\n this: ServerResponse,\n statusCode: number,\n ...args: any[]\n ) {\n capturedStatus = statusCode;\n if (args.length > 0) {\n capturedHeaders = args[0];\n }\n return originalWriteHead(statusCode, ...args) as ServerResponse;\n } as any;\n\n (ctx.response as ServerResponse).end = function (\n this: ServerResponse,\n chunk?: any,\n ...args: any[]\n ) {\n if (chunk) {\n chunks.push(\n Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n );\n }\n return originalEnd(chunk, ...args) as ServerResponse;\n } as any;\n\n await next();\n\n // Store in cache on success\n if (\n capturedStatus >= 200 &&\n capturedStatus < 300 &&\n isCurrent(snapshot)\n ) {\n const body = Buffer.concat(chunks);\n const ttl = tags.reduce((max, tag) => {\n const t =\n ttlByTag[tag.name] !== undefined\n ? ttlByTag[tag.name]\n : defaultTtl;\n return t > max ? t : max;\n }, 0);\n\n if (ttl > 0) {\n for (const key of keys) {\n cache.set(key, {\n status: capturedStatus,\n headers: capturedHeaders,\n body,\n expiresAt: Date.now() + ttl,\n generations: snapshot\n });\n }\n }\n }\n return;\n }\n\n // Non-GET, non-mutation — pass through\n return next();\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","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 { Busboy } from '@fastify/busboy';\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 FilePart,\n Middleware,\n RejectedFile,\n ServerBatchingOptions,\n ServerOptions,\n SubscriptionRegistration,\n UploadOptions\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 * Optional ordered authentication fallback.\n *\n * By default, only `defaultScheme` is attempted. Set to `'all'` to try\n * every registered scheme in registration order, or pass scheme names to\n * try a specific ordered subset.\n */\n trySchemes?: 'all' | readonly string[];\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// Multipart / file-upload helpers\n// ---------------------------------------------------------------------------\n\nasync function parseMultipart(\n req: http.IncomingMessage,\n options: UploadOptions\n): Promise<{\n fields: Record<string, string>;\n files: Record<string, FilePart>;\n rejectedFiles: RejectedFile[];\n}> {\n const maxFileCount = options.maxFileCount ?? 10;\n const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;\n const allowedMimeTypes = options.allowedMimeTypes;\n\n return new Promise((resolve, reject) => {\n const fields: Record<string, string> = {};\n const files: Record<string, FilePart> = {};\n const rejectedFiles: RejectedFile[] = [];\n let fileCount = 0;\n\n const busboy = Busboy({\n headers: req.headers as {\n 'content-type': string;\n } & http.IncomingHttpHeaders,\n limits: {\n fileSize: maxFileSize,\n files: maxFileCount\n }\n });\n\n busboy.on('field', (fieldname: string, value: string) => {\n fields[fieldname] = value;\n });\n\n busboy.on(\n 'file',\n (\n fieldname: string,\n stream: import('@fastify/busboy').BusboyFileStream,\n filename: string,\n _transferEncoding: string,\n mimeType: string\n ) => {\n if (fileCount >= maxFileCount) {\n rejectedFiles.push({\n filename,\n mimeType,\n reason: `Exceeded max file count (${maxFileCount})`\n });\n stream.resume();\n return;\n }\n\n if (allowedMimeTypes) {\n const allowed = allowedMimeTypes.some(pattern => {\n if (pattern.endsWith('/*')) {\n return mimeType.startsWith(pattern.slice(0, -1));\n }\n return mimeType === pattern;\n });\n if (!allowed) {\n rejectedFiles.push({\n filename,\n mimeType,\n reason: `MIME type \"${mimeType}\" not allowed (allowed: ${allowedMimeTypes.join(', ')})`\n });\n stream.resume();\n return;\n }\n }\n\n fileCount++;\n const chunks: Buffer[] = [];\n\n stream.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n stream.on('end', () => {\n const buffer = Buffer.concat(chunks);\n files[fieldname] = {\n filename,\n mimeType,\n buffer,\n size: buffer.length\n };\n });\n\n stream.on('error', reject);\n }\n );\n\n busboy.on('error', reject);\n busboy.on('finish', () => resolve({ fields, files, rejectedFiles }));\n\n req.pipe(busboy);\n });\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 constructor(options: ServerOptions = {}) {\n this.#options = options;\n }\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 router.finalize();\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 const batchSubrequest = (\n req as http.IncomingMessage & {\n __cleverbrushBatchSubrequest?: {\n index: number;\n size: number;\n path: string;\n };\n }\n ).__cleverbrushBatchSubrequest;\n if (batchSubrequest) {\n ctx.items.set('__batch_subrequest', batchSubrequest);\n }\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 — it has no endpoint metadata, but should still\n // pass through global middleware so observability/logging can wrap\n // the physical batch request.\n if (\n this.#batchConfig !== null &&\n method === 'POST' &&\n urlPath === (this.#batchConfig.path ?? '/__batch')\n ) {\n ctx.services = scope.serviceProvider;\n\n const pipeline = new MiddlewarePipeline();\n for (const mw of this.#globalMiddlewares) {\n pipeline.add(mw);\n }\n\n await pipeline.execute(ctx, async () => {\n if (ctx.responded) return;\n await this.#handleBatchRequest(req, res);\n ctx.responded = true;\n });\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 let uploadedFiles: Record<string, FilePart> | undefined;\n let rejectedFiles: RejectedFile[] | undefined;\n if (needsBody(meta)) {\n const contentType = req.headers['content-type'] ?? '';\n\n // Multipart / file-upload path\n if (\n meta.fileUpload &&\n contentType.startsWith('multipart/form-data')\n ) {\n try {\n const result = await parseMultipart(\n req,\n meta.fileUpload\n );\n parsedBody = result.fields;\n uploadedFiles = result.files;\n rejectedFiles = result.rejectedFiles;\n } catch {\n const pd = createProblemDetails(\n 400,\n 'Malformed multipart request'\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 } else if (meta.fileUpload) {\n const pd = createProblemDetails(\n 400,\n 'File upload endpoint requires multipart/form-data content type'\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 } else {\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 =\n ctHandler.deserialize(bodyText);\n } catch {\n const pd = createProblemDetails(\n 400,\n 'Malformed request body'\n );\n res.writeHead(400, {\n 'content-type':\n 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\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 // Inject uploaded files into the action context\n if (uploadedFiles && resolveResult.args.length > 0) {\n const ctx = resolveResult.args[0] as Record<\n string,\n unknown\n >;\n ctx.files = uploadedFiles;\n if (rejectedFiles && rejectedFiles.length > 0) {\n ctx.rejectedFiles = rejectedFiles;\n }\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 index: number\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 virtualReq.__cleverbrushBatchSubrequest = {\n index,\n size: outerBody.requests.length,\n path: config.path ?? '/__batch'\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 (let i = 0; i < outerBody.requests.length; i++) {\n results.push(await execute(outerBody.requests[i]!, i));\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 return new ServerBuilder(options);\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 let schemesToTry: AuthenticationScheme<any>[];\n if (config.trySchemes === 'all') {\n schemesToTry = config.schemes;\n } else {\n const schemeNames: readonly string[] =\n config.trySchemes && config.trySchemes.length > 0\n ? config.trySchemes\n : [config.defaultScheme];\n schemesToTry = schemeNames\n .map(name => schemeMap.get(name))\n .filter(\n (scheme): scheme is AuthenticationScheme<any> =>\n scheme !== undefined\n );\n }\n\n return async (ctx, next) => {\n // Skip authentication for public endpoints (authRoles === null)\n const epMeta = ctx.items.get('__endpoint_meta') as\n | import('./Endpoint.js').EndpointMetadata\n | import('./Subscription.js').SubscriptionMetadata\n | undefined;\n if (epMeta?.authRoles === null) {\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 ctx.principal = Principal.anonymous();\n for (const scheme of schemesToTry) {\n const result = await scheme.authenticate(authCtx);\n if (result.succeeded) {\n ctx.principal = result.principal;\n break;\n }\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 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\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\n/** Structural route precedence used to rank successful path matches. */\ninterface RouteSpecificity {\n readonly isStatic: boolean;\n readonly literalSegments: number;\n readonly dynamicSegments: number;\n}\n\ninterface RegisteredRoute<TRegistration> {\n readonly basePath: string;\n readonly routePath: RoutePath;\n readonly specificity: RouteSpecificity;\n readonly exactPath: string | null;\n readonly registration: TRegistration;\n}\n\ninterface RegisteredRouteMatch<TRegistration> {\n readonly registration: TRegistration;\n readonly parsedPath: Record<string, any> | null;\n}\n\ninterface RouteCollection<TRegistration> {\n readonly exactRoutes: Map<string, RegisteredRoute<TRegistration>>;\n readonly dynamicRoutes: RegisteredRoute<TRegistration>[];\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: RoutePath\n): p is ParseStringSchemaBuilder<any, any, any, any, any> {\n return typeof p !== 'string' && typeof (p as any).validate === 'function';\n}\n\n/**\n * Counts slash-delimited segments that contain literal route content.\n * Interpolations do not add literal content, even when they can consume `/`.\n */\nfunction countLiteralSegments(literals: readonly string[]): number {\n let count = 0;\n let currentSegmentHasLiteral = false;\n\n for (const literal of literals) {\n for (const character of literal) {\n if (character === '/') {\n if (currentSegmentHasLiteral) count++;\n currentSegmentHasLiteral = false;\n } else {\n currentSegmentHasLiteral = true;\n }\n }\n }\n\n return count + (currentSegmentHasLiteral ? 1 : 0);\n}\n\n/** Derives registration-order-independent precedence from route structure. */\nfunction getSpecificity(\n basePath: string,\n routePath: RoutePath\n): RouteSpecificity {\n if (!isParseStringSchema(routePath)) {\n const suffix = normalizePath(routePath);\n const fullPath = suffix === '/' ? basePath || '/' : basePath + suffix;\n return {\n isStatic: true,\n literalSegments: countLiteralSegments([fullPath]),\n dynamicSegments: 0\n };\n }\n\n const { literals, segments } = routePath.introspect().templateDefinition;\n const fullLiterals = [basePath + (literals[0] ?? ''), ...literals.slice(1)];\n\n return {\n isStatic: segments.length === 0,\n literalSegments: countLiteralSegments(fullLiterals),\n dynamicSegments: segments.length\n };\n}\n\n/**\n * Returns a positive value when `candidate` is more specific than `current`.\n * Equal scores deliberately preserve registration order.\n */\nfunction compareSpecificity(\n candidate: RouteSpecificity,\n current: RouteSpecificity\n): number {\n if (candidate.isStatic !== current.isStatic) {\n return candidate.isStatic ? 1 : -1;\n }\n if (candidate.literalSegments !== current.literalSegments) {\n return candidate.literalSegments - current.literalSegments;\n }\n return current.dynamicSegments - candidate.dynamicSegments;\n}\n\n/** Returns the complete exact path for routes without interpolations. */\nfunction getExactPath(basePath: string, routePath: RoutePath): string | null {\n if (!isParseStringSchema(routePath)) {\n const suffix = normalizePath(routePath);\n return suffix === '/' ? basePath || '/' : basePath + suffix;\n }\n\n const { literals, segments } = routePath.introspect().templateDefinition;\n if (segments.length > 0) return null;\n return basePath + (literals[0] ?? '');\n}\n\nfunction createRegisteredRoute<TRegistration>(\n basePath: string,\n routePath: RoutePath,\n registration: TRegistration\n): RegisteredRoute<TRegistration> {\n const normalizedBase = normalizePath(basePath);\n return {\n basePath: normalizedBase,\n routePath,\n specificity: getSpecificity(normalizedBase, routePath),\n exactPath: getExactPath(normalizedBase, routePath),\n registration\n };\n}\n\nfunction createRouteCollection<\n TRegistration\n>(): RouteCollection<TRegistration> {\n return {\n exactRoutes: new Map(),\n dynamicRoutes: []\n };\n}\n\n/** Adds a route while preserving the first registration for exact-path ties. */\nfunction addRegisteredRoute<TRegistration>(\n collection: RouteCollection<TRegistration>,\n route: RegisteredRoute<TRegistration>\n): void {\n if (route.exactPath !== null) {\n if (!collection.exactRoutes.has(route.exactPath)) {\n collection.exactRoutes.set(route.exactPath, route);\n }\n return;\n }\n\n collection.dynamicRoutes.push(route);\n}\n\n/**\n * HTTP and WebSocket router that maps paths 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 * When multiple templates validate the same URL, static routes win, followed\n * by routes with more literal segments and then fewer dynamic segments.\n * Registration order breaks equal-specificity ties.\n */\nexport class Router {\n readonly #routes: Map<string, RouteCollection<EndpointRegistration>> =\n new Map();\n readonly #subscriptionRoutes: RouteCollection<SubscriptionRegistration> =\n createRouteCollection();\n #finalized = true;\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 route = createRegisteredRoute(\n basePath,\n pathTemplate,\n registration\n );\n\n let collection = this.#routes.get(upperMethod);\n if (!collection) {\n collection = createRouteCollection();\n this.#routes.set(upperMethod, collection);\n }\n addRegisteredRoute(collection, route);\n this.#finalized = false;\n }\n\n /**\n * Prepare all dynamic route collections for request-time matching.\n *\n * Sorting is stable, so registration order remains the tie-breaker for\n * routes with equal specificity. The method is idempotent and is also\n * called lazily by match operations if routes have changed.\n */\n finalize(): void {\n if (this.#finalized) return;\n\n for (const collection of this.#routes.values()) {\n this.#sortDynamicRoutes(collection.dynamicRoutes);\n }\n this.#sortDynamicRoutes(this.#subscriptionRoutes.dynamicRoutes);\n this.#finalized = true;\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 * Exact paths are indexed directly. Dynamic candidates are ordered by\n * specificity during finalization and evaluated until the first match.\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 this.finalize();\n\n // Try exact method match first\n const methodRoutes = this.#routes.get(upperMethod);\n if (methodRoutes) {\n const result = this.#findFirstMatch(methodRoutes, normalized);\n if (result) {\n 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 if (this.#findFirstMatch(routes, normalized)) {\n allowedMethods.push(m);\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 /** Returns the first successful match from an indexed route collection. */\n #findFirstMatch<TRegistration>(\n collection: RouteCollection<TRegistration>,\n normalizedUrl: string\n ): RegisteredRouteMatch<TRegistration> | null {\n const exactRoute = collection.exactRoutes.get(normalizedUrl);\n if (exactRoute) {\n const match = this.#tryMatch(exactRoute, normalizedUrl);\n if (match) return match;\n }\n\n for (const route of collection.dynamicRoutes) {\n const match = this.#tryMatch(route, normalizedUrl);\n if (match) return match;\n }\n\n return null;\n }\n\n #sortDynamicRoutes<TRegistration>(\n routes: RegisteredRoute<TRegistration>[]\n ): void {\n routes.sort((a, b) => compareSpecificity(b.specificity, a.specificity));\n }\n\n #tryMatch<TRegistration>(\n route: RegisteredRoute<TRegistration>,\n normalizedUrl: string\n ): RegisteredRouteMatch<TRegistration> | 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 addRegisteredRoute(\n this.#subscriptionRoutes,\n createRegisteredRoute(basePath, pathTemplate, registration)\n );\n this.#finalized = false;\n }\n\n /**\n * Match an incoming WebSocket upgrade URL to a registered subscription.\n *\n * Returns the most specific matched registration and parsed path params,\n * or `null`. Registration order breaks equal-specificity ties.\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 this.finalize();\n return this.#findFirstMatch(this.#subscriptionRoutes, normalized);\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 __cleverbrushBatchSubrequest?: {\n index: number;\n size: number;\n path: string;\n };\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","handler","RawResult","StatusCodeResult","req","res","_contentNegotiator","_req","key","value","filename","sanitized","disposition","resolve","reject","safeJsonParse","raw","key","value","checkJsonDepth","maxDepth","walk","current","max","item","v","jsonContentTypeHandler","value","raw","parsed","safeJsonParse","checkJsonDepth","formUrlEncodedContentTypeHandler","params","key","item","nested","result","isUnsafeFormKey","existing","parseAcceptHeader","accept","part","trimmed","mimeType","s","quality","p","val","a","b","ContentNegotiator","#handlers","handler","acceptHeader","contentTypeHeader","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","isMutating","method","CLEANUP_INTERVAL","idempotency","options","ttl","headerName","skip","ctx","store","cleanupTimer","now","key","entry","next","stored","res","originalWriteHead","originalEnd","capturedStatus","capturedHeaders","chunks","statusCode","args","chunk","body","isMutating","method","cacheResponse","options","ttlByTag","defaultTtl","cache","generations","isCurrent","snapshot","name","generation","ctx","next","tags","names","tag","prefix","key","entry","root","keys","computeCacheKey","res","originalWriteHead","originalEnd","capturedStatus","capturedHeaders","chunks","statusCode","args","chunk","body","ttl","max","t","URL","any","boolean","func","object","promise","record","string","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","Busboy","WebSocketServer","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","countLiteralSegments","literals","count","currentSegmentHasLiteral","literal","character","getSpecificity","basePath","routePath","suffix","fullPath","segments","fullLiterals","compareSpecificity","candidate","current","getExactPath","createRegisteredRoute","registration","normalizedBase","createRouteCollection","addRegisteredRoute","collection","route","Router","#routes","#subscriptionRoutes","#finalized","method","pathTemplate","upperMethod","#sortDynamicRoutes","url","normalized","methodRoutes","result","#findFirstMatch","allowedMethods","m","routes","normalizedUrl","exactRoute","match","#tryMatch","a","b","remainder","normalizedRoutePath","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","parseMultipart","req","options","maxFileCount","maxFileSize","allowedMimeTypes","resolve","reject","fields","files","rejectedFiles","fileCount","busboy","Busboy","fieldname","value","stream","filename","_transferEncoding","mimeType","pattern","chunks","chunk","buffer","ServerBuilder","#serviceCollection","ServiceCollection","#registrations","#subscriptionRegistrations","#webhooks","#globalMiddlewares","#contentNegotiator","ContentNegotiator","#options","#authConfig","#authzConfig","#healthcheck","#batchConfig","configureFn","middleware","handler","config","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","res","#handleRequest","_err","PROBLEM_JSON_CONTENT_TYPE","serializeProblemDetails","createProblemDetails","WebSocketServer","socket","head","urlPath","result","ws","#handleWebSocket","err","addr","scope","ctx","RequestContext","method","batchSubrequest","pipeline","MiddlewarePipeline","mw","#handleBatchRequest","routeResult","pd","registration","parsedPath","meta","rawParams","flattenToStrings","parsedBody","uploadedFiles","needsBody","contentType","ctHandler","bodyText","resolveResult","resolveArgs","#sendResult","HttpError","maxSize","parallel","outerBody","raw","readBuffer","parsed","safeJsonParse","checkJsonDepth","execute","item","index","virtualReq","VirtualIncomingMessage","virtualRes","VirtualServerResponse","results","i","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","frameToSend","isTrackedEvent","outData","trackedFrame","messageFrame","createServer","totalSize","obj","prefix","fullKey","schemeMap","scheme","schemesToTry","next","Principal","authCtx","parseCookies","authConfig","challengeHeaders","ch","principal","headers","requireRole","defineWebhook","name","options"]}
1
+ {"version":3,"sources":["../src/ActionResult.ts","../src/safeJson.ts","../src/ContentNegotiator.ts","../src/ErrorMap.ts","../src/ProblemDetails.ts","../src/HttpError.ts","../src/Implementation.ts","../src/middlewares/Idempotency.ts","../src/middlewares/ResponseSnapshot.ts","../src/middlewares/ResponseCache.ts","../src/RequestContext.ts","../src/Server.ts","../src/Cors.ts","../src/MiddlewarePipeline.ts","../src/multipart.ts","../src/ParameterResolver.ts","../src/Router.ts","../src/VirtualHttp.ts","../src/WebSocketProtocol.ts","../src/Webhook.ts","../src/withRequestSignal.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 * Handler used by {@link ActionResult.raw}.\n *\n * The handler receives the native Node request and response and is responsible\n * for writing headers/body as needed.\n */\nexport type RawResultHandler = (\n req: http.IncomingMessage,\n res: http.ServerResponse\n) => void | Promise<void>;\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 /**\n * Execute a raw Node request/response handler.\n *\n * Use this for integrations that already know how to write to\n * `http.ServerResponse`, such as webhook libraries or compatibility\n * adapters. The callback is responsible for ending the response when it\n * writes a body.\n */\n static raw(handler: RawResultHandler): RawResult {\n return new RawResult(handler);\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// RawResult\n// ---------------------------------------------------------------------------\n\n/**\n * Runs a native Node request/response callback.\n * Created by `ActionResult.raw()`.\n */\nexport class RawResult extends ActionResult {\n readonly handler: RawResultHandler;\n\n constructor(handler: RawResultHandler) {\n super();\n this.handler = handler;\n }\n\n async executeAsync(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n _contentNegotiator: ContentNegotiator\n ): Promise<void> {\n await this.handler(req, res);\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 const filename = this.fileName;\n const sanitized = filename.replace(/[^\\x20-\\x7E]/g, '_');\n const disposition =\n sanitized === filename\n ? `attachment; filename=\"${filename}\"`\n : `attachment; filename=\"${sanitized}\"; filename*=UTF-8''${encodeURIComponent(filename)}`;\n res.writeHead(200, {\n 'content-type': this.contentType,\n 'content-disposition': disposition,\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 * 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 { checkJsonDepth, safeJsonParse } from './safeJson.js';\nimport type { ContentTypeHandler } from './types.js';\n\n/**\n * Built-in JSON content type handler.\n */\nexport const jsonContentTypeHandler: 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\n/**\n * Built-in `application/x-www-form-urlencoded` content type handler.\n *\n * Repeated fields are deserialized as arrays in insertion order:\n * `tag=a&tag=b` becomes `{ tag: ['a', 'b'] }`.\n */\nexport const formUrlEncodedContentTypeHandler: ContentTypeHandler = {\n mimeType: 'application/x-www-form-urlencoded',\n serialize(value: unknown): string {\n const params = new URLSearchParams();\n if (value && typeof value === 'object') {\n for (const [key, item] of Object.entries(\n value as Record<string, unknown>\n )) {\n if (item === undefined || item === null) continue;\n if (Array.isArray(item)) {\n for (const nested of item) {\n params.append(key, String(nested));\n }\n } else {\n params.append(key, String(item));\n }\n }\n }\n return params.toString();\n },\n deserialize(raw: string): unknown {\n const result: Record<string, string | string[]> = {};\n const params = new URLSearchParams(raw);\n\n for (const [key, value] of params) {\n if (isUnsafeFormKey(key)) continue;\n\n const existing = result[key];\n if (existing === undefined) {\n result[key] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n result[key] = [existing, value];\n }\n }\n\n return result;\n }\n};\n\nfunction isUnsafeFormKey(key: string): boolean {\n return key === '__proto__' || key === 'constructor' || key === 'prototype';\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(jsonContentTypeHandler);\n this.register(formUrlEncodedContentTypeHandler);\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 {\n type ContentResult,\n type FileResult,\n JsonResult,\n NoContentResult,\n type RawResult,\n type RedirectResult,\n StatusCodeResult,\n type StreamResult\n} from './ActionResult.js';\nimport type { EndpointBuilder, Handler, ResponsesOf } from './Endpoint.js';\n\n/** Explicit, contract-checkable responses supported by error policies. */\nexport type ErrorResponse =\n | JsonResult<number, unknown>\n | NoContentResult\n | StatusCodeResult<number>;\n\n/** The declared JSON and bodyless responses of an HTTP endpoint. */\nexport type ErrorResponsesOf<E> = {\n [K in keyof ResponsesOf<E> & number]: ResponsesOf<E>[K] extends null\n ? K extends 204\n ? NoContentResult\n : StatusCodeResult<K>\n : JsonResult<K, ResponsesOf<E>[K]>;\n}[keyof ResponsesOf<E> & number];\n\ntype ErrorConstructor<E extends Error> = abstract new (...args: any[]) => E;\nconst responseTypes: unique symbol = Symbol('errorResponseTypes');\n// Result classes are structural (NoContentResult only has executeAsync).\n// Keep a discriminated description as well as R so a declared 204 cannot\n// accidentally admit every other ActionResult through structural assignability.\ntype ResponseSignature<R> = R extends\n | FileResult\n | StreamResult\n | ContentResult\n | RedirectResult\n | RawResult\n ? { kind: 'unsupported' }\n : R extends JsonResult<infer S, infer B>\n ? { kind: 'json'; status: S; body: B }\n : R extends StatusCodeResult<infer S>\n ? { kind: 'empty'; status: S }\n : R extends NoContentResult\n ? { kind: 'empty'; status: 204 }\n : never;\ntype Rule = {\n matches: (error: unknown) => boolean;\n translate: (error: any) => ErrorResponse | Promise<ErrorResponse>;\n};\n\n/**\n * Immutable, ordered exception translators. Create one with {@link errorMap}.\n *\n * Policies retain their exact response union until attached to an endpoint.\n * They do not change that endpoint's responses, catch infrastructure errors,\n * or automatically expose exception messages.\n *\n * @typeParam R - Union of all responses produced by this policy.\n */\nexport class ErrorMap<\n R extends ErrorResponse = never,\n Signature = ResponseSignature<R>\n> {\n /** @internal Retains status/body discrimination across exported policies. */\n declare readonly [responseTypes]: Signature;\n readonly #rules: readonly Rule[];\n\n /** @internal Use {@link errorMap}. */\n constructor(rules: readonly Rule[] = []) {\n this.#rules = [...rules];\n }\n\n /**\n * Return a new policy with a translator appended. First matching rule wins.\n * Put subclass rules before base-class rules. Translators may be async;\n * translator failures propagate without re-entering this policy.\n *\n * @param errorType - Error constructor matched using `instanceof`.\n * @param translate - Application-owned conversion to an explicit response.\n */\n on<E extends Error, T extends ErrorResponse>(\n errorType: ErrorConstructor<E>,\n translate: (error: E) => T | Promise<T>\n ): ErrorMap<R | T, Signature | ResponseSignature<T>> {\n return new ErrorMap([\n ...this.#rules,\n {\n matches: error => error instanceof errorType,\n translate\n }\n ]);\n }\n\n /**\n * Translate a recognized exception or rethrow the original value unchanged.\n * Usually invoked by {@link withErrors}, not directly by an application.\n */\n async translate(error: unknown): Promise<R> {\n const rule = this.#rules.find(candidate => candidate.matches(error));\n if (!rule) throw error;\n const result = await rule.translate(error);\n if (\n !(result instanceof JsonResult) &&\n !(result instanceof NoContentResult) &&\n !(result instanceof StatusCodeResult)\n ) {\n throw new TypeError(\n 'Error translators must return an explicit JSON or bodyless status result'\n );\n }\n return result as R;\n }\n}\n\n/**\n * Create an empty, immutable error policy. Unknown exceptions are rethrown.\n *\n * @example\n * ```ts\n * const errors = errorMap().on(MissingItemError, () =>\n * ActionResult.notFound({ message: 'Item not found' })\n * );\n * ```\n */\nexport function errorMap(): ErrorMap<never> {\n return new ErrorMap();\n}\n\n/**\n * Wrap only a handler's invocation in an endpoint-checked error policy.\n *\n * Requires explicit `.responses()` declarations. Every possible translated\n * status/body must fit the endpoint; file/raw/stream escape hatches are not\n * accepted. Request and injected-service types are unchanged. Authentication,\n * validation, middleware, DI and response serialization remain outside this\n * wrapper and use the server's normal error handling.\n *\n * @param endpoint - Configured HTTP endpoint, the sole source of handler types.\n * @param policy - Reusable policy compatible with the endpoint's responses.\n * @param handler - Named or inline handler to wrap.\n * @returns A handler suitable for `handle`, `mapHandlers`, or implementation scopes.\n */\nexport function withErrors<\n E extends EndpointBuilder<any, any, any, any, any, any, any, any, any, any>\n>(\n endpoint: E,\n policy: keyof ResponsesOf<NoInfer<E>> extends never\n ? never\n : ErrorMap<ErrorResponsesOf<NoInfer<E>>>,\n handler: Handler<NoInfer<E>>\n): Handler<E> {\n validateErrorMap(endpoint);\n return (async (...args: unknown[]) => {\n try {\n return await (handler as (...args: unknown[]) => unknown)(...args);\n } catch (error) {\n return policy.translate(error);\n }\n }) as Handler<E>;\n}\n\n/** @internal Shared registration validation. */\nexport function validateErrorMap(endpoint: {\n introspect(): { responsesSchemas: unknown };\n}): void {\n const responses = endpoint.introspect().responsesSchemas;\n if (!responses || Object.keys(responses).length === 0) {\n throw new TypeError(\n 'Error policies require explicit endpoint responses'\n );\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 413: 'Payload Too Large',\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 { InferType, SchemaBuilder } from '@cleverbrush/schema';\nimport type { ApiContract, ApiGroup } from './contract.js';\nimport {\n type EndpointBuilder,\n type Handler,\n type HandlerMapping,\n mapHandlers\n} from './Endpoint.js';\nimport type {\n EndpointOptions,\n RuntimeEndpointOptions\n} from './EndpointOptions.js';\nimport { type ErrorMap, withErrors } from './ErrorMap.js';\nimport {\n isSubscriptionBuilder,\n type SubscriptionBuilder,\n type SubscriptionHandlerEntry\n} from './Subscription.js';\nimport type { Middleware } from './types.js';\n\ntype AnySchema = SchemaBuilder<any, any, any, any, any>;\ntype Services = Record<string, AnySchema>;\ntype AnyEndpoint = EndpointBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\ntype AnySubscription = SubscriptionBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>;\ntype Definition = AnyEndpoint | AnySubscription;\n// Preserve service declarations through defaults/overrides, with B winning.\ntype Merge<A, B> = {\n -readonly [K in keyof Required<A> as K extends keyof B ? never : K]: A[K];\n} & { -readonly [K in keyof Required<B>]: B[K] };\n\n/** Server-only enrichment shared by every operation in a scope. */\nexport interface ImplementationDefaults {\n /** Merged with contract services; later same-name bindings win. */\n readonly inject?: Services;\n /** Explicitly require authentication, retaining any existing role requirements. */\n readonly authorize?: AnySchema;\n /** Replace inherited documentation tags when supplied. */\n readonly tags?: readonly string[];\n}\n\n/** Per-operation overrides. Wire schemas and cache metadata are not configurable here. */\nexport interface ImplementationOperationOptions extends ImplementationDefaults {\n /** Short documentation summary. */\n readonly summary?: string;\n /** Longer documentation description. */\n readonly description?: string;\n /** Stable OpenAPI/AsyncAPI operation identifier. */\n readonly operationId?: string;\n /** Mark deprecated; this cannot clear an inherited deprecation. */\n readonly deprecated?: true;\n}\n\n/** Typed defaults and operation-specific server settings for a contract group. */\nexport interface ImplementationGroupOptions<G extends ApiGroup>\n extends ImplementationDefaults {\n /** Keys must belong to the selected group. */\n readonly operations?: {\n readonly [K in keyof G]?: ImplementationOperationOptions;\n };\n}\n\ntype Injected<O> = O extends { readonly inject: infer I extends Services }\n ? I\n : {};\ntype Principal<P, O> = O extends {\n readonly authorize: infer S extends AnySchema;\n}\n ? InferType<S>\n : P;\ntype OperationOptions<O, K extends PropertyKey> = O extends {\n readonly operations: infer Operations;\n}\n ? K extends keyof Operations\n ? Operations[K]\n : {}\n : {};\n\ntype Configured<E, Defaults, Options> =\n E extends EndpointBuilder<\n infer P,\n infer B,\n infer Q,\n infer H,\n infer S,\n infer A,\n infer Roles,\n infer R,\n infer Rs,\n infer U\n >\n ? EndpointBuilder<\n P,\n B,\n Q,\n H,\n Merge<Merge<S, Injected<Defaults>>, Injected<Options>>,\n Principal<Principal<A, Defaults>, Options>,\n Roles,\n R,\n Rs,\n U\n >\n : E extends SubscriptionBuilder<\n infer P,\n infer Q,\n infer H,\n infer S,\n infer A,\n infer Roles,\n infer I,\n infer O\n >\n ? SubscriptionBuilder<\n P,\n Q,\n H,\n Merge<Merge<S, Injected<Defaults>>, Injected<Options>>,\n Principal<Principal<A, Defaults>, Options>,\n Roles,\n I,\n O\n >\n : never;\n\ntype ConfiguredGroup<G extends ApiGroup, O> = {\n readonly [K in keyof G]: Configured<G[K], O, OperationOptions<O, K>>;\n};\n\n/** Handler binding accepted by an implementation scope. Policies apply only to HTTP handlers. */\nexport type ImplementationHandlerEntry<E> = E extends AnySubscription\n ? SubscriptionHandlerEntry<E> & { errors?: never }\n : E extends AnyEndpoint\n ? Handler<E> | ({ handler: Handler<E> } & EndpointOptions<E>)\n : never;\n\n/** Complete, endpoint-specific handler bindings for one configured scope. */\nexport type ImplementationHandlers<G extends ApiGroup> = {\n [K in keyof G]: ImplementationHandlerEntry<G[K]>;\n};\n\ntype ExactKeys<Given, Expected> = Record<\n Exclude<keyof Given, keyof Expected>,\n never\n>;\ntype ExactOperations<O, G> = O extends { readonly operations: infer Ops }\n ? { readonly operations: ExactKeys<Ops, G> }\n : unknown;\n\ntype Entry = RuntimeEndpointOptions & {\n readonly group: string;\n readonly name: string;\n readonly source: Definition;\n readonly endpoint: Definition;\n readonly handler: (...args: any[]) => any;\n readonly middlewares?: Middleware[];\n};\ntype RuntimeBinding =\n | Entry['handler']\n | (RuntimeEndpointOptions & {\n handler: Entry['handler'];\n middlewares?: Middleware[];\n errors?: ErrorMap<any, any>;\n });\n\nconst moduleContract: unique symbol = Symbol('implementationContract');\nconst moduleEntries: unique symbol = Symbol('implementationEntries');\n\n/**\n * Exportable, immutable set of bound operations. Its type retains the original\n * contract slice, not just an erased array of registrations.\n * Obtain modules with `scope.withHandlers()` or `implement(api).use(...)`.\n */\nexport class ImplementationModule<C extends ApiContract> {\n /** @internal Compile-time contract coverage. */\n declare readonly [moduleContract]: C;\n readonly #entries: readonly Entry[];\n\n /** @internal Construct modules through an implementation scope. */\n constructor(entries: readonly Entry[]) {\n this.#entries = Object.freeze(\n entries.map(entry =>\n Object.freeze({\n ...entry,\n middlewares: entry.middlewares\n ? [...entry.middlewares]\n : undefined\n })\n )\n );\n }\n\n /** @internal */\n [moduleEntries](): readonly Entry[] {\n return this.#entries;\n }\n}\n\n/**\n * Configured, unbound operations from a single contract group.\n * Export the scope from a configuration-only module, then use\n * `Handler<typeof scope.endpoints.operation>` in separate handler files.\n * No handler imports or registration side effects are needed here.\n */\nexport class ImplementationScope<\n G extends string,\n Source extends ApiGroup,\n Endpoints extends ApiGroup\n> {\n readonly #group: G;\n readonly #source: Source;\n readonly #endpoints: Endpoints;\n\n /** @internal Use `implement(api).group(...)`. */\n constructor(group: G, source: Source, endpoints: Endpoints) {\n this.#group = group;\n this.#source = Object.freeze({ ...source });\n this.#endpoints = Object.freeze({ ...endpoints });\n }\n\n /** Configured endpoint builders: the source of handler request, DI and response types. */\n get endpoints(): Endpoints {\n return this.#endpoints;\n }\n\n /**\n * Select operations for a smaller module without losing source identity.\n * The composition root still checks coverage of the full contract.\n */\n pick<const K extends keyof Source & keyof Endpoints & string>(\n ...names: K[]\n ): ImplementationScope<G, Pick<Source, K>, Pick<Endpoints, K>> {\n const source: ApiGroup = {};\n const endpoints: ApiGroup = {};\n const seen = new Set<string>();\n for (const name of names) {\n if (!Object.hasOwn(this.#source, name)) {\n throw new TypeError(`Unknown operation ${this.#group}.${name}`);\n }\n if (seen.has(name))\n throw new TypeError(\n `Duplicate operation ${this.#group}.${name}`\n );\n seen.add(name);\n Object.defineProperty(source, name, {\n value: this.#source[name],\n enumerable: true\n });\n Object.defineProperty(endpoints, name, {\n value: this.#endpoints[name],\n enumerable: true\n });\n }\n return new ImplementationScope(\n this.#group,\n source,\n endpoints\n ) as ImplementationScope<G, Pick<Source, K>, Pick<Endpoints, K>>;\n }\n\n /**\n * Bind exactly one handler for every operation in this scope.\n * Named handlers and inline functions receive the same endpoint typing.\n * Optional descriptors attach middleware and HTTP error policies.\n */\n withHandlers<const H extends ImplementationHandlers<Endpoints>>(\n handlers: H & ExactKeys<H, Endpoints>\n ): ImplementationModule<{ [K in G]: Source }> {\n for (const key of Object.keys(handlers)) {\n if (!Object.hasOwn(this.#endpoints, key)) {\n throw new TypeError(`Unknown handler ${this.#group}.${key}`);\n }\n }\n const entries: Entry[] = [];\n for (const name of Object.keys(this.#endpoints)) {\n const endpoint = this.#endpoints[name];\n const binding = (\n Object.hasOwn(handlers, name) ? handlers[name] : undefined\n ) as RuntimeBinding | undefined;\n const handler =\n typeof binding === 'function' ? binding : binding?.handler;\n if (typeof handler !== 'function') {\n throw new TypeError(`Missing handler ${this.#group}.${name}`);\n }\n const errors =\n typeof binding === 'function' ? undefined : binding?.errors;\n if (errors && isSubscriptionBuilder(endpoint)) {\n throw new TypeError(\n 'HTTP error policies cannot wrap subscription handlers'\n );\n }\n entries.push({\n group: this.#group,\n name,\n source: this.#source[name],\n endpoint,\n handler: errors\n ? withErrors(endpoint as AnyEndpoint, errors, handler)\n : handler,\n handlerErrorsMapped: !!errors,\n errors,\n prepare:\n typeof binding === 'function'\n ? undefined\n : binding?.prepare,\n idempotency:\n typeof binding === 'function'\n ? undefined\n : binding?.idempotency,\n middlewares:\n typeof binding === 'function'\n ? undefined\n : binding?.middlewares\n });\n }\n return new ImplementationModule(entries);\n }\n}\n\ntype ContractOf<M> = M extends ImplementationModule<infer C> ? C : never;\n// Map over the combined object so groups keep their original declarations.\ntype MergeContracts<A extends ApiContract, B extends ApiContract> = {\n -readonly [G in keyof Required<A & B>]: G extends keyof A\n ? G extends keyof B\n ? A[G] & B[G]\n : A[G]\n : G extends keyof B\n ? B[G]\n : never;\n};\ntype Joined<\n Bound extends ApiContract,\n Modules extends readonly ImplementationModule<any>[]\n> = Modules extends readonly [\n infer Head,\n ...infer Tail extends readonly ImplementationModule<any>[]\n]\n ? Joined<MergeContracts<Bound, ContractOf<Head>>, Tail>\n : Bound;\ntype Missing<C, B> = {\n [G in keyof C &\n string]: `${G}.${Exclude<keyof C[G], G extends keyof B ? keyof B[G] : never> & string}`;\n}[keyof C & string];\ntype Overlap<A, B> = {\n [G in keyof A &\n keyof B &\n string]: `${G}.${keyof A[G] & keyof B[G] & string}`;\n}[keyof A & keyof B & string];\ntype Incompatible<C, S> = {\n [G in keyof S & string]: G extends keyof C\n ? {\n [K in keyof S[G] & string]: K extends keyof C[G]\n ? S[G][K] extends C[G][K]\n ? never\n : `${G}.${K}`\n : `${G}.${K}`;\n }[keyof S[G] & string]\n : `${G}.${keyof S[G] & string}`;\n}[keyof S & string];\ntype CheckModules<\n C,\n B extends ApiContract,\n M extends readonly ImplementationModule<any>[]\n> = number extends M['length']\n ? {\n readonly implementationError: 'Use a tuple of modules with known operation keys';\n }\n : M extends readonly [\n infer Head,\n ...infer Tail extends readonly ImplementationModule<any>[]\n ]\n ? [\n Overlap<B, ContractOf<Head>> | Incompatible<C, ContractOf<Head>>\n ] extends [never]\n ? CheckModules<C, MergeContracts<B, ContractOf<Head>>, Tail>\n : {\n readonly duplicateOrIncompatibleOperations:\n | Overlap<B, ContractOf<Head>>\n | Incompatible<C, ContractOf<Head>>;\n }\n : unknown;\n\n/**\n * Immutable composition root bound to a shared contract. Compose exported\n * feature modules with `use`, then pass `complete()` to `server.handleAll`.\n * Partial roots are themselves modules and can be composed in larger roots.\n */\nexport class ApiImplementation<\n C extends ApiContract,\n Bound extends ApiContract = {}\n> extends ImplementationModule<Bound> {\n readonly #contract: C;\n\n /** @internal Use {@link implement}. */\n constructor(contract: C, entries: readonly Entry[] = []) {\n super(entries);\n this.#contract = Object.freeze(\n Object.fromEntries(\n Object.entries(contract).map(([name, group]) => [\n name,\n Object.freeze({ ...group })\n ])\n )\n ) as C;\n }\n\n /**\n * Create an unbound scope; does not register handlers or change this root.\n * Services merge contract → group → operation. Only explicitly supplied\n * documentation fields override inherited values. Authorization retains roles.\n */\n group<\n K extends keyof C & string,\n const O extends ImplementationGroupOptions<C[K]> = {}\n >(\n name: K,\n options?: O & ExactOperations<O, C[K]>\n ): ImplementationScope<K, C[K], ConfiguredGroup<C[K], O>> {\n if (!Object.hasOwn(this.#contract, name)) {\n throw new TypeError(`Unknown contract group ${name}`);\n }\n const source = this.#contract[name];\n const config: ImplementationGroupOptions<C[K]> = options ?? {};\n for (const key of Object.keys(config.operations ?? {})) {\n if (!Object.hasOwn(source, key))\n throw new TypeError(`Unknown operation ${name}.${key}`);\n }\n const endpoints = Object.fromEntries(\n Object.entries(source).map(([key, endpoint]) => [\n key,\n configure(endpoint, config, config.operations?.[key] ?? {})\n ])\n );\n return new ImplementationScope(\n name,\n source,\n endpoints\n ) as ImplementationScope<K, C[K], ConfiguredGroup<C[K], O>>;\n }\n\n /**\n * Compose modules without erasing their operation coverage. Rejects duplicate\n * and incompatible bindings, including runtime source-identity mismatches.\n */\n use<const M extends readonly ImplementationModule<any>[]>(\n ...modules: M & CheckModules<C, Bound, M>\n ): ApiImplementation<C, Joined<Bound, M>> {\n const entries = [...this[moduleEntries]()];\n const seen = new Map<string, Set<string>>();\n for (const entry of entries) {\n if (!seen.has(entry.group)) seen.set(entry.group, new Set());\n seen.get(entry.group)!.add(entry.name);\n }\n for (const module of modules) {\n if (!(module instanceof ImplementationModule))\n throw new TypeError('Expected an implementation module');\n for (const entry of module[moduleEntries]()) {\n if (\n !Object.hasOwn(this.#contract, entry.group) ||\n !Object.hasOwn(this.#contract[entry.group], entry.name) ||\n this.#contract[entry.group][entry.name] !== entry.source\n ) {\n throw new TypeError(\n `Incompatible source endpoint ${entry.group}.${entry.name}`\n );\n }\n if (seen.get(entry.group)?.has(entry.name)) {\n throw new TypeError(\n `Duplicate implementation ${entry.group}.${entry.name}`\n );\n }\n if (!seen.has(entry.group)) seen.set(entry.group, new Set());\n seen.get(entry.group)!.add(entry.name);\n entries.push(entry);\n }\n }\n return new ApiImplementation<C, Joined<Bound, M>>(\n this.#contract,\n entries\n );\n }\n\n /**\n * Finalize only when every operation in the original contract is covered.\n * Rechecks coverage at runtime for JavaScript callers and erased types.\n * Returns the existing registration format; the server pipeline is unchanged.\n */\n complete(\n this: [Missing<C, Bound>] extends [never]\n ? ApiImplementation<C, Bound>\n : { readonly missingOperations: Missing<C, Bound> }\n ): HandlerMapping;\n complete(): HandlerMapping {\n const endpoints: Record<string, ApiGroup> = Object.create(null);\n const handlers: Record<string, Record<string, any>> = Object.create(\n null\n );\n for (const entry of this[moduleEntries]()) {\n endpoints[entry.group] ??= Object.create(null);\n handlers[entry.group] ??= Object.create(null);\n endpoints[entry.group][entry.name] = entry.endpoint;\n handlers[entry.group][entry.name] = {\n handler: entry.handler,\n handlerErrorsMapped: entry.handlerErrorsMapped,\n prepare: entry.prepare,\n idempotency: entry.idempotency,\n errors: entry.errors,\n middlewares: entry.middlewares\n ? [...entry.middlewares]\n : undefined\n };\n }\n const missing: string[] = [];\n for (const [group, operations] of Object.entries(this.#contract)) {\n for (const name of Object.keys(operations)) {\n if (!Object.hasOwn(endpoints[group] ?? {}, name))\n missing.push(`${group}.${name}`);\n }\n }\n if (missing.length)\n throw new TypeError(\n `Missing implementations: ${missing.join(', ')}`\n );\n return mapHandlers<any>(endpoints, handlers);\n }\n}\n\nfunction configure(\n endpoint: Definition,\n defaults: ImplementationDefaults,\n options: ImplementationOperationOptions\n): Definition {\n let result: any = endpoint;\n if (defaults.inject || options.inject) {\n result = result.inject({\n ...endpoint.introspect().serviceSchemas,\n ...defaults.inject,\n ...options.inject\n });\n }\n const authorize = options.authorize ?? defaults.authorize;\n if (authorize) result = result.authorize(authorize);\n const tags = options.tags ?? defaults.tags;\n if (tags) result = result.tags(...tags);\n if (options.summary !== undefined) result = result.summary(options.summary);\n if (options.description !== undefined)\n result = result.description(options.description);\n if (options.operationId !== undefined)\n result = result.operationId(options.operationId);\n if (options.deprecated) result = result.deprecated();\n return result;\n}\n\n/**\n * Start an immutable implementation bound to a shared API contract.\n *\n * @param contract - Complete shared contract, or an intentional audience slice.\n * @example\n * ```ts\n * const todos = implement(api).group('todos', { inject: { db: DbToken } });\n * const module = todos.withHandlers({ list: listHandler, create: createHandler });\n * server.handleAll(implement(api).use(module).complete());\n * ```\n */\nexport function implement<C extends ApiContract>(\n contract: C\n): ApiImplementation<C> {\n return new ApiImplementation(contract);\n}\n","import { createHash } from 'node:crypto';\nimport { HttpError } from '../HttpError.js';\nimport type { RequestContext } from '../RequestContext.js';\nimport type { Middleware } from '../types.js';\nimport { captureResponse, type ResponseSnapshot } from './ResponseSnapshot.js';\n\n/** Configuration for bounded, process-local mutation response replay. */\nexport interface ServerIdempotencyOptions {\n /**\n * Explicit authorization/tenant scope. Run after authentication and\n * authorization. Return undefined to skip; use a fixed string only for\n * deliberately public operations. Never use an unverified identity header.\n */\n scope: (ctx: RequestContext) => string | undefined;\n /** Stored response lifetime in milliseconds. Default: 86,400,000. */\n ttl?: number;\n /** Case-insensitive request header. Default: x-idempotency-key. */\n headerName?: string;\n /** Default: skip GET, HEAD, OPTIONS and other non-mutating methods. */\n skip?: (ctx: RequestContext) => boolean;\n /** Maximum retained keys, including pending requests. Default: 1000. */\n maxEntries?: number;\n /** Maximum response body retained per key. Default: 65,536 bytes. */\n maxResponseBytes?: number;\n}\n\ninterface Entry {\n result: Promise<ResponseSnapshot | undefined>;\n expiresAt: number;\n}\n\n/**\n * Replay completed mutations and coalesce concurrent requests within one\n * middleware instance. Keys include explicit scope, method and full request URL.\n * Reusing a key means retrying the same input; bodies are not fingerprinted.\n * This is not durable exactly-once execution: restarts, expiry and thrown\n * failures may permit another execution. Use database deduplication for that.\n * Oversized/incomplete responses retain a non-replayable reservation (409).\n * Capacity exhaustion returns 503 without executing the operation.\n */\nexport function idempotency(options: ServerIdempotencyOptions): Middleware {\n if (typeof options?.scope !== 'function')\n throw new TypeError('idempotency requires an explicit scope');\n const {\n scope,\n ttl = 86_400_000,\n headerName = 'x-idempotency-key',\n skip = ctx => !['POST', 'PUT', 'DELETE', 'PATCH'].includes(ctx.method),\n maxEntries = 1000,\n maxResponseBytes = 65_536\n } = options;\n for (const [name, value] of Object.entries({\n ttl,\n maxEntries,\n maxResponseBytes\n })) {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new TypeError(name + ' must be a positive safe integer');\n }\n const store = new Map<string, Entry>();\n return async (ctx, next) => {\n const supplied =\n ctx.headers[headerName.toLowerCase()] ?? ctx.headers[headerName];\n if (skip(ctx) || !supplied) return next();\n if (supplied.length > 256)\n throw new HttpError(400, 'Idempotency key is too long');\n const identity = scope(ctx);\n if (identity === undefined) return next();\n if (typeof identity !== 'string')\n throw new TypeError('Idempotency scope must be a string');\n const key = createHash('sha256')\n .update(\n JSON.stringify([\n identity,\n ctx.method,\n ctx.url?.href ?? '/',\n supplied\n ])\n )\n .digest('hex');\n const now = Date.now();\n for (const [k, entry] of store)\n if (entry.expiresAt <= now) store.delete(k);\n const existing = store.get(key);\n if (existing) {\n const snapshot = await existing.result;\n if (!snapshot)\n throw new HttpError(\n 409,\n 'The previous response cannot be replayed; verify the operation outcome'\n );\n ctx.response.writeHead(snapshot.status, snapshot.headers);\n ctx.response.end(snapshot.body);\n ctx.responded = true;\n return;\n }\n if (store.size >= maxEntries)\n throw new HttpError(503, 'Idempotency capacity reached');\n let resolve!: (value: ResponseSnapshot | undefined) => void;\n let reject!: (error: unknown) => void;\n const result = new Promise<ResponseSnapshot | undefined>((yes, no) => {\n resolve = yes;\n reject = no;\n });\n // The first caller observes errors directly even when no waiter exists.\n void result.catch(() => undefined);\n const entry: Entry = { result, expiresAt: Infinity };\n store.set(key, entry);\n try {\n const snapshot = await captureResponse(\n ctx.response,\n next,\n maxResponseBytes\n );\n entry.expiresAt = Date.now() + ttl;\n resolve(snapshot);\n } catch (error) {\n store.delete(key);\n reject(error);\n throw error;\n }\n };\n}\n","import type { OutgoingHttpHeaders, ServerResponse } from 'node:http';\n\n/** @internal A complete, bounded response suitable for replay. */\nexport interface ResponseSnapshot {\n status: number;\n headers: OutgoingHttpHeaders;\n body: Buffer;\n}\n\n/** @internal Capture all write/end overloads without changing their behavior. */\nexport async function captureResponse(\n response: ServerResponse,\n next: () => Promise<void>,\n maxBytes: number\n): Promise<ResponseSnapshot | undefined> {\n const { write, writeHead, end } = response;\n const chunks: Buffer[] = [];\n let size = 0;\n let ended = false;\n let headers: OutgoingHttpHeaders = {};\n const capture = (chunk: unknown, encoding?: unknown) => {\n if (chunk == null || typeof chunk === 'function' || size > maxBytes)\n return;\n const buffer =\n typeof chunk === 'string'\n ? Buffer.from(\n chunk,\n typeof encoding === 'string'\n ? (encoding as BufferEncoding)\n : 'utf8'\n )\n : Buffer.from(chunk as Uint8Array);\n size += buffer.length;\n if (size > maxBytes) chunks.length = 0;\n else chunks.push(buffer);\n };\n response.writeHead = function (\n this: ServerResponse,\n status: number,\n ...args: any[]\n ) {\n const supplied = typeof args[0] === 'string' ? args[1] : args[0];\n if (Array.isArray(supplied)) {\n const raw: OutgoingHttpHeaders = {};\n for (let i = 0; i < supplied.length; i += 2) {\n const name = supplied[i].toLowerCase();\n const previous = raw[name];\n raw[name] =\n previous === undefined\n ? supplied[i + 1]\n : [\n ...(Array.isArray(previous)\n ? previous\n : [String(previous)]),\n supplied[i + 1]\n ];\n }\n headers = { ...headers, ...raw };\n } else if (supplied) {\n for (const [name, value] of Object.entries(supplied))\n headers[name.toLowerCase()] =\n value as OutgoingHttpHeaders[string];\n }\n const result = Reflect.apply(writeHead, this, [status, ...args]);\n headers = { ...headers, ...this.getHeaders?.() };\n return result;\n } as typeof writeHead;\n response.write = function (\n this: ServerResponse,\n chunk: any,\n ...args: any[]\n ) {\n capture(chunk, args[0]);\n return Reflect.apply(write, this, [chunk, ...args]);\n } as typeof write;\n response.end = function (\n this: ServerResponse,\n chunk?: any,\n ...args: any[]\n ) {\n capture(chunk, args[0]);\n const result = Reflect.apply(end, this, [chunk, ...args]);\n ended = true;\n headers = { ...headers, ...this.getHeaders?.() };\n return result;\n } as typeof end;\n try {\n await next();\n if (!ended || size > maxBytes) return undefined;\n return {\n status: response.statusCode,\n headers,\n body: Buffer.concat(chunks)\n };\n } finally {\n response.write = write;\n response.writeHead = writeHead;\n response.end = end;\n }\n}\n","/**\n * Server-side cache response middleware.\n *\n * Caches successful handler responses keyed by endpoint-defined cache tags.\n * On cache hit, the response is served directly — the handler never runs.\n * Mutating requests invalidate matching cache entries after the handler\n * completes successfully.\n *\n * @module\n */\n\nimport type { OutgoingHttpHeaders, ServerResponse } from 'node:http';\nimport type { CacheTagDefinition } from '../CacheTag.js';\nimport { computeCacheKey } from '../cacheKey.js';\nimport type { RequestContext } from '../RequestContext.js';\nimport type { Middleware } from '../types.js';\nimport { captureResponse } from './ResponseSnapshot.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for {@link cacheResponse}.\n */\nexport interface ServerCacheOptions {\n /**\n * Default TTL in milliseconds for tags without an explicit TTL.\n * Defaults to `60000` (60 seconds).\n */\n defaultTtl?: number;\n\n /**\n * Per-tag TTL overrides: `{ [tagName]: ttlMs }`.\n */\n ttlByTag?: Record<string, number>;\n /** Maximum retained cache keys. Default: 1000 (oldest keys are evicted). */\n maxEntries?: number;\n /** Maximum retained response body size. Default: 65,536 bytes. */\n maxResponseBytes?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry {\n status: number;\n headers: OutgoingHttpHeaders;\n body: Buffer;\n expiresAt: number;\n generations: ReadonlyArray<readonly [string, number]>;\n}\n\nfunction isMutating(method: string): boolean {\n return ['POST', 'PUT', 'DELETE', 'PATCH'].includes(method.toUpperCase());\n}\n\n// ---------------------------------------------------------------------------\n// Middleware\n// ---------------------------------------------------------------------------\n\n/**\n * Server-side cache response middleware.\n *\n * Uses cache-tag definitions from the matched endpoint (already available\n * on `ctx.items.__endpoint_meta.cacheTags`) to compute deterministic cache\n * keys from request data (params, query, body, headers).\n *\n * - **GET**: Computes cache key → serves cached response if valid →\n * handler never executes. On cache miss, runs the handler and caches\n * the response.\n * - **Mutation (POST/PUT/PATCH/DELETE)**: Lets the handler run, then\n * invalidates all cache entries whose tag names start with any of the\n * endpoint's cache tag names. Older in-flight reads cannot refill them.\n * - By default, retain at most 1000 keys and bodies up to 65,536 bytes. Private/no-store and\n * Set-Cookie responses are not cached. Install after authorization and encode\n * every identity/tenant/representation dimension in tag properties.\n *\n * @param options - Cache configuration.\n * @returns A server-side {@link Middleware}.\n *\n * @example\n * ```ts\n * server.handle(ListTodos, listHandler, {\n * middlewares: [cacheResponse({ defaultTtl: 30_000 })]\n * });\n * ```\n */\nexport function cacheResponse(options: ServerCacheOptions = {}): Middleware {\n const {\n defaultTtl = 60_000,\n maxEntries = 1000,\n maxResponseBytes = 65_536\n } = options;\n const ttlByTag = { ...options.ttlByTag };\n for (const [name, value] of Object.entries({\n maxEntries,\n maxResponseBytes\n })) {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new TypeError(name + ' must be a positive safe integer');\n }\n for (const value of [defaultTtl, ...Object.values(ttlByTag)]) {\n if (!Number.isFinite(value) || value < 0)\n throw new TypeError('Cache TTL must be finite and non-negative');\n }\n\n const cache = new Map<string, CacheEntry>();\n const generations = new Map<string, number>();\n const isCurrent = (snapshot: CacheEntry['generations']) =>\n snapshot.every(\n ([name, generation]) => generations.get(name) === generation\n );\n\n return async (ctx: RequestContext, next: () => Promise<void>) => {\n const meta = ctx.items.get('__endpoint_meta') as any;\n const tags: readonly CacheTagDefinition[] = meta?.cacheTags ?? [];\n\n if (tags.length === 0) {\n return next();\n }\n\n if (isMutating(ctx.method)) {\n const names = tags.map(tag => tag.name);\n // Run handler first (so cache is invalidated only on success)\n await next();\n\n if (\n (ctx.response as ServerResponse).statusCode >= 200 &&\n (ctx.response as ServerResponse).statusCode < 300\n ) {\n for (const [name, generation] of generations) {\n if (names.some(prefix => name.startsWith(prefix))) {\n generations.set(name, generation + 1);\n }\n }\n for (const [key, entry] of cache) {\n if (!isCurrent(entry.generations)) cache.delete(key);\n }\n }\n return;\n }\n\n if (ctx.method === 'GET') {\n for (const [key, entry] of cache)\n if (entry.expiresAt <= Date.now()) cache.delete(key);\n // Build root for key computation\n const root = {\n params: ctx.pathParams ?? {},\n body: undefined,\n query: ctx.queryParams ?? {},\n headers: ctx.headers ?? {}\n };\n\n const keys = tags.map(tag => computeCacheKey(tag, root));\n const snapshot = tags.map(({ name }) => {\n if (!generations.has(name)) generations.set(name, 0);\n return [name, generations.get(name)!] as const;\n });\n\n // Check all keys — first valid cache hit wins\n for (const key of keys) {\n const entry = cache.get(key);\n if (\n entry &&\n entry.expiresAt > Date.now() &&\n isCurrent(entry.generations)\n ) {\n // Serve from cache\n const res = ctx.response as ServerResponse;\n res.writeHead(entry.status, entry.headers);\n res.end(entry.body);\n ctx.responded = true;\n return;\n }\n if (entry) {\n cache.delete(key);\n }\n }\n\n const snapshotResponse = await captureResponse(\n ctx.response,\n next,\n maxResponseBytes\n );\n if (!snapshotResponse) return;\n const {\n status: capturedStatus,\n headers: capturedHeaders,\n body\n } = snapshotResponse;\n if (\n Object.keys(capturedHeaders).some(\n name => name.toLowerCase() === 'set-cookie'\n )\n )\n return;\n if (\n /\\b(?:private|no-store)\\b/i.test(\n String(capturedHeaders['cache-control'] ?? '')\n )\n )\n return;\n\n // Store in cache on success\n if (\n capturedStatus >= 200 &&\n capturedStatus < 300 &&\n isCurrent(snapshot)\n ) {\n const ttl = tags.reduce((max, tag) => {\n const t =\n ttlByTag[tag.name] !== undefined\n ? ttlByTag[tag.name]\n : defaultTtl;\n return t > max ? t : max;\n }, 0);\n\n if (ttl > 0) {\n for (const key of keys) {\n if (!cache.has(key) && cache.size >= maxEntries)\n cache.delete(cache.keys().next().value!);\n cache.set(key, {\n status: capturedStatus,\n headers: capturedHeaders,\n body,\n expiresAt: Date.now() + ttl,\n generations: snapshot\n });\n }\n }\n }\n return;\n }\n\n // Non-GET, non-mutation — pass through\n return next();\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: any().hasType<URL>(),\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 #bodyPromise?: Promise<Buffer>;\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> = Object.create(null);\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> = Object.create(null);\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 once; concurrent readers share the same result or failure. */\n async body(): Promise<Buffer> {\n if (this.#bodyPromise) return this.#bodyPromise;\n this.#bodyPromise = new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n let settled = false;\n const cleanup = () => {\n this.request.off('data', data);\n this.request.off('end', end);\n this.request.off('error', error);\n this.request.off('aborted', aborted);\n this.request.off('close', closed);\n };\n const error = (reason: Error) => {\n if (settled) return;\n settled = true;\n cleanup();\n chunks.length = 0;\n reject(reason);\n };\n const aborted = () =>\n error(new HttpError(400, 'Request body interrupted'));\n const closed = () => {\n if (!this.request.readableEnded) aborted();\n };\n const data = (chunk: Buffer) => {\n if (settled) return;\n totalSize += chunk.length;\n if (totalSize > this.maxBodySize) {\n error(new HttpError(413, 'Payload Too Large'));\n this.request.destroy();\n return;\n }\n chunks.push(chunk);\n };\n const end = () => {\n if (settled) return;\n settled = true;\n cleanup();\n resolve(Buffer.concat(chunks));\n };\n if (this.request.destroyed || this.request.readableEnded) {\n aborted();\n return;\n }\n this.request.on('data', data);\n this.request.on('end', end);\n this.request.on('error', error);\n this.request.on('aborted', aborted);\n this.request.on('close', closed);\n });\n return this.#bodyPromise;\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","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 { CorsPolicy, type ServerCorsOptions } from './Cors.js';\nimport type { EndpointBuilder, Handler, HandlerMapping } from './Endpoint.js';\nimport type { EndpointOptions } from './EndpointOptions.js';\nimport { validateErrorMap } from './ErrorMap.js';\nimport { HttpError } from './HttpError.js';\nimport { MiddlewarePipeline } from './MiddlewarePipeline.js';\nimport { idempotency } from './middlewares/Idempotency.js';\nimport { parseMultipart } from './multipart.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 FilePart,\n Middleware,\n RejectedFile,\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 * Optional ordered authentication fallback.\n *\n * By default, only `defaultScheme` is attempted. Set to `'all'` to try\n * every registered scheme in registration order, or pass scheme names to\n * try a specific ordered subset.\n */\n trySchemes?: 'all' | readonly string[];\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 #corsOptions?: ServerCorsOptions;\n\n constructor(options: ServerOptions = {}) {\n this.#options = options;\n }\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 middleware to the matched-request pipeline after authentication.\n * Middleware is executed in the order it is added. CORS short-circuits\n * and unmatched routes do not enter this pipeline.\n */\n use(middleware: Middleware): this {\n this.#globalMiddlewares.push(middleware);\n return this;\n }\n\n /**\n * Enable server-wide CORS before routing and authentication.\n * Accepted preflights return 204 without running ordinary middleware.\n * Requests with disallowed origins return 403 before handlers run.\n * Configuration is validated and copied when the server starts listening.\n */\n useCors(options: ServerCorsOptions): this {\n if (options === undefined) {\n throw new TypeError('CORS requires an explicit origin policy');\n }\n this.#corsOptions = options;\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 middleware that authenticates protected requests and sets\n * `ctx.principal`. CORS preflights are handled before this middleware.\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 `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 >(endpointDef: E, handler: Handler<E>, options?: EndpointOptions<E>): this {\n this.#registrations.push({\n endpoint: endpointDef.introspect(),\n handler,\n middlewares: options?.middlewares,\n prepare: options?.prepare,\n idempotency: options?.idempotency,\n errors: options?.errors\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 handlerErrorsMapped: entry.handlerErrorsMapped,\n prepare: entry.prepare,\n idempotency: entry.idempotency,\n errors: entry.errors\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 if (\n reg.endpoint.idempotent &&\n typeof reg.idempotency?.scope !== 'function'\n )\n throw new TypeError(\n 'Idempotent endpoints require an explicit scope policy'\n );\n if (reg.idempotency && !reg.endpoint.idempotent)\n throw new TypeError(\n 'An idempotency policy requires an idempotent contract'\n );\n if (reg.errors)\n validateErrorMap({ introspect: () => reg.endpoint });\n if (reg.idempotency) {\n // Validate limits before opening the listening socket.\n idempotency({ ...reg.idempotency, scope: () => undefined });\n }\n router.addRoute(reg);\n }\n for (const reg of this.#subscriptionRegistrations) {\n router.addSubscriptionRoute(reg);\n }\n router.finalize();\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 this.#corsOptions\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 #replay = new Map<EndpointRegistration, Middleware>();\n readonly #replayScopes = new WeakMap<RequestContext, string>();\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 readonly #cors?: CorsPolicy;\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 corsOptions?: ServerCorsOptions\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 this.#cors =\n corsOptions === undefined ? undefined : new CorsPolicy(corsOptions);\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 physicalRequest = true\n ): Promise<void> {\n if (\n physicalRequest &&\n this.#cors &&\n (await this.#cors.handle(req, res, (method, path) =>\n this.#matchCorsRoute(method, path)\n ))\n )\n return;\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 const batchSubrequest = (\n req as http.IncomingMessage & {\n __cleverbrushBatchSubrequest?: {\n index: number;\n size: number;\n path: string;\n };\n }\n ).__cleverbrushBatchSubrequest;\n if (batchSubrequest) {\n ctx.items.set('__batch_subrequest', batchSubrequest);\n }\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 — it has no endpoint metadata, but should still\n // pass through global middleware so observability/logging can wrap\n // the physical batch request.\n if (\n this.#batchConfig !== null &&\n method === 'POST' &&\n urlPath === (this.#batchConfig.path ?? '/__batch')\n ) {\n ctx.services = scope.serviceProvider;\n\n const pipeline = new MiddlewarePipeline();\n for (const mw of this.#globalMiddlewares) {\n pipeline.add(mw);\n }\n\n await pipeline.execute(ctx, async () => {\n if (ctx.responded) return;\n await this.#handleBatchRequest(req, res);\n ctx.responded = true;\n });\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 let uploadedFiles:\n | Record<string, FilePart | FilePart[]>\n | undefined;\n let rejectedFiles: RejectedFile[] | undefined;\n if (needsBody(meta) || meta.fileUpload) {\n const contentType = req.headers['content-type'] ?? '';\n\n // Multipart / file-upload path\n if (\n meta.fileUpload &&\n contentType.startsWith('multipart/form-data')\n ) {\n try {\n const result = await parseMultipart(\n req,\n meta.fileUpload,\n this.#maxBodySize\n );\n if (\n meta.fileUpload.schema &&\n !meta.bodySchema &&\n Object.keys(result.fields).length\n ) {\n throw new HttpError(\n 400,\n 'Bad Request',\n 'This upload endpoint does not declare text fields',\n {\n errors: Object.keys(result.fields).map(\n name => ({\n pointer: `/body/${name.replace(/~/g, '~0').replace(/\\//g, '~1')}`,\n detail: 'Unexpected multipart text field'\n })\n )\n }\n );\n }\n parsedBody = result.fields;\n uploadedFiles = result.files;\n rejectedFiles = result.rejectedFiles;\n } catch (error) {\n if (error instanceof HttpError) throw error;\n const pd = createProblemDetails(\n 400,\n 'Malformed multipart request'\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 } else if (meta.fileUpload) {\n const pd = createProblemDetails(\n 400,\n 'File upload endpoint requires multipart/form-data content type'\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 } else {\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 =\n ctHandler.deserialize(bodyText);\n } catch {\n const pd = createProblemDetails(\n 400,\n 'Malformed request body'\n );\n res.writeHead(400, {\n 'content-type':\n 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\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 // Inject uploaded files into the action context\n if (uploadedFiles && resolveResult.args.length > 0) {\n const ctx = resolveResult.args[0] as Record<\n string,\n unknown\n >;\n ctx.files = uploadedFiles;\n if (rejectedFiles && rejectedFiles.length > 0) {\n ctx.rejectedFiles = rejectedFiles;\n }\n }\n\n const args = resolveResult.args;\n const send = async (result: unknown) => {\n if (ctx.responded) return;\n await this.#sendResult(req, res, result);\n ctx.responded = true;\n };\n const translate = async (error: unknown) => {\n if (!registration.errors) throw error;\n return registration.errors.translate(error);\n };\n // Framework transport validation precedes application callbacks.\n const key = ctx.headers['x-idempotency-key'];\n if (\n meta.idempotent &&\n key !== undefined &&\n (key.length === 0 || key.length > 256)\n )\n throw new HttpError(\n 400,\n 'Idempotency key must contain 1 to 256 characters'\n );\n try {\n if (registration.prepare)\n args[0] = await registration.prepare(...args);\n if (registration.idempotency && key !== undefined) {\n const scope = await registration.idempotency.scope(\n ...args\n );\n if (\n typeof scope !== 'string' &&\n !(\n Array.isArray(scope) &&\n scope.length > 0 &&\n scope.every(\n part =>\n typeof part === 'string' ||\n (typeof part === 'number' &&\n Number.isFinite(part))\n )\n )\n )\n throw new TypeError(\n 'Idempotency scope must be a string or identity components'\n );\n this.#replayScopes.set(ctx, JSON.stringify(scope));\n }\n } catch (error) {\n await send(await translate(error));\n return;\n }\n const execute = async () => {\n let result: unknown;\n try {\n result = await registration.handler(...args);\n } catch (error) {\n if (registration.handlerErrorsMapped) throw error;\n result = await translate(error);\n }\n await send(result);\n };\n if (registration.idempotency) {\n let replay = this.#replay.get(registration);\n if (!replay) {\n replay = idempotency({\n ...registration.idempotency,\n scope: request => this.#replayScopes.get(request)\n });\n this.#replay.set(registration, replay);\n }\n await replay(ctx, execute);\n } else {\n await execute();\n }\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 // CORS target lookup includes built-in routes without changing normal routing.\n // -----------------------------------------------------------------------\n\n #matchCorsRoute(\n method: string,\n path: string\n ): {\n status: 200 | 400 | 404 | 405;\n allowedMethods?: string[];\n } {\n const builtins: string[] = [];\n if (this.#healthcheck && path === '/health') builtins.push('GET');\n if (\n this.#batchConfig &&\n path === (this.#batchConfig.path ?? '/__batch')\n ) {\n builtins.push('POST');\n }\n if (builtins.includes(method)) return { status: 200 };\n const result = this.#router.match(method, path);\n if (result.badRequest) return { status: 400 };\n if (result.match) return { status: 200 };\n const allowedMethods = [\n ...new Set([...builtins, ...(result.allowedMethods ?? [])])\n ];\n return allowedMethods.length\n ? { status: 405, allowedMethods }\n : { status: 404 };\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 index: number\n ): Promise<BatchSubResponse> => {\n const virtualReq = new VirtualIncomingMessage({\n method: (item.method ?? 'GET').toUpperCase(),\n url: item.url,\n headers: {\n host: req.headers.host ?? 'localhost',\n ...item.headers\n },\n body: item.body\n });\n virtualReq.__cleverbrushBatchSubrequest = {\n index,\n size: outerBody.requests.length,\n path: config.path ?? '/__batch'\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 false\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 (let i = 0; i < outerBody.requests.length; i++) {\n results.push(await execute(outerBody.requests[i]!, i));\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 .then(() => {\n // Short-circuiting middleware never invokes the terminal handler.\n if (ctx.responded) ws.close(1008, 'Unauthorized');\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 return new ServerBuilder(options);\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 let schemesToTry: AuthenticationScheme<any>[];\n if (config.trySchemes === 'all') {\n schemesToTry = config.schemes;\n } else {\n const schemeNames: readonly string[] =\n config.trySchemes && config.trySchemes.length > 0\n ? config.trySchemes\n : [config.defaultScheme];\n schemesToTry = schemeNames\n .map(name => schemeMap.get(name))\n .filter(\n (scheme): scheme is AuthenticationScheme<any> =>\n scheme !== undefined\n );\n }\n\n return async (ctx, next) => {\n // Skip authentication for public endpoints (authRoles === null)\n const epMeta = ctx.items.get('__endpoint_meta') as\n | import('./Endpoint.js').EndpointMetadata\n | import('./Subscription.js').SubscriptionMetadata\n | undefined;\n if (epMeta?.authRoles === null) {\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 ctx.principal = Principal.anonymous();\n for (const scheme of schemesToTry) {\n const result = await scheme.authenticate(authCtx);\n if (result.succeeded) {\n ctx.principal = result.principal;\n break;\n }\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 type {\n IncomingMessage,\n OutgoingHttpHeaders,\n ServerResponse\n} from 'node:http';\nimport {\n createProblemDetails,\n PROBLEM_JSON_CONTENT_TYPE,\n serializeProblemDetails\n} from './ProblemDetails.js';\n\n/** Server-wide CORS policy, enabled explicitly with ServerBuilder.useCors(). */\nexport interface ServerCorsOptions {\n /** Exact serialized origins, '*', or a predicate evaluated once per HTTP request with Origin. */\n origin:\n | string\n | readonly string[]\n | ((origin: string) => boolean | Promise<boolean>);\n /** Optional preflight allowlist, restricted to registered routes. No wildcards. */\n methods?: readonly string[];\n /** Permitted preflight header names (case-insensitive). Defaults to none; no wildcards. */\n allowedHeaders?: readonly string[];\n /** Additional response headers browsers may read. Defaults to none; no wildcards. */\n exposedHeaders?: readonly string[];\n /** Allow browser credentials. Defaults to false; incompatible with origin: '*'. */\n credentials?: boolean;\n /** Browser preflight cache duration in seconds. Defaults to zero. */\n maxAgeSeconds?: number;\n}\n\ntype OriginPredicate = (origin: string) => boolean | Promise<boolean>;\ntype RouteStatus = {\n status: 200 | 400 | 404 | 405;\n allowedMethods?: readonly string[];\n};\n\nconst INVALID_TOKEN_CHARACTER = /[^!#$%&'*+\\-.^_`|~\\da-zA-Z]/;\nconst MANAGED_HEADERS = new Set([\n 'access-control-allow-origin',\n 'access-control-allow-credentials',\n 'access-control-allow-methods',\n 'access-control-allow-headers',\n 'access-control-expose-headers',\n 'access-control-max-age'\n]);\n\nfunction isOrigin(value: unknown): value is string {\n if (typeof value !== 'string') return false;\n if (value === 'null') return true;\n try {\n const origin = new URL(value).origin;\n return origin !== 'null' && origin === value;\n } catch {\n return false;\n }\n}\n\nfunction isToken(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n !INVALID_TOKEN_CHARACTER.test(value)\n );\n}\n\nfunction names(values: readonly string[] | undefined, field: string): string[] {\n if (values === undefined) return [];\n if (\n !Array.isArray(values) ||\n values.some(value => !isToken(value) || value === '*')\n ) {\n throw new TypeError(`CORS ${field} must contain explicit HTTP tokens`);\n }\n return [...new Set(values.map(value => value.toLowerCase()))];\n}\n\nfunction varyValue(values: unknown[], required: readonly string[]): string {\n const tokens = new Map<string, string>();\n for (const value of [...values, ...required]) {\n if (value === undefined) continue;\n for (const token of String(value).split(',')) {\n const trimmed = token.trim();\n if (trimmed === '*') return '*';\n if (trimmed && !tokens.has(trimmed.toLowerCase())) {\n tokens.set(trimmed.toLowerCase(), trimmed);\n }\n }\n }\n return [...tokens.values()].join(', ');\n}\n\n/** Finalize only the physical response; never mutate headers held by a cache. */\nfunction finalizeHeaders(\n res: ServerResponse,\n corsHeaders: Record<string, string>,\n vary: readonly string[]\n): void {\n const writeHead = res.writeHead;\n res.writeHead = function (\n this: ServerResponse,\n status: number,\n ...args: any[]\n ) {\n // Preserve both writeHead(status, headers) and (status, message, headers).\n const headerIndex =\n typeof args[0] === 'string' || args.length > 1 ? 1 : 0;\n const headers = args[headerIndex] as\n | OutgoingHttpHeaders\n | string[]\n | undefined;\n const values: unknown[] = [this.getHeader('vary')];\n const keep = (name: string, value: unknown) => {\n const lower = name.toLowerCase();\n if (lower === 'vary') values.push(value);\n return lower !== 'vary' && !MANAGED_HEADERS.has(lower);\n };\n if (Array.isArray(headers)) {\n const copy: string[] = [];\n for (let i = 0; i < headers.length; i += 2) {\n if (keep(headers[i], headers[i + 1])) {\n copy.push(headers[i], headers[i + 1]);\n }\n }\n args[headerIndex] = copy;\n } else if (headers !== undefined) {\n args[headerIndex] = Object.fromEntries(\n Object.entries(headers).filter(([name, value]) =>\n keep(name, value)\n )\n );\n }\n for (const name of MANAGED_HEADERS) this.removeHeader(name);\n for (const [name, value] of Object.entries(corsHeaders)) {\n this.setHeader(name, value);\n }\n this.setHeader('vary', varyValue(values, vary));\n return writeHead.call(this, status, ...args);\n } as ServerResponse['writeHead'];\n}\n\nfunction problem(\n res: ServerResponse,\n status: number,\n allow?: readonly string[]\n): true {\n res.writeHead(status, {\n 'content-type': PROBLEM_JSON_CONTENT_TYPE,\n ...(allow ? { allow: allow.join(', ') } : {})\n });\n res.end(serializeProblemDetails(createProblemDetails(status)));\n return true;\n}\n\n/** @internal Compiled CORS policy; deliberately separate from route middleware. */\nexport class CorsPolicy {\n readonly #origin: '*' | ReadonlySet<string> | OriginPredicate;\n readonly #methods?: ReadonlySet<string>;\n readonly #allowedHeaders: ReadonlySet<string>;\n readonly #exposedHeaders: string;\n readonly #credentials: boolean;\n readonly #maxAgeSeconds: number;\n\n constructor(options: ServerCorsOptions) {\n const origin = options?.origin;\n if (origin === '*') this.#origin = '*';\n else if (typeof origin === 'function') this.#origin = origin;\n else {\n const origins = typeof origin === 'string' ? [origin] : origin;\n if (\n !Array.isArray(origins) ||\n origins.some(value => !isOrigin(value))\n ) {\n throw new TypeError(\n 'CORS origin must be a serialized origin, list, wildcard or predicate'\n );\n }\n this.#origin = new Set(origins);\n }\n if (\n options.credentials !== undefined &&\n typeof options.credentials !== 'boolean'\n ) {\n throw new TypeError('CORS credentials must be a boolean');\n }\n this.#credentials = options.credentials ?? false;\n if (this.#credentials && this.#origin === '*') {\n throw new TypeError(\n 'CORS credentials require explicit origins or a predicate'\n );\n }\n this.#maxAgeSeconds =\n options.maxAgeSeconds === undefined ? 0 : options.maxAgeSeconds;\n if (\n !Number.isSafeInteger(this.#maxAgeSeconds) ||\n this.#maxAgeSeconds < 0\n ) {\n throw new TypeError(\n 'CORS maxAgeSeconds must be a non-negative safe integer'\n );\n }\n const methods = names(options.methods, 'methods');\n if (options.methods !== undefined) {\n this.#methods = new Set(\n methods.map(method => method.toUpperCase())\n );\n }\n this.#allowedHeaders = new Set(\n names(options.allowedHeaders, 'allowedHeaders')\n );\n this.#exposedHeaders = names(\n options.exposedHeaders,\n 'exposedHeaders'\n ).join(', ');\n }\n\n /** Returns true when CORS has ended the response, false to run the usual pipeline. */\n async handle(\n req: IncomingMessage,\n res: ServerResponse,\n matchRoute: (method: string, path: string) => RouteStatus\n ): Promise<boolean> {\n const origin = req.headers.origin;\n const preflight =\n req.method?.toUpperCase() === 'OPTIONS' &&\n origin !== undefined &&\n req.headers['access-control-request-method'] !== undefined;\n const headers: Record<string, string> = {};\n finalizeHeaders(\n res,\n headers,\n preflight\n ? [\n 'Origin',\n 'Access-Control-Request-Method',\n 'Access-Control-Request-Headers'\n ]\n : ['Origin']\n );\n if (origin === undefined) return false;\n if (!isOrigin(origin)) return problem(res, 403);\n\n let allowed: boolean;\n try {\n allowed =\n this.#origin === '*' ||\n (typeof this.#origin === 'function'\n ? await this.#origin(origin)\n : this.#origin.has(origin));\n if (typeof allowed !== 'boolean') return problem(res, 500);\n } catch {\n return problem(res, 500);\n }\n if (!allowed) return problem(res, 403);\n\n if (preflight) {\n const method = req.headers['access-control-request-method'];\n const requested = req.headers['access-control-request-headers'];\n if (\n !isToken(method) ||\n method === '*' ||\n (requested !== undefined && typeof requested !== 'string')\n ) {\n return problem(res, 400);\n }\n const requestedHeaders =\n requested === undefined\n ? []\n : requested\n .split(',')\n .map(name => name.trim().toLowerCase());\n if (requestedHeaders.some(name => !isToken(name) || name === '*')) {\n return problem(res, 400);\n }\n let path: string;\n try {\n path = new URL(\n req.url ?? '/',\n `http://${req.headers.host ?? 'localhost'}`\n ).pathname;\n } catch {\n return problem(res, 400);\n }\n const result = matchRoute(method.toUpperCase(), path);\n if (result.status !== 200)\n return problem(res, result.status, result.allowedMethods);\n if (\n (this.#methods && !this.#methods.has(method.toUpperCase())) ||\n requestedHeaders.some(name => !this.#allowedHeaders.has(name))\n ) {\n return problem(res, 403);\n }\n headers['access-control-allow-methods'] = method;\n if (requestedHeaders.length) {\n headers['access-control-allow-headers'] = [\n ...new Set(requestedHeaders)\n ].join(', ');\n }\n headers['access-control-max-age'] = String(this.#maxAgeSeconds);\n } else if (this.#exposedHeaders) {\n headers['access-control-expose-headers'] = this.#exposedHeaders;\n }\n headers['access-control-allow-origin'] =\n this.#origin === '*' ? '*' : origin;\n if (this.#credentials)\n headers['access-control-allow-credentials'] = 'true';\n if (preflight) {\n res.writeHead(204);\n res.end();\n return true;\n }\n return false;\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 { IncomingMessage } from 'node:http';\nimport { Busboy, type BusboyFileStream } from '@fastify/busboy';\nimport { HttpError } from './HttpError.js';\nimport type { FilePart, RejectedFile } from './types.js';\nimport type { UploadConfiguration } from './upload.js';\n\nfunction failure(status: number, detail: string, field?: string): HttpError {\n const pointer =\n field === undefined\n ? '/files'\n : `/files/${field.replace(/~/g, '~0').replace(/\\//g, '~1')}`;\n return new HttpError(\n status,\n status === 413 ? 'Payload Too Large' : 'Bad Request',\n detail,\n {\n errors: [{ pointer, detail }]\n }\n );\n}\n\n/** @internal Buffered multipart parsing with a bound on the entire wire body. */\nexport async function parseMultipart(\n req: IncomingMessage,\n options: UploadConfiguration,\n maxBodySize: number\n): Promise<{\n fields: Record<string, string>;\n files: Record<string, FilePart | FilePart[]>;\n rejectedFiles: RejectedFile[];\n}> {\n const maxFileCount = options.maxFileCount ?? 10;\n const maxFieldCount = options.maxFieldCount ?? 100;\n const properties = options.schema?.introspect().properties;\n const fields: Record<string, string> = Object.create(null);\n const collected: Record<string, FilePart[]> = Object.create(null);\n const rejectedFiles: RejectedFile[] = [];\n const seenFiles = new Set<string>();\n const contentLength = Number(req.headers['content-length']);\n if (contentLength > maxBodySize) {\n req.resume();\n throw failure(413, 'Multipart request exceeds maxBodySize');\n }\n await new Promise<void>((resolve, reject) => {\n let finished = false;\n let bytes = 0;\n const active = new Map<BusboyFileStream, Buffer[]>();\n const parser = Busboy({\n headers: req.headers as { 'content-type': string },\n limits: {\n fileSize: options.maxFileSize ?? 10 * 1024 * 1024,\n files: maxFileCount,\n fieldSize: options.maxFieldSize ?? 1024 * 1024,\n fields: maxFieldCount,\n parts: options.maxPartCount ?? maxFileCount + maxFieldCount\n }\n });\n const cleanup = () => {\n req.unpipe(parser);\n req.off('data', countBytes);\n req.off('aborted', aborted);\n req.off('error', onError);\n req.off('close', closed);\n for (const [stream, chunks] of active) {\n chunks.length = 0;\n stream.destroy();\n }\n active.clear();\n };\n const fail = (error: unknown) => {\n if (finished) return;\n finished = true;\n cleanup();\n for (const key of Object.keys(collected)) delete collected[key];\n parser.destroy();\n req.resume();\n reject(\n error instanceof HttpError\n ? error\n : failure(400, 'Malformed multipart request')\n );\n };\n const countBytes = (chunk: Buffer) => {\n bytes += chunk.length;\n if (bytes > maxBodySize)\n fail(failure(413, 'Multipart request exceeds maxBodySize'));\n };\n const aborted = () =>\n fail(failure(400, 'Multipart request interrupted'));\n const closed = () => {\n if (!req.complete) aborted();\n };\n const onError = (error: Error) => fail(error);\n const checkName = (name: string) => {\n if (Buffer.byteLength(name) > (options.maxFieldNameSize ?? 100)) {\n fail(failure(413, 'Multipart field name is too long', name));\n return false;\n }\n return true;\n };\n parser.on('field', (name, value, nameTruncated, valueTruncated) => {\n if (finished || !checkName(name)) return;\n if (nameTruncated || valueTruncated) {\n fail(\n failure(413, 'Multipart field exceeds its size limit', name)\n );\n return;\n }\n if (\n Object.hasOwn(fields, name) ||\n seenFiles.has(name) ||\n (properties && Object.hasOwn(properties, name))\n ) {\n fail(\n failure(\n 400,\n 'Duplicate or incorrectly encoded multipart field',\n name\n )\n );\n return;\n }\n fields[name] = value;\n });\n parser.on('file', (name, stream, filename, _encoding, mimeType) => {\n const chunks: Buffer[] = [];\n active.set(stream, chunks);\n stream.on('error', onError);\n stream.on('limit', () =>\n fail(failure(413, 'File exceeds maxFileSize', name))\n );\n if (finished || !checkName(name)) {\n stream.resume();\n return;\n }\n const property =\n properties && Object.hasOwn(properties, name)\n ? properties[name]\n : undefined;\n const multiple = property?.introspect().type === 'array';\n if (\n (properties && !property) ||\n Object.hasOwn(fields, name) ||\n (seenFiles.has(name) && !multiple)\n ) {\n fail(failure(400, 'Unknown or duplicate file field', name));\n return;\n }\n seenFiles.add(name);\n const allowed =\n !options.allowedMimeTypes ||\n options.allowedMimeTypes.some(pattern =>\n pattern.endsWith('/*')\n ? mimeType.startsWith(pattern.slice(0, -1))\n : mimeType === pattern\n );\n if (!allowed) {\n if (properties) {\n fail(\n failure(\n 400,\n `MIME type \"${mimeType}\" is not allowed`,\n name\n )\n );\n return;\n }\n rejectedFiles.push({\n fieldName: name,\n filename,\n mimeType,\n reason: `MIME type \"${mimeType}\" is not allowed`\n });\n stream.on('end', () => active.delete(stream));\n stream.resume();\n return;\n }\n // Reserve the position at part arrival, not when its stream ends.\n const list = collected[name] ?? [];\n collected[name] = list;\n const index = list.length;\n list.push(undefined as unknown as FilePart);\n stream.on('data', (chunk: Buffer) => {\n if (!finished) chunks.push(chunk);\n });\n stream.on('end', () => {\n active.delete(stream);\n if (finished) return;\n if (stream.truncated) {\n fail(failure(413, 'File was truncated', name));\n return;\n }\n const buffer = Buffer.concat(chunks);\n chunks.length = 0;\n list[index] = {\n filename,\n mimeType,\n buffer,\n size: buffer.length\n };\n });\n });\n parser.on('filesLimit', () =>\n fail(failure(413, 'Exceeded maxFileCount'))\n );\n parser.on('fieldsLimit', () =>\n fail(failure(413, 'Exceeded maxFieldCount'))\n );\n parser.on('partsLimit', () =>\n fail(failure(413, 'Exceeded maxPartCount'))\n );\n parser.on('error', onError);\n parser.on('finish', () => {\n if (finished) return;\n finished = true;\n cleanup();\n resolve();\n });\n req.on('data', countBytes);\n req.once('aborted', aborted);\n req.once('error', onError);\n req.once('close', closed);\n req.pipe(parser);\n });\n const files: Record<string, FilePart | FilePart[]> = Object.create(null);\n for (const [name, values] of Object.entries(collected)) {\n files[name] =\n properties?.[name]?.introspect().type === 'array'\n ? values\n : values[0];\n }\n if (options.schema) {\n for (const [name, property] of Object.entries(properties!)) {\n const info = (property as any).introspect();\n if (\n info.type === 'array' &&\n info.isRequired &&\n !Object.hasOwn(files, name)\n )\n files[name] = [];\n }\n const result = await options.schema.validateAsync(files, {\n doNotStopOnFirstError: true\n });\n if (!result.valid) {\n const errors = result.getInvalidProperties().flatMap(prop =>\n prop.errors.map(detail => ({\n pointer: `/files${prop.descriptor.toJsonPointer()}`,\n detail\n }))\n );\n throw new HttpError(\n 400,\n 'Bad Request',\n 'Upload contract validation failed',\n {\n errors: errors.length\n ? errors\n : result.errors?.map(error => ({\n pointer: '/files',\n detail: error.message\n }))\n }\n );\n }\n return {\n fields,\n files: Object.fromEntries(\n Object.entries(result.object!).filter(\n ([, value]) => value !== undefined\n )\n ),\n rejectedFiles\n };\n }\n return { fields, files, rejectedFiles };\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.replace(/~/g, '~0').replace(/\\//g, '~1')}`,\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.replace(/~/g, '~0').replace(/\\//g, '~1')}`,\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 { matchRoutePath } from './EndpointPath.js';\nimport type {\n EndpointRegistration,\n RouteMatch,\n SubscriptionRegistration\n} from './types.js';\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\n/** Structural route precedence used to rank successful path matches. */\ninterface RouteSpecificity {\n readonly isStatic: boolean;\n readonly literalSegments: number;\n readonly dynamicSegments: number;\n}\n\ninterface RegisteredRoute<TRegistration> {\n readonly basePath: string;\n readonly routePath: RoutePath;\n readonly specificity: RouteSpecificity;\n readonly exactPath: string | null;\n readonly registration: TRegistration;\n}\n\ninterface RegisteredRouteMatch<TRegistration> {\n readonly registration: TRegistration;\n readonly parsedPath: Record<string, any> | null;\n}\n\ninterface RouteCollection<TRegistration> {\n readonly exactRoutes: Map<string, RegisteredRoute<TRegistration>>;\n readonly dynamicRoutes: RegisteredRoute<TRegistration>[];\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: RoutePath\n): p is ParseStringSchemaBuilder<any, any, any, any, any> {\n return typeof p !== 'string' && typeof (p as any).validate === 'function';\n}\n\n/**\n * Counts slash-delimited segments that contain literal route content.\n * Interpolations do not add literal content, even when they can consume `/`.\n */\nfunction countLiteralSegments(literals: readonly string[]): number {\n let count = 0;\n let currentSegmentHasLiteral = false;\n\n for (const literal of literals) {\n for (const character of literal) {\n if (character === '/') {\n if (currentSegmentHasLiteral) count++;\n currentSegmentHasLiteral = false;\n } else {\n currentSegmentHasLiteral = true;\n }\n }\n }\n\n return count + (currentSegmentHasLiteral ? 1 : 0);\n}\n\n/** Derives registration-order-independent precedence from route structure. */\nfunction getSpecificity(\n basePath: string,\n routePath: RoutePath\n): RouteSpecificity {\n if (!isParseStringSchema(routePath)) {\n const suffix = normalizePath(routePath);\n const fullPath = suffix === '/' ? basePath || '/' : basePath + suffix;\n return {\n isStatic: true,\n literalSegments: countLiteralSegments([fullPath]),\n dynamicSegments: 0\n };\n }\n\n const { literals, segments } = routePath.introspect().templateDefinition;\n const fullLiterals = [basePath + (literals[0] ?? ''), ...literals.slice(1)];\n\n return {\n isStatic: segments.length === 0,\n literalSegments: countLiteralSegments(fullLiterals),\n dynamicSegments: segments.length\n };\n}\n\n/**\n * Returns a positive value when `candidate` is more specific than `current`.\n * Equal scores deliberately preserve registration order.\n */\nfunction compareSpecificity(\n candidate: RouteSpecificity,\n current: RouteSpecificity\n): number {\n if (candidate.isStatic !== current.isStatic) {\n return candidate.isStatic ? 1 : -1;\n }\n if (candidate.literalSegments !== current.literalSegments) {\n return candidate.literalSegments - current.literalSegments;\n }\n return current.dynamicSegments - candidate.dynamicSegments;\n}\n\n/** Returns the complete exact path for routes without interpolations. */\nfunction getExactPath(basePath: string, routePath: RoutePath): string | null {\n if (!isParseStringSchema(routePath)) {\n const suffix = normalizePath(routePath);\n return suffix === '/' ? basePath || '/' : basePath + suffix;\n }\n\n const { literals, segments } = routePath.introspect().templateDefinition;\n if (segments.length > 0) return null;\n return basePath + (literals[0] ?? '');\n}\n\nfunction createRegisteredRoute<TRegistration>(\n basePath: string,\n routePath: RoutePath,\n registration: TRegistration\n): RegisteredRoute<TRegistration> {\n const normalizedBase = normalizePath(basePath);\n return {\n basePath: normalizedBase,\n routePath,\n specificity: getSpecificity(normalizedBase, routePath),\n exactPath: getExactPath(normalizedBase, routePath),\n registration\n };\n}\n\nfunction createRouteCollection<\n TRegistration\n>(): RouteCollection<TRegistration> {\n return {\n exactRoutes: new Map(),\n dynamicRoutes: []\n };\n}\n\n/** Adds a route while preserving the first registration for exact-path ties. */\nfunction addRegisteredRoute<TRegistration>(\n collection: RouteCollection<TRegistration>,\n route: RegisteredRoute<TRegistration>\n): void {\n if (route.exactPath !== null) {\n if (!collection.exactRoutes.has(route.exactPath)) {\n collection.exactRoutes.set(route.exactPath, route);\n }\n return;\n }\n\n collection.dynamicRoutes.push(route);\n}\n\n/**\n * HTTP and WebSocket router that maps paths 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 * When multiple templates validate the same URL, static routes win, followed\n * by routes with more literal segments and then fewer dynamic segments.\n * Registration order breaks equal-specificity ties.\n */\nexport class Router {\n readonly #routes: Map<string, RouteCollection<EndpointRegistration>> =\n new Map();\n readonly #subscriptionRoutes: RouteCollection<SubscriptionRegistration> =\n createRouteCollection();\n #finalized = true;\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 route = createRegisteredRoute(\n basePath,\n pathTemplate,\n registration\n );\n\n let collection = this.#routes.get(upperMethod);\n if (!collection) {\n collection = createRouteCollection();\n this.#routes.set(upperMethod, collection);\n }\n addRegisteredRoute(collection, route);\n this.#finalized = false;\n }\n\n /**\n * Prepare all dynamic route collections for request-time matching.\n *\n * Sorting is stable, so registration order remains the tie-breaker for\n * routes with equal specificity. The method is idempotent and is also\n * called lazily by match operations if routes have changed.\n */\n finalize(): void {\n if (this.#finalized) return;\n\n for (const collection of this.#routes.values()) {\n this.#sortDynamicRoutes(collection.dynamicRoutes);\n }\n this.#sortDynamicRoutes(this.#subscriptionRoutes.dynamicRoutes);\n this.#finalized = true;\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 * Exact paths are indexed directly. Dynamic candidates are ordered by\n * specificity during finalization and evaluated until the first match.\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 this.finalize();\n\n // Try exact method match first\n const methodRoutes = this.#routes.get(upperMethod);\n if (methodRoutes) {\n const result = this.#findFirstMatch(methodRoutes, normalized, url);\n if (result) {\n 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 if (this.#findFirstMatch(routes, normalized, url)) {\n allowedMethods.push(m);\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 /** Returns the first successful match from an indexed route collection. */\n #findFirstMatch<TRegistration>(\n collection: RouteCollection<TRegistration>,\n normalizedUrl: string,\n rawUrl: string\n ): RegisteredRouteMatch<TRegistration> | null {\n const exactRoute = collection.exactRoutes.get(normalizedUrl);\n if (exactRoute) {\n const match = this.#tryMatch(exactRoute, normalizedUrl, rawUrl);\n if (match) return match;\n }\n\n for (const route of collection.dynamicRoutes) {\n const match = this.#tryMatch(route, normalizedUrl, rawUrl);\n if (match) return match;\n }\n\n return null;\n }\n\n #sortDynamicRoutes<TRegistration>(\n routes: RegisteredRoute<TRegistration>[]\n ): void {\n routes.sort((a, b) => compareSpecificity(b.specificity, a.specificity));\n }\n\n #tryMatch<TRegistration>(\n route: RegisteredRoute<TRegistration>,\n normalizedUrl: string,\n rawUrl: string\n ): RegisteredRouteMatch<TRegistration> | 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 = matchRoutePath(\n { basePath, pathTemplate: routePath },\n rawUrl\n );\n if (result !== undefined) {\n return {\n registration: route.registration,\n parsedPath: result 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 addRegisteredRoute(\n this.#subscriptionRoutes,\n createRegisteredRoute(basePath, pathTemplate, registration)\n );\n this.#finalized = false;\n }\n\n /**\n * Match an incoming WebSocket upgrade URL to a registered subscription.\n *\n * Returns the most specific matched registration and parsed path params,\n * or `null`. Registration order breaks equal-specificity ties.\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 this.finalize();\n return this.#findFirstMatch(this.#subscriptionRoutes, normalized, url);\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 __cleverbrushBatchSubrequest?: {\n index: number;\n size: number;\n path: string;\n };\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\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.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 /** Return a snapshot of response headers, matching ServerResponse. */\n getHeaders(): Record<string, string> {\n return { ...this.#customHeaders };\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.statusCode,\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","import type { IncomingMessage, ServerResponse } from 'node:http';\n\n/** HTTP lifecycle fields required to cancel request-owned work. */\nexport interface RequestSignalContext {\n readonly request: Pick<IncomingMessage, 'aborted' | 'once' | 'off'>;\n readonly response: Pick<\n ServerResponse,\n 'destroyed' | 'writableFinished' | 'once' | 'off'\n >;\n}\n\n/** Application-owned deadline and optional cancellation for request work. */\nexport interface RequestSignalOptions {\n /** Positive integer deadline in milliseconds, at most 2,147,483,647. */\n timeoutMs: number;\n /** Additional cancellation, such as application shutdown. */\n signal?: AbortSignal;\n}\n\n/**\n * Run cooperative work with cancellation on disconnect, deadline or caller abort.\n *\n * The visitor must observe its signal; this helper does not forcibly interrupt\n * arbitrary promises. A normal response finish does not cancel the work. The\n * deadline timer and all listeners are removed when work settles, including on\n * failure. Already-aborted inputs reject without invoking the visitor.\n *\n * @example\n * ```ts\n * await withRequestSignal(context, signal => loadReport(signal), {\n * timeoutMs: 30_000\n * });\n * ```\n */\nexport async function withRequestSignal<T>(\n context: RequestSignalContext,\n work: (signal: AbortSignal) => T | PromiseLike<T>,\n options: RequestSignalOptions\n): Promise<T> {\n if (\n !Number.isSafeInteger(options.timeoutMs) ||\n options.timeoutMs <= 0 ||\n options.timeoutMs > 2_147_483_647\n ) {\n throw new RangeError(\n 'timeoutMs must be a positive timer-safe integer.'\n );\n }\n const controller = new AbortController();\n const disconnected = () =>\n controller.abort(\n new DOMException('Request disconnected.', 'AbortError')\n );\n const closed = () => {\n if (!context.response.writableFinished) disconnected();\n };\n const cancelled = () => controller.abort(options.signal!.reason);\n const timer = setTimeout(\n () =>\n controller.abort(\n new DOMException('Request deadline exceeded.', 'TimeoutError')\n ),\n options.timeoutMs\n );\n context.request.once('aborted', disconnected);\n context.response.once('close', closed);\n options.signal?.addEventListener('abort', cancelled, { once: true });\n try {\n if (context.request.aborted || context.response.destroyed) closed();\n if (context.request.aborted) disconnected();\n if (options.signal?.aborted) cancelled();\n controller.signal.throwIfAborted();\n return await work(controller.signal);\n } finally {\n clearTimeout(timer);\n context.request.off('aborted', disconnected);\n context.response.off('close', closed);\n options.signal?.removeEventListener('abort', cancelled);\n }\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","handler","RawResult","StatusCodeResult","req","res","_contentNegotiator","_req","key","value","filename","sanitized","disposition","resolve","reject","safeJsonParse","raw","key","value","checkJsonDepth","maxDepth","walk","current","max","item","v","jsonContentTypeHandler","value","raw","parsed","safeJsonParse","checkJsonDepth","formUrlEncodedContentTypeHandler","params","key","item","nested","result","isUnsafeFormKey","existing","parseAcceptHeader","accept","part","trimmed","mimeType","s","quality","p","val","a","b","ContentNegotiator","#handlers","handler","acceptHeader","contentTypeHeader","ErrorMap","_ErrorMap","#rules","rules","errorType","translate","error","rule","candidate","result","JsonResult","NoContentResult","StatusCodeResult","errorMap","withErrors","endpoint","policy","handler","validateErrorMap","args","responses","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","moduleEntries","ImplementationModule","#entries","entries","entry","ImplementationScope","_ImplementationScope","#group","#source","#endpoints","group","source","endpoints","names","seen","name","handlers","key","endpoint","binding","handler","errors","isSubscriptionBuilder","withErrors","ApiImplementation","_ApiImplementation","#contract","contract","options","config","configure","modules","module","missing","operations","mapHandlers","defaults","result","authorize","tags","implement","createHash","captureResponse","response","next","maxBytes","write","writeHead","end","chunks","size","ended","headers","capture","chunk","encoding","buffer","status","args","supplied","raw","i","name","previous","value","result","idempotency","options","scope","ttl","headerName","skip","ctx","maxEntries","maxResponseBytes","name","value","store","next","supplied","HttpError","identity","key","createHash","now","k","entry","existing","snapshot","resolve","reject","result","yes","no","captureResponse","error","isMutating","method","cacheResponse","options","defaultTtl","maxEntries","maxResponseBytes","ttlByTag","name","value","cache","generations","isCurrent","snapshot","generation","ctx","next","tags","names","tag","prefix","key","entry","root","keys","computeCacheKey","res","snapshotResponse","captureResponse","capturedStatus","capturedHeaders","body","ttl","max","t","URL","any","boolean","func","object","promise","record","string","IRequestContext","object","string","any","record","func","promise","boolean","DEFAULT_MAX_BODY_SIZE","RequestContext","#pathParams","#services","#bodyPromise","#jsonCache","#jsonParsed","request","response","maxBodySize","rawUrl","URL","headers","key","value","params","resolve","reject","chunks","totalSize","settled","cleanup","data","end","error","aborted","closed","reason","HttpError","chunk","text","safeJsonParse","checkJsonDepth","http","https","AuthorizationService","PolicyBuilder","Principal","parseCookies","requireRole","ServiceCollection","WebSocketServer","INVALID_TOKEN_CHARACTER","MANAGED_HEADERS","isOrigin","value","origin","isToken","names","values","field","varyValue","required","tokens","token","trimmed","finalizeHeaders","res","corsHeaders","vary","writeHead","status","args","headerIndex","headers","keep","name","lower","copy","i","problem","allow","PROBLEM_JSON_CONTENT_TYPE","serializeProblemDetails","createProblemDetails","CorsPolicy","#origin","#methods","#allowedHeaders","#exposedHeaders","#credentials","#maxAgeSeconds","options","origins","methods","method","req","matchRoute","preflight","allowed","requested","requestedHeaders","path","result","MiddlewarePipeline","#middlewares","middleware","context","finalHandler","index","next","Busboy","failure","status","detail","field","pointer","HttpError","parseMultipart","req","options","maxBodySize","maxFileCount","maxFieldCount","properties","fields","collected","rejectedFiles","seenFiles","resolve","reject","finished","bytes","active","parser","Busboy","cleanup","countBytes","aborted","onError","closed","stream","chunks","fail","error","key","chunk","checkName","name","value","nameTruncated","valueTruncated","filename","_encoding","mimeType","property","multiple","pattern","list","index","buffer","files","values","info","result","errors","prop","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","countLiteralSegments","literals","count","currentSegmentHasLiteral","literal","character","getSpecificity","basePath","routePath","suffix","fullPath","segments","fullLiterals","compareSpecificity","candidate","current","getExactPath","createRegisteredRoute","registration","normalizedBase","createRouteCollection","addRegisteredRoute","collection","route","Router","#routes","#subscriptionRoutes","#finalized","method","pathTemplate","upperMethod","#sortDynamicRoutes","url","normalized","methodRoutes","result","#findFirstMatch","allowedMethods","m","routes","normalizedUrl","rawUrl","exactRoute","match","#tryMatch","a","b","remainder","matchRoutePath","normalizedRoutePath","Readable","Writable","VirtualIncomingMessage","#body","#pushed","init","lowercased","key","value","VirtualServerResponse","#chunks","#customHeaders","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","#corsOptions","options","configureFn","middleware","handler","config","endpointDef","mapping","entry","def","port","host","router","Router","reg","validateErrorMap","idempotency","serviceProvider","authMiddlewares","createAuthenticationMiddleware","policies","name","builder","PolicyBuilder","authzService","AuthorizationService","createAuthorizationMiddleware","allMiddlewares","server","Server","listenPort","listenHost","MAX_WS_QUEUE_SIZE","#router","#replay","#replayScopes","#serviceProvider","#hasSubscriptions","#maxBodySize","#cors","#httpServer","#wss","#activeConnections","contentNegotiator","globalMiddlewares","healthcheck","hasSubscriptions","batchConfig","maxBodySize","DEFAULT_MAX_BODY_SIZE","corsOptions","CorsPolicy","req","res","#handleRequest","_err","PROBLEM_JSON_CONTENT_TYPE","serializeProblemDetails","createProblemDetails","resolve","WebSocketServer","socket","head","urlPath","result","ws","#handleWebSocket","reject","err","addr","physicalRequest","method","path","#matchCorsRoute","scope","ctx","RequestContext","batchSubrequest","pipeline","MiddlewarePipeline","mw","#handleBatchRequest","routeResult","pd","registration","parsedPath","meta","rawParams","flattenToStrings","parsedBody","uploadedFiles","rejectedFiles","needsBody","contentType","parseMultipart","HttpError","error","ctHandler","bodyText","resolveResult","resolveArgs","args","send","#sendResult","translate","key","part","execute","replay","request","builtins","allowedMethods","maxSize","parallel","outerBody","raw","readBuffer","parsed","safeJsonParse","checkJsonDepth","item","index","virtualReq","VirtualIncomingMessage","virtualRes","VirtualServerResponse","results","i","ActionResult","JsonResult","abortController","dummyRes","authPipeline","#runSubscription","incomingQueue","incomingResolve","incomingDone","incoming","text","frame","parseClientFrame","errorFrame","pongFrame","errors","e","subscriptionCtx","queryObj","k","v","handlerArgs","services","schema","generator","value","frameToSend","isTrackedEvent","outData","trackedFrame","messageFrame","createServer","chunks","totalSize","chunk","obj","prefix","fullKey","schemeMap","scheme","schemesToTry","next","Principal","authCtx","parseCookies","authConfig","challengeHeaders","ch","principal","headers","requireRole","defineWebhook","name","options","withRequestSignal","context","work","options","controller","disconnected","closed","cancelled","timer"]}