@richie-rpc/server 1.2.4 → 1.2.6
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/cjs/index.cjs +19 -3
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/websocket.cjs +243 -0
- package/dist/cjs/websocket.cjs.map +10 -0
- package/dist/mjs/index.mjs +3 -3
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/websocket.mjs +212 -0
- package/dist/mjs/websocket.mjs.map +10 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +2 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __reExport = (target, mod, secondTarget) => {
|
|
7
|
+
for (let key of __getOwnPropNames(mod))
|
|
8
|
+
if (!__hasOwnProp.call(target, key) && key !== "default")
|
|
9
|
+
__defProp(target, key, {
|
|
10
|
+
get: () => mod[key],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
13
|
+
if (secondTarget) {
|
|
14
|
+
for (let key of __getOwnPropNames(mod))
|
|
15
|
+
if (!__hasOwnProp.call(secondTarget, key) && key !== "default")
|
|
16
|
+
__defProp(secondTarget, key, {
|
|
17
|
+
get: () => mod[key],
|
|
18
|
+
enumerable: true
|
|
19
|
+
});
|
|
20
|
+
return secondTarget;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
6
23
|
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
24
|
var __toCommonJS = (from) => {
|
|
8
25
|
var entry = __moduleCache.get(from), desc;
|
|
@@ -30,7 +47,6 @@ var __export = (target, all) => {
|
|
|
30
47
|
// packages/server/index.ts
|
|
31
48
|
var exports_server = {};
|
|
32
49
|
__export(exports_server, {
|
|
33
|
-
createWebSocketRouter: () => import_websocket.createWebSocketRouter,
|
|
34
50
|
createRouter: () => createRouter,
|
|
35
51
|
ValidationError: () => ValidationError,
|
|
36
52
|
Status: () => import_core.Status,
|
|
@@ -39,7 +55,7 @@ __export(exports_server, {
|
|
|
39
55
|
});
|
|
40
56
|
module.exports = __toCommonJS(exports_server);
|
|
41
57
|
var import_core = require("@richie-rpc/core");
|
|
42
|
-
|
|
58
|
+
__reExport(exports_server, require("./websocket.cjs"), module.exports);
|
|
43
59
|
class ValidationError extends Error {
|
|
44
60
|
field;
|
|
45
61
|
issues;
|
|
@@ -451,4 +467,4 @@ function createRouter(contract, handlers, options) {
|
|
|
451
467
|
}
|
|
452
468
|
})
|
|
453
469
|
|
|
454
|
-
//# debugId=
|
|
470
|
+
//# debugId=04227CF08860524664756E2164756E21
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type {\n Contract,\n DownloadEndpointDefinition,\n EndpointDefinition,\n ExtractBody,\n ExtractChunk,\n ExtractFinalResponse,\n ExtractHeaders,\n ExtractParams,\n ExtractQuery,\n ExtractSSEEventData,\n SSEEndpointDefinition,\n StandardEndpointDefinition,\n StreamingEndpointDefinition,\n} from '@richie-rpc/core';\nimport { formDataToObject, matchPath, parseQuery, Status } from '@richie-rpc/core';\nimport type { z } from 'zod';\n\n// Re-export Status for convenience\nexport { Status };\n\n// Handler input types (for standard endpoints)\nexport type HandlerInput<T extends StandardEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n};\n\n// Handler response type (for standard endpoints)\nexport type HandlerResponse<T extends StandardEndpointDefinition> = {\n [Status in keyof T['responses']]: {\n status: Status;\n body: T['responses'][Status] extends z.ZodTypeAny ? z.infer<T['responses'][Status]> : never;\n headers?: Record<string, string>;\n };\n}[keyof T['responses']];\n\n// Handler function type (for standard endpoints)\nexport type Handler<T extends StandardEndpointDefinition, C = unknown> = (\n input: HandlerInput<T, C>,\n) => Promise<HandlerResponse<T>> | HandlerResponse<T>;\n\n// ============================================\n// Streaming Endpoint Types\n// ============================================\n\n/**\n * Emitter for streaming responses - push-based API\n */\nexport interface StreamEmitter<T extends StreamingEndpointDefinition> {\n /** Send a chunk to the client */\n send(chunk: ExtractChunk<T>): void;\n /** Close the stream with optional final response */\n close(final?: ExtractFinalResponse<T>): void;\n /** Check if stream is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for streaming endpoints\n */\nexport type StreamingHandlerInput<T extends StreamingEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n stream: StreamEmitter<T>;\n};\n\n/**\n * Handler function type for streaming endpoints\n */\nexport type StreamingHandler<T extends StreamingEndpointDefinition, C = unknown> = (\n input: StreamingHandlerInput<T, C>,\n) => void | Promise<void>;\n\n// ============================================\n// SSE Endpoint Types\n// ============================================\n\n/**\n * Emitter for SSE responses\n */\nexport interface SSEEmitter<T extends SSEEndpointDefinition> {\n /** Send an event to the client */\n send<K extends keyof T['events']>(\n event: K,\n data: ExtractSSEEventData<T, K>,\n options?: { id?: string },\n ): void;\n /** Close the connection */\n close(): void;\n /** Check if connection is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for SSE endpoints\n */\nexport type SSEHandlerInput<T extends SSEEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n emitter: SSEEmitter<T>;\n /** AbortSignal for detecting client disconnect */\n signal: AbortSignal;\n};\n\n/**\n * Handler function type for SSE endpoints\n * Returns an optional cleanup function\n */\nexport type SSEHandler<T extends SSEEndpointDefinition, C = unknown> = (\n input: SSEHandlerInput<T, C>,\n) => void | (() => void) | Promise<void | (() => void)>;\n\n// ============================================\n// Download Endpoint Types\n// ============================================\n\n/**\n * Handler input for download endpoints\n */\nexport type DownloadHandlerInput<T extends DownloadEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n};\n\n/**\n * Handler response for download endpoints\n * Success (200) returns File, errors return typed response\n */\nexport type DownloadHandlerResponse<T extends DownloadEndpointDefinition> =\n | { status: 200; body: File; headers?: Record<string, string> }\n | (T['errorResponses'] extends Record<number, z.ZodTypeAny>\n ? {\n [S in keyof T['errorResponses']]: {\n status: S;\n body: T['errorResponses'][S] extends z.ZodTypeAny\n ? z.infer<T['errorResponses'][S]>\n : never;\n headers?: Record<string, string>;\n };\n }[keyof T['errorResponses']]\n : never);\n\n/**\n * Handler function type for download endpoints\n */\nexport type DownloadHandler<T extends DownloadEndpointDefinition, C = unknown> = (\n input: DownloadHandlerInput<T, C>,\n) => Promise<DownloadHandlerResponse<T>> | DownloadHandlerResponse<T>;\n\n// ============================================\n// Contract Handlers (supports all endpoint types)\n// ============================================\n\n/**\n * Contract handlers mapping - conditionally applies handler type based on endpoint type\n */\nexport type ContractHandlers<T extends Contract, C = unknown> = {\n [K in keyof T]: T[K] extends StandardEndpointDefinition\n ? Handler<T[K], C>\n : T[K] extends StreamingEndpointDefinition\n ? StreamingHandler<T[K], C>\n : T[K] extends SSEEndpointDefinition\n ? SSEHandler<T[K], C>\n : T[K] extends DownloadEndpointDefinition\n ? DownloadHandler<T[K], C>\n : never;\n};\n\n// Error classes\nexport class ValidationError extends Error {\n constructor(\n public field: string,\n public issues: z.ZodIssue[],\n message?: string,\n ) {\n super(message || `Validation failed for ${field}`);\n this.name = 'ValidationError';\n }\n}\n\nexport class RouteNotFoundError extends Error {\n constructor(\n public path: string,\n public method: string,\n ) {\n super(`Route not found: ${method} ${path}`);\n this.name = 'RouteNotFoundError';\n }\n}\n\n/**\n * Parse and validate request data\n */\nasync function parseRequest<T extends StandardEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<HandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as HandlerInput<T, C>;\n}\n\n/**\n * Parse and validate request data for streaming endpoints\n */\nasync function parseStreamingRequest<T extends StreamingEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<StreamingHandlerInput<T, C>, 'stream'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as Omit<\n StreamingHandlerInput<T, C>,\n 'stream'\n >;\n}\n\n/**\n * Parse and validate request data for SSE endpoints\n */\nasync function parseSSERequest<T extends SSEEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<SSEHandlerInput<T, C>, 'emitter' | 'signal'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // SSE endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as Omit<\n SSEHandlerInput<T, C>,\n 'emitter' | 'signal'\n >;\n}\n\n/**\n * Parse and validate request data for download endpoints\n */\nasync function parseDownloadRequest<T extends DownloadEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<DownloadHandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Download endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as DownloadHandlerInput<T, C>;\n}\n\n/**\n * Validate and create response\n */\nfunction createResponse<T extends StandardEndpointDefinition>(\n endpoint: T,\n handlerResponse: HandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n\n // Validate response body\n const responseSchema = endpoint.responses[status as keyof typeof endpoint.responses];\n if (responseSchema) {\n const result = responseSchema.safeParse(body);\n if (!result.success) {\n throw new ValidationError(`response[${String(status)}]`, result.error.issues);\n }\n }\n\n // Create response headers\n const responseHeaders = new Headers(customHeaders);\n\n // Handle 204 No Content - must have no body\n if (status === 204) {\n return new Response(null, {\n status: 204,\n headers: responseHeaders,\n });\n }\n\n // For all other responses, return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create response for download endpoints\n */\nfunction createDownloadResponse<T extends DownloadEndpointDefinition>(\n _endpoint: T,\n handlerResponse: DownloadHandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n const responseHeaders = new Headers(customHeaders);\n\n // Success: return File as binary\n if (status === 200 && body instanceof File) {\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', body.type || 'application/octet-stream');\n }\n responseHeaders.set('content-length', String(body.size));\n if (!responseHeaders.has('content-disposition')) {\n const filename = encodeURIComponent(body.name);\n responseHeaders.set(\n 'content-disposition',\n `attachment; filename=\"${filename}\"; filename*=UTF-8''${filename}`,\n );\n }\n return new Response(body, { status: 200, headers: responseHeaders });\n }\n\n // Error: return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create a streaming NDJSON response\n */\nfunction createStreamingResponse<T extends StreamingEndpointDefinition>(\n handler: (stream: StreamEmitter<T>) => void | Promise<void>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let closed = false;\n\n const stream: StreamEmitter<T> = {\n send(chunk) {\n if (!closed) {\n writer.write(encoder.encode(`${JSON.stringify(chunk)}\\n`));\n }\n },\n close(final) {\n if (!closed) {\n closed = true;\n if (final !== undefined) {\n writer.write(encoder.encode(`${JSON.stringify({ __final__: true, data: final })}\\n`));\n }\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler (don't await - let it run in background)\n Promise.resolve(handler(stream)).catch((err) => {\n console.error('Streaming handler error:', err);\n stream.close();\n });\n\n return new Response(readable, {\n headers: {\n 'Content-Type': 'application/x-ndjson',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Create an SSE response\n */\nfunction createSSEResponse<T extends SSEEndpointDefinition>(\n handler: (\n emitter: SSEEmitter<T>,\n signal: AbortSignal,\n ) => void | (() => void) | Promise<void | (() => void)>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n const controller = new AbortController();\n let closed = false;\n let cleanup: (() => void) | undefined;\n\n const emitter: SSEEmitter<T> = {\n send(event, data, options) {\n if (!closed) {\n let message = '';\n if (options?.id) {\n message += `id: ${options.id}\\n`;\n }\n message += `event: ${String(event)}\\n`;\n message += `data: ${JSON.stringify(data)}\\n\\n`;\n writer.write(encoder.encode(message));\n }\n },\n close() {\n if (!closed) {\n closed = true;\n if (cleanup) cleanup();\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler and get cleanup function\n Promise.resolve(handler(emitter, controller.signal))\n .then((cleanupFn) => {\n if (typeof cleanupFn === 'function') {\n cleanup = cleanupFn;\n }\n })\n .catch((err) => {\n console.error('SSE handler error:', err);\n emitter.close();\n });\n\n // Tee the stream - one for the response, one for disconnect detection\n const [responseStream, detectStream] = readable.tee();\n\n // Handle client disconnect by detecting when the stream is cancelled\n detectStream.pipeTo(new WritableStream()).catch(() => {\n controller.abort();\n emitter.close();\n });\n\n return new Response(responseStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Router configuration options\n */\nexport interface RouterOptions<C = unknown> {\n basePath?: string;\n context?: (request: Request, routeName?: string, endpoint?: EndpointDefinition) => C | Promise<C>;\n}\n\n/**\n * Router class that manages contract endpoints\n */\nexport class Router<T extends Contract, C = unknown> {\n private basePath: string;\n private contextFactory?: (\n request: Request,\n routeName: string,\n endpoint: EndpointDefinition,\n ) => C | Promise<C>;\n\n constructor(\n private contract: T,\n private handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n ) {\n // Normalize basePath: ensure it starts with / and doesn't end with /\n const bp = options?.basePath || '';\n if (bp) {\n this.basePath = bp.startsWith('/') ? bp : `/${bp}`;\n this.basePath = this.basePath.endsWith('/') ? this.basePath.slice(0, -1) : this.basePath;\n } else {\n this.basePath = '';\n }\n this.contextFactory = options?.context;\n }\n\n /**\n * Find matching endpoint for a request\n */\n private findEndpoint(\n method: string,\n path: string,\n ): {\n name: keyof T;\n endpoint: EndpointDefinition;\n params: Record<string, string>;\n } | null {\n for (const [name, endpoint] of Object.entries(this.contract)) {\n if (endpoint.method === method) {\n const params = matchPath(endpoint.path, path);\n if (params !== null) {\n return { name, endpoint, params };\n }\n }\n }\n return null;\n }\n\n /**\n * Handle a request\n */\n async handle(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const method = request.method;\n let path = url.pathname;\n\n // Strip basePath if configured\n if (this.basePath && path.startsWith(this.basePath)) {\n path = path.slice(this.basePath.length) || '/';\n }\n\n const match = this.findEndpoint(method, path);\n if (!match) {\n throw new RouteNotFoundError(path, method);\n }\n\n const { name, endpoint, params } = match;\n const handler = this.handlers[name];\n\n // Create context if factory is provided\n const context = this.contextFactory\n ? await this.contextFactory(request, String(name), endpoint)\n : (undefined as C);\n\n // Dispatch based on endpoint type\n if (endpoint.type === 'streaming') {\n // Parse request for streaming endpoint\n const input = await parseStreamingRequest(request, endpoint, params, context);\n return createStreamingResponse((stream) => {\n return (handler as StreamingHandler<StreamingEndpointDefinition, C>)({\n ...input,\n stream,\n });\n });\n }\n\n if (endpoint.type === 'sse') {\n // Parse request for SSE endpoint\n const input = await parseSSERequest(request, endpoint, params, context);\n return createSSEResponse((emitter, signal) => {\n return (handler as SSEHandler<SSEEndpointDefinition, C>)({\n ...input,\n emitter,\n signal,\n });\n });\n }\n\n if (endpoint.type === 'download') {\n // Parse request for download endpoint\n const input = await parseDownloadRequest(request, endpoint, params, context);\n const downloadHandler = handler as unknown as DownloadHandler<DownloadEndpointDefinition, C>;\n const response = await downloadHandler(\n input as DownloadHandlerInput<DownloadEndpointDefinition, C>,\n );\n return createDownloadResponse(\n endpoint as DownloadEndpointDefinition,\n response as DownloadHandlerResponse<DownloadEndpointDefinition>,\n );\n }\n\n // Standard endpoint\n const input = await parseRequest(request, endpoint, params, context);\n const standardHandler = handler as unknown as Handler<StandardEndpointDefinition, C>;\n const handlerResponse = await standardHandler(\n input as HandlerInput<StandardEndpointDefinition, C>,\n );\n return createResponse(\n endpoint as StandardEndpointDefinition,\n handlerResponse as HandlerResponse<StandardEndpointDefinition>,\n );\n }\n\n /**\n * Get fetch handler compatible with Bun.serve\n */\n get fetch() {\n return (request: Request) => this.handle(request);\n }\n}\n\n/**\n * Create a router from a contract and handlers\n */\nexport function createRouter<T extends Contract, C = unknown>(\n contract: T,\n handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n): Router<T, C> {\n return new Router(contract, handlers, options);\n}\n\nexport { createWebSocketRouter } from './websocket.cjs';"
|
|
5
|
+
"import type {\n Contract,\n DownloadEndpointDefinition,\n EndpointDefinition,\n ExtractBody,\n ExtractChunk,\n ExtractFinalResponse,\n ExtractHeaders,\n ExtractParams,\n ExtractQuery,\n ExtractSSEEventData,\n SSEEndpointDefinition,\n StandardEndpointDefinition,\n StreamingEndpointDefinition,\n} from '@richie-rpc/core';\nimport { formDataToObject, matchPath, parseQuery, Status } from '@richie-rpc/core';\nimport type { z } from 'zod';\n\n// Re-export Status for convenience\nexport { Status };\n\n// Handler input types (for standard endpoints)\nexport type HandlerInput<T extends StandardEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n};\n\n// Handler response type (for standard endpoints)\nexport type HandlerResponse<T extends StandardEndpointDefinition> = {\n [Status in keyof T['responses']]: {\n status: Status;\n body: T['responses'][Status] extends z.ZodTypeAny ? z.infer<T['responses'][Status]> : never;\n headers?: Record<string, string>;\n };\n}[keyof T['responses']];\n\n// Handler function type (for standard endpoints)\nexport type Handler<T extends StandardEndpointDefinition, C = unknown> = (\n input: HandlerInput<T, C>,\n) => Promise<HandlerResponse<T>> | HandlerResponse<T>;\n\n// ============================================\n// Streaming Endpoint Types\n// ============================================\n\n/**\n * Emitter for streaming responses - push-based API\n */\nexport interface StreamEmitter<T extends StreamingEndpointDefinition> {\n /** Send a chunk to the client */\n send(chunk: ExtractChunk<T>): void;\n /** Close the stream with optional final response */\n close(final?: ExtractFinalResponse<T>): void;\n /** Check if stream is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for streaming endpoints\n */\nexport type StreamingHandlerInput<T extends StreamingEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n stream: StreamEmitter<T>;\n};\n\n/**\n * Handler function type for streaming endpoints\n */\nexport type StreamingHandler<T extends StreamingEndpointDefinition, C = unknown> = (\n input: StreamingHandlerInput<T, C>,\n) => void | Promise<void>;\n\n// ============================================\n// SSE Endpoint Types\n// ============================================\n\n/**\n * Emitter for SSE responses\n */\nexport interface SSEEmitter<T extends SSEEndpointDefinition> {\n /** Send an event to the client */\n send<K extends keyof T['events']>(\n event: K,\n data: ExtractSSEEventData<T, K>,\n options?: { id?: string },\n ): void;\n /** Close the connection */\n close(): void;\n /** Check if connection is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for SSE endpoints\n */\nexport type SSEHandlerInput<T extends SSEEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n emitter: SSEEmitter<T>;\n /** AbortSignal for detecting client disconnect */\n signal: AbortSignal;\n};\n\n/**\n * Handler function type for SSE endpoints\n * Returns an optional cleanup function\n */\nexport type SSEHandler<T extends SSEEndpointDefinition, C = unknown> = (\n input: SSEHandlerInput<T, C>,\n) => void | (() => void) | Promise<void | (() => void)>;\n\n// ============================================\n// Download Endpoint Types\n// ============================================\n\n/**\n * Handler input for download endpoints\n */\nexport type DownloadHandlerInput<T extends DownloadEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n};\n\n/**\n * Handler response for download endpoints\n * Success (200) returns File, errors return typed response\n */\nexport type DownloadHandlerResponse<T extends DownloadEndpointDefinition> =\n | { status: 200; body: File; headers?: Record<string, string> }\n | (T['errorResponses'] extends Record<number, z.ZodTypeAny>\n ? {\n [S in keyof T['errorResponses']]: {\n status: S;\n body: T['errorResponses'][S] extends z.ZodTypeAny\n ? z.infer<T['errorResponses'][S]>\n : never;\n headers?: Record<string, string>;\n };\n }[keyof T['errorResponses']]\n : never);\n\n/**\n * Handler function type for download endpoints\n */\nexport type DownloadHandler<T extends DownloadEndpointDefinition, C = unknown> = (\n input: DownloadHandlerInput<T, C>,\n) => Promise<DownloadHandlerResponse<T>> | DownloadHandlerResponse<T>;\n\n// ============================================\n// Contract Handlers (supports all endpoint types)\n// ============================================\n\n/**\n * Contract handlers mapping - conditionally applies handler type based on endpoint type\n */\nexport type ContractHandlers<T extends Contract, C = unknown> = {\n [K in keyof T]: T[K] extends StandardEndpointDefinition\n ? Handler<T[K], C>\n : T[K] extends StreamingEndpointDefinition\n ? StreamingHandler<T[K], C>\n : T[K] extends SSEEndpointDefinition\n ? SSEHandler<T[K], C>\n : T[K] extends DownloadEndpointDefinition\n ? DownloadHandler<T[K], C>\n : never;\n};\n\n// Error classes\nexport class ValidationError extends Error {\n constructor(\n public field: string,\n public issues: z.ZodIssue[],\n message?: string,\n ) {\n super(message || `Validation failed for ${field}`);\n this.name = 'ValidationError';\n }\n}\n\nexport class RouteNotFoundError extends Error {\n constructor(\n public path: string,\n public method: string,\n ) {\n super(`Route not found: ${method} ${path}`);\n this.name = 'RouteNotFoundError';\n }\n}\n\n/**\n * Parse and validate request data\n */\nasync function parseRequest<T extends StandardEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<HandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as HandlerInput<T, C>;\n}\n\n/**\n * Parse and validate request data for streaming endpoints\n */\nasync function parseStreamingRequest<T extends StreamingEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<StreamingHandlerInput<T, C>, 'stream'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as Omit<\n StreamingHandlerInput<T, C>,\n 'stream'\n >;\n}\n\n/**\n * Parse and validate request data for SSE endpoints\n */\nasync function parseSSERequest<T extends SSEEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<SSEHandlerInput<T, C>, 'emitter' | 'signal'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // SSE endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as Omit<\n SSEHandlerInput<T, C>,\n 'emitter' | 'signal'\n >;\n}\n\n/**\n * Parse and validate request data for download endpoints\n */\nasync function parseDownloadRequest<T extends DownloadEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<DownloadHandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Download endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as DownloadHandlerInput<T, C>;\n}\n\n/**\n * Validate and create response\n */\nfunction createResponse<T extends StandardEndpointDefinition>(\n endpoint: T,\n handlerResponse: HandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n\n // Validate response body\n const responseSchema = endpoint.responses[status as keyof typeof endpoint.responses];\n if (responseSchema) {\n const result = responseSchema.safeParse(body);\n if (!result.success) {\n throw new ValidationError(`response[${String(status)}]`, result.error.issues);\n }\n }\n\n // Create response headers\n const responseHeaders = new Headers(customHeaders);\n\n // Handle 204 No Content - must have no body\n if (status === 204) {\n return new Response(null, {\n status: 204,\n headers: responseHeaders,\n });\n }\n\n // For all other responses, return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create response for download endpoints\n */\nfunction createDownloadResponse<T extends DownloadEndpointDefinition>(\n _endpoint: T,\n handlerResponse: DownloadHandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n const responseHeaders = new Headers(customHeaders);\n\n // Success: return File as binary\n if (status === 200 && body instanceof File) {\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', body.type || 'application/octet-stream');\n }\n responseHeaders.set('content-length', String(body.size));\n if (!responseHeaders.has('content-disposition')) {\n const filename = encodeURIComponent(body.name);\n responseHeaders.set(\n 'content-disposition',\n `attachment; filename=\"${filename}\"; filename*=UTF-8''${filename}`,\n );\n }\n return new Response(body, { status: 200, headers: responseHeaders });\n }\n\n // Error: return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create a streaming NDJSON response\n */\nfunction createStreamingResponse<T extends StreamingEndpointDefinition>(\n handler: (stream: StreamEmitter<T>) => void | Promise<void>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let closed = false;\n\n const stream: StreamEmitter<T> = {\n send(chunk) {\n if (!closed) {\n writer.write(encoder.encode(`${JSON.stringify(chunk)}\\n`));\n }\n },\n close(final) {\n if (!closed) {\n closed = true;\n if (final !== undefined) {\n writer.write(encoder.encode(`${JSON.stringify({ __final__: true, data: final })}\\n`));\n }\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler (don't await - let it run in background)\n Promise.resolve(handler(stream)).catch((err) => {\n console.error('Streaming handler error:', err);\n stream.close();\n });\n\n return new Response(readable, {\n headers: {\n 'Content-Type': 'application/x-ndjson',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Create an SSE response\n */\nfunction createSSEResponse<T extends SSEEndpointDefinition>(\n handler: (\n emitter: SSEEmitter<T>,\n signal: AbortSignal,\n ) => void | (() => void) | Promise<void | (() => void)>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n const controller = new AbortController();\n let closed = false;\n let cleanup: (() => void) | undefined;\n\n const emitter: SSEEmitter<T> = {\n send(event, data, options) {\n if (!closed) {\n let message = '';\n if (options?.id) {\n message += `id: ${options.id}\\n`;\n }\n message += `event: ${String(event)}\\n`;\n message += `data: ${JSON.stringify(data)}\\n\\n`;\n writer.write(encoder.encode(message));\n }\n },\n close() {\n if (!closed) {\n closed = true;\n if (cleanup) cleanup();\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler and get cleanup function\n Promise.resolve(handler(emitter, controller.signal))\n .then((cleanupFn) => {\n if (typeof cleanupFn === 'function') {\n cleanup = cleanupFn;\n }\n })\n .catch((err) => {\n console.error('SSE handler error:', err);\n emitter.close();\n });\n\n // Tee the stream - one for the response, one for disconnect detection\n const [responseStream, detectStream] = readable.tee();\n\n // Handle client disconnect by detecting when the stream is cancelled\n detectStream.pipeTo(new WritableStream()).catch(() => {\n controller.abort();\n emitter.close();\n });\n\n return new Response(responseStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Router configuration options\n */\nexport interface RouterOptions<C = unknown> {\n basePath?: string;\n context?: (request: Request, routeName?: string, endpoint?: EndpointDefinition) => C | Promise<C>;\n}\n\n/**\n * Router class that manages contract endpoints\n */\nexport class Router<T extends Contract, C = unknown> {\n private basePath: string;\n private contextFactory?: (\n request: Request,\n routeName: string,\n endpoint: EndpointDefinition,\n ) => C | Promise<C>;\n\n constructor(\n private contract: T,\n private handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n ) {\n // Normalize basePath: ensure it starts with / and doesn't end with /\n const bp = options?.basePath || '';\n if (bp) {\n this.basePath = bp.startsWith('/') ? bp : `/${bp}`;\n this.basePath = this.basePath.endsWith('/') ? this.basePath.slice(0, -1) : this.basePath;\n } else {\n this.basePath = '';\n }\n this.contextFactory = options?.context;\n }\n\n /**\n * Find matching endpoint for a request\n */\n private findEndpoint(\n method: string,\n path: string,\n ): {\n name: keyof T;\n endpoint: EndpointDefinition;\n params: Record<string, string>;\n } | null {\n for (const [name, endpoint] of Object.entries(this.contract)) {\n if (endpoint.method === method) {\n const params = matchPath(endpoint.path, path);\n if (params !== null) {\n return { name, endpoint, params };\n }\n }\n }\n return null;\n }\n\n /**\n * Handle a request\n */\n async handle(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const method = request.method;\n let path = url.pathname;\n\n // Strip basePath if configured\n if (this.basePath && path.startsWith(this.basePath)) {\n path = path.slice(this.basePath.length) || '/';\n }\n\n const match = this.findEndpoint(method, path);\n if (!match) {\n throw new RouteNotFoundError(path, method);\n }\n\n const { name, endpoint, params } = match;\n const handler = this.handlers[name];\n\n // Create context if factory is provided\n const context = this.contextFactory\n ? await this.contextFactory(request, String(name), endpoint)\n : (undefined as C);\n\n // Dispatch based on endpoint type\n if (endpoint.type === 'streaming') {\n // Parse request for streaming endpoint\n const input = await parseStreamingRequest(request, endpoint, params, context);\n return createStreamingResponse((stream) => {\n return (handler as StreamingHandler<StreamingEndpointDefinition, C>)({\n ...input,\n stream,\n });\n });\n }\n\n if (endpoint.type === 'sse') {\n // Parse request for SSE endpoint\n const input = await parseSSERequest(request, endpoint, params, context);\n return createSSEResponse((emitter, signal) => {\n return (handler as SSEHandler<SSEEndpointDefinition, C>)({\n ...input,\n emitter,\n signal,\n });\n });\n }\n\n if (endpoint.type === 'download') {\n // Parse request for download endpoint\n const input = await parseDownloadRequest(request, endpoint, params, context);\n const downloadHandler = handler as unknown as DownloadHandler<DownloadEndpointDefinition, C>;\n const response = await downloadHandler(\n input as DownloadHandlerInput<DownloadEndpointDefinition, C>,\n );\n return createDownloadResponse(\n endpoint as DownloadEndpointDefinition,\n response as DownloadHandlerResponse<DownloadEndpointDefinition>,\n );\n }\n\n // Standard endpoint\n const input = await parseRequest(request, endpoint, params, context);\n const standardHandler = handler as unknown as Handler<StandardEndpointDefinition, C>;\n const handlerResponse = await standardHandler(\n input as HandlerInput<StandardEndpointDefinition, C>,\n );\n return createResponse(\n endpoint as StandardEndpointDefinition,\n handlerResponse as HandlerResponse<StandardEndpointDefinition>,\n );\n }\n\n /**\n * Get fetch handler compatible with Bun.serve\n */\n get fetch() {\n return (request: Request) => this.handle(request);\n }\n}\n\n/**\n * Create a router from a contract and handlers\n */\nexport function createRouter<T extends Contract, C = unknown>(\n contract: T,\n handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n): Router<T, C> {\n return new Router(contract, handlers, options);\n}\n\nexport * from './websocket.cjs';"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "04227CF08860524664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// packages/server/websocket.ts
|
|
31
|
+
var exports_websocket = {};
|
|
32
|
+
__export(exports_websocket, {
|
|
33
|
+
createWebSocketRouter: () => createWebSocketRouter,
|
|
34
|
+
WebSocketValidationError: () => WebSocketValidationError,
|
|
35
|
+
WebSocketRouter: () => WebSocketRouter
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(exports_websocket);
|
|
38
|
+
var import_core = require("@richie-rpc/core");
|
|
39
|
+
|
|
40
|
+
class WebSocketValidationError extends Error {
|
|
41
|
+
messageType;
|
|
42
|
+
issues;
|
|
43
|
+
constructor(messageType, issues) {
|
|
44
|
+
super(`Validation failed for WebSocket message type: ${messageType}`);
|
|
45
|
+
this.messageType = messageType;
|
|
46
|
+
this.issues = issues;
|
|
47
|
+
this.name = "WebSocketValidationError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function createTypedWebSocket(ws) {
|
|
51
|
+
return {
|
|
52
|
+
get raw() {
|
|
53
|
+
return ws;
|
|
54
|
+
},
|
|
55
|
+
send(type, payload) {
|
|
56
|
+
ws.send(JSON.stringify({ type, payload }));
|
|
57
|
+
},
|
|
58
|
+
subscribe(topic) {
|
|
59
|
+
ws.subscribe(topic);
|
|
60
|
+
},
|
|
61
|
+
unsubscribe(topic) {
|
|
62
|
+
ws.unsubscribe(topic);
|
|
63
|
+
},
|
|
64
|
+
publish(topic, type, payload) {
|
|
65
|
+
ws.publish(topic, JSON.stringify({ type, payload }));
|
|
66
|
+
},
|
|
67
|
+
close(code, reason) {
|
|
68
|
+
ws.close(code, reason);
|
|
69
|
+
},
|
|
70
|
+
get data() {
|
|
71
|
+
return ws.data;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class WebSocketRouter {
|
|
77
|
+
contract;
|
|
78
|
+
handlers;
|
|
79
|
+
basePath;
|
|
80
|
+
contextFactory;
|
|
81
|
+
constructor(contract, handlers, options) {
|
|
82
|
+
this.contract = contract;
|
|
83
|
+
this.handlers = handlers;
|
|
84
|
+
const bp = options?.basePath || "";
|
|
85
|
+
if (bp) {
|
|
86
|
+
this.basePath = bp.startsWith("/") ? bp : `/${bp}`;
|
|
87
|
+
this.basePath = this.basePath.endsWith("/") ? this.basePath.slice(0, -1) : this.basePath;
|
|
88
|
+
} else {
|
|
89
|
+
this.basePath = "";
|
|
90
|
+
}
|
|
91
|
+
this.contextFactory = options?.context;
|
|
92
|
+
}
|
|
93
|
+
findEndpoint(path) {
|
|
94
|
+
for (const [name, endpoint] of Object.entries(this.contract)) {
|
|
95
|
+
const params = import_core.matchPath(endpoint.path, path);
|
|
96
|
+
if (params !== null) {
|
|
97
|
+
return {
|
|
98
|
+
name,
|
|
99
|
+
endpoint,
|
|
100
|
+
params
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
parseUpgradeParams(request, endpoint, pathParams) {
|
|
107
|
+
const url = new URL(request.url);
|
|
108
|
+
let params = pathParams;
|
|
109
|
+
if (endpoint.params) {
|
|
110
|
+
const result = endpoint.params.safeParse(pathParams);
|
|
111
|
+
if (!result.success) {
|
|
112
|
+
throw new Error(`Invalid path params: ${result.error.message}`);
|
|
113
|
+
}
|
|
114
|
+
params = result.data;
|
|
115
|
+
}
|
|
116
|
+
let query = {};
|
|
117
|
+
if (endpoint.query) {
|
|
118
|
+
const queryData = import_core.parseQuery(url.searchParams);
|
|
119
|
+
const result = endpoint.query.safeParse(queryData);
|
|
120
|
+
if (!result.success) {
|
|
121
|
+
throw new Error(`Invalid query params: ${result.error.message}`);
|
|
122
|
+
}
|
|
123
|
+
query = result.data;
|
|
124
|
+
}
|
|
125
|
+
let headers = {};
|
|
126
|
+
if (endpoint.headers) {
|
|
127
|
+
const headersObj = {};
|
|
128
|
+
request.headers.forEach((value, key) => {
|
|
129
|
+
headersObj[key] = value;
|
|
130
|
+
});
|
|
131
|
+
const result = endpoint.headers.safeParse(headersObj);
|
|
132
|
+
if (!result.success) {
|
|
133
|
+
throw new Error(`Invalid headers: ${result.error.message}`);
|
|
134
|
+
}
|
|
135
|
+
headers = result.data;
|
|
136
|
+
}
|
|
137
|
+
return { params, query, headers };
|
|
138
|
+
}
|
|
139
|
+
async matchAndPrepareUpgrade(request) {
|
|
140
|
+
const url = new URL(request.url);
|
|
141
|
+
let path = url.pathname;
|
|
142
|
+
if (this.basePath && path.startsWith(this.basePath)) {
|
|
143
|
+
path = path.slice(this.basePath.length) || "/";
|
|
144
|
+
}
|
|
145
|
+
const match = this.findEndpoint(path);
|
|
146
|
+
if (!match) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const { name, endpoint, params: rawParams } = match;
|
|
150
|
+
try {
|
|
151
|
+
const { params, query, headers } = this.parseUpgradeParams(request, endpoint, rawParams);
|
|
152
|
+
const context = this.contextFactory ? await this.contextFactory(request, String(name), endpoint) : undefined;
|
|
153
|
+
return {
|
|
154
|
+
endpointName: String(name),
|
|
155
|
+
endpoint,
|
|
156
|
+
params,
|
|
157
|
+
query,
|
|
158
|
+
headers,
|
|
159
|
+
context,
|
|
160
|
+
state: {}
|
|
161
|
+
};
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error("WebSocket upgrade validation failed:", err);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
validateMessage(endpoint, rawMessage) {
|
|
168
|
+
const messageStr = typeof rawMessage === "string" ? rawMessage : new TextDecoder().decode(rawMessage);
|
|
169
|
+
const parsed = JSON.parse(messageStr);
|
|
170
|
+
const { type, payload } = parsed;
|
|
171
|
+
const messageDef = endpoint.clientMessages[type];
|
|
172
|
+
if (!messageDef) {
|
|
173
|
+
throw new WebSocketValidationError(type, [
|
|
174
|
+
{
|
|
175
|
+
code: "custom",
|
|
176
|
+
path: ["type"],
|
|
177
|
+
message: `Unknown message type: ${type}`
|
|
178
|
+
}
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
const result = messageDef.payload.safeParse(payload);
|
|
182
|
+
if (!result.success) {
|
|
183
|
+
throw new WebSocketValidationError(type, result.error.issues);
|
|
184
|
+
}
|
|
185
|
+
return { type, payload: result.data };
|
|
186
|
+
}
|
|
187
|
+
get websocketHandler() {
|
|
188
|
+
return {
|
|
189
|
+
open: async (ws) => {
|
|
190
|
+
const data = ws.data;
|
|
191
|
+
const endpointHandlers = this.handlers[data.endpointName];
|
|
192
|
+
if (!endpointHandlers)
|
|
193
|
+
return;
|
|
194
|
+
const typedWs = createTypedWebSocket(ws);
|
|
195
|
+
if (endpointHandlers.open) {
|
|
196
|
+
await endpointHandlers.open(typedWs, data.context);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
message: async (ws, message) => {
|
|
200
|
+
const data = ws.data;
|
|
201
|
+
const endpointHandlers = this.handlers[data.endpointName];
|
|
202
|
+
if (!endpointHandlers)
|
|
203
|
+
return;
|
|
204
|
+
const typedWs = createTypedWebSocket(ws);
|
|
205
|
+
try {
|
|
206
|
+
const validatedMessage = this.validateMessage(data.endpoint, typeof message === "string" ? message : message.buffer);
|
|
207
|
+
await endpointHandlers.message(typedWs, validatedMessage, data.context);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
if (err instanceof WebSocketValidationError) {
|
|
210
|
+
if (endpointHandlers.validationError) {
|
|
211
|
+
endpointHandlers.validationError(typedWs, err, data.context);
|
|
212
|
+
} else {
|
|
213
|
+
typedWs.send("error", {
|
|
214
|
+
code: "VALIDATION_ERROR",
|
|
215
|
+
message: err.message,
|
|
216
|
+
issues: err.issues
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
console.error("WebSocket message handler error:", err);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
close: (ws, _code, _reason) => {
|
|
225
|
+
const data = ws.data;
|
|
226
|
+
const endpointHandlers = this.handlers[data.endpointName];
|
|
227
|
+
if (!endpointHandlers)
|
|
228
|
+
return;
|
|
229
|
+
const typedWs = createTypedWebSocket(ws);
|
|
230
|
+
if (endpointHandlers.close) {
|
|
231
|
+
endpointHandlers.close(typedWs, data.context);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
drain: () => {}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function createWebSocketRouter(contract, handlers, options) {
|
|
239
|
+
return new WebSocketRouter(contract, handlers, options);
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
//# debugId=6D55F1711647622B64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../websocket.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n ExtractClientMessage,\n ExtractWSHeaders,\n ExtractWSParams,\n ExtractWSQuery,\n WebSocketContract,\n WebSocketContractDefinition,\n} from '@richie-rpc/core';\nimport { matchPath, parseQuery } from '@richie-rpc/core';\nimport type { z } from 'zod';\n\n/**\n * Validation error for WebSocket messages\n */\nexport class WebSocketValidationError extends Error {\n constructor(\n public messageType: string,\n public issues: z.ZodIssue[],\n ) {\n super(`Validation failed for WebSocket message type: ${messageType}`);\n this.name = 'WebSocketValidationError';\n }\n}\n\n/**\n * Data attached to WebSocket connections for routing\n */\nexport interface WebSocketData<\n T extends WebSocketContractDefinition = WebSocketContractDefinition,\n S = unknown,\n> {\n endpointName: string;\n endpoint: T;\n params: ExtractWSParams<T>;\n query: ExtractWSQuery<T>;\n headers: ExtractWSHeaders<T>;\n context: unknown;\n state: S;\n}\n\n/**\n * Typed WebSocket wrapper for sending messages\n */\nexport interface TypedServerWebSocket<T extends WebSocketContractDefinition, S = unknown> {\n /** The underlying Bun WebSocket */\n readonly raw: WebSocket;\n /** Send a typed message to the client */\n send<K extends keyof T['serverMessages']>(\n type: K,\n payload: z.infer<T['serverMessages'][K]['payload']>,\n ): void;\n /** Subscribe to a topic for pub/sub */\n subscribe(topic: string): void;\n /** Unsubscribe from a topic */\n unsubscribe(topic: string): void;\n /** Publish a message to a topic */\n publish<K extends keyof T['serverMessages']>(\n topic: string,\n type: K,\n payload: z.infer<T['serverMessages'][K]['payload']>,\n ): void;\n /** Close the connection */\n close(code?: number, reason?: string): void;\n /** Connection data */\n readonly data: WebSocketData<T, S>;\n}\n\n/**\n * Handler functions for a WebSocket endpoint\n */\nexport interface WebSocketEndpointHandlers<\n T extends WebSocketContractDefinition,\n C = unknown,\n S = unknown,\n> {\n /** Called when connection opens */\n open?(ws: TypedServerWebSocket<T, S>, ctx: C): void | Promise<void>;\n /** Called for each validated message */\n message(\n ws: TypedServerWebSocket<T, S>,\n message: ExtractClientMessage<T>,\n ctx: C,\n ): void | Promise<void>;\n /** Called when connection closes */\n close?(ws: TypedServerWebSocket<T, S>, ctx: C): void;\n /** Called when message validation fails */\n validationError?(ws: TypedServerWebSocket<T, S>, error: WebSocketValidationError, ctx: C): void;\n}\n\n/**\n * Contract handlers mapping for WebSocket endpoints\n */\nexport type WebSocketContractHandlers<T extends WebSocketContract, C = unknown, S = unknown> = {\n [K in keyof T]: WebSocketEndpointHandlers<T[K], C, S>;\n};\n\n/**\n * Upgrade data returned by matchAndPrepareUpgrade\n */\nexport interface UpgradeData<S = unknown> {\n endpointName: string;\n endpoint: WebSocketContractDefinition;\n params: Record<string, string>;\n query: Record<string, string | string[]>;\n headers: Record<string, string>;\n context: unknown;\n state: S;\n}\n\n/**\n * Options for WebSocket router\n */\nexport interface WebSocketRouterOptions<C = unknown, S = unknown> {\n basePath?: string;\n context?: (\n request: Request,\n endpointName: string,\n endpoint: WebSocketContractDefinition,\n ) => C | Promise<C>;\n /** Type hint for per-connection state. Use `{} as YourStateType` */\n state?: S;\n}\n\n/**\n * Bun WebSocket handler type (subset of Bun's types)\n */\nexport interface BunWebSocketHandler<T = unknown> {\n open(ws: Bun.ServerWebSocket<T>): void | Promise<void>;\n message(ws: Bun.ServerWebSocket<T>, message: string | Buffer<ArrayBuffer>): void | Promise<void>;\n close(ws: Bun.ServerWebSocket<T>, code: number, reason: string): void;\n drain(ws: Bun.ServerWebSocket<T>): void;\n}\n\n/**\n * Create a typed WebSocket wrapper\n */\nfunction createTypedWebSocket<T extends WebSocketContractDefinition, S = unknown>(\n ws: WebSocket & { data: WebSocketData<T, S> },\n): TypedServerWebSocket<T, S> {\n return {\n get raw() {\n return ws;\n },\n send(type, payload) {\n ws.send(JSON.stringify({ type, payload }));\n },\n subscribe(topic) {\n (ws as any).subscribe(topic);\n },\n unsubscribe(topic) {\n (ws as any).unsubscribe(topic);\n },\n publish(topic, type, payload) {\n (ws as any).publish(topic, JSON.stringify({ type, payload }));\n },\n close(code, reason) {\n ws.close(code, reason);\n },\n get data() {\n return ws.data;\n },\n };\n}\n\n/**\n * WebSocket router for managing WebSocket contract endpoints\n */\nexport class WebSocketRouter<T extends WebSocketContract, C = unknown, S = unknown> {\n private basePath: string;\n private contextFactory?: (\n request: Request,\n endpointName: string,\n endpoint: WebSocketContractDefinition,\n ) => C | Promise<C>;\n\n constructor(\n private contract: T,\n private handlers: WebSocketContractHandlers<T, C, S>,\n options?: WebSocketRouterOptions<C, S>,\n ) {\n // Normalize basePath\n const bp = options?.basePath || '';\n if (bp) {\n this.basePath = bp.startsWith('/') ? bp : `/${bp}`;\n this.basePath = this.basePath.endsWith('/') ? this.basePath.slice(0, -1) : this.basePath;\n } else {\n this.basePath = '';\n }\n this.contextFactory = options?.context;\n }\n\n /**\n * Find matching endpoint for a path\n */\n private findEndpoint(path: string): {\n name: keyof T;\n endpoint: WebSocketContractDefinition;\n params: Record<string, string>;\n } | null {\n for (const [name, endpoint] of Object.entries(this.contract)) {\n const params = matchPath(endpoint.path, path);\n if (params !== null) {\n return {\n name,\n endpoint: endpoint as WebSocketContractDefinition,\n params,\n };\n }\n }\n return null;\n }\n\n /**\n * Parse and validate upgrade request parameters\n */\n private parseUpgradeParams(\n request: Request,\n endpoint: WebSocketContractDefinition,\n pathParams: Record<string, string>,\n ): { params: any; query: any; headers: any } {\n const url = new URL(request.url);\n\n // Parse and validate path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new Error(`Invalid path params: ${result.error.message}`);\n }\n params = result.data;\n }\n\n // Parse and validate query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new Error(`Invalid query params: ${result.error.message}`);\n }\n query = result.data;\n }\n\n // Parse and validate headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new Error(`Invalid headers: ${result.error.message}`);\n }\n headers = result.data;\n }\n\n return { params, query, headers };\n }\n\n /**\n * Match a request and prepare upgrade data\n * Returns null if no match, or UpgradeData for server.upgrade()\n */\n async matchAndPrepareUpgrade(request: Request): Promise<UpgradeData<S> | null> {\n const url = new URL(request.url);\n let path = url.pathname;\n\n // Strip basePath if configured\n if (this.basePath && path.startsWith(this.basePath)) {\n path = path.slice(this.basePath.length) || '/';\n }\n\n const match = this.findEndpoint(path);\n if (!match) {\n return null;\n }\n\n const { name, endpoint, params: rawParams } = match;\n\n try {\n const { params, query, headers } = this.parseUpgradeParams(request, endpoint, rawParams);\n\n // Create context if factory provided\n const context = this.contextFactory\n ? await this.contextFactory(request, String(name), endpoint)\n : (undefined as C);\n\n return {\n endpointName: String(name),\n endpoint,\n params,\n query,\n headers,\n context,\n state: {} as S,\n };\n } catch (err) {\n // Validation failed during upgrade\n console.error('WebSocket upgrade validation failed:', err);\n return null;\n }\n }\n\n /**\n * Validate an incoming client message\n */\n private validateMessage(\n endpoint: WebSocketContractDefinition,\n rawMessage: string | ArrayBuffer,\n ): ExtractClientMessage<typeof endpoint> {\n // Parse message\n const messageStr =\n typeof rawMessage === 'string' ? rawMessage : new TextDecoder().decode(rawMessage);\n const parsed = JSON.parse(messageStr) as { type: string; payload: unknown };\n\n const { type, payload } = parsed;\n\n // Find the schema for this message type\n const messageDef = endpoint.clientMessages[type];\n if (!messageDef) {\n throw new WebSocketValidationError(type, [\n {\n code: 'custom',\n path: ['type'],\n message: `Unknown message type: ${type}`,\n },\n ]);\n }\n\n // Validate payload\n const result = messageDef.payload.safeParse(payload);\n if (!result.success) {\n throw new WebSocketValidationError(type, result.error.issues);\n }\n\n return { type, payload: result.data } as ExtractClientMessage<typeof endpoint>;\n }\n\n /**\n * Get Bun-compatible WebSocket handler\n */\n get websocketHandler(): BunWebSocketHandler<UpgradeData<S>> {\n return {\n open: async (ws) => {\n const data = ws.data;\n const endpointHandlers = this.handlers[data.endpointName as keyof T];\n if (!endpointHandlers) return;\n\n // Create typed wrapper with properly typed data\n const typedWs = createTypedWebSocket(\n ws as unknown as WebSocket & {\n data: WebSocketData<WebSocketContractDefinition, S>;\n },\n );\n\n if (endpointHandlers.open) {\n await endpointHandlers.open(typedWs as any, data.context as C);\n }\n },\n\n message: async (ws, message) => {\n const data = ws.data;\n const endpointHandlers = this.handlers[data.endpointName as keyof T];\n if (!endpointHandlers) return;\n\n const typedWs = createTypedWebSocket(\n ws as unknown as WebSocket & {\n data: WebSocketData<WebSocketContractDefinition, S>;\n },\n );\n\n try {\n // Validate the message\n const validatedMessage = this.validateMessage(\n data.endpoint,\n typeof message === 'string' ? message : message.buffer,\n );\n\n // Call handler with validated message\n await endpointHandlers.message(\n typedWs as any,\n validatedMessage as any,\n data.context as C,\n );\n } catch (err) {\n if (err instanceof WebSocketValidationError) {\n // Call validation error handler if provided\n if (endpointHandlers.validationError) {\n endpointHandlers.validationError(typedWs as any, err, data.context as C);\n } else {\n // Default: send error message back\n typedWs.send(\n 'error' as any,\n {\n code: 'VALIDATION_ERROR',\n message: err.message,\n issues: err.issues,\n } as any,\n );\n }\n } else {\n console.error('WebSocket message handler error:', err);\n }\n }\n },\n\n close: (ws, _code, _reason) => {\n const data = ws.data;\n const endpointHandlers = this.handlers[data.endpointName as keyof T];\n if (!endpointHandlers) return;\n\n const typedWs = createTypedWebSocket(\n ws as unknown as WebSocket & {\n data: WebSocketData<WebSocketContractDefinition, S>;\n },\n );\n\n if (endpointHandlers.close) {\n endpointHandlers.close(typedWs as any, data.context as C);\n }\n },\n drain: () => {\n // not used\n },\n };\n }\n}\n\n/**\n * Create a WebSocket router from a contract and handlers\n */\nexport function createWebSocketRouter<T extends WebSocketContract, C = unknown, S = unknown>(\n contract: T,\n handlers: WebSocketContractHandlers<T, C, S>,\n options?: WebSocketRouterOptions<C, S>,\n): WebSocketRouter<T, C, S> {\n return new WebSocketRouter(contract, handlers, options);\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "6D55F1711647622B64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/mjs/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/server/index.ts
|
|
3
3
|
import { formDataToObject, matchPath, parseQuery, Status } from "@richie-rpc/core";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
export * from "./websocket.mjs";
|
|
5
6
|
class ValidationError extends Error {
|
|
6
7
|
field;
|
|
7
8
|
issues;
|
|
@@ -412,7 +413,6 @@ function createRouter(contract, handlers, options) {
|
|
|
412
413
|
return new Router(contract, handlers, options);
|
|
413
414
|
}
|
|
414
415
|
export {
|
|
415
|
-
createWebSocketRouter,
|
|
416
416
|
createRouter,
|
|
417
417
|
ValidationError,
|
|
418
418
|
Status,
|
|
@@ -420,4 +420,4 @@ export {
|
|
|
420
420
|
RouteNotFoundError
|
|
421
421
|
};
|
|
422
422
|
|
|
423
|
-
//# debugId=
|
|
423
|
+
//# debugId=DA8C8B41358461A564756E2164756E21
|
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type {\n Contract,\n DownloadEndpointDefinition,\n EndpointDefinition,\n ExtractBody,\n ExtractChunk,\n ExtractFinalResponse,\n ExtractHeaders,\n ExtractParams,\n ExtractQuery,\n ExtractSSEEventData,\n SSEEndpointDefinition,\n StandardEndpointDefinition,\n StreamingEndpointDefinition,\n} from '@richie-rpc/core';\nimport { formDataToObject, matchPath, parseQuery, Status } from '@richie-rpc/core';\nimport type { z } from 'zod';\n\n// Re-export Status for convenience\nexport { Status };\n\n// Handler input types (for standard endpoints)\nexport type HandlerInput<T extends StandardEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n};\n\n// Handler response type (for standard endpoints)\nexport type HandlerResponse<T extends StandardEndpointDefinition> = {\n [Status in keyof T['responses']]: {\n status: Status;\n body: T['responses'][Status] extends z.ZodTypeAny ? z.infer<T['responses'][Status]> : never;\n headers?: Record<string, string>;\n };\n}[keyof T['responses']];\n\n// Handler function type (for standard endpoints)\nexport type Handler<T extends StandardEndpointDefinition, C = unknown> = (\n input: HandlerInput<T, C>,\n) => Promise<HandlerResponse<T>> | HandlerResponse<T>;\n\n// ============================================\n// Streaming Endpoint Types\n// ============================================\n\n/**\n * Emitter for streaming responses - push-based API\n */\nexport interface StreamEmitter<T extends StreamingEndpointDefinition> {\n /** Send a chunk to the client */\n send(chunk: ExtractChunk<T>): void;\n /** Close the stream with optional final response */\n close(final?: ExtractFinalResponse<T>): void;\n /** Check if stream is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for streaming endpoints\n */\nexport type StreamingHandlerInput<T extends StreamingEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n stream: StreamEmitter<T>;\n};\n\n/**\n * Handler function type for streaming endpoints\n */\nexport type StreamingHandler<T extends StreamingEndpointDefinition, C = unknown> = (\n input: StreamingHandlerInput<T, C>,\n) => void | Promise<void>;\n\n// ============================================\n// SSE Endpoint Types\n// ============================================\n\n/**\n * Emitter for SSE responses\n */\nexport interface SSEEmitter<T extends SSEEndpointDefinition> {\n /** Send an event to the client */\n send<K extends keyof T['events']>(\n event: K,\n data: ExtractSSEEventData<T, K>,\n options?: { id?: string },\n ): void;\n /** Close the connection */\n close(): void;\n /** Check if connection is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for SSE endpoints\n */\nexport type SSEHandlerInput<T extends SSEEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n emitter: SSEEmitter<T>;\n /** AbortSignal for detecting client disconnect */\n signal: AbortSignal;\n};\n\n/**\n * Handler function type for SSE endpoints\n * Returns an optional cleanup function\n */\nexport type SSEHandler<T extends SSEEndpointDefinition, C = unknown> = (\n input: SSEHandlerInput<T, C>,\n) => void | (() => void) | Promise<void | (() => void)>;\n\n// ============================================\n// Download Endpoint Types\n// ============================================\n\n/**\n * Handler input for download endpoints\n */\nexport type DownloadHandlerInput<T extends DownloadEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n};\n\n/**\n * Handler response for download endpoints\n * Success (200) returns File, errors return typed response\n */\nexport type DownloadHandlerResponse<T extends DownloadEndpointDefinition> =\n | { status: 200; body: File; headers?: Record<string, string> }\n | (T['errorResponses'] extends Record<number, z.ZodTypeAny>\n ? {\n [S in keyof T['errorResponses']]: {\n status: S;\n body: T['errorResponses'][S] extends z.ZodTypeAny\n ? z.infer<T['errorResponses'][S]>\n : never;\n headers?: Record<string, string>;\n };\n }[keyof T['errorResponses']]\n : never);\n\n/**\n * Handler function type for download endpoints\n */\nexport type DownloadHandler<T extends DownloadEndpointDefinition, C = unknown> = (\n input: DownloadHandlerInput<T, C>,\n) => Promise<DownloadHandlerResponse<T>> | DownloadHandlerResponse<T>;\n\n// ============================================\n// Contract Handlers (supports all endpoint types)\n// ============================================\n\n/**\n * Contract handlers mapping - conditionally applies handler type based on endpoint type\n */\nexport type ContractHandlers<T extends Contract, C = unknown> = {\n [K in keyof T]: T[K] extends StandardEndpointDefinition\n ? Handler<T[K], C>\n : T[K] extends StreamingEndpointDefinition\n ? StreamingHandler<T[K], C>\n : T[K] extends SSEEndpointDefinition\n ? SSEHandler<T[K], C>\n : T[K] extends DownloadEndpointDefinition\n ? DownloadHandler<T[K], C>\n : never;\n};\n\n// Error classes\nexport class ValidationError extends Error {\n constructor(\n public field: string,\n public issues: z.ZodIssue[],\n message?: string,\n ) {\n super(message || `Validation failed for ${field}`);\n this.name = 'ValidationError';\n }\n}\n\nexport class RouteNotFoundError extends Error {\n constructor(\n public path: string,\n public method: string,\n ) {\n super(`Route not found: ${method} ${path}`);\n this.name = 'RouteNotFoundError';\n }\n}\n\n/**\n * Parse and validate request data\n */\nasync function parseRequest<T extends StandardEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<HandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as HandlerInput<T, C>;\n}\n\n/**\n * Parse and validate request data for streaming endpoints\n */\nasync function parseStreamingRequest<T extends StreamingEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<StreamingHandlerInput<T, C>, 'stream'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as Omit<\n StreamingHandlerInput<T, C>,\n 'stream'\n >;\n}\n\n/**\n * Parse and validate request data for SSE endpoints\n */\nasync function parseSSERequest<T extends SSEEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<SSEHandlerInput<T, C>, 'emitter' | 'signal'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // SSE endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as Omit<\n SSEHandlerInput<T, C>,\n 'emitter' | 'signal'\n >;\n}\n\n/**\n * Parse and validate request data for download endpoints\n */\nasync function parseDownloadRequest<T extends DownloadEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<DownloadHandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Download endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as DownloadHandlerInput<T, C>;\n}\n\n/**\n * Validate and create response\n */\nfunction createResponse<T extends StandardEndpointDefinition>(\n endpoint: T,\n handlerResponse: HandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n\n // Validate response body\n const responseSchema = endpoint.responses[status as keyof typeof endpoint.responses];\n if (responseSchema) {\n const result = responseSchema.safeParse(body);\n if (!result.success) {\n throw new ValidationError(`response[${String(status)}]`, result.error.issues);\n }\n }\n\n // Create response headers\n const responseHeaders = new Headers(customHeaders);\n\n // Handle 204 No Content - must have no body\n if (status === 204) {\n return new Response(null, {\n status: 204,\n headers: responseHeaders,\n });\n }\n\n // For all other responses, return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create response for download endpoints\n */\nfunction createDownloadResponse<T extends DownloadEndpointDefinition>(\n _endpoint: T,\n handlerResponse: DownloadHandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n const responseHeaders = new Headers(customHeaders);\n\n // Success: return File as binary\n if (status === 200 && body instanceof File) {\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', body.type || 'application/octet-stream');\n }\n responseHeaders.set('content-length', String(body.size));\n if (!responseHeaders.has('content-disposition')) {\n const filename = encodeURIComponent(body.name);\n responseHeaders.set(\n 'content-disposition',\n `attachment; filename=\"${filename}\"; filename*=UTF-8''${filename}`,\n );\n }\n return new Response(body, { status: 200, headers: responseHeaders });\n }\n\n // Error: return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create a streaming NDJSON response\n */\nfunction createStreamingResponse<T extends StreamingEndpointDefinition>(\n handler: (stream: StreamEmitter<T>) => void | Promise<void>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let closed = false;\n\n const stream: StreamEmitter<T> = {\n send(chunk) {\n if (!closed) {\n writer.write(encoder.encode(`${JSON.stringify(chunk)}\\n`));\n }\n },\n close(final) {\n if (!closed) {\n closed = true;\n if (final !== undefined) {\n writer.write(encoder.encode(`${JSON.stringify({ __final__: true, data: final })}\\n`));\n }\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler (don't await - let it run in background)\n Promise.resolve(handler(stream)).catch((err) => {\n console.error('Streaming handler error:', err);\n stream.close();\n });\n\n return new Response(readable, {\n headers: {\n 'Content-Type': 'application/x-ndjson',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Create an SSE response\n */\nfunction createSSEResponse<T extends SSEEndpointDefinition>(\n handler: (\n emitter: SSEEmitter<T>,\n signal: AbortSignal,\n ) => void | (() => void) | Promise<void | (() => void)>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n const controller = new AbortController();\n let closed = false;\n let cleanup: (() => void) | undefined;\n\n const emitter: SSEEmitter<T> = {\n send(event, data, options) {\n if (!closed) {\n let message = '';\n if (options?.id) {\n message += `id: ${options.id}\\n`;\n }\n message += `event: ${String(event)}\\n`;\n message += `data: ${JSON.stringify(data)}\\n\\n`;\n writer.write(encoder.encode(message));\n }\n },\n close() {\n if (!closed) {\n closed = true;\n if (cleanup) cleanup();\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler and get cleanup function\n Promise.resolve(handler(emitter, controller.signal))\n .then((cleanupFn) => {\n if (typeof cleanupFn === 'function') {\n cleanup = cleanupFn;\n }\n })\n .catch((err) => {\n console.error('SSE handler error:', err);\n emitter.close();\n });\n\n // Tee the stream - one for the response, one for disconnect detection\n const [responseStream, detectStream] = readable.tee();\n\n // Handle client disconnect by detecting when the stream is cancelled\n detectStream.pipeTo(new WritableStream()).catch(() => {\n controller.abort();\n emitter.close();\n });\n\n return new Response(responseStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Router configuration options\n */\nexport interface RouterOptions<C = unknown> {\n basePath?: string;\n context?: (request: Request, routeName?: string, endpoint?: EndpointDefinition) => C | Promise<C>;\n}\n\n/**\n * Router class that manages contract endpoints\n */\nexport class Router<T extends Contract, C = unknown> {\n private basePath: string;\n private contextFactory?: (\n request: Request,\n routeName: string,\n endpoint: EndpointDefinition,\n ) => C | Promise<C>;\n\n constructor(\n private contract: T,\n private handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n ) {\n // Normalize basePath: ensure it starts with / and doesn't end with /\n const bp = options?.basePath || '';\n if (bp) {\n this.basePath = bp.startsWith('/') ? bp : `/${bp}`;\n this.basePath = this.basePath.endsWith('/') ? this.basePath.slice(0, -1) : this.basePath;\n } else {\n this.basePath = '';\n }\n this.contextFactory = options?.context;\n }\n\n /**\n * Find matching endpoint for a request\n */\n private findEndpoint(\n method: string,\n path: string,\n ): {\n name: keyof T;\n endpoint: EndpointDefinition;\n params: Record<string, string>;\n } | null {\n for (const [name, endpoint] of Object.entries(this.contract)) {\n if (endpoint.method === method) {\n const params = matchPath(endpoint.path, path);\n if (params !== null) {\n return { name, endpoint, params };\n }\n }\n }\n return null;\n }\n\n /**\n * Handle a request\n */\n async handle(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const method = request.method;\n let path = url.pathname;\n\n // Strip basePath if configured\n if (this.basePath && path.startsWith(this.basePath)) {\n path = path.slice(this.basePath.length) || '/';\n }\n\n const match = this.findEndpoint(method, path);\n if (!match) {\n throw new RouteNotFoundError(path, method);\n }\n\n const { name, endpoint, params } = match;\n const handler = this.handlers[name];\n\n // Create context if factory is provided\n const context = this.contextFactory\n ? await this.contextFactory(request, String(name), endpoint)\n : (undefined as C);\n\n // Dispatch based on endpoint type\n if (endpoint.type === 'streaming') {\n // Parse request for streaming endpoint\n const input = await parseStreamingRequest(request, endpoint, params, context);\n return createStreamingResponse((stream) => {\n return (handler as StreamingHandler<StreamingEndpointDefinition, C>)({\n ...input,\n stream,\n });\n });\n }\n\n if (endpoint.type === 'sse') {\n // Parse request for SSE endpoint\n const input = await parseSSERequest(request, endpoint, params, context);\n return createSSEResponse((emitter, signal) => {\n return (handler as SSEHandler<SSEEndpointDefinition, C>)({\n ...input,\n emitter,\n signal,\n });\n });\n }\n\n if (endpoint.type === 'download') {\n // Parse request for download endpoint\n const input = await parseDownloadRequest(request, endpoint, params, context);\n const downloadHandler = handler as unknown as DownloadHandler<DownloadEndpointDefinition, C>;\n const response = await downloadHandler(\n input as DownloadHandlerInput<DownloadEndpointDefinition, C>,\n );\n return createDownloadResponse(\n endpoint as DownloadEndpointDefinition,\n response as DownloadHandlerResponse<DownloadEndpointDefinition>,\n );\n }\n\n // Standard endpoint\n const input = await parseRequest(request, endpoint, params, context);\n const standardHandler = handler as unknown as Handler<StandardEndpointDefinition, C>;\n const handlerResponse = await standardHandler(\n input as HandlerInput<StandardEndpointDefinition, C>,\n );\n return createResponse(\n endpoint as StandardEndpointDefinition,\n handlerResponse as HandlerResponse<StandardEndpointDefinition>,\n );\n }\n\n /**\n * Get fetch handler compatible with Bun.serve\n */\n get fetch() {\n return (request: Request) => this.handle(request);\n }\n}\n\n/**\n * Create a router from a contract and handlers\n */\nexport function createRouter<T extends Contract, C = unknown>(\n contract: T,\n handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n): Router<T, C> {\n return new Router(contract, handlers, options);\n}\n\nexport { createWebSocketRouter } from './websocket.mjs';"
|
|
5
|
+
"import type {\n Contract,\n DownloadEndpointDefinition,\n EndpointDefinition,\n ExtractBody,\n ExtractChunk,\n ExtractFinalResponse,\n ExtractHeaders,\n ExtractParams,\n ExtractQuery,\n ExtractSSEEventData,\n SSEEndpointDefinition,\n StandardEndpointDefinition,\n StreamingEndpointDefinition,\n} from '@richie-rpc/core';\nimport { formDataToObject, matchPath, parseQuery, Status } from '@richie-rpc/core';\nimport type { z } from 'zod';\n\n// Re-export Status for convenience\nexport { Status };\n\n// Handler input types (for standard endpoints)\nexport type HandlerInput<T extends StandardEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n};\n\n// Handler response type (for standard endpoints)\nexport type HandlerResponse<T extends StandardEndpointDefinition> = {\n [Status in keyof T['responses']]: {\n status: Status;\n body: T['responses'][Status] extends z.ZodTypeAny ? z.infer<T['responses'][Status]> : never;\n headers?: Record<string, string>;\n };\n}[keyof T['responses']];\n\n// Handler function type (for standard endpoints)\nexport type Handler<T extends StandardEndpointDefinition, C = unknown> = (\n input: HandlerInput<T, C>,\n) => Promise<HandlerResponse<T>> | HandlerResponse<T>;\n\n// ============================================\n// Streaming Endpoint Types\n// ============================================\n\n/**\n * Emitter for streaming responses - push-based API\n */\nexport interface StreamEmitter<T extends StreamingEndpointDefinition> {\n /** Send a chunk to the client */\n send(chunk: ExtractChunk<T>): void;\n /** Close the stream with optional final response */\n close(final?: ExtractFinalResponse<T>): void;\n /** Check if stream is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for streaming endpoints\n */\nexport type StreamingHandlerInput<T extends StreamingEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n body: ExtractBody<T>;\n request: Request;\n context: C;\n stream: StreamEmitter<T>;\n};\n\n/**\n * Handler function type for streaming endpoints\n */\nexport type StreamingHandler<T extends StreamingEndpointDefinition, C = unknown> = (\n input: StreamingHandlerInput<T, C>,\n) => void | Promise<void>;\n\n// ============================================\n// SSE Endpoint Types\n// ============================================\n\n/**\n * Emitter for SSE responses\n */\nexport interface SSEEmitter<T extends SSEEndpointDefinition> {\n /** Send an event to the client */\n send<K extends keyof T['events']>(\n event: K,\n data: ExtractSSEEventData<T, K>,\n options?: { id?: string },\n ): void;\n /** Close the connection */\n close(): void;\n /** Check if connection is still open */\n readonly isOpen: boolean;\n}\n\n/**\n * Handler input for SSE endpoints\n */\nexport type SSEHandlerInput<T extends SSEEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n emitter: SSEEmitter<T>;\n /** AbortSignal for detecting client disconnect */\n signal: AbortSignal;\n};\n\n/**\n * Handler function type for SSE endpoints\n * Returns an optional cleanup function\n */\nexport type SSEHandler<T extends SSEEndpointDefinition, C = unknown> = (\n input: SSEHandlerInput<T, C>,\n) => void | (() => void) | Promise<void | (() => void)>;\n\n// ============================================\n// Download Endpoint Types\n// ============================================\n\n/**\n * Handler input for download endpoints\n */\nexport type DownloadHandlerInput<T extends DownloadEndpointDefinition, C = unknown> = {\n params: ExtractParams<T>;\n query: ExtractQuery<T>;\n headers: ExtractHeaders<T>;\n request: Request;\n context: C;\n};\n\n/**\n * Handler response for download endpoints\n * Success (200) returns File, errors return typed response\n */\nexport type DownloadHandlerResponse<T extends DownloadEndpointDefinition> =\n | { status: 200; body: File; headers?: Record<string, string> }\n | (T['errorResponses'] extends Record<number, z.ZodTypeAny>\n ? {\n [S in keyof T['errorResponses']]: {\n status: S;\n body: T['errorResponses'][S] extends z.ZodTypeAny\n ? z.infer<T['errorResponses'][S]>\n : never;\n headers?: Record<string, string>;\n };\n }[keyof T['errorResponses']]\n : never);\n\n/**\n * Handler function type for download endpoints\n */\nexport type DownloadHandler<T extends DownloadEndpointDefinition, C = unknown> = (\n input: DownloadHandlerInput<T, C>,\n) => Promise<DownloadHandlerResponse<T>> | DownloadHandlerResponse<T>;\n\n// ============================================\n// Contract Handlers (supports all endpoint types)\n// ============================================\n\n/**\n * Contract handlers mapping - conditionally applies handler type based on endpoint type\n */\nexport type ContractHandlers<T extends Contract, C = unknown> = {\n [K in keyof T]: T[K] extends StandardEndpointDefinition\n ? Handler<T[K], C>\n : T[K] extends StreamingEndpointDefinition\n ? StreamingHandler<T[K], C>\n : T[K] extends SSEEndpointDefinition\n ? SSEHandler<T[K], C>\n : T[K] extends DownloadEndpointDefinition\n ? DownloadHandler<T[K], C>\n : never;\n};\n\n// Error classes\nexport class ValidationError extends Error {\n constructor(\n public field: string,\n public issues: z.ZodIssue[],\n message?: string,\n ) {\n super(message || `Validation failed for ${field}`);\n this.name = 'ValidationError';\n }\n}\n\nexport class RouteNotFoundError extends Error {\n constructor(\n public path: string,\n public method: string,\n ) {\n super(`Route not found: ${method} ${path}`);\n this.name = 'RouteNotFoundError';\n }\n}\n\n/**\n * Parse and validate request data\n */\nasync function parseRequest<T extends StandardEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<HandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as HandlerInput<T, C>;\n}\n\n/**\n * Parse and validate request data for streaming endpoints\n */\nasync function parseStreamingRequest<T extends StreamingEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<StreamingHandlerInput<T, C>, 'stream'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Parse body\n let body: any;\n if (endpoint.body) {\n const contentType = request.headers.get('content-type') || '';\n let bodyData: any;\n\n if (contentType.includes('application/json')) {\n bodyData = await request.json();\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n bodyData = formDataToObject(formData as FormData);\n } else {\n bodyData = await request.text();\n }\n\n const result = endpoint.body.safeParse(bodyData);\n if (!result.success) {\n throw new ValidationError('body', result.error.issues);\n }\n body = result.data;\n }\n\n return { params, query, headers, body, request, context } as Omit<\n StreamingHandlerInput<T, C>,\n 'stream'\n >;\n}\n\n/**\n * Parse and validate request data for SSE endpoints\n */\nasync function parseSSERequest<T extends SSEEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<Omit<SSEHandlerInput<T, C>, 'emitter' | 'signal'>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // SSE endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as Omit<\n SSEHandlerInput<T, C>,\n 'emitter' | 'signal'\n >;\n}\n\n/**\n * Parse and validate request data for download endpoints\n */\nasync function parseDownloadRequest<T extends DownloadEndpointDefinition, C = unknown>(\n request: Request,\n endpoint: T,\n pathParams: Record<string, string>,\n context: C,\n): Promise<DownloadHandlerInput<T, C>> {\n const url = new URL(request.url);\n\n // Parse path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new ValidationError('params', result.error.issues);\n }\n params = result.data;\n }\n\n // Parse query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new ValidationError('query', result.error.issues);\n }\n query = result.data;\n }\n\n // Parse headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new ValidationError('headers', result.error.issues);\n }\n headers = result.data;\n }\n\n // Download endpoints don't have a body (GET only)\n return { params, query, headers, request, context } as DownloadHandlerInput<T, C>;\n}\n\n/**\n * Validate and create response\n */\nfunction createResponse<T extends StandardEndpointDefinition>(\n endpoint: T,\n handlerResponse: HandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n\n // Validate response body\n const responseSchema = endpoint.responses[status as keyof typeof endpoint.responses];\n if (responseSchema) {\n const result = responseSchema.safeParse(body);\n if (!result.success) {\n throw new ValidationError(`response[${String(status)}]`, result.error.issues);\n }\n }\n\n // Create response headers\n const responseHeaders = new Headers(customHeaders);\n\n // Handle 204 No Content - must have no body\n if (status === 204) {\n return new Response(null, {\n status: 204,\n headers: responseHeaders,\n });\n }\n\n // For all other responses, return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create response for download endpoints\n */\nfunction createDownloadResponse<T extends DownloadEndpointDefinition>(\n _endpoint: T,\n handlerResponse: DownloadHandlerResponse<T>,\n): Response {\n const { status, body, headers: customHeaders } = handlerResponse;\n const responseHeaders = new Headers(customHeaders);\n\n // Success: return File as binary\n if (status === 200 && body instanceof File) {\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', body.type || 'application/octet-stream');\n }\n responseHeaders.set('content-length', String(body.size));\n if (!responseHeaders.has('content-disposition')) {\n const filename = encodeURIComponent(body.name);\n responseHeaders.set(\n 'content-disposition',\n `attachment; filename=\"${filename}\"; filename*=UTF-8''${filename}`,\n );\n }\n return new Response(body, { status: 200, headers: responseHeaders });\n }\n\n // Error: return JSON\n if (!responseHeaders.has('content-type')) {\n responseHeaders.set('content-type', 'application/json');\n }\n return new Response(JSON.stringify(body), {\n status: status as number,\n headers: responseHeaders,\n });\n}\n\n/**\n * Create a streaming NDJSON response\n */\nfunction createStreamingResponse<T extends StreamingEndpointDefinition>(\n handler: (stream: StreamEmitter<T>) => void | Promise<void>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let closed = false;\n\n const stream: StreamEmitter<T> = {\n send(chunk) {\n if (!closed) {\n writer.write(encoder.encode(`${JSON.stringify(chunk)}\\n`));\n }\n },\n close(final) {\n if (!closed) {\n closed = true;\n if (final !== undefined) {\n writer.write(encoder.encode(`${JSON.stringify({ __final__: true, data: final })}\\n`));\n }\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler (don't await - let it run in background)\n Promise.resolve(handler(stream)).catch((err) => {\n console.error('Streaming handler error:', err);\n stream.close();\n });\n\n return new Response(readable, {\n headers: {\n 'Content-Type': 'application/x-ndjson',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Create an SSE response\n */\nfunction createSSEResponse<T extends SSEEndpointDefinition>(\n handler: (\n emitter: SSEEmitter<T>,\n signal: AbortSignal,\n ) => void | (() => void) | Promise<void | (() => void)>,\n): Response {\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n const controller = new AbortController();\n let closed = false;\n let cleanup: (() => void) | undefined;\n\n const emitter: SSEEmitter<T> = {\n send(event, data, options) {\n if (!closed) {\n let message = '';\n if (options?.id) {\n message += `id: ${options.id}\\n`;\n }\n message += `event: ${String(event)}\\n`;\n message += `data: ${JSON.stringify(data)}\\n\\n`;\n writer.write(encoder.encode(message));\n }\n },\n close() {\n if (!closed) {\n closed = true;\n if (cleanup) cleanup();\n writer.close();\n }\n },\n get isOpen() {\n return !closed;\n },\n };\n\n // Execute handler and get cleanup function\n Promise.resolve(handler(emitter, controller.signal))\n .then((cleanupFn) => {\n if (typeof cleanupFn === 'function') {\n cleanup = cleanupFn;\n }\n })\n .catch((err) => {\n console.error('SSE handler error:', err);\n emitter.close();\n });\n\n // Tee the stream - one for the response, one for disconnect detection\n const [responseStream, detectStream] = readable.tee();\n\n // Handle client disconnect by detecting when the stream is cancelled\n detectStream.pipeTo(new WritableStream()).catch(() => {\n controller.abort();\n emitter.close();\n });\n\n return new Response(responseStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Router configuration options\n */\nexport interface RouterOptions<C = unknown> {\n basePath?: string;\n context?: (request: Request, routeName?: string, endpoint?: EndpointDefinition) => C | Promise<C>;\n}\n\n/**\n * Router class that manages contract endpoints\n */\nexport class Router<T extends Contract, C = unknown> {\n private basePath: string;\n private contextFactory?: (\n request: Request,\n routeName: string,\n endpoint: EndpointDefinition,\n ) => C | Promise<C>;\n\n constructor(\n private contract: T,\n private handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n ) {\n // Normalize basePath: ensure it starts with / and doesn't end with /\n const bp = options?.basePath || '';\n if (bp) {\n this.basePath = bp.startsWith('/') ? bp : `/${bp}`;\n this.basePath = this.basePath.endsWith('/') ? this.basePath.slice(0, -1) : this.basePath;\n } else {\n this.basePath = '';\n }\n this.contextFactory = options?.context;\n }\n\n /**\n * Find matching endpoint for a request\n */\n private findEndpoint(\n method: string,\n path: string,\n ): {\n name: keyof T;\n endpoint: EndpointDefinition;\n params: Record<string, string>;\n } | null {\n for (const [name, endpoint] of Object.entries(this.contract)) {\n if (endpoint.method === method) {\n const params = matchPath(endpoint.path, path);\n if (params !== null) {\n return { name, endpoint, params };\n }\n }\n }\n return null;\n }\n\n /**\n * Handle a request\n */\n async handle(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const method = request.method;\n let path = url.pathname;\n\n // Strip basePath if configured\n if (this.basePath && path.startsWith(this.basePath)) {\n path = path.slice(this.basePath.length) || '/';\n }\n\n const match = this.findEndpoint(method, path);\n if (!match) {\n throw new RouteNotFoundError(path, method);\n }\n\n const { name, endpoint, params } = match;\n const handler = this.handlers[name];\n\n // Create context if factory is provided\n const context = this.contextFactory\n ? await this.contextFactory(request, String(name), endpoint)\n : (undefined as C);\n\n // Dispatch based on endpoint type\n if (endpoint.type === 'streaming') {\n // Parse request for streaming endpoint\n const input = await parseStreamingRequest(request, endpoint, params, context);\n return createStreamingResponse((stream) => {\n return (handler as StreamingHandler<StreamingEndpointDefinition, C>)({\n ...input,\n stream,\n });\n });\n }\n\n if (endpoint.type === 'sse') {\n // Parse request for SSE endpoint\n const input = await parseSSERequest(request, endpoint, params, context);\n return createSSEResponse((emitter, signal) => {\n return (handler as SSEHandler<SSEEndpointDefinition, C>)({\n ...input,\n emitter,\n signal,\n });\n });\n }\n\n if (endpoint.type === 'download') {\n // Parse request for download endpoint\n const input = await parseDownloadRequest(request, endpoint, params, context);\n const downloadHandler = handler as unknown as DownloadHandler<DownloadEndpointDefinition, C>;\n const response = await downloadHandler(\n input as DownloadHandlerInput<DownloadEndpointDefinition, C>,\n );\n return createDownloadResponse(\n endpoint as DownloadEndpointDefinition,\n response as DownloadHandlerResponse<DownloadEndpointDefinition>,\n );\n }\n\n // Standard endpoint\n const input = await parseRequest(request, endpoint, params, context);\n const standardHandler = handler as unknown as Handler<StandardEndpointDefinition, C>;\n const handlerResponse = await standardHandler(\n input as HandlerInput<StandardEndpointDefinition, C>,\n );\n return createResponse(\n endpoint as StandardEndpointDefinition,\n handlerResponse as HandlerResponse<StandardEndpointDefinition>,\n );\n }\n\n /**\n * Get fetch handler compatible with Bun.serve\n */\n get fetch() {\n return (request: Request) => this.handle(request);\n }\n}\n\n/**\n * Create a router from a contract and handlers\n */\nexport function createRouter<T extends Contract, C = unknown>(\n contract: T,\n handlers: ContractHandlers<T, C>,\n options?: RouterOptions<C>,\n): Router<T, C> {\n return new Router(contract, handlers, options);\n}\n\nexport * from './websocket.mjs';"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "DA8C8B41358461A564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/server/websocket.ts
|
|
3
|
+
import { matchPath, parseQuery } from "@richie-rpc/core";
|
|
4
|
+
|
|
5
|
+
class WebSocketValidationError extends Error {
|
|
6
|
+
messageType;
|
|
7
|
+
issues;
|
|
8
|
+
constructor(messageType, issues) {
|
|
9
|
+
super(`Validation failed for WebSocket message type: ${messageType}`);
|
|
10
|
+
this.messageType = messageType;
|
|
11
|
+
this.issues = issues;
|
|
12
|
+
this.name = "WebSocketValidationError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function createTypedWebSocket(ws) {
|
|
16
|
+
return {
|
|
17
|
+
get raw() {
|
|
18
|
+
return ws;
|
|
19
|
+
},
|
|
20
|
+
send(type, payload) {
|
|
21
|
+
ws.send(JSON.stringify({ type, payload }));
|
|
22
|
+
},
|
|
23
|
+
subscribe(topic) {
|
|
24
|
+
ws.subscribe(topic);
|
|
25
|
+
},
|
|
26
|
+
unsubscribe(topic) {
|
|
27
|
+
ws.unsubscribe(topic);
|
|
28
|
+
},
|
|
29
|
+
publish(topic, type, payload) {
|
|
30
|
+
ws.publish(topic, JSON.stringify({ type, payload }));
|
|
31
|
+
},
|
|
32
|
+
close(code, reason) {
|
|
33
|
+
ws.close(code, reason);
|
|
34
|
+
},
|
|
35
|
+
get data() {
|
|
36
|
+
return ws.data;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class WebSocketRouter {
|
|
42
|
+
contract;
|
|
43
|
+
handlers;
|
|
44
|
+
basePath;
|
|
45
|
+
contextFactory;
|
|
46
|
+
constructor(contract, handlers, options) {
|
|
47
|
+
this.contract = contract;
|
|
48
|
+
this.handlers = handlers;
|
|
49
|
+
const bp = options?.basePath || "";
|
|
50
|
+
if (bp) {
|
|
51
|
+
this.basePath = bp.startsWith("/") ? bp : `/${bp}`;
|
|
52
|
+
this.basePath = this.basePath.endsWith("/") ? this.basePath.slice(0, -1) : this.basePath;
|
|
53
|
+
} else {
|
|
54
|
+
this.basePath = "";
|
|
55
|
+
}
|
|
56
|
+
this.contextFactory = options?.context;
|
|
57
|
+
}
|
|
58
|
+
findEndpoint(path) {
|
|
59
|
+
for (const [name, endpoint] of Object.entries(this.contract)) {
|
|
60
|
+
const params = matchPath(endpoint.path, path);
|
|
61
|
+
if (params !== null) {
|
|
62
|
+
return {
|
|
63
|
+
name,
|
|
64
|
+
endpoint,
|
|
65
|
+
params
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
parseUpgradeParams(request, endpoint, pathParams) {
|
|
72
|
+
const url = new URL(request.url);
|
|
73
|
+
let params = pathParams;
|
|
74
|
+
if (endpoint.params) {
|
|
75
|
+
const result = endpoint.params.safeParse(pathParams);
|
|
76
|
+
if (!result.success) {
|
|
77
|
+
throw new Error(`Invalid path params: ${result.error.message}`);
|
|
78
|
+
}
|
|
79
|
+
params = result.data;
|
|
80
|
+
}
|
|
81
|
+
let query = {};
|
|
82
|
+
if (endpoint.query) {
|
|
83
|
+
const queryData = parseQuery(url.searchParams);
|
|
84
|
+
const result = endpoint.query.safeParse(queryData);
|
|
85
|
+
if (!result.success) {
|
|
86
|
+
throw new Error(`Invalid query params: ${result.error.message}`);
|
|
87
|
+
}
|
|
88
|
+
query = result.data;
|
|
89
|
+
}
|
|
90
|
+
let headers = {};
|
|
91
|
+
if (endpoint.headers) {
|
|
92
|
+
const headersObj = {};
|
|
93
|
+
request.headers.forEach((value, key) => {
|
|
94
|
+
headersObj[key] = value;
|
|
95
|
+
});
|
|
96
|
+
const result = endpoint.headers.safeParse(headersObj);
|
|
97
|
+
if (!result.success) {
|
|
98
|
+
throw new Error(`Invalid headers: ${result.error.message}`);
|
|
99
|
+
}
|
|
100
|
+
headers = result.data;
|
|
101
|
+
}
|
|
102
|
+
return { params, query, headers };
|
|
103
|
+
}
|
|
104
|
+
async matchAndPrepareUpgrade(request) {
|
|
105
|
+
const url = new URL(request.url);
|
|
106
|
+
let path = url.pathname;
|
|
107
|
+
if (this.basePath && path.startsWith(this.basePath)) {
|
|
108
|
+
path = path.slice(this.basePath.length) || "/";
|
|
109
|
+
}
|
|
110
|
+
const match = this.findEndpoint(path);
|
|
111
|
+
if (!match) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const { name, endpoint, params: rawParams } = match;
|
|
115
|
+
try {
|
|
116
|
+
const { params, query, headers } = this.parseUpgradeParams(request, endpoint, rawParams);
|
|
117
|
+
const context = this.contextFactory ? await this.contextFactory(request, String(name), endpoint) : undefined;
|
|
118
|
+
return {
|
|
119
|
+
endpointName: String(name),
|
|
120
|
+
endpoint,
|
|
121
|
+
params,
|
|
122
|
+
query,
|
|
123
|
+
headers,
|
|
124
|
+
context,
|
|
125
|
+
state: {}
|
|
126
|
+
};
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error("WebSocket upgrade validation failed:", err);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
validateMessage(endpoint, rawMessage) {
|
|
133
|
+
const messageStr = typeof rawMessage === "string" ? rawMessage : new TextDecoder().decode(rawMessage);
|
|
134
|
+
const parsed = JSON.parse(messageStr);
|
|
135
|
+
const { type, payload } = parsed;
|
|
136
|
+
const messageDef = endpoint.clientMessages[type];
|
|
137
|
+
if (!messageDef) {
|
|
138
|
+
throw new WebSocketValidationError(type, [
|
|
139
|
+
{
|
|
140
|
+
code: "custom",
|
|
141
|
+
path: ["type"],
|
|
142
|
+
message: `Unknown message type: ${type}`
|
|
143
|
+
}
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
const result = messageDef.payload.safeParse(payload);
|
|
147
|
+
if (!result.success) {
|
|
148
|
+
throw new WebSocketValidationError(type, result.error.issues);
|
|
149
|
+
}
|
|
150
|
+
return { type, payload: result.data };
|
|
151
|
+
}
|
|
152
|
+
get websocketHandler() {
|
|
153
|
+
return {
|
|
154
|
+
open: async (ws) => {
|
|
155
|
+
const data = ws.data;
|
|
156
|
+
const endpointHandlers = this.handlers[data.endpointName];
|
|
157
|
+
if (!endpointHandlers)
|
|
158
|
+
return;
|
|
159
|
+
const typedWs = createTypedWebSocket(ws);
|
|
160
|
+
if (endpointHandlers.open) {
|
|
161
|
+
await endpointHandlers.open(typedWs, data.context);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
message: async (ws, message) => {
|
|
165
|
+
const data = ws.data;
|
|
166
|
+
const endpointHandlers = this.handlers[data.endpointName];
|
|
167
|
+
if (!endpointHandlers)
|
|
168
|
+
return;
|
|
169
|
+
const typedWs = createTypedWebSocket(ws);
|
|
170
|
+
try {
|
|
171
|
+
const validatedMessage = this.validateMessage(data.endpoint, typeof message === "string" ? message : message.buffer);
|
|
172
|
+
await endpointHandlers.message(typedWs, validatedMessage, data.context);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (err instanceof WebSocketValidationError) {
|
|
175
|
+
if (endpointHandlers.validationError) {
|
|
176
|
+
endpointHandlers.validationError(typedWs, err, data.context);
|
|
177
|
+
} else {
|
|
178
|
+
typedWs.send("error", {
|
|
179
|
+
code: "VALIDATION_ERROR",
|
|
180
|
+
message: err.message,
|
|
181
|
+
issues: err.issues
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
console.error("WebSocket message handler error:", err);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
close: (ws, _code, _reason) => {
|
|
190
|
+
const data = ws.data;
|
|
191
|
+
const endpointHandlers = this.handlers[data.endpointName];
|
|
192
|
+
if (!endpointHandlers)
|
|
193
|
+
return;
|
|
194
|
+
const typedWs = createTypedWebSocket(ws);
|
|
195
|
+
if (endpointHandlers.close) {
|
|
196
|
+
endpointHandlers.close(typedWs, data.context);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
drain: () => {}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function createWebSocketRouter(contract, handlers, options) {
|
|
204
|
+
return new WebSocketRouter(contract, handlers, options);
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
createWebSocketRouter,
|
|
208
|
+
WebSocketValidationError,
|
|
209
|
+
WebSocketRouter
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
//# debugId=47CE29C068793DD164756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../websocket.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n ExtractClientMessage,\n ExtractWSHeaders,\n ExtractWSParams,\n ExtractWSQuery,\n WebSocketContract,\n WebSocketContractDefinition,\n} from '@richie-rpc/core';\nimport { matchPath, parseQuery } from '@richie-rpc/core';\nimport type { z } from 'zod';\n\n/**\n * Validation error for WebSocket messages\n */\nexport class WebSocketValidationError extends Error {\n constructor(\n public messageType: string,\n public issues: z.ZodIssue[],\n ) {\n super(`Validation failed for WebSocket message type: ${messageType}`);\n this.name = 'WebSocketValidationError';\n }\n}\n\n/**\n * Data attached to WebSocket connections for routing\n */\nexport interface WebSocketData<\n T extends WebSocketContractDefinition = WebSocketContractDefinition,\n S = unknown,\n> {\n endpointName: string;\n endpoint: T;\n params: ExtractWSParams<T>;\n query: ExtractWSQuery<T>;\n headers: ExtractWSHeaders<T>;\n context: unknown;\n state: S;\n}\n\n/**\n * Typed WebSocket wrapper for sending messages\n */\nexport interface TypedServerWebSocket<T extends WebSocketContractDefinition, S = unknown> {\n /** The underlying Bun WebSocket */\n readonly raw: WebSocket;\n /** Send a typed message to the client */\n send<K extends keyof T['serverMessages']>(\n type: K,\n payload: z.infer<T['serverMessages'][K]['payload']>,\n ): void;\n /** Subscribe to a topic for pub/sub */\n subscribe(topic: string): void;\n /** Unsubscribe from a topic */\n unsubscribe(topic: string): void;\n /** Publish a message to a topic */\n publish<K extends keyof T['serverMessages']>(\n topic: string,\n type: K,\n payload: z.infer<T['serverMessages'][K]['payload']>,\n ): void;\n /** Close the connection */\n close(code?: number, reason?: string): void;\n /** Connection data */\n readonly data: WebSocketData<T, S>;\n}\n\n/**\n * Handler functions for a WebSocket endpoint\n */\nexport interface WebSocketEndpointHandlers<\n T extends WebSocketContractDefinition,\n C = unknown,\n S = unknown,\n> {\n /** Called when connection opens */\n open?(ws: TypedServerWebSocket<T, S>, ctx: C): void | Promise<void>;\n /** Called for each validated message */\n message(\n ws: TypedServerWebSocket<T, S>,\n message: ExtractClientMessage<T>,\n ctx: C,\n ): void | Promise<void>;\n /** Called when connection closes */\n close?(ws: TypedServerWebSocket<T, S>, ctx: C): void;\n /** Called when message validation fails */\n validationError?(ws: TypedServerWebSocket<T, S>, error: WebSocketValidationError, ctx: C): void;\n}\n\n/**\n * Contract handlers mapping for WebSocket endpoints\n */\nexport type WebSocketContractHandlers<T extends WebSocketContract, C = unknown, S = unknown> = {\n [K in keyof T]: WebSocketEndpointHandlers<T[K], C, S>;\n};\n\n/**\n * Upgrade data returned by matchAndPrepareUpgrade\n */\nexport interface UpgradeData<S = unknown> {\n endpointName: string;\n endpoint: WebSocketContractDefinition;\n params: Record<string, string>;\n query: Record<string, string | string[]>;\n headers: Record<string, string>;\n context: unknown;\n state: S;\n}\n\n/**\n * Options for WebSocket router\n */\nexport interface WebSocketRouterOptions<C = unknown, S = unknown> {\n basePath?: string;\n context?: (\n request: Request,\n endpointName: string,\n endpoint: WebSocketContractDefinition,\n ) => C | Promise<C>;\n /** Type hint for per-connection state. Use `{} as YourStateType` */\n state?: S;\n}\n\n/**\n * Bun WebSocket handler type (subset of Bun's types)\n */\nexport interface BunWebSocketHandler<T = unknown> {\n open(ws: Bun.ServerWebSocket<T>): void | Promise<void>;\n message(ws: Bun.ServerWebSocket<T>, message: string | Buffer<ArrayBuffer>): void | Promise<void>;\n close(ws: Bun.ServerWebSocket<T>, code: number, reason: string): void;\n drain(ws: Bun.ServerWebSocket<T>): void;\n}\n\n/**\n * Create a typed WebSocket wrapper\n */\nfunction createTypedWebSocket<T extends WebSocketContractDefinition, S = unknown>(\n ws: WebSocket & { data: WebSocketData<T, S> },\n): TypedServerWebSocket<T, S> {\n return {\n get raw() {\n return ws;\n },\n send(type, payload) {\n ws.send(JSON.stringify({ type, payload }));\n },\n subscribe(topic) {\n (ws as any).subscribe(topic);\n },\n unsubscribe(topic) {\n (ws as any).unsubscribe(topic);\n },\n publish(topic, type, payload) {\n (ws as any).publish(topic, JSON.stringify({ type, payload }));\n },\n close(code, reason) {\n ws.close(code, reason);\n },\n get data() {\n return ws.data;\n },\n };\n}\n\n/**\n * WebSocket router for managing WebSocket contract endpoints\n */\nexport class WebSocketRouter<T extends WebSocketContract, C = unknown, S = unknown> {\n private basePath: string;\n private contextFactory?: (\n request: Request,\n endpointName: string,\n endpoint: WebSocketContractDefinition,\n ) => C | Promise<C>;\n\n constructor(\n private contract: T,\n private handlers: WebSocketContractHandlers<T, C, S>,\n options?: WebSocketRouterOptions<C, S>,\n ) {\n // Normalize basePath\n const bp = options?.basePath || '';\n if (bp) {\n this.basePath = bp.startsWith('/') ? bp : `/${bp}`;\n this.basePath = this.basePath.endsWith('/') ? this.basePath.slice(0, -1) : this.basePath;\n } else {\n this.basePath = '';\n }\n this.contextFactory = options?.context;\n }\n\n /**\n * Find matching endpoint for a path\n */\n private findEndpoint(path: string): {\n name: keyof T;\n endpoint: WebSocketContractDefinition;\n params: Record<string, string>;\n } | null {\n for (const [name, endpoint] of Object.entries(this.contract)) {\n const params = matchPath(endpoint.path, path);\n if (params !== null) {\n return {\n name,\n endpoint: endpoint as WebSocketContractDefinition,\n params,\n };\n }\n }\n return null;\n }\n\n /**\n * Parse and validate upgrade request parameters\n */\n private parseUpgradeParams(\n request: Request,\n endpoint: WebSocketContractDefinition,\n pathParams: Record<string, string>,\n ): { params: any; query: any; headers: any } {\n const url = new URL(request.url);\n\n // Parse and validate path params\n let params: any = pathParams;\n if (endpoint.params) {\n const result = endpoint.params.safeParse(pathParams);\n if (!result.success) {\n throw new Error(`Invalid path params: ${result.error.message}`);\n }\n params = result.data;\n }\n\n // Parse and validate query params\n let query: any = {};\n if (endpoint.query) {\n const queryData = parseQuery(url.searchParams);\n const result = endpoint.query.safeParse(queryData);\n if (!result.success) {\n throw new Error(`Invalid query params: ${result.error.message}`);\n }\n query = result.data;\n }\n\n // Parse and validate headers\n let headers: any = {};\n if (endpoint.headers) {\n const headersObj: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headersObj[key] = value;\n });\n const result = endpoint.headers.safeParse(headersObj);\n if (!result.success) {\n throw new Error(`Invalid headers: ${result.error.message}`);\n }\n headers = result.data;\n }\n\n return { params, query, headers };\n }\n\n /**\n * Match a request and prepare upgrade data\n * Returns null if no match, or UpgradeData for server.upgrade()\n */\n async matchAndPrepareUpgrade(request: Request): Promise<UpgradeData<S> | null> {\n const url = new URL(request.url);\n let path = url.pathname;\n\n // Strip basePath if configured\n if (this.basePath && path.startsWith(this.basePath)) {\n path = path.slice(this.basePath.length) || '/';\n }\n\n const match = this.findEndpoint(path);\n if (!match) {\n return null;\n }\n\n const { name, endpoint, params: rawParams } = match;\n\n try {\n const { params, query, headers } = this.parseUpgradeParams(request, endpoint, rawParams);\n\n // Create context if factory provided\n const context = this.contextFactory\n ? await this.contextFactory(request, String(name), endpoint)\n : (undefined as C);\n\n return {\n endpointName: String(name),\n endpoint,\n params,\n query,\n headers,\n context,\n state: {} as S,\n };\n } catch (err) {\n // Validation failed during upgrade\n console.error('WebSocket upgrade validation failed:', err);\n return null;\n }\n }\n\n /**\n * Validate an incoming client message\n */\n private validateMessage(\n endpoint: WebSocketContractDefinition,\n rawMessage: string | ArrayBuffer,\n ): ExtractClientMessage<typeof endpoint> {\n // Parse message\n const messageStr =\n typeof rawMessage === 'string' ? rawMessage : new TextDecoder().decode(rawMessage);\n const parsed = JSON.parse(messageStr) as { type: string; payload: unknown };\n\n const { type, payload } = parsed;\n\n // Find the schema for this message type\n const messageDef = endpoint.clientMessages[type];\n if (!messageDef) {\n throw new WebSocketValidationError(type, [\n {\n code: 'custom',\n path: ['type'],\n message: `Unknown message type: ${type}`,\n },\n ]);\n }\n\n // Validate payload\n const result = messageDef.payload.safeParse(payload);\n if (!result.success) {\n throw new WebSocketValidationError(type, result.error.issues);\n }\n\n return { type, payload: result.data } as ExtractClientMessage<typeof endpoint>;\n }\n\n /**\n * Get Bun-compatible WebSocket handler\n */\n get websocketHandler(): BunWebSocketHandler<UpgradeData<S>> {\n return {\n open: async (ws) => {\n const data = ws.data;\n const endpointHandlers = this.handlers[data.endpointName as keyof T];\n if (!endpointHandlers) return;\n\n // Create typed wrapper with properly typed data\n const typedWs = createTypedWebSocket(\n ws as unknown as WebSocket & {\n data: WebSocketData<WebSocketContractDefinition, S>;\n },\n );\n\n if (endpointHandlers.open) {\n await endpointHandlers.open(typedWs as any, data.context as C);\n }\n },\n\n message: async (ws, message) => {\n const data = ws.data;\n const endpointHandlers = this.handlers[data.endpointName as keyof T];\n if (!endpointHandlers) return;\n\n const typedWs = createTypedWebSocket(\n ws as unknown as WebSocket & {\n data: WebSocketData<WebSocketContractDefinition, S>;\n },\n );\n\n try {\n // Validate the message\n const validatedMessage = this.validateMessage(\n data.endpoint,\n typeof message === 'string' ? message : message.buffer,\n );\n\n // Call handler with validated message\n await endpointHandlers.message(\n typedWs as any,\n validatedMessage as any,\n data.context as C,\n );\n } catch (err) {\n if (err instanceof WebSocketValidationError) {\n // Call validation error handler if provided\n if (endpointHandlers.validationError) {\n endpointHandlers.validationError(typedWs as any, err, data.context as C);\n } else {\n // Default: send error message back\n typedWs.send(\n 'error' as any,\n {\n code: 'VALIDATION_ERROR',\n message: err.message,\n issues: err.issues,\n } as any,\n );\n }\n } else {\n console.error('WebSocket message handler error:', err);\n }\n }\n },\n\n close: (ws, _code, _reason) => {\n const data = ws.data;\n const endpointHandlers = this.handlers[data.endpointName as keyof T];\n if (!endpointHandlers) return;\n\n const typedWs = createTypedWebSocket(\n ws as unknown as WebSocket & {\n data: WebSocketData<WebSocketContractDefinition, S>;\n },\n );\n\n if (endpointHandlers.close) {\n endpointHandlers.close(typedWs as any, data.context as C);\n }\n },\n drain: () => {\n // not used\n },\n };\n }\n}\n\n/**\n * Create a WebSocket router from a contract and handlers\n */\nexport function createWebSocketRouter<T extends WebSocketContract, C = unknown, S = unknown>(\n contract: T,\n handlers: WebSocketContractHandlers<T, C, S>,\n options?: WebSocketRouterOptions<C, S>,\n): WebSocketRouter<T, C, S> {\n return new WebSocketRouter(contract, handlers, options);\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "47CE29C068793DD164756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -154,4 +154,4 @@ export declare class Router<T extends Contract, C = unknown> {
|
|
|
154
154
|
* Create a router from a contract and handlers
|
|
155
155
|
*/
|
|
156
156
|
export declare function createRouter<T extends Contract, C = unknown>(contract: T, handlers: ContractHandlers<T, C>, options?: RouterOptions<C>): Router<T, C>;
|
|
157
|
-
export
|
|
157
|
+
export * from './websocket';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@richie-rpc/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@richie-rpc/core": "^1.2.
|
|
13
|
+
"@richie-rpc/core": "^1.2.4",
|
|
14
14
|
"typescript": "^5",
|
|
15
15
|
"zod": "^4.1.12"
|
|
16
16
|
},
|