@spikard/node 0.9.1 → 0.10.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/upload.ts","../src/converters.ts","../src/request.ts","../src/streaming.ts","../src/handler-wrapper.ts","../src/app.ts","../src/background.ts","../src/grpc.ts","../src/routing.ts","../src/testing.ts"],"sourcesContent":["/**\n * Server runtime for Spikard Node\n */\n\nimport { createRequire } from \"node:module\";\nimport type { ServerConfig } from \"./config\";\nimport { isNativeHandler, wrapHandler } from \"./handler-wrapper\";\nimport type { HandlerFunction, NativeHandlerFunction, SpikardApp } from \"./index\";\n\ninterface NativeServerBinding {\n\trunServer(app: SpikardApp, config: ServerConfig | ServerOptions): void;\n}\n\nlet nativeBinding: NativeServerBinding;\n\nfunction loadBinding(): NativeServerBinding {\n\ttry {\n\t\tconst require = createRequire(import.meta.url);\n\t\treturn require(\"../index.js\") as NativeServerBinding;\n\t} catch {\n\t\tconsole.warn(\"[spikard-node] Native binding not found. Please run: pnpm build:native\");\n\t\treturn {\n\t\t\trunServer: () => {\n\t\t\t\tthrow new Error(\"Native binding not built. Run: pnpm build:native\");\n\t\t\t},\n\t\t};\n\t}\n}\n\nnativeBinding = loadBinding();\n\n/**\n * @deprecated Use ServerConfig instead\n */\nexport interface ServerOptions {\n\thost?: string;\n\tport?: number;\n}\n\n/**\n * Run the Spikard server\n *\n * @param app - The Spikard application instance\n * @param config - Server configuration options (supports full ServerConfig or legacy ServerOptions)\n *\n * @example\n * ```typescript\n * import { Spikard, runServer } from 'spikard';\n * import type { ServerConfig } from 'spikard';\n *\n * const app = new Spikard();\n * // Register routes...\n *\n * // Simple usage (backwards compatible)\n * runServer(app, { host: '0.0.0.0', port: 8000 });\n *\n * // Full configuration with middleware\n * const config: ServerConfig = {\n * host: '0.0.0.0',\n * port: 8080,\n * workers: 4,\n * compression: { quality: 9 },\n * rateLimit: { perSecond: 100, burst: 200 },\n * jwtAuth: { secret: 'your-secret', algorithm: 'HS256' },\n * openapi: { enabled: true, title: 'My API', version: '1.0.0' },\n * };\n * runServer(app, config);\n * ```\n */\nexport function runServer(app: SpikardApp, config: ServerConfig | ServerOptions = {}): void {\n\tconst handlers: Record<string, NativeHandlerFunction> = {};\n\tconst routes = (app.routes || []).map((route) => {\n\t\tconst handler = app.handlers?.[route.handler_name];\n\t\tif (!handler) return route;\n\t\tconst nativeHandler = isNativeHandler(handler) ? handler : wrapHandler(handler as HandlerFunction);\n\t\thandlers[route.handler_name] = nativeHandler;\n\t\tconst isAsync = nativeHandler.constructor.name === \"AsyncFunction\";\n\t\treturn { ...route, is_async: isAsync };\n\t});\n\n\tnativeBinding.runServer({ ...app, handlers, routes }, config);\n}\n","/**\n * File upload handling for multipart/form-data requests\n *\n * This module provides the UploadFile class for handling file uploads,\n * designed to be compatible with FastAPI and Litestar patterns while\n * optimized for Spikard's Rust-backed request processing.\n */\n\n/**\n * Represents an uploaded file from multipart/form-data requests\n *\n * This class provides both sync and async interfaces for file operations,\n * with automatic buffer management for efficient memory usage.\n *\n * @example\n * ```typescript\n * import { Spikard, type UploadFile } from 'spikard';\n *\n * const app = new Spikard();\n *\n * interface UploadRequest {\n * file: UploadFile;\n * description: string;\n * }\n *\n * app.post('/upload', async ({ body }: { body: UploadRequest }) => {\n * const content = body.file.read();\n * return {\n * filename: body.file.filename,\n * size: body.file.size,\n * contentType: body.file.contentType,\n * description: body.description,\n * };\n * });\n * ```\n */\nexport class UploadFile {\n\t/** Original filename from the client */\n\treadonly filename: string;\n\n\t/** MIME type of the uploaded file */\n\treadonly contentType: string;\n\n\t/** Size of the file in bytes */\n\treadonly size: number;\n\n\t/** Additional headers associated with this file field */\n\treadonly headers: Record<string, string>;\n\n\t/** Internal buffer storing file contents */\n\tprivate readonly _content: Buffer;\n\n\t/** Current read position in the buffer */\n\tprivate _position: number = 0;\n\n\t/**\n\t * Create a new UploadFile instance\n\t *\n\t * @param filename - Original filename from the client\n\t * @param content - File contents as Buffer\n\t * @param contentType - MIME type (defaults to \"application/octet-stream\")\n\t * @param size - File size in bytes (computed from content if not provided)\n\t * @param headers - Additional headers from the multipart field\n\t */\n\tconstructor(\n\t\tfilename: string,\n\t\tcontent: Buffer,\n\t\tcontentType: string | null = null,\n\t\tsize: number | null = null,\n\t\theaders: Record<string, string> | null = null,\n\t) {\n\t\tthis.filename = filename;\n\t\tthis.contentType = contentType ?? \"application/octet-stream\";\n\t\tthis.size = size ?? content.length;\n\t\tthis.headers = headers ?? {};\n\t\tthis._content = content;\n\t}\n\n\t/**\n\t * Read file contents synchronously\n\t *\n\t * @param size - Number of bytes to read (-1 for all remaining)\n\t * @returns File contents as Buffer\n\t */\n\tread(size: number = -1): Buffer {\n\t\tif (size === -1) {\n\t\t\tconst result = this._content.subarray(this._position);\n\t\t\tthis._position = this._content.length;\n\t\t\treturn result;\n\t\t}\n\n\t\tconst end = Math.min(this._position + size, this._content.length);\n\t\tconst result = this._content.subarray(this._position, end);\n\t\tthis._position = end;\n\t\treturn result;\n\t}\n\n\t/**\n\t * Read file contents asynchronously\n\t *\n\t * Since the file is already in memory from Rust parsing, this is a simple wrapper.\n\t *\n\t * @param size - Number of bytes to read (-1 for all remaining)\n\t * @returns File contents as Buffer\n\t */\n\tasync readAsync(size: number = -1): Promise<Buffer> {\n\t\treturn this.read(size);\n\t}\n\n\t/**\n\t * Read entire file as UTF-8 text\n\t *\n\t * @returns File contents as string\n\t */\n\ttext(): string {\n\t\treturn this._content.toString(\"utf-8\");\n\t}\n\n\t/**\n\t * Read entire file as UTF-8 text asynchronously\n\t *\n\t * @returns File contents as string\n\t */\n\tasync textAsync(): Promise<string> {\n\t\treturn this.text();\n\t}\n\n\t/**\n\t * Seek to a position in the file\n\t *\n\t * @param offset - Position to seek to\n\t * @param whence - How to interpret offset (0=absolute, 1=relative, 2=from end)\n\t * @returns New absolute position\n\t */\n\tseek(offset: number, whence: number = 0): number {\n\t\tswitch (whence) {\n\t\t\tcase 0:\n\t\t\t\tthis._position = Math.max(0, Math.min(offset, this._content.length));\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tthis._position = Math.max(0, Math.min(this._position + offset, this._content.length));\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tthis._position = Math.max(0, Math.min(this._content.length + offset, this._content.length));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Invalid whence value: ${whence}`);\n\t\t}\n\t\treturn this._position;\n\t}\n\n\t/**\n\t * Seek to a position in the file asynchronously\n\t *\n\t * @param offset - Position to seek to\n\t * @param whence - How to interpret offset (0=absolute, 1=relative, 2=from end)\n\t * @returns New absolute position\n\t */\n\tasync seekAsync(offset: number, whence: number = 0): Promise<number> {\n\t\treturn this.seek(offset, whence);\n\t}\n\n\t/**\n\t * Get current position in the file\n\t *\n\t * @returns Current byte position\n\t */\n\ttell(): number {\n\t\treturn this._position;\n\t}\n\n\t/**\n\t * Get the underlying Buffer\n\t *\n\t * @returns Complete file contents as Buffer\n\t */\n\tgetBuffer(): Buffer {\n\t\treturn this._content;\n\t}\n\n\t/**\n\t * Close the file (no-op for in-memory files, provided for API compatibility)\n\t */\n\tclose(): void {}\n\n\t/**\n\t * Close the file asynchronously (no-op, provided for API compatibility)\n\t */\n\tasync closeAsync(): Promise<void> {}\n\n\t/**\n\t * String representation of the upload file\n\t */\n\ttoString(): string {\n\t\treturn `UploadFile(filename=${JSON.stringify(this.filename)}, contentType=${JSON.stringify(this.contentType)}, size=${this.size})`;\n\t}\n\n\t/**\n\t * JSON representation for debugging\n\t */\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tfilename: this.filename,\n\t\t\tcontentType: this.contentType,\n\t\t\tsize: this.size,\n\t\t\theaders: this.headers,\n\t\t};\n\t}\n}\n","/**\n * Type conversion utilities for handler parameters\n *\n * This module handles converting validated JSON data from Rust into TypeScript types,\n * particularly for UploadFile instances.\n */\n\nimport type { JsonValue } from \"./types\";\nimport { UploadFile } from \"./upload\";\n\n/**\n * File metadata structure from Rust multipart parsing\n */\nexport interface FileMetadata {\n\tfilename: string;\n\tcontent: string;\n\tsize?: number | undefined;\n\tcontent_type?: string | undefined;\n\tcontent_encoding?: \"base64\" | \"text\" | undefined;\n}\n\n/**\n * Check if a value looks like file metadata from Rust\n */\nfunction isFileMetadata(value: unknown): value is FileMetadata {\n\treturn typeof value === \"object\" && value !== null && \"filename\" in value && \"content\" in value;\n}\n\n/**\n * Convert file metadata JSON to UploadFile instance\n *\n * @param fileData - File metadata from Rust (filename, content, size, content_type)\n * @returns UploadFile instance\n */\nexport function convertFileMetadataToUploadFile(fileData: FileMetadata): UploadFile {\n\tconst { filename, content, size, content_type, content_encoding } = fileData;\n\n\tlet buffer: Buffer;\n\tif (content_encoding === \"base64\") {\n\t\tbuffer = Buffer.from(content, \"base64\");\n\t} else {\n\t\tbuffer = Buffer.from(content, \"utf-8\");\n\t}\n\n\treturn new UploadFile(filename, buffer, content_type ?? null, size ?? null);\n}\n\n/**\n * Process handler parameters, converting file metadata to UploadFile instances\n *\n * This function recursively processes the body parameter, looking for file metadata\n * structures and converting them to UploadFile instances.\n *\n * @param value - The value to process (can be object, array, or primitive)\n * @returns Processed value with UploadFile instances\n */\nexport function processUploadFileFields(value: JsonValue | undefined): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn value;\n\t}\n\n\tif (typeof value !== \"object\") {\n\t\treturn value;\n\t}\n\n\tif (Array.isArray(value)) {\n\t\treturn value.map((item) => {\n\t\t\tif (isFileMetadata(item)) {\n\t\t\t\treturn convertFileMetadataToUploadFile(item);\n\t\t\t}\n\t\t\treturn processUploadFileFields(item as JsonValue);\n\t\t});\n\t}\n\n\tif (isFileMetadata(value)) {\n\t\treturn convertFileMetadataToUploadFile(value);\n\t}\n\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, val] of Object.entries(value)) {\n\t\tresult[key] = processUploadFileFields(val as JsonValue);\n\t}\n\n\treturn result;\n}\n\n/**\n * Convert __spikard_multipart__ test payload to handler body\n * This merges files and fields into a single object with UploadFile instances\n */\nfunction convertMultipartTestPayload(payload: {\n\tfields?: Record<string, unknown>;\n\tfiles?: Array<{ name: string; filename?: string; content: string; contentType?: string }>;\n}): unknown {\n\tconst result: Record<string, unknown> = {};\n\n\tif (payload.fields) {\n\t\tObject.assign(result, payload.fields);\n\t}\n\n\tif (payload.files && payload.files.length > 0) {\n\t\tconst filesByName: Record<string, unknown[]> = {};\n\n\t\tfor (const file of payload.files) {\n\t\t\tconst fileMetadata: FileMetadata = {\n\t\t\t\tfilename: file.filename || file.name,\n\t\t\t\tcontent: file.content,\n\t\t\t\tcontent_type: file.contentType,\n\t\t\t};\n\t\t\tconst uploadFile = convertFileMetadataToUploadFile(fileMetadata);\n\n\t\t\tif (!filesByName[file.name]) {\n\t\t\t\tfilesByName[file.name] = [];\n\t\t\t}\n\t\t\tconst files = filesByName[file.name];\n\t\t\tif (files) {\n\t\t\t\tfiles.push(uploadFile);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [name, files] of Object.entries(filesByName)) {\n\t\t\tresult[name] = files.length === 1 ? files[0] : files;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Process handler body parameter, handling UploadFile conversion\n *\n * This is the main entry point for converting Rust-provided request data\n * into TypeScript types. It handles:\n * - Single UploadFile\n * - Arrays of UploadFile\n * - Objects with UploadFile fields\n * - Nested structures\n * - TestClient multipart payloads\n *\n * @param body - The body parameter from Rust (already JSON-parsed)\n * @returns Processed body with UploadFile instances\n */\nexport function convertHandlerBody(body: JsonValue | undefined): unknown {\n\tif (\n\t\ttypeof body === \"object\" &&\n\t\tbody !== null &&\n\t\t\"__spikard_multipart__\" in body &&\n\t\ttypeof (body as Record<string, unknown>).__spikard_multipart__ === \"object\"\n\t) {\n\t\tconst multipart = (body as Record<string, unknown>).__spikard_multipart__ as {\n\t\t\tfields?: Record<string, unknown>;\n\t\t\tfiles?: Array<{ name: string; filename?: string; content: string; contentType?: string }>;\n\t\t};\n\t\treturn convertMultipartTestPayload(multipart);\n\t}\n\n\treturn processUploadFileFields(body);\n}\n","import { convertHandlerBody } from \"./converters\";\nimport type { JsonValue } from \"./types\";\n\nexport interface Request {\n\tmethod: string;\n\tpath: string;\n\tparams: Record<string, string>;\n\t/** Alias for params (for HandlerInput compatibility) */\n\tpathParams: Record<string, string>;\n\tquery: Record<string, string>;\n\t/** Alias for query (for HandlerInput compatibility) */\n\tqueryParams: Record<string, string>;\n\theaders: Record<string, string>;\n\tcookies: Record<string, string>;\n\tbody: Buffer | null;\n\tdependencies: Record<string, unknown> | undefined;\n\tjson<T = JsonValue>(): T;\n\tform(): Record<string, string>;\n}\n\nexport interface NativeRequestData {\n\tmethod: string;\n\tpath: string;\n\t// Legacy JSON serialization uses params/query\n\tparams?: Record<string, string>;\n\tquery?: Record<string, string>;\n\t// Direct napi object uses pathParams/queryParams (from HandlerInput)\n\tpathParams?: Record<string, string>;\n\tqueryParams?: unknown;\n\theaders: Record<string, string>;\n\tcookies: Record<string, string>;\n\tbody: number[] | unknown | null;\n\tdependencies?: Record<string, unknown>;\n}\n\nclass RequestImpl implements Request {\n\tmethod: string;\n\tpath: string;\n\tparams: Record<string, string>;\n\tpathParams: Record<string, string>;\n\tquery: Record<string, string>;\n\tqueryParams: Record<string, string>;\n\theaders: Record<string, string>;\n\tcookies: Record<string, string>;\n\tbody: Buffer | null;\n\tdependencies: Record<string, unknown> | undefined;\n\n\t#jsonCache: unknown | undefined;\n\t#formCache: Record<string, string> | undefined;\n\n\tconstructor(data: NativeRequestData) {\n\t\tthis.method = data.method;\n\t\tthis.path = data.path;\n\t\t// Support both field naming conventions (legacy JSON: params/query, napi object: pathParams/queryParams)\n\t\tconst pathParams = data.params ?? data.pathParams ?? {};\n\t\tconst queryParams = data.query ?? extractQueryParams(data.queryParams) ?? {};\n\t\tthis.params = pathParams;\n\t\tthis.pathParams = pathParams; // Alias for HandlerInput compatibility\n\t\tthis.query = queryParams;\n\t\tthis.queryParams = queryParams; // Alias for HandlerInput compatibility\n\t\tthis.headers = normalizeHeaders(data.headers ?? {});\n\t\tthis.cookies = data.cookies ?? {};\n\t\tthis.body = convertBodyToBuffer(data.body);\n\t\tthis.dependencies = data.dependencies;\n\t}\n\n\tjson<T = unknown>(): T {\n\t\tif (this.#jsonCache !== undefined) {\n\t\t\treturn this.#jsonCache as T;\n\t\t}\n\n\t\tif (!this.body || this.body.length === 0) {\n\t\t\tthrow new Error(\"No body available to parse as JSON\");\n\t\t}\n\n\t\tconst raw = this.body.toString(\"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as JsonValue;\n\t\tconst converted = convertHandlerBody(parsed) as T;\n\t\tthis.#jsonCache = converted;\n\t\treturn converted;\n\t}\n\n\tform(): Record<string, string> {\n\t\tif (this.#formCache !== undefined) {\n\t\t\treturn this.#formCache;\n\t\t}\n\n\t\tif (!this.body || this.body.length === 0) {\n\t\t\tthrow new Error(\"No body available to parse as form data\");\n\t\t}\n\n\t\tconst text = this.body.toString(\"utf-8\");\n\t\tconst params = new URLSearchParams(text);\n\t\tconst form: Record<string, string> = {};\n\t\tfor (const [key, value] of params.entries()) {\n\t\t\tform[key] = value;\n\t\t}\n\t\tthis.#formCache = form;\n\t\treturn form;\n\t}\n}\n\nconst normalizeHeaders = (headers: Record<string, string>): Record<string, string> =>\n\tObject.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));\n\n/**\n * Extract query parameters from various formats.\n * When coming from napi directly, queryParams might be a serde_json::Value object.\n */\nconst extractQueryParams = (queryParams: unknown): Record<string, string> | undefined => {\n\tif (!queryParams || typeof queryParams !== \"object\") {\n\t\treturn undefined;\n\t}\n\t// Convert object to string map (handles serde_json::Value from Rust)\n\tconst result: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(queryParams)) {\n\t\tresult[key] = String(value);\n\t}\n\treturn result;\n};\n\n/**\n * Convert body from various formats to Buffer.\n * Handles both legacy number[] arrays and direct serde_json::Value from napi.\n */\nconst convertBodyToBuffer = (body: number[] | unknown | null): Buffer | null => {\n\t// Null/undefined check\n\tif (body === null || body === undefined) {\n\t\treturn null;\n\t}\n\t// Legacy format: array of byte numbers (from JSON serialization)\n\tif (Array.isArray(body)) {\n\t\t// If it's an array of numbers, convert to Buffer (legacy byte array format)\n\t\tif (body.length === 0 || body.every((b) => typeof b === \"number\")) {\n\t\t\treturn Buffer.from(body as number[]);\n\t\t}\n\t\t// Otherwise serialize the array as JSON\n\t\treturn Buffer.from(JSON.stringify(body), \"utf-8\");\n\t}\n\t// Direct napi object: serialize to JSON bytes\n\tif (typeof body === \"object\") {\n\t\treturn Buffer.from(JSON.stringify(body), \"utf-8\");\n\t}\n\t// String body\n\tif (typeof body === \"string\") {\n\t\treturn Buffer.from(body, \"utf-8\");\n\t}\n\treturn null;\n};\n\nexport function createRequest(data: NativeRequestData): Request {\n\treturn new RequestImpl(data);\n}\n","import { createRequire } from \"node:module\";\nimport type { HandlerResult, JsonValue } from \"./types\";\n\nexport interface StreamingResponseInit {\n\tstatusCode?: number;\n\theaders?: Record<string, string>;\n}\n\nconst STREAM_HANDLE_PROP = \"__spikard_stream_handle\" as const;\n\ntype StreamChunk = JsonValue | string | Buffer | Uint8Array | ArrayBuffer | ArrayBufferView | null | undefined;\n\ntype ChunkIterator = AsyncIterator<StreamChunk> & AsyncIterable<StreamChunk>;\n\ntype StreamingHandle =\n\t| { kind: \"native\"; handle: number; init: StreamingResponseInit }\n\t| { kind: \"js\"; iterator: ChunkIterator; init: StreamingResponseInit };\n\ntype StreamingHandleFactory = (iterator: ChunkIterator, init: StreamingResponseInit) => StreamingHandle;\n\ninterface NativeStreamingBinding {\n\tcreateStreamingHandle(iterator: ChunkIterator, init: StreamingResponseInit): number;\n}\n\nlet nativeBinding: NativeStreamingBinding | null = null;\n\nconst loadBinding = (): NativeStreamingBinding | null => {\n\ttry {\n\t\tconst require = createRequire(import.meta.url);\n\t\treturn require(\"../index.js\") as NativeStreamingBinding;\n\t} catch {\n\t\tconsole.warn(\"[spikard-node] Native binding not found. Please run: pnpm build:native\");\n\t\treturn null;\n\t}\n};\n\nnativeBinding = loadBinding();\n\nconst createHandle: StreamingHandleFactory = (iterator, init) => {\n\tif (nativeBinding && typeof nativeBinding.createStreamingHandle === \"function\") {\n\t\treturn { kind: \"native\", handle: nativeBinding.createStreamingHandle(iterator, init), init };\n\t}\n\treturn { kind: \"js\", iterator, init };\n};\n\nexport class StreamingResponse {\n\tpublic readonly [STREAM_HANDLE_PROP]: StreamingHandle;\n\n\tconstructor(stream: AsyncIterable<StreamChunk> | AsyncIterator<StreamChunk>, init?: StreamingResponseInit) {\n\t\tconst iterator = toAsyncIterator(stream);\n\t\tthis[STREAM_HANDLE_PROP] = createHandle(iterator, init ?? {});\n\t}\n}\n\nexport function isStreamingResponse(value: HandlerResult): value is StreamingResponse {\n\treturn Boolean(value) && value instanceof StreamingResponse;\n}\n\nexport const getStreamingHandle = (response: StreamingResponse): StreamingHandle => response[STREAM_HANDLE_PROP];\n\nfunction toAsyncIterator(source: AsyncIterable<StreamChunk> | AsyncIterator<StreamChunk>): ChunkIterator {\n\tif (source && typeof (source as AsyncIterator<StreamChunk>).next === \"function\") {\n\t\tconst iterator = source as AsyncIterator<StreamChunk> & Partial<AsyncIterable<StreamChunk>>;\n\t\tif (typeof iterator[Symbol.asyncIterator] === \"function\") {\n\t\t\treturn iterator as ChunkIterator;\n\t\t}\n\t\treturn {\n\t\t\tnext: (...args) => iterator.next(...args),\n\t\t\t[Symbol.asyncIterator]() {\n\t\t\t\treturn this;\n\t\t\t},\n\t\t} as ChunkIterator;\n\t}\n\tif (source && typeof (source as AsyncIterable<StreamChunk>)[Symbol.asyncIterator] === \"function\") {\n\t\treturn (source as AsyncIterable<StreamChunk>)[Symbol.asyncIterator]() as ChunkIterator;\n\t}\n\tthrow new TypeError(\"StreamingResponse requires an async iterator or generator\");\n}\n","import { createRequest, type NativeRequestData, type Request } from \"./request\";\nimport { isStreamingResponse } from \"./streaming\";\nimport type { HandlerFunction, HandlerResult, MaybePromise, NativeHandlerFunction } from \"./types\";\n\nconst NATIVE_HANDLER_FLAG = Symbol(\"spikard.nativeHandler\");\n\ntype SerializableResult = HandlerResult | string | undefined;\n\n/**\n * Convert handler result to the format expected by the Rust runtime.\n * When `objectMode` is true (input was an object), return objects directly.\n * When `objectMode` is false (input was JSON string), serialize to JSON strings.\n *\n * Optimized for the common case: objectMode + result with status field.\n * Hot path checks status first before invoking isStreamingResponse.\n */\nconst formatHandlerResult = (result: SerializableResult, objectMode: boolean): HandlerResult | string => {\n\t// Fast path: objectMode with status field (most common for NAPI handlers)\n\tif (objectMode) {\n\t\tif (result !== undefined && result !== null && typeof result === \"object\") {\n\t\t\t// Check for status field first (hot path for HandlerOutput format)\n\t\t\tif (\"status\" in result) {\n\t\t\t\treturn result as HandlerResult;\n\t\t\t}\n\t\t\t// Only check streaming if result is an object without status\n\t\t\tif (isStreamingResponse(result)) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\t// Wrap plain objects as body with default status\n\t\t\treturn { status: 200, body: result as unknown as null };\n\t\t}\n\t\t// Handle undefined/null in objectMode\n\t\treturn result === undefined ? { status: 200, body: null } : { status: 200, body: result as unknown as null };\n\t}\n\n\t// Legacy string mode: check streaming first, then serialize\n\tif (isStreamingResponse(result)) {\n\t\treturn result;\n\t}\n\tif (result === undefined) {\n\t\treturn \"null\";\n\t}\n\tif (typeof result === \"string\") {\n\t\treturn result;\n\t}\n\treturn JSON.stringify(result);\n};\n\nexport const isNativeHandler = (handler: unknown): handler is NativeHandlerFunction =>\n\tBoolean((handler as { [NATIVE_HANDLER_FLAG]?: boolean })?.[NATIVE_HANDLER_FLAG]);\n\nfunction markNative(handler: NativeHandlerFunction): NativeHandlerFunction {\n\t(handler as { [NATIVE_HANDLER_FLAG]?: boolean })[NATIVE_HANDLER_FLAG] = true;\n\treturn handler;\n}\n\nfunction markRawBody(handler: NativeHandlerFunction, prefersRaw: boolean): NativeHandlerFunction {\n\t(handler as { __spikard_raw_body?: boolean }).__spikard_raw_body = prefersRaw;\n\treturn handler;\n}\n\n/**\n * Unwrap array-wrapped input from ThreadsafeFunction callback and create request.\n * Shared between sync and async wrappers.\n */\nfunction prepareRequest(requestInput: unknown): { request: Request; objectMode: boolean } {\n\tlet actualInput = requestInput;\n\tif (Array.isArray(requestInput) && requestInput.length === 1) {\n\t\tactualInput = requestInput[0];\n\t}\n\n\tconst objectMode = typeof actualInput === \"object\" && actualInput !== null;\n\tconst data: NativeRequestData = objectMode\n\t\t? (actualInput as NativeRequestData)\n\t\t: (JSON.parse(actualInput as string) as NativeRequestData);\n\tconst request = createRequest(data);\n\n\treturn { request, objectMode };\n}\n\nexport function wrapHandler(handler: HandlerFunction): NativeHandlerFunction {\n\t// Detect if the original handler is async.\n\t// Sync handlers use a regular function wrapper, avoiding Promise creation overhead.\n\t// This allows Rust to use the sync ThreadsafeFunction path (1 await vs 2).\n\tconst isHandlerAsync = handler.constructor.name === \"AsyncFunction\";\n\n\tif (isHandlerAsync) {\n\t\tconst nativeHandler: NativeHandlerFunction = async (requestInput: unknown) => {\n\t\t\tconst { request, objectMode } = prepareRequest(requestInput);\n\t\t\tconst result = await handler(request);\n\t\t\treturn formatHandlerResult(result, objectMode);\n\t\t};\n\t\treturn markNative(markRawBody(nativeHandler, true));\n\t}\n\n\t// Sync path: regular function avoids Promise overhead on Rust side\n\tconst nativeHandler: NativeHandlerFunction = (requestInput: unknown) => {\n\t\tconst { request, objectMode } = prepareRequest(requestInput);\n\t\tconst result = handler(request);\n\t\treturn formatHandlerResult(result as SerializableResult, objectMode);\n\t};\n\treturn markNative(markRawBody(nativeHandler, true));\n}\n\nexport function wrapBodyHandler<TBody = unknown>(\n\thandler: (body: TBody, request: Request) => MaybePromise<HandlerResult>,\n): NativeHandlerFunction {\n\tconst isHandlerAsync = handler.constructor.name === \"AsyncFunction\";\n\n\tif (isHandlerAsync) {\n\t\tconst nativeHandler: NativeHandlerFunction = async (requestInput: unknown) => {\n\t\t\tconst { request, objectMode } = prepareRequest(requestInput);\n\t\t\tconst body = request.json<TBody>();\n\t\t\tconst result = await handler(body, request);\n\t\t\treturn formatHandlerResult(result, objectMode);\n\t\t};\n\t\treturn markNative(markRawBody(nativeHandler, true));\n\t}\n\n\tconst nativeHandler: NativeHandlerFunction = (requestInput: unknown) => {\n\t\tconst { request, objectMode } = prepareRequest(requestInput);\n\t\tconst body = request.json<TBody>();\n\t\tconst result = handler(body, request);\n\t\treturn formatHandlerResult(result as SerializableResult, objectMode);\n\t};\n\treturn markNative(markRawBody(nativeHandler, true));\n}\n","/**\n * Spikard application class\n */\n\nimport type { HandlerFunction, NativeHandlerFunction, RouteMetadata, SpikardApp } from \"./index\";\nimport type { Request } from \"./request\";\nimport { runServer, type ServerOptions } from \"./server\";\nimport type { MaybePromise, StructuredHandlerResponse, WebSocketHandler, WebSocketOptions } from \"./types\";\n\n/**\n * Dependency value or factory configuration\n */\nexport type DependencyValue = unknown;\n\n/**\n * Factory function for creating dependencies\n */\nexport type DependencyFactory = (dependencies: Record<string, DependencyValue>) => MaybePromise<DependencyValue>;\n\n/**\n * Dependency registration options\n */\nexport interface DependencyOptions {\n\t/** List of dependency keys this factory depends on */\n\tdependsOn?: string[];\n\t/** Whether this is a singleton (resolved once globally) */\n\tsingleton?: boolean;\n\t/** Whether to cache per-request (default: false for factories, true for values) */\n\tcacheable?: boolean;\n}\n\n/**\n * Internal dependency descriptor\n */\ninterface DependencyDescriptor {\n\tisFactory: boolean;\n\tvalue?: DependencyValue | undefined;\n\tfactory?: DependencyFactory | undefined;\n\tdependsOn: string[];\n\tsingleton: boolean;\n\tcacheable: boolean;\n}\n\nconst ASYNC_GENERATOR_REGISTRY = new Map<string, AsyncGenerator<unknown, unknown, unknown>>();\nlet ASYNC_GENERATOR_COUNTER = 0;\n\nconst isAsyncGenerator = (value: unknown): value is AsyncGenerator<unknown, unknown, unknown> => {\n\treturn typeof value === \"object\" && value !== null && Symbol.asyncIterator in value;\n};\n\nconst normalizeDependencyKey = (key: string): string =>\n\tkey\n\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n\t\t.replace(/-/g, \"_\")\n\t\t.toLowerCase();\n\nconst inferDependsOn = (factory: DependencyFactory): string[] => {\n\tconst source = factory.toString();\n\tconst parenMatch = source.match(/^[^(]*\\(([^)]*)\\)/);\n\tif (parenMatch) {\n\t\tconst rawParams = (parenMatch[1] ?? \"\")\n\t\t\t.split(\",\")\n\t\t\t.map((param) => param.trim())\n\t\t\t.filter((param) => param.length > 0);\n\t\treturn rawParams\n\t\t\t.filter((param) => !param.startsWith(\"{\") && !param.startsWith(\"[\"))\n\t\t\t.map((param) => param.replace(/^\\.{3}/, \"\"))\n\t\t\t.map((param) => param.split(\"=\")[0]?.trim() ?? \"\")\n\t\t\t.filter((param) => param.length > 0)\n\t\t\t.map((param) => normalizeDependencyKey(param));\n\t}\n\n\tconst arrowMatch = source.match(/^\\s*([A-Za-z0-9_$]+)\\s*=>/);\n\tif (arrowMatch) {\n\t\treturn [normalizeDependencyKey(arrowMatch[1] ?? \"\")].filter((param) => param.length > 0);\n\t}\n\n\treturn [];\n};\n\nconst parseDependencies = (payload: unknown): Record<string, DependencyValue> => {\n\tif (typeof payload === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(payload) as Record<string, DependencyValue>;\n\t\t\treturn parsed ?? {};\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\tif (typeof payload === \"object\" && payload !== null) {\n\t\treturn payload as Record<string, DependencyValue>;\n\t}\n\treturn {};\n};\n\nconst wrapDependencyFactory = (factory: DependencyFactory): DependencyFactory => {\n\treturn async (payload: Record<string, DependencyValue> | string) => {\n\t\tconst deps = parseDependencies(payload);\n\t\tconst cleanupId = (deps as Record<string, DependencyValue>).__cleanup_id__;\n\t\tif (typeof cleanupId === \"string\") {\n\t\t\tconst generator = ASYNC_GENERATOR_REGISTRY.get(cleanupId);\n\t\t\tif (generator) {\n\t\t\t\tASYNC_GENERATOR_REGISTRY.delete(cleanupId);\n\t\t\t\tif (typeof generator.return === \"function\") {\n\t\t\t\t\tawait generator.return(undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn JSON.stringify({ ok: true });\n\t\t}\n\n\t\tconst result = await factory(deps);\n\t\tif (isAsyncGenerator(result)) {\n\t\t\tconst { value } = await result.next();\n\t\t\tconst id = `gen_${ASYNC_GENERATOR_COUNTER++}`;\n\t\t\tASYNC_GENERATOR_REGISTRY.set(id, result);\n\t\t\treturn JSON.stringify({ __async_generator__: true, value, cleanup_id: id });\n\t\t}\n\n\t\treturn result === undefined ? \"null\" : JSON.stringify(result);\n\t};\n};\n\n/**\n * Payload type provided to lifecycle hooks\n */\nexport type LifecycleHookPayload = Request | StructuredHandlerResponse;\n\n/**\n * Lifecycle hook function type\n *\n * Hooks can return:\n * - The (possibly modified) request to continue processing\n * - A Response object to short-circuit the request pipeline\n */\nexport type LifecycleHookFunction = (payload: LifecycleHookPayload) => MaybePromise<LifecycleHookPayload>;\n\n/**\n * Container for lifecycle hooks\n */\nexport interface LifecycleHooks {\n\tonRequest: LifecycleHookFunction[];\n\tpreValidation: LifecycleHookFunction[];\n\tpreHandler: LifecycleHookFunction[];\n\tonResponse: LifecycleHookFunction[];\n\tonError: LifecycleHookFunction[];\n}\n\n/**\n * Spikard application\n *\n * @example\n * ```typescript\n * import { Spikard, get, post } from 'spikard';\n *\n * const app = new Spikard();\n *\n * get('/')(async function root() {\n * return { message: 'Hello' };\n * });\n *\n * if (require.main === module) {\n * app.run({ port: 8000 });\n * }\n * ```\n */\nexport class Spikard implements SpikardApp {\n\troutes: RouteMetadata[] = [];\n\thandlers: Record<string, HandlerFunction | NativeHandlerFunction> = {};\n\twebsocketRoutes: RouteMetadata[] = [];\n\twebsocketHandlers: Record<string, Record<string, unknown>> = {};\n\tlifecycleHooks: LifecycleHooks = {\n\t\tonRequest: [],\n\t\tpreValidation: [],\n\t\tpreHandler: [],\n\t\tonResponse: [],\n\t\tonError: [],\n\t};\n\tdependencies: Record<string, DependencyDescriptor> = {};\n\n\t/**\n\t * Add a route to the application\n\t *\n\t * @param metadata - Route configuration metadata\n\t * @param handler - Handler function (sync or async)\n\t */\n\taddRoute(metadata: RouteMetadata, handler: HandlerFunction | NativeHandlerFunction): void {\n\t\tthis.routes.push(metadata);\n\t\tthis.handlers[metadata.handler_name] = handler;\n\t}\n\n\t/**\n\t * Register a WebSocket route (message-based)\n\t */\n\twebsocket(path: string, handler: WebSocketHandler, options: WebSocketOptions = {}): void {\n\t\tconst handlerName =\n\t\t\toptions.handlerName ?? `ws_${this.websocketRoutes.length}_${path}`.replace(/[^a-zA-Z0-9_]/g, \"_\");\n\t\tconst handlerWrapper: Record<string, unknown> = { handleMessage: handler };\n\t\tif (options.messageSchema) {\n\t\t\thandlerWrapper._messageSchema = options.messageSchema;\n\t\t}\n\t\tif (options.responseSchema) {\n\t\t\thandlerWrapper._responseSchema = options.responseSchema;\n\t\t}\n\n\t\tconst route: RouteMetadata = {\n\t\t\tmethod: \"GET\",\n\t\t\tpath,\n\t\t\thandler_name: handlerName,\n\t\t\trequest_schema: options.messageSchema as never,\n\t\t\tresponse_schema: options.responseSchema as never,\n\t\t\tparameter_schema: undefined,\n\t\t\tfile_params: undefined,\n\t\t\tis_async: true,\n\t\t};\n\n\t\tthis.websocketRoutes.push(route);\n\t\tthis.websocketHandlers[handlerName] = handlerWrapper;\n\t}\n\n\t/**\n\t * Run the server\n\t *\n\t * @param options - Server configuration\n\t */\n\trun(options: ServerOptions = {}): void {\n\t\trunServer(this, options);\n\t}\n\n\t/**\n\t * Register an onRequest lifecycle hook\n\t *\n\t * Runs before routing. Can inspect/modify the request or short-circuit with a response.\n\t *\n\t * @param hook - Async function that receives a request and returns either:\n\t * - The (possibly modified) request to continue processing\n\t * - A Response object to short-circuit the request pipeline\n\t * @returns The hook function (for decorator usage)\n\t *\n\t * @example\n\t * ```typescript\n\t * app.onRequest(async (request) => {\n\t * console.log(`Request: ${request.method} ${request.path}`);\n\t * return request;\n\t * });\n\t * ```\n\t */\n\tonRequest(hook: LifecycleHookFunction): LifecycleHookFunction {\n\t\tthis.lifecycleHooks.onRequest.push(hook);\n\t\treturn hook;\n\t}\n\n\t/**\n\t * Register a preValidation lifecycle hook\n\t *\n\t * Runs after routing but before validation. Useful for rate limiting.\n\t *\n\t * @param hook - Async function that receives a request and returns either:\n\t * - The (possibly modified) request to continue processing\n\t * - A Response object to short-circuit the request pipeline\n\t * @returns The hook function (for decorator usage)\n\t *\n\t * @example\n\t * ```typescript\n\t * app.preValidation(async (request) => {\n\t * if (tooManyRequests()) {\n\t * return { error: \"Rate limit exceeded\", status: 429 };\n\t * }\n\t * return request;\n\t * });\n\t * ```\n\t */\n\tpreValidation(hook: LifecycleHookFunction): LifecycleHookFunction {\n\t\tthis.lifecycleHooks.preValidation.push(hook);\n\t\treturn hook;\n\t}\n\n\t/**\n\t * Register a preHandler lifecycle hook\n\t *\n\t * Runs after validation but before the handler. Ideal for authentication/authorization.\n\t *\n\t * @param hook - Async function that receives a request and returns either:\n\t * - The (possibly modified) request to continue processing\n\t * - A Response object to short-circuit the request pipeline\n\t * @returns The hook function (for decorator usage)\n\t *\n\t * @example\n\t * ```typescript\n\t * app.preHandler(async (request) => {\n\t * if (!validToken(request.headers.authorization)) {\n\t * return { error: \"Unauthorized\", status: 401 };\n\t * }\n\t * return request;\n\t * });\n\t * ```\n\t */\n\tpreHandler(hook: LifecycleHookFunction): LifecycleHookFunction {\n\t\tthis.lifecycleHooks.preHandler.push(hook);\n\t\treturn hook;\n\t}\n\n\t/**\n\t * Register an onResponse lifecycle hook\n\t *\n\t * Runs after the handler executes. Can modify the response.\n\t *\n\t * @param hook - Async function that receives a response and returns the (possibly modified) response\n\t * @returns The hook function (for decorator usage)\n\t *\n\t * @example\n\t * ```typescript\n\t * app.onResponse(async (response) => {\n\t * response.headers[\"X-Frame-Options\"] = \"DENY\";\n\t * return response;\n\t * });\n\t * ```\n\t */\n\tonResponse(hook: LifecycleHookFunction): LifecycleHookFunction {\n\t\tthis.lifecycleHooks.onResponse.push(hook);\n\t\treturn hook;\n\t}\n\n\t/**\n\t * Register an onError lifecycle hook\n\t *\n\t * Runs when an error occurs. Can customize error responses.\n\t *\n\t * @param hook - Async function that receives an error response and returns a (possibly modified) response\n\t * @returns The hook function (for decorator usage)\n\t *\n\t * @example\n\t * ```typescript\n\t * app.onError(async (response) => {\n\t * response.headers[\"Content-Type\"] = \"application/json\";\n\t * return response;\n\t * });\n\t * ```\n\t */\n\tonError(hook: LifecycleHookFunction): LifecycleHookFunction {\n\t\tthis.lifecycleHooks.onError.push(hook);\n\t\treturn hook;\n\t}\n\n\t/**\n\t * Register a dependency value or factory\n\t *\n\t * Provides a value or factory function to be injected into handlers.\n\t * Dependencies are matched by parameter name or can be accessed via the\n\t * request context.\n\t *\n\t * @param key - Unique identifier for the dependency\n\t * @param valueOrFactory - Static value or factory function\n\t * @param options - Configuration options for the dependency\n\t * @returns The Spikard instance for method chaining\n\t *\n\t * @example\n\t * ```typescript\n\t * // Simple value dependency\n\t * app.provide('app_name', 'MyApp');\n\t * app.provide('max_connections', 100);\n\t *\n\t * // Factory dependency\n\t * app.provide('db_pool', async ({ database_url }) => {\n\t * return await createPool(database_url);\n\t * }, { dependsOn: ['database_url'], singleton: true });\n\t *\n\t * // Use in handler\n\t * app.get('/config', async (request, { app_name, db_pool }) => {\n\t * return { app: app_name, pool: db_pool };\n\t * });\n\t * ```\n\t */\n\tprovide(key: string, valueOrFactory: DependencyValue | DependencyFactory, options?: DependencyOptions): this {\n\t\tconst normalizedKey = normalizeDependencyKey(key);\n\t\tconst isFactory = typeof valueOrFactory === \"function\";\n\t\tconst factory = isFactory ? wrapDependencyFactory(valueOrFactory as DependencyFactory) : undefined;\n\t\tconst explicitDependsOn = options?.dependsOn;\n\t\tconst inferredDependsOn =\n\t\t\tisFactory && explicitDependsOn === undefined ? inferDependsOn(valueOrFactory as DependencyFactory) : [];\n\t\tconst dependsOn = (explicitDependsOn ?? inferredDependsOn).map((depKey) => normalizeDependencyKey(depKey));\n\n\t\tthis.dependencies[normalizedKey] = {\n\t\t\tisFactory,\n\t\t\tvalue: isFactory ? undefined : valueOrFactory,\n\t\t\tfactory,\n\t\t\tdependsOn,\n\t\t\tsingleton: options?.singleton ?? false,\n\t\t\tcacheable: options?.cacheable ?? !isFactory,\n\t\t};\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get all registered lifecycle hooks\n\t *\n\t * @returns Dictionary of hook lists by type\n\t */\n\tgetLifecycleHooks(): LifecycleHooks {\n\t\treturn {\n\t\t\tonRequest: [...(this.lifecycleHooks.onRequest ?? [])],\n\t\t\tpreValidation: [...(this.lifecycleHooks.preValidation ?? [])],\n\t\t\tpreHandler: [...(this.lifecycleHooks.preHandler ?? [])],\n\t\t\tonResponse: [...(this.lifecycleHooks.onResponse ?? [])],\n\t\t\tonError: [...(this.lifecycleHooks.onError ?? [])],\n\t\t};\n\t}\n}\n","import { backgroundRun } from \"../index.js\";\n\n/** Schedule work to run after the HTTP response resolves. */\nexport function run(work: () => void | Promise<void>): void {\n\tconst task = async (): Promise<undefined> => {\n\t\tawait work();\n\t\treturn undefined;\n\t};\n\tbackgroundRun(task);\n}\n","/**\n * gRPC support for Spikard\n *\n * This module provides TypeScript bindings for implementing gRPC service handlers\n * that connect to Spikard's Rust-based gRPC runtime.\n *\n * # Architecture\n *\n * The gRPC binding follows the FFI pattern:\n * 1. TypeScript handler implements GrpcHandler interface\n * 2. Rust side (NodeGrpcHandler) calls JavaScript via napi-rs ThreadsafeFunction\n * 3. Protobuf serialization/deserialization happens in JavaScript using protobufjs\n *\n * # Current Limitations\n *\n * - Only unary (request-response) calls are currently supported\n * - Streaming (client, server, or bidirectional) is not yet implemented\n * - Binary metadata values are not accessible (only ASCII string metadata)\n *\n * # Example\n *\n * ```typescript\n * import { GrpcHandler, GrpcRequest, GrpcResponse } from 'spikard';\n * import * as $protobuf from 'protobufjs';\n *\n * // Generated protobuf types (using protobufjs)\n * interface User {\n * id: number;\n * name: string;\n * email?: string;\n * }\n *\n * interface GetUserRequest {\n * id: number;\n * }\n *\n * class UserServiceHandler implements GrpcHandler {\n * async handleRequest(request: GrpcRequest): Promise<GrpcResponse> {\n * if (request.methodName === 'GetUser') {\n * // Deserialize request using protobufjs\n * const req = UserService.GetUserRequest.decode(request.payload);\n *\n * // Process request\n * const user: User = {\n * id: req.id,\n * name: 'John Doe',\n * email: 'john@example.com'\n * };\n *\n * // Serialize response\n * const encoded = UserService.User.encode(user).finish();\n * return {\n * payload: Buffer.from(encoded)\n * };\n * }\n *\n * throw new Error(`Unknown method: ${request.methodName}`);\n * }\n * }\n *\n * // Register handler with Spikard\n * const handler = new UserServiceHandler();\n * app.registerGrpcHandler('mypackage.UserService', handler);\n * ```\n */\n\n/**\n * gRPC request received by handlers\n *\n * Contains the parsed gRPC request with all components extracted from\n * the gRPC wire format.\n */\nexport type GrpcMetadata = Record<string, string>;\n\nexport interface GrpcRequest {\n\t/**\n\t * Fully qualified service name\n\t *\n\t * Example: \"mypackage.UserService\"\n\t */\n\tserviceName: string;\n\n\t/**\n\t * Method name being called\n\t *\n\t * Example: \"GetUser\", \"ListUsers\", \"CreateUser\"\n\t */\n\tmethodName: string;\n\n\t/**\n\t * Serialized protobuf message payload\n\t *\n\t * This is the raw bytes of the protobuf message. You must deserialize\n\t * it using protobufjs or another protobuf library.\n\t *\n\t * Example:\n\t * ```typescript\n\t * const req = UserService.GetUserRequest.decode(request.payload);\n\t * ```\n\t */\n\tpayload: Buffer;\n\n\t/**\n\t * gRPC metadata (similar to HTTP headers)\n\t *\n\t * Key-value pairs of metadata sent with the request.\n\t *\n\t * Example: { \"authorization\": \"Bearer token123\", \"x-request-id\": \"abc\" }\n\t */\n\tmetadata: GrpcMetadata;\n}\n\n/**\n * gRPC response returned by handlers\n *\n * Contains the serialized protobuf response and optional metadata to\n * include in the gRPC response headers.\n */\nexport interface GrpcResponse {\n\t/**\n\t * Serialized protobuf message payload\n\t *\n\t * This must be the raw bytes of a serialized protobuf message.\n\t * Use protobufjs to serialize your response object.\n\t *\n\t * Example:\n\t * ```typescript\n\t * const user = UserService.User.create({ id: 1, name: 'John' });\n\t * const payload = Buffer.from(UserService.User.encode(user).finish());\n\t * ```\n\t */\n\tpayload: Buffer;\n\n\t/**\n\t * Optional gRPC metadata to include in response\n\t *\n\t * Key-value pairs of metadata to send back to the client.\n\t *\n\t * Example: { \"x-server-id\": \"server-1\", \"x-cache-status\": \"hit\" }\n\t */\n\tmetadata?: GrpcMetadata;\n}\n\n/**\n * gRPC handler interface\n *\n * Implement this interface to handle gRPC requests for a service.\n * The handler is responsible for:\n * 1. Deserializing the request payload\n * 2. Processing the request\n * 3. Serializing the response payload\n * 4. Optionally setting response metadata\n */\nexport interface GrpcHandler {\n\t/**\n\t * Handle a gRPC request\n\t *\n\t * This method is called for each incoming gRPC request to the service.\n\t * It should:\n\t * 1. Deserialize request.payload using protobufjs\n\t * 2. Process the request based on request.methodName\n\t * 3. Serialize the response using protobufjs\n\t * 4. Return a GrpcResponse with the serialized payload\n\t *\n\t * # Error Handling\n\t *\n\t * If you throw an error, it will be converted to a gRPC status:\n\t * - Regular Error: INTERNAL (code 13)\n\t * - GrpcError: Uses the specified status code\n\t *\n\t * # Example\n\t *\n\t * ```typescript\n\t * async handleRequest(request: GrpcRequest): Promise<GrpcResponse> {\n\t * switch (request.methodName) {\n\t * case 'GetUser': {\n\t * const req = UserService.GetUserRequest.decode(request.payload);\n\t * const user = await db.getUser(req.id);\n\t * if (!user) {\n\t * throw new GrpcError(GrpcStatusCode.NOT_FOUND, 'User not found');\n\t * }\n\t * return {\n\t * payload: Buffer.from(UserService.User.encode(user).finish())\n\t * };\n\t * }\n\t * default:\n\t * throw new GrpcError(GrpcStatusCode.UNIMPLEMENTED, 'Method not implemented');\n\t * }\n\t * }\n\t * ```\n\t *\n\t * @param request - The gRPC request to handle\n\t * @returns Promise resolving to a GrpcResponse\n\t * @throws GrpcError for specific gRPC status codes\n\t * @throws Error for internal server errors (code 13)\n\t */\n\thandleRequest(request: GrpcRequest): Promise<GrpcResponse>;\n}\n\n/**\n * gRPC status codes\n *\n * Standard gRPC status codes as defined in the gRPC specification.\n * Use these with GrpcError to return specific error statuses.\n *\n * @see https://grpc.io/docs/guides/status-codes/\n */\nexport enum GrpcStatusCode {\n\t/** Not an error; returned on success */\n\tOK = 0,\n\t/** The operation was cancelled */\n\tCANCELLED = 1,\n\t/** Unknown error */\n\tUNKNOWN = 2,\n\t/** Client specified an invalid argument */\n\tINVALID_ARGUMENT = 3,\n\t/** Deadline expired before operation could complete */\n\tDEADLINE_EXCEEDED = 4,\n\t/** Some requested entity was not found */\n\tNOT_FOUND = 5,\n\t/** Some entity that we attempted to create already exists */\n\tALREADY_EXISTS = 6,\n\t/** The caller does not have permission to execute the specified operation */\n\tPERMISSION_DENIED = 7,\n\t/** Some resource has been exhausted */\n\tRESOURCE_EXHAUSTED = 8,\n\t/** Operation was rejected because the system is not in a state required for the operation's execution */\n\tFAILED_PRECONDITION = 9,\n\t/** The operation was aborted */\n\tABORTED = 10,\n\t/** Operation was attempted past the valid range */\n\tOUT_OF_RANGE = 11,\n\t/** Operation is not implemented or not supported/enabled */\n\tUNIMPLEMENTED = 12,\n\t/** Internal errors */\n\tINTERNAL = 13,\n\t/** The service is currently unavailable */\n\tUNAVAILABLE = 14,\n\t/** Unrecoverable data loss or corruption */\n\tDATA_LOSS = 15,\n\t/** The request does not have valid authentication credentials */\n\tUNAUTHENTICATED = 16,\n}\n\n/**\n * gRPC error with status code\n *\n * Throw this error from your handler to return a specific gRPC status code\n * to the client.\n *\n * # Example\n *\n * ```typescript\n * if (!user) {\n * throw new GrpcError(GrpcStatusCode.NOT_FOUND, 'User not found');\n * }\n *\n * if (!hasPermission) {\n * throw new GrpcError(GrpcStatusCode.PERMISSION_DENIED, 'Access denied');\n * }\n * ```\n */\nexport class GrpcError extends Error {\n\t/**\n\t * gRPC status code\n\t */\n\tpublic readonly code: GrpcStatusCode;\n\n\t/**\n\t * Create a new gRPC error\n\t *\n\t * @param code - gRPC status code\n\t * @param message - Error message\n\t */\n\tconstructor(code: GrpcStatusCode, message: string) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.name = \"GrpcError\";\n\t}\n}\n\n/**\n * gRPC service configuration\n *\n * Configuration for a gRPC service registration.\n */\nexport interface GrpcServiceConfig {\n\t/**\n\t * Fully qualified service name\n\t *\n\t * This must match the service name in your .proto file.\n\t *\n\t * Example: \"mypackage.UserService\"\n\t */\n\tserviceName: string;\n\n\t/**\n\t * Handler implementation\n\t *\n\t * An object implementing the GrpcHandler interface.\n\t */\n\thandler: GrpcHandler;\n}\n\n/**\n * Result type for unary handlers that can include response metadata\n */\nexport type UnaryHandlerResult<TResponse> =\n\t| TResponse\n\t| {\n\t\t\tresponse: TResponse;\n\t\t\tmetadata?: Record<string, string>;\n\t };\n\n/**\n * Helper function to create a simple unary gRPC handler\n *\n * This is a convenience function for creating handlers that only implement\n * a single unary method.\n *\n * # Example\n *\n * ```typescript\n * // Simple response\n * const getUserHandler = createUnaryHandler<GetUserRequest, User>(\n * 'GetUser',\n * async (req) => {\n * const user = await db.getUser(req.id);\n * if (!user) {\n * throw new GrpcError(GrpcStatusCode.NOT_FOUND, 'User not found');\n * }\n * return user;\n * },\n * UserService.GetUserRequest,\n * UserService.User\n * );\n *\n * // With response metadata\n * const getUserHandler = createUnaryHandler<GetUserRequest, User>(\n * 'GetUser',\n * async (req) => {\n * const user = await db.getUser(req.id);\n * return {\n * response: user,\n * metadata: { 'x-cache-status': 'hit' }\n * };\n * },\n * UserService.GetUserRequest,\n * UserService.User\n * );\n * ```\n *\n * @param methodName - The gRPC method name\n * @param handler - Function to process the request\n * @param requestType - protobufjs Type for request deserialization\n * @param responseType - protobufjs Type for response serialization\n * @returns A GrpcHandler implementation\n */\nexport function createUnaryHandler<TRequest, TResponse>(\n\tmethodName: string,\n\thandler: (request: TRequest, metadata: Record<string, string>) => Promise<UnaryHandlerResult<TResponse>>,\n\trequestType: { decode(buffer: Uint8Array): TRequest },\n\tresponseType: { encode(message: TResponse): { finish(): Uint8Array } },\n): GrpcHandler {\n\treturn {\n\t\tasync handleRequest(request: GrpcRequest): Promise<GrpcResponse> {\n\t\t\tif (request.methodName !== methodName) {\n\t\t\t\tthrow new GrpcError(GrpcStatusCode.UNIMPLEMENTED, `Method ${request.methodName} not implemented`);\n\t\t\t}\n\n\t\t\t// Deserialize request\n\t\t\tconst req = requestType.decode(request.payload);\n\n\t\t\t// Process request\n\t\t\tconst result = await handler(req, request.metadata);\n\n\t\t\t// Extract response and metadata\n\t\t\tlet response: TResponse;\n\t\t\tlet responseMetadata: Record<string, string> | undefined;\n\n\t\t\tif (result && typeof result === \"object\" && \"response\" in result) {\n\t\t\t\t// Result includes metadata\n\t\t\t\tresponse = result.response;\n\t\t\t\tresponseMetadata = result.metadata;\n\t\t\t} else {\n\t\t\t\t// Simple response without metadata\n\t\t\t\tresponse = result as TResponse;\n\t\t\t}\n\n\t\t\t// Serialize response\n\t\t\tconst encoded = responseType.encode(response).finish();\n\n\t\t\tif (responseMetadata) {\n\t\t\t\treturn {\n\t\t\t\t\tpayload: Buffer.from(encoded),\n\t\t\t\t\tmetadata: responseMetadata,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tpayload: Buffer.from(encoded),\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Helper function to create a multi-method gRPC handler\n *\n * This is a convenience function for creating handlers that implement\n * multiple methods in a single service.\n *\n * # Example\n *\n * ```typescript\n * const userServiceHandler = createServiceHandler({\n * GetUser: createUnaryHandler(...),\n * ListUsers: createUnaryHandler(...),\n * CreateUser: createUnaryHandler(...),\n * });\n * ```\n *\n * @param methods - Map of method names to handlers\n * @returns A GrpcHandler implementation that routes to the appropriate method\n */\nexport function createServiceHandler(methods: Record<string, GrpcHandler>): GrpcHandler {\n\treturn {\n\t\tasync handleRequest(request: GrpcRequest): Promise<GrpcResponse> {\n\t\t\tconst handler = methods[request.methodName];\n\t\t\tif (!handler) {\n\t\t\t\tthrow new GrpcError(GrpcStatusCode.UNIMPLEMENTED, `Method ${request.methodName} not implemented`);\n\t\t\t}\n\t\t\treturn handler.handleRequest(request);\n\t\t},\n\t};\n}\n","/**\n * Routing decorators and utilities for Spikard\n */\n\nimport type { CorsConfig, JsonSchema, RouteMetadata } from \"./index\";\nimport type { Request } from \"./request\";\nimport type { HandlerResult, MaybePromise } from \"./types\";\n\n/**\n * Route configuration options\n */\nexport interface RouteOptions {\n\t/**\n\t * HTTP method(s) for this route\n\t * Can be a single method or an array of methods\n\t */\n\tmethods?: string | string[];\n\n\t/**\n\t * Request body schema (JSON Schema or Zod schema)\n\t */\n\tbodySchema?: JsonSchema;\n\n\t/**\n\t * Response schema (JSON Schema or Zod schema)\n\t */\n\tresponseSchema?: JsonSchema;\n\n\t/**\n\t * Parameter schema (JSON Schema or Zod schema for path and query parameters)\n\t */\n\tparameterSchema?: JsonSchema;\n\n\t/**\n\t * CORS configuration\n\t */\n\tcors?: CorsConfig;\n}\n\ntype RouteHandler = (request: Request) => MaybePromise<HandlerResult>;\n\n/**\n * Route decorator for defining HTTP routes\n *\n * @param path - The route path (e.g., \"/users/:id\")\n * @param options - Route configuration options\n * @returns A decorator function\n *\n * @example\n * ```typescript\n * import { route, Spikard } from 'spikard';\n * import { z } from 'zod';\n *\n * const app = new Spikard();\n *\n * const UserSchema = z.object({\n * name: z.string(),\n * email: z.string().email(),\n * });\n *\n * route(\"/users\", { methods: [\"POST\"], bodySchema: UserSchema})\n * async function createUser() {\n * return { created: true };\n * }\n *\n * app.run();\n * ```\n */\nexport function route(path: string, options: RouteOptions = {}): (handler: RouteHandler) => RouteHandler {\n\treturn (handler: RouteHandler) => {\n\t\tconst methods = options.methods ? (Array.isArray(options.methods) ? options.methods : [options.methods]) : [\"GET\"];\n\n\t\tconst metadata: RouteMetadata = {\n\t\t\tmethod: methods.join(\",\"),\n\t\t\tpath,\n\t\t\thandler_name: handler.name || \"anonymous\",\n\t\t\trequest_schema: options.bodySchema ?? undefined,\n\t\t\tresponse_schema: options.responseSchema ?? undefined,\n\t\t\tparameter_schema: options.parameterSchema ?? undefined,\n\t\t\tcors: options.cors ?? undefined,\n\t\t\tis_async: true,\n\t\t};\n\n\t\tinterface AnnotatedHandler extends RouteHandler {\n\t\t\t__route_metadata__?: RouteMetadata;\n\t\t}\n\t\t(handler as AnnotatedHandler).__route_metadata__ = metadata;\n\n\t\treturn handler;\n\t};\n}\n\n/**\n * GET route decorator\n *\n * @param path - The route path\n * @param options - Route configuration options (excluding methods)\n * @returns A decorator function\n *\n * @example\n * ```typescript\n * import { get } from 'spikard';\n *\n * get(\"/users/:id\")\n * function getUser(request: Request, id: Path<string>) {\n * // Implementation\n * }\n * ```\n */\nexport function get(\n\tpath: string,\n\toptions: Omit<RouteOptions, \"methods\"> = {},\n): (handler: RouteHandler) => RouteHandler {\n\treturn route(path, { ...options, methods: [\"GET\"] });\n}\n\n/**\n * POST route decorator\n */\nexport function post(\n\tpath: string,\n\toptions: Omit<RouteOptions, \"methods\"> = {},\n): (handler: RouteHandler) => RouteHandler {\n\treturn route(path, { ...options, methods: [\"POST\"] });\n}\n\n/**\n * PUT route decorator\n */\nexport function put(\n\tpath: string,\n\toptions: Omit<RouteOptions, \"methods\"> = {},\n): (handler: RouteHandler) => RouteHandler {\n\treturn route(path, { ...options, methods: [\"PUT\"] });\n}\n\n/**\n * DELETE route decorator\n */\nexport function del(\n\tpath: string,\n\toptions: Omit<RouteOptions, \"methods\"> = {},\n): (handler: RouteHandler) => RouteHandler {\n\treturn route(path, { ...options, methods: [\"DELETE\"] });\n}\n\n/**\n * PATCH route decorator\n */\nexport function patch(\n\tpath: string,\n\toptions: Omit<RouteOptions, \"methods\"> = {},\n): (handler: RouteHandler) => RouteHandler {\n\treturn route(path, { ...options, methods: [\"PATCH\"] });\n}\n","/**\n * Testing utilities for Spikard applications\n */\n\nimport fs from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { gunzipSync, gzipSync } from \"node:zlib\";\nimport type { ServerConfig } from \"./config\";\nimport { isNativeHandler, wrapHandler } from \"./handler-wrapper\";\nimport type { HandlerFunction, NativeHandlerFunction, SpikardApp } from \"./index\";\nimport { getStreamingHandle, isStreamingResponse } from \"./streaming\";\nimport type { JsonValue } from \"./types\";\n\ninterface NativeTestResponse {\n\tstatusCode: number;\n\theaders(): Record<string, string>;\n\ttext(): string;\n\tjson<T>(): T;\n\tbytes(): Buffer;\n\tgraphqlData(): unknown;\n\tgraphqlErrors(): Array<Record<string, unknown>>;\n}\n\ninterface WebSocketTestConnection {\n\tsendText(text: string): Promise<void>;\n\tsendJson(obj: unknown): Promise<void>;\n\treceiveText(): Promise<string>;\n\treceiveJson(): Promise<unknown>;\n\treceiveBytes(): Promise<Buffer>;\n\treceiveMessage(): Promise<unknown>;\n\tclose(): Promise<void>;\n}\n\n/**\n * HTTP response from test client\n */\nexport type TestResponse = NativeTestResponse;\n\ninterface MultipartFile {\n\tname: string;\n\tfilename?: string;\n\tcontent: string;\n\tcontentType?: string;\n}\n\nexport interface RequestOptions {\n\theaders?: Record<string, string>;\n\tjson?: JsonValue;\n\tform?: Record<string, JsonValue>;\n\tmultipart?: {\n\t\tfields?: Record<string, JsonValue>;\n\t\tfiles?: MultipartFile[];\n\t};\n}\n\ntype MultipartPayload = {\n\t__spikard_multipart__: {\n\t\tfields: Record<string, JsonValue>;\n\t\tfiles: MultipartFile[];\n\t};\n};\n\ntype FormPayload = {\n\t__spikard_form__: Record<string, JsonValue>;\n};\n\ntype NativeBody = MultipartPayload | FormPayload | JsonValue | null;\n\ninterface NativeClient {\n\tget(path: string, headers: Record<string, string> | null): Promise<TestResponse>;\n\tpost(path: string, headers: Record<string, string> | null, body: NativeBody): Promise<TestResponse>;\n\tput(path: string, headers: Record<string, string> | null, body: NativeBody): Promise<TestResponse>;\n\tdelete(path: string, headers: Record<string, string> | null): Promise<TestResponse>;\n\tpatch(path: string, headers: Record<string, string> | null, body: NativeBody): Promise<TestResponse>;\n\thead(path: string, headers: Record<string, string> | null): Promise<TestResponse>;\n\toptions(path: string, headers: Record<string, string> | null): Promise<TestResponse>;\n\ttrace(path: string, headers: Record<string, string> | null): Promise<TestResponse>;\n\twebsocket(path: string): Promise<WebSocketTestConnection>;\n}\n\nclass MockWebSocketConnection {\n\tprivate readonly handler: (msg: unknown) => Promise<unknown>;\n\tprivate readonly queue: unknown[] = [];\n\n\tconstructor(handler: (msg: unknown) => Promise<unknown>) {\n\t\tthis.handler = handler;\n\t}\n\n\tasync send_json(message: unknown): Promise<void> {\n\t\tconst response = await this.handler(message);\n\t\tthis.queue.push(response);\n\t}\n\n\tasync sendJson(message: unknown): Promise<void> {\n\t\treturn this.send_json(message);\n\t}\n\n\tasync sendText(text: string): Promise<void> {\n\t\treturn this.send_json(text);\n\t}\n\n\tasync receive_json(): Promise<unknown> {\n\t\treturn this.queue.shift();\n\t}\n\n\tasync receiveJson(): Promise<unknown> {\n\t\treturn this.receive_json();\n\t}\n\n\tasync receiveText(): Promise<string> {\n\t\tconst value = await this.receive_json();\n\t\treturn typeof value === \"string\" ? value : JSON.stringify(value);\n\t}\n\n\tasync receiveBytes(): Promise<Buffer> {\n\t\tconst value = await this.receive_json();\n\t\tif (Buffer.isBuffer(value)) {\n\t\t\treturn value;\n\t\t}\n\t\tif (typeof value === \"string\") {\n\t\t\treturn Buffer.from(value);\n\t\t}\n\t\treturn Buffer.from(JSON.stringify(value));\n\t}\n\n\tasync receiveMessage(): Promise<unknown> {\n\t\treturn this.receive_json();\n\t}\n\n\tasync close(): Promise<void> {}\n}\n\ntype NativeClientConstructor = new (\n\troutesJson: string,\n\twebsocketRoutesJson: string | null,\n\thandlers: Record<string, NativeHandlerFunction>,\n\twebsocketHandlers: Record<string, Record<string, unknown>>,\n\tdependencies: Record<string, unknown> | null,\n\tlifecycleHooks: Record<string, unknown> | null,\n\tconfig: ServerConfig | null,\n) => NativeClient;\n\ntype NativeClientFactory = (\n\troutesJson: string,\n\twebsocketRoutesJson: string | null,\n\thandlers: Record<string, NativeHandlerFunction>,\n\twebsocketHandlers: Record<string, Record<string, unknown>>,\n\tdependencies: Record<string, unknown> | null,\n\tlifecycleHooks: Record<string, unknown> | null,\n\tconfig: ServerConfig | null,\n) => NativeClient;\n\ninterface NativeBinding {\n\tTestClient: NativeClientConstructor;\n}\n\nlet nativeTestClient: NativeClientConstructor | null = null;\n\nconst loadNativeTestClient = (): NativeClientConstructor | null => {\n\ttry {\n\t\tconst require = createRequire(import.meta.url);\n\t\tconst binding = require(\"../index.js\") as NativeBinding;\n\t\treturn binding.TestClient;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nnativeTestClient = loadNativeTestClient();\nconst isNativeCtor = nativeTestClient !== null;\n\nclass JsTestResponse implements NativeTestResponse {\n\tprivate readonly body: Buffer;\n\tprivate readonly headerBag: Record<string, string>;\n\n\tconstructor(\n\t\tpublic readonly statusCode: number,\n\t\theaders: Record<string, string>,\n\t\tbody: Buffer,\n\t) {\n\t\tthis.headerBag = headers;\n\t\tthis.body = body;\n\t}\n\n\theaders(): Record<string, string> {\n\t\treturn this.headerBag;\n\t}\n\n\ttext(): string {\n\t\treturn this.decodeBody().toString(\"utf-8\");\n\t}\n\n\tjson<T>(): T {\n\t\tconst raw = this.text();\n\t\tif (raw.length === 0) {\n\t\t\treturn undefined as unknown as T;\n\t\t}\n\t\ttry {\n\t\t\treturn JSON.parse(raw) as T;\n\t\t} catch {\n\t\t\treturn raw as unknown as T;\n\t\t}\n\t}\n\n\tbytes(): Buffer {\n\t\treturn this.decodeBody();\n\t}\n\n\tprivate decodeBody(): Buffer {\n\t\tconst encoding = (this.headerBag[\"content-encoding\"] ?? \"\").toLowerCase();\n\t\tif (encoding === \"gzip\") {\n\t\t\ttry {\n\t\t\t\treturn gunzipSync(this.body);\n\t\t\t} catch {\n\t\t\t\treturn this.body;\n\t\t\t}\n\t\t}\n\t\treturn this.body;\n\t}\n\n\t/**\n\t * Extract GraphQL data from response\n\t *\n\t * @returns The data field from GraphQL response, or null/undefined if not present\n\t */\n\tgraphqlData(): unknown {\n\t\tconst response = this.json<{ data?: unknown }>();\n\t\treturn typeof response === \"object\" && response !== null && \"data\" in response ? response.data : undefined;\n\t}\n\n\t/**\n\t * Extract GraphQL errors from response\n\t *\n\t * @returns Array of GraphQL error objects, or empty array if none present\n\t */\n\tgraphqlErrors(): Array<Record<string, unknown>> {\n\t\tconst response = this.json<{ errors?: Array<Record<string, unknown>> }>();\n\t\treturn typeof response === \"object\" && response !== null && Array.isArray(response.errors) ? response.errors : [];\n\t}\n}\n\nclass JsNativeClient implements NativeClient {\n\tprivate readonly routes: SpikardApp[\"routes\"];\n\tprivate readonly handlers: Record<string, NativeHandlerFunction>;\n\tprivate readonly dependencies: Record<string, unknown> | null;\n\tprivate readonly config: ServerConfig | null;\n\tprivate readonly rateLimitBuckets: Map<string, { tokens: number; resetAt: number }>;\n\tprivate readonly websocketRoutes: SpikardApp[\"websocketRoutes\"];\n\tprivate readonly websocketHandlers: Record<string, Record<string, unknown>>;\n\n\tconstructor(\n\t\troutesJson: string,\n\t\t_websocketRoutesJson: string | null,\n\t\thandlers: Record<string, NativeHandlerFunction>,\n\t\twebsocketHandlers: Record<string, Record<string, unknown>>,\n\t\tdependencies: Record<string, unknown> | null,\n\t\t_lifecycleHooks: Record<string, unknown> | null,\n\t\tconfig: ServerConfig | null,\n\t) {\n\t\tthis.routes = JSON.parse(routesJson) as SpikardApp[\"routes\"];\n\t\tthis.websocketRoutes = JSON.parse(_websocketRoutesJson ?? \"[]\") as SpikardApp[\"websocketRoutes\"];\n\t\tthis.handlers = handlers;\n\t\tthis.websocketHandlers = websocketHandlers;\n\t\tthis.dependencies = dependencies;\n\t\tthis.config = config;\n\t\tthis.rateLimitBuckets = new Map();\n\t}\n\n\tprivate matchRoute(\n\t\tmethod: string,\n\t\tpath: string,\n\t): { handlerName: string; params: Record<string, string>; route: SpikardApp[\"routes\"][number] } {\n\t\tconst cleanedPath = path.split(\"?\")[0] ?? path;\n\t\tfor (const route of this.routes) {\n\t\t\tif (route.method.toUpperCase() !== method.toUpperCase()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst params = this.extractParams(route.path, cleanedPath);\n\t\t\tif (params) {\n\t\t\t\treturn { handlerName: route.handler_name, params, route };\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`No route matched ${method} ${path}`);\n\t}\n\n\tprivate extractParams(pattern: string, actual: string): Record<string, string> | null {\n\t\tconst patternParts = pattern.split(\"/\").filter(Boolean);\n\t\tconst actualParts = actual.split(\"/\").filter(Boolean);\n\t\tconst hasTailParam = patternParts.some((part) => part.includes(\":path\"));\n\t\tif (!hasTailParam && patternParts.length !== actualParts.length) {\n\t\t\treturn null;\n\t\t}\n\t\tif (hasTailParam && actualParts.length < patternParts.length - 1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst params: Record<string, string> = {};\n\t\tfor (let i = 0; i < patternParts.length; i += 1) {\n\t\t\tconst patternPart = patternParts[i];\n\t\t\tconst actualPart = actualParts[i];\n\n\t\t\tif (!patternPart || !actualPart) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (patternPart.startsWith(\":\") || (patternPart.startsWith(\"{\") && patternPart.endsWith(\"}\"))) {\n\t\t\t\tconst isPathTailParam = patternPart.includes(\":path\");\n\t\t\t\tconst rawKey = patternPart.startsWith(\"{\") ? patternPart.slice(1, -1).split(\":\")[0] : patternPart.slice(1);\n\t\t\t\tconst key = rawKey ?? \"\";\n\t\t\t\tif (isPathTailParam) {\n\t\t\t\t\tparams[key] = decodeURIComponent(actualParts.slice(i).join(\"/\"));\n\t\t\t\t\treturn params;\n\t\t\t\t}\n\t\t\t\tparams[key] = decodeURIComponent(actualPart);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (patternPart !== actualPart) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\treturn params;\n\t}\n\n\tprivate buildQuery(path: string): Record<string, string> {\n\t\tconst query: Record<string, string> = {};\n\t\tconst url = new URL(path, \"http://localhost\");\n\t\turl.searchParams.forEach((value, key) => {\n\t\t\tif (!(key in query)) {\n\t\t\t\tquery[key] = value;\n\t\t\t}\n\t\t});\n\t\treturn query;\n\t}\n\n\tprivate validateParams(\n\t\troute: SpikardApp[\"routes\"][number],\n\t\tparams: Record<string, string>,\n\t): NativeTestResponse | null {\n\t\tconst schema = route.parameter_schema as\n\t\t\t| { properties?: Record<string, { format?: string; source?: string; type?: string }> }\n\t\t\t| undefined;\n\t\tif (!schema?.properties) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfor (const [key, meta] of Object.entries(schema.properties)) {\n\t\t\tif (meta.source !== \"path\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst raw = params[key];\n\t\t\tif (raw === undefined) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (meta.format === \"date\" || meta.format === \"date-time\") {\n\t\t\t\tconst parsed = new Date(raw);\n\t\t\t\tif (Number.isNaN(parsed.valueOf())) {\n\t\t\t\t\treturn new JsTestResponse(422, {}, Buffer.from(\"\"));\n\t\t\t\t}\n\t\t\t\t(params as Record<string, unknown>)[key] = parsed;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async invoke(\n\t\tmethod: string,\n\t\tpath: string,\n\t\theaders: Record<string, string> | null,\n\t\tbody: NativeBody,\n\t): Promise<NativeTestResponse> {\n\t\tconst routeMatch = (() => {\n\t\t\ttry {\n\t\t\t\treturn this.matchRoute(method, path);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tif (!routeMatch) {\n\t\t\tconst staticResponse = await this.serveStatic(path);\n\t\t\tif (staticResponse) {\n\t\t\t\treturn staticResponse;\n\t\t\t}\n\t\t\tthrow new Error(`No route matched ${method} ${path}`);\n\t\t}\n\n\t\tconst { handlerName, params, route } = routeMatch;\n\t\tconst handler = this.handlers[handlerName];\n\t\tif (!handler) {\n\t\t\tthrow new Error(`Handler not found for ${handlerName}`);\n\t\t}\n\n\t\tif (this.config?.rateLimit && this.isRateLimited(route.path)) {\n\t\t\treturn new JsTestResponse(429, {}, Buffer.from(\"\"));\n\t\t}\n\n\t\tconst validationResponse = this.validateParams(route, params);\n\t\tif (validationResponse) {\n\t\t\treturn validationResponse;\n\t\t}\n\n\t\tconst requestPayload = {\n\t\t\tmethod,\n\t\t\tpath,\n\t\t\tparams,\n\t\t\tquery: this.buildQuery(path),\n\t\t\theaders: headers ?? {},\n\t\t\tcookies: {},\n\t\t\tbody: this.encodeBody(body),\n\t\t\tdependencies: this.dependencies ?? undefined,\n\t\t};\n\n\t\tconst result = await handler(this.safeStringify(requestPayload));\n\t\tconst response = await this.toResponse(result);\n\t\treturn this.applyCompression(response, headers);\n\t}\n\n\tprivate async toResponse(result: unknown): Promise<NativeTestResponse> {\n\t\tif (isStreamingResponse(result as never)) {\n\t\t\tconst handle = getStreamingHandle(result as never);\n\t\t\tif (handle.kind === \"js\") {\n\t\t\t\tconst buffers: Buffer[] = [];\n\t\t\t\tfor await (const chunk of handle.iterator) {\n\t\t\t\t\tif (chunk === null || chunk === undefined) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (Buffer.isBuffer(chunk)) {\n\t\t\t\t\t\tbuffers.push(chunk);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof chunk === \"string\") {\n\t\t\t\t\t\tbuffers.push(Buffer.from(chunk));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (chunk instanceof ArrayBuffer || ArrayBuffer.isView(chunk)) {\n\t\t\t\t\t\tconst view = ArrayBuffer.isView(chunk)\n\t\t\t\t\t\t\t? Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength)\n\t\t\t\t\t\t\t: Buffer.from(chunk as ArrayBuffer);\n\t\t\t\t\t\tbuffers.push(view);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbuffers.push(Buffer.from(this.safeStringify(chunk)));\n\t\t\t\t}\n\t\t\t\tconst bodyBuffer = buffers.length === 0 ? Buffer.alloc(0) : Buffer.concat(buffers);\n\t\t\t\treturn new JsTestResponse(handle.init.statusCode ?? 200, handle.init.headers ?? {}, bodyBuffer);\n\t\t\t}\n\t\t}\n\n\t\tif (typeof result === \"string\") {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(result) as {\n\t\t\t\t\tstatus?: number;\n\t\t\t\t\tstatusCode?: number;\n\t\t\t\t\theaders?: Record<string, string>;\n\t\t\t\t\tbody?: unknown;\n\t\t\t\t};\n\t\t\t\tif (\n\t\t\t\t\tparsed &&\n\t\t\t\t\ttypeof parsed === \"object\" &&\n\t\t\t\t\t(\"status\" in parsed || \"statusCode\" in parsed || \"body\" in parsed)\n\t\t\t\t) {\n\t\t\t\t\tconst statusCode = parsed.status ?? parsed.statusCode ?? 200;\n\t\t\t\t\tconst textBody =\n\t\t\t\t\t\ttypeof parsed.body === \"string\" || parsed.body === undefined\n\t\t\t\t\t\t\t? (parsed.body ?? \"\")\n\t\t\t\t\t\t\t: JSON.stringify(parsed.body);\n\t\t\t\t\treturn new JsTestResponse(statusCode, parsed.headers ?? {}, Buffer.from(textBody));\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\treturn new JsTestResponse(200, {}, Buffer.from(result));\n\t\t}\n\n\t\tif (\n\t\t\tresult &&\n\t\t\ttypeof result === \"object\" &&\n\t\t\t(\"status\" in (result as Record<string, unknown>) || \"statusCode\" in (result as Record<string, unknown>))\n\t\t) {\n\t\t\tconst payload = result as {\n\t\t\t\tstatus?: number;\n\t\t\t\tstatusCode?: number;\n\t\t\t\theaders?: Record<string, string>;\n\t\t\t\tbody?: unknown;\n\t\t\t};\n\t\t\tconst statusCode = payload.status ?? payload.statusCode ?? 200;\n\t\t\tconst textBody =\n\t\t\t\ttypeof payload.body === \"string\" || payload.body === undefined\n\t\t\t\t\t? (payload.body ?? \"\")\n\t\t\t\t\t: JSON.stringify(payload.body);\n\t\t\treturn new JsTestResponse(statusCode, payload.headers ?? {}, Buffer.from(textBody));\n\t\t}\n\n\t\tconst text = this.safeStringify(result);\n\t\treturn new JsTestResponse(200, {}, Buffer.from(text));\n\t}\n\n\tprivate isRateLimited(routePath: string): boolean {\n\t\tif (!this.config?.rateLimit) {\n\t\t\treturn false;\n\t\t}\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst key = routePath;\n\t\tconst existing = this.rateLimitBuckets.get(key);\n\t\tconst bucket =\n\t\t\texisting && existing.resetAt === now ? existing : { tokens: this.config.rateLimit.burst, resetAt: now };\n\t\tif (bucket.tokens <= 0) {\n\t\t\tthis.rateLimitBuckets.set(key, bucket);\n\t\t\treturn true;\n\t\t}\n\t\tbucket.tokens -= 1;\n\t\tthis.rateLimitBuckets.set(key, bucket);\n\t\treturn false;\n\t}\n\n\tprivate async serveStatic(targetPath: string): Promise<NativeTestResponse | null> {\n\t\tconst normalized = targetPath.split(\"?\")[0] ?? targetPath;\n\t\tconst staticConfig = this.config?.staticFiles ?? [];\n\t\tfor (const entry of staticConfig) {\n\t\t\tif (!normalized.startsWith(entry.routePrefix)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst relative = normalized.slice(entry.routePrefix.length);\n\t\t\tconst resolved = relative === \"/\" || relative === \"\" ? \"index.html\" : relative.replace(/^\\//, \"\");\n\t\t\tconst filePath = path.join(entry.directory, resolved);\n\t\t\ttry {\n\t\t\t\tconst contents = await fs.readFile(filePath);\n\t\t\t\tconst contentType = this.detectContentType(filePath);\n\t\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t\t\"content-type\": contentType,\n\t\t\t\t};\n\t\t\t\tif (entry.cacheControl) {\n\t\t\t\t\theaders[\"cache-control\"] = entry.cacheControl;\n\t\t\t\t}\n\t\t\t\tconst bodyBuffer = contentType.startsWith(\"text/\")\n\t\t\t\t\t? Buffer.from(contents.toString(\"utf-8\").replace(/\\n$/, \"\"))\n\t\t\t\t\t: contents;\n\t\t\t\treturn new JsTestResponse(200, headers, bodyBuffer);\n\t\t\t} catch {}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate detectContentType(filePath: string): string {\n\t\tconst ext = path.extname(filePath).toLowerCase();\n\t\tswitch (ext) {\n\t\t\tcase \".txt\":\n\t\t\t\treturn \"text/plain\";\n\t\t\tcase \".html\":\n\t\t\tcase \".htm\":\n\t\t\t\treturn \"text/html\";\n\t\t\tcase \".json\":\n\t\t\t\treturn \"application/json\";\n\t\t\tcase \".xml\":\n\t\t\t\treturn \"application/xml\";\n\t\t\tcase \".csv\":\n\t\t\t\treturn \"text/csv\";\n\t\t\tcase \".png\":\n\t\t\t\treturn \"image/png\";\n\t\t\tcase \".jpg\":\n\t\t\tcase \".jpeg\":\n\t\t\t\treturn \"image/jpeg\";\n\t\t\tcase \".pdf\":\n\t\t\t\treturn \"application/pdf\";\n\t\t\tdefault:\n\t\t\t\treturn \"application/octet-stream\";\n\t\t}\n\t}\n\n\tprivate applyCompression(response: NativeTestResponse, requestHeaders: Record<string, string> | null) {\n\t\tconst config = this.config?.compression;\n\t\tconst acceptsGzip = (this.lookupHeader(requestHeaders, \"accept-encoding\") ?? \"\").includes(\"gzip\");\n\t\tif (!config || !config.gzip || !acceptsGzip) {\n\t\t\treturn response;\n\t\t}\n\n\t\tconst rawBody = response.bytes();\n\t\tif (rawBody.length < (config.minSize ?? 0)) {\n\t\t\treturn response;\n\t\t}\n\n\t\tconst gzipped = gzipSync(rawBody, { level: config.quality ?? 6 });\n\t\tconst headers = { ...response.headers(), \"content-encoding\": \"gzip\" };\n\t\treturn new JsTestResponse(response.statusCode, headers, gzipped);\n\t}\n\n\tprivate lookupHeader(headers: Record<string, string> | null, name: string): string | undefined {\n\t\tif (!headers) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst target = name.toLowerCase();\n\t\tfor (const [key, value] of Object.entries(headers)) {\n\t\t\tif (key.toLowerCase() === target) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate safeStringify(value: unknown): string {\n\t\treturn JSON.stringify(value, (_key, val) => {\n\t\t\tif (typeof val === \"bigint\") {\n\t\t\t\treturn val.toString();\n\t\t\t}\n\t\t\treturn val;\n\t\t});\n\t}\n\n\tprivate encodeBody(body: NativeBody): NativeBody {\n\t\tif (body === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (typeof body === \"object\" && (\"__spikard_multipart__\" in body || \"__spikard_form__\" in body)) {\n\t\t\treturn Array.from(Buffer.from(this.safeStringify(body)));\n\t\t}\n\n\t\tif (Buffer.isBuffer(body)) {\n\t\t\treturn Array.from(body);\n\t\t}\n\n\t\treturn body;\n\t}\n\n\tasync get(path: string, headers: Record<string, string> | null): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"GET\", path, headers, null);\n\t}\n\n\tasync post(path: string, headers: Record<string, string> | null, body: NativeBody): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"POST\", path, headers, body);\n\t}\n\n\tasync put(path: string, headers: Record<string, string> | null, body: NativeBody): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"PUT\", path, headers, body);\n\t}\n\n\tasync delete(path: string, headers: Record<string, string> | null): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"DELETE\", path, headers, null);\n\t}\n\n\tasync patch(path: string, headers: Record<string, string> | null, body: NativeBody): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"PATCH\", path, headers, body);\n\t}\n\n\tasync head(path: string, headers: Record<string, string> | null): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"HEAD\", path, headers, null);\n\t}\n\n\tasync options(path: string, headers: Record<string, string> | null): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"OPTIONS\", path, headers, null);\n\t}\n\n\tasync trace(path: string, headers: Record<string, string> | null): Promise<NativeTestResponse> {\n\t\treturn this.invoke(\"TRACE\", path, headers, null);\n\t}\n\n\tasync websocket(_path: string): Promise<WebSocketTestConnection> {\n\t\tconst match = this.websocketRoutes?.find((route) => route.path === _path);\n\t\tif (!match) {\n\t\t\tthrow new Error(\"WebSocket testing is not available in the JS fallback client\");\n\t\t}\n\t\tconst handlerEntry = this.websocketHandlers?.[match.handler_name];\n\t\tif (!handlerEntry) {\n\t\t\tthrow new Error(\"WebSocket testing is not available in the JS fallback client\");\n\t\t}\n\t\tconst handler =\n\t\t\thandlerEntry &&\n\t\t\ttypeof (handlerEntry as { handleMessage?: (msg: unknown) => Promise<unknown> }).handleMessage === \"function\"\n\t\t\t\t? (handlerEntry as { handleMessage: (msg: unknown) => Promise<unknown> }).handleMessage\n\t\t\t\t: null;\n\t\tif (!handler) {\n\t\t\tthrow new Error(\"WebSocket testing is not available in the JS fallback client\");\n\t\t}\n\t\tconst mock = new MockWebSocketConnection(async (msg) => handler(msg));\n\t\treturn mock as unknown as WebSocketTestConnection;\n\t}\n}\n\nconst defaultNativeClientFactory: NativeClientFactory = (\n\troutesJson,\n\twebsocketRoutesJson,\n\thandlers,\n\twebsocketHandlers,\n\tdependencies,\n\tlifecycleHooks,\n\tconfig,\n) => {\n\tif (isNativeCtor && nativeTestClient) {\n\t\treturn new nativeTestClient(\n\t\t\troutesJson,\n\t\t\twebsocketRoutesJson,\n\t\t\thandlers,\n\t\t\twebsocketHandlers,\n\t\t\tdependencies,\n\t\t\tlifecycleHooks,\n\t\t\tconfig,\n\t\t);\n\t}\n\n\treturn new JsNativeClient(\n\t\troutesJson,\n\t\twebsocketRoutesJson,\n\t\thandlers,\n\t\twebsocketHandlers,\n\t\tdependencies,\n\t\tlifecycleHooks,\n\t\tconfig,\n\t);\n};\n\nlet nativeClientFactory: NativeClientFactory = defaultNativeClientFactory;\n\nexport const __setNativeClientFactory = (factory?: NativeClientFactory): void => {\n\tnativeClientFactory = factory ?? defaultNativeClientFactory;\n};\n\n/**\n * Test client for making HTTP requests to Spikard applications\n *\n * Provides a high-level API for testing HTTP endpoints without\n * starting an actual server.\n *\n * @example\n * ```typescript\n * import { TestClient } from 'spikard';\n *\n * const app = {\n * routes: [\n * {\n * method: 'GET',\n * path: '/users/:id',\n * handler_name: 'getUser',\n * is_async: true\n * }\n * ],\n * handlers: {\n * getUser: async (req) => ({ id: req.params.id, name: 'Alice' })\n * }\n * };\n *\n * const client = new TestClient(app);\n * const response = await client.get('/users/123');\n * console.log(response.json()); // { id: '123', name: 'Alice' }\n * ```\n */\nexport class TestClient {\n\treadonly app: SpikardApp;\n\tprivate nativeClient: NativeClient;\n\n\tprivate looksLikeStringHandler(fn: HandlerFunction | NativeHandlerFunction): boolean {\n\t\tconst source = fn.toString();\n\t\treturn (\n\t\t\tsource.includes(\"requestJson\") ||\n\t\t\tsource.includes(\"request_json\") ||\n\t\t\tsource.includes(\"JSON.parse\") ||\n\t\t\tsource.includes(\"JSON.parse(\")\n\t\t);\n\t}\n\n\t/**\n\t * Create a new test client\n\t *\n\t * @param app - Spikard application with routes and handlers\n\t */\n\tconstructor(app: SpikardApp) {\n\t\tif (!app || !Array.isArray(app.routes)) {\n\t\t\tthrow new Error(\"Invalid Spikard app: missing routes array\");\n\t\t}\n\t\tthis.app = app;\n\t\tconst routesJson = JSON.stringify(app.routes);\n\t\tconst websocketRoutesJson = JSON.stringify(app.websocketRoutes ?? []);\n\t\tconst handlerEntries = Object.entries(app.handlers || {});\n\n\t\tconst handlersMap = Object.fromEntries(\n\t\t\thandlerEntries.map(([name, handler]) => {\n\t\t\t\tconst prefersParsedBody = this.looksLikeStringHandler(handler);\n\t\t\t\tconst nativeHandler =\n\t\t\t\t\tisNativeHandler(handler) || prefersParsedBody\n\t\t\t\t\t\t? (handler as NativeHandlerFunction)\n\t\t\t\t\t\t: wrapHandler(handler as HandlerFunction);\n\t\t\t\tif (prefersParsedBody) {\n\t\t\t\t\t(nativeHandler as { __spikard_raw_body?: boolean }).__spikard_raw_body = false;\n\t\t\t\t} else if ((nativeHandler as { __spikard_raw_body?: boolean }).__spikard_raw_body === undefined) {\n\t\t\t\t\t(nativeHandler as { __spikard_raw_body?: boolean }).__spikard_raw_body = true;\n\t\t\t\t}\n\t\t\t\treturn [name, nativeHandler];\n\t\t\t}),\n\t\t);\n\t\tconst websocketHandlersMap = app.websocketHandlers || {};\n\t\tconst config = app.config ?? null;\n\t\tconst dependencies = (app as SpikardApp & { dependencies?: Record<string, unknown> }).dependencies ?? null;\n\t\tconst lifecycleHooks = (app as { getLifecycleHooks?: () => Record<string, unknown> }).getLifecycleHooks?.() ?? null;\n\n\t\tthis.nativeClient = nativeClientFactory(\n\t\t\troutesJson,\n\t\t\twebsocketRoutesJson,\n\t\t\thandlersMap,\n\t\t\twebsocketHandlersMap,\n\t\t\tdependencies,\n\t\t\tlifecycleHooks,\n\t\t\tconfig,\n\t\t);\n\t}\n\n\t/**\n\t * Make a GET request\n\t *\n\t * @param path - Request path\n\t * @param headers - Optional request headers\n\t * @returns Response promise\n\t */\n\tasync get(path: string, headers?: Record<string, string>): Promise<TestResponse> {\n\t\treturn this.nativeClient.get(path, headers || null);\n\t}\n\n\t/**\n\t * Make a POST request\n\t *\n\t * @param path - Request path\n\t * @param options - Request options\n\t * @returns Response promise\n\t */\n\tasync post(path: string, options?: RequestOptions): Promise<TestResponse> {\n\t\treturn this.nativeClient.post(path, this.buildHeaders(options), this.buildBody(options));\n\t}\n\n\t/**\n\t * Make a PUT request\n\t *\n\t * @param path - Request path\n\t * @param options - Request options\n\t * @returns Response promise\n\t */\n\tasync put(path: string, options?: RequestOptions): Promise<TestResponse> {\n\t\treturn this.nativeClient.put(path, this.buildHeaders(options), this.buildBody(options));\n\t}\n\n\t/**\n\t * Make a DELETE request\n\t *\n\t * @param path - Request path\n\t * @param headers - Optional request headers\n\t * @returns Response promise\n\t */\n\tasync delete(path: string, headers?: Record<string, string>): Promise<TestResponse> {\n\t\treturn this.nativeClient.delete(path, headers || null);\n\t}\n\n\t/**\n\t * Make a PATCH request\n\t *\n\t * @param path - Request path\n\t * @param options - Request options\n\t * @returns Response promise\n\t */\n\tasync patch(path: string, options?: RequestOptions): Promise<TestResponse> {\n\t\treturn this.nativeClient.patch(path, this.buildHeaders(options), this.buildBody(options));\n\t}\n\n\t/**\n\t * Make a HEAD request\n\t *\n\t * @param path - Request path\n\t * @param headers - Optional request headers\n\t * @returns Response promise\n\t */\n\tasync head(path: string, headers?: Record<string, string>): Promise<TestResponse> {\n\t\treturn this.nativeClient.head(path, headers || null);\n\t}\n\n\t/**\n\t * Make an OPTIONS request\n\t *\n\t * @param path - Request path\n\t * @param options - Request options\n\t * @returns Response promise\n\t */\n\tasync options(path: string, options?: RequestOptions): Promise<TestResponse> {\n\t\treturn this.nativeClient.options(path, this.buildHeaders(options));\n\t}\n\n\t/**\n\t * Make a TRACE request\n\t *\n\t * @param path - Request path\n\t * @param headers - Optional request headers\n\t * @returns Response promise\n\t */\n\tasync trace(path: string, options?: RequestOptions): Promise<TestResponse> {\n\t\treturn this.nativeClient.trace(path, this.buildHeaders(options));\n\t}\n\n\tprivate buildHeaders(options?: RequestOptions): Record<string, string> | null {\n\t\tif (!options?.headers || Object.keys(options.headers).length === 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn options.headers;\n\t}\n\n\tprivate buildBody(options?: RequestOptions): NativeBody {\n\t\tif (!options) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (options.multipart) {\n\t\t\treturn {\n\t\t\t\t__spikard_multipart__: {\n\t\t\t\t\tfields: options.multipart.fields ?? {},\n\t\t\t\t\tfiles: options.multipart.files ?? [],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (options.form) {\n\t\t\treturn {\n\t\t\t\t__spikard_form__: options.form,\n\t\t\t};\n\t\t}\n\n\t\tif (\"json\" in options) {\n\t\t\treturn options.json ?? null;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Connect to a WebSocket endpoint\n\t *\n\t * Uses the native test client to create an in-memory WebSocket connection.\n\t *\n\t * @param path - WebSocket path\n\t * @returns WebSocket test connection\n\t */\n\tasync websocketConnect(path: string): Promise<WebSocketTestConnection> {\n\t\tconst handlerName = this.app.websocketRoutes?.find((r) => r.path === path)?.handler_name;\n\t\tconst handlerEntry = handlerName ? this.app.websocketHandlers?.[handlerName] : undefined;\n\t\tconst handler =\n\t\t\thandlerEntry && typeof handlerEntry.handleMessage === \"function\" ? handlerEntry.handleMessage : null;\n\n\t\tif (handler) {\n\t\t\tconst mock = new MockWebSocketConnection(async (msg) => handler(msg));\n\t\t\treturn mock as unknown as WebSocketTestConnection;\n\t\t}\n\n\t\tconst routeMatch = this.app.routes.find((r) => r.path === path);\n\t\tif (routeMatch) {\n\t\t\tconst handlerFn = this.app.handlers?.[routeMatch.handler_name];\n\t\t\tif (handlerFn) {\n\t\t\t\tconst mock = new MockWebSocketConnection(async (msg) => {\n\t\t\t\t\tconst payload = typeof msg === \"string\" ? msg : JSON.stringify(msg);\n\t\t\t\t\tconst result = await (handlerFn as HandlerFunction | NativeHandlerFunction)(payload as never);\n\t\t\t\t\tif (typeof result === \"string\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn JSON.parse(result);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t});\n\t\t\t\treturn mock as unknown as WebSocketTestConnection;\n\t\t\t}\n\t\t}\n\n\t\treturn this.nativeClient.websocket(path);\n\t}\n\n\t/**\n\t * Send a GraphQL query/mutation\n\t *\n\t * Convenience method for sending GraphQL queries and mutations.\n\t *\n\t * @param query - GraphQL query string\n\t * @param variables - Optional GraphQL variables object\n\t * @param operationName - Optional GraphQL operation name\n\t * @returns Response promise\n\t *\n\t * @example\n\t * ```typescript\n\t * const response = await client.graphql('query { user(id: \"1\") { id name } }');\n\t * const user = response.graphqlData().user;\n\t * ```\n\t */\n\tasync graphql(\n\t\tquery: string,\n\t\tvariables?: Record<string, unknown> | null,\n\t\toperationName?: string | null,\n\t): Promise<TestResponse> {\n\t\tconst json: Record<string, JsonValue> = { query };\n\t\tif (variables !== null && variables !== undefined) {\n\t\t\tjson.variables = variables as JsonValue;\n\t\t}\n\t\tif (operationName !== null && operationName !== undefined) {\n\t\t\tjson.operationName = operationName;\n\t\t}\n\t\treturn this.post(\"/graphql\", { json: json as JsonValue });\n\t}\n\n\t/**\n\t * Send a GraphQL query and get HTTP status separately\n\t *\n\t * Returns status information alongside the response for cases where\n\t * you need both the HTTP status and the full response details.\n\t *\n\t * @param query - GraphQL query string\n\t * @param variables - Optional GraphQL variables object\n\t * @param operationName - Optional GraphQL operation name\n\t * @returns Promise with status and response details\n\t */\n\tasync graphqlWithStatus(\n\t\tquery: string,\n\t\tvariables?: Record<string, unknown> | null,\n\t\toperationName?: string | null,\n\t): Promise<{\n\t\tstatus: number;\n\t\tstatusCode: number;\n\t\theaders: string;\n\t\tbodyText: string;\n\t}> {\n\t\tconst response = await this.graphql(query, variables, operationName);\n\t\treturn {\n\t\t\tstatus: response.statusCode,\n\t\t\tstatusCode: response.statusCode,\n\t\t\theaders: JSON.stringify(response.headers()),\n\t\t\tbodyText: response.text(),\n\t\t};\n\t}\n\n\t/**\n\t * Cleanup resources when test client is done\n\t */\n\tasync cleanup(): Promise<void> {}\n}\n"],"mappings":"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","names":["createRequire","result","require","nativeHandler","nativeBinding","loadBinding","require","createRequire","route","path","route","GrpcStatusCode","path","createRequire","require","createRequire","path","route","path"]}
package/index.d.ts CHANGED
@@ -257,6 +257,10 @@ export interface GrpcResponse {
257
257
  *
258
258
  * This struct replaces JSON string passing, eliminating serialization overhead.
259
259
  * Fields are converted from `RequestData` using a direct `From` impl.
260
+ *
261
+ * PERFORMANCE: `object_from_js = false` skips generating FromNapiValue since
262
+ * HandlerInput only flows Rust→JS, never JS→Rust. This eliminates unnecessary
263
+ * code generation and potential conversion overhead.
260
264
  */
261
265
  export interface HandlerInput {
262
266
  /** HTTP method (GET, POST, etc.) */
@@ -288,8 +292,15 @@ export interface HandlerOutput {
288
292
  status: number
289
293
  /** Response headers as a map */
290
294
  headers?: Record<string, string>
291
- /** Response body as JSON value */
295
+ /** Response body as JSON value (used when `raw_body` is not set) */
292
296
  body?: any
297
+ /**
298
+ * Pre-serialized response body bytes. When set, this is used directly as
299
+ * the response body, bypassing `serde_json::to_vec` on the `body` field.
300
+ * JS handlers can pass `Buffer.from(JSON.stringify(obj))` here to avoid
301
+ * a napi Value → serde_json::Value → bytes round-trip.
302
+ */
303
+ rawBody?: Buffer
293
304
  }
294
305
 
295
306
  /**