@richie-rpc/server 1.2.3 → 1.2.4

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.
@@ -1,6 +1,7 @@
1
1
  // @bun
2
2
  // packages/server/index.ts
3
3
  import { formDataToObject, matchPath, parseQuery, Status } from "@richie-rpc/core";
4
+ import { createWebSocketRouter } from "./websocket.mjs";
4
5
  class ValidationError extends Error {
5
6
  field;
6
7
  issues;
@@ -73,6 +74,123 @@ async function parseRequest(request, endpoint, pathParams, context) {
73
74
  }
74
75
  return { params, query, headers, body, request, context };
75
76
  }
77
+ async function parseStreamingRequest(request, endpoint, pathParams, context) {
78
+ const url = new URL(request.url);
79
+ let params = pathParams;
80
+ if (endpoint.params) {
81
+ const result = endpoint.params.safeParse(pathParams);
82
+ if (!result.success) {
83
+ throw new ValidationError("params", result.error.issues);
84
+ }
85
+ params = result.data;
86
+ }
87
+ let query = {};
88
+ if (endpoint.query) {
89
+ const queryData = parseQuery(url.searchParams);
90
+ const result = endpoint.query.safeParse(queryData);
91
+ if (!result.success) {
92
+ throw new ValidationError("query", result.error.issues);
93
+ }
94
+ query = result.data;
95
+ }
96
+ let headers = {};
97
+ if (endpoint.headers) {
98
+ const headersObj = {};
99
+ request.headers.forEach((value, key) => {
100
+ headersObj[key] = value;
101
+ });
102
+ const result = endpoint.headers.safeParse(headersObj);
103
+ if (!result.success) {
104
+ throw new ValidationError("headers", result.error.issues);
105
+ }
106
+ headers = result.data;
107
+ }
108
+ let body;
109
+ if (endpoint.body) {
110
+ const contentType = request.headers.get("content-type") || "";
111
+ let bodyData;
112
+ if (contentType.includes("application/json")) {
113
+ bodyData = await request.json();
114
+ } else if (contentType.includes("multipart/form-data")) {
115
+ const formData = await request.formData();
116
+ bodyData = formDataToObject(formData);
117
+ } else {
118
+ bodyData = await request.text();
119
+ }
120
+ const result = endpoint.body.safeParse(bodyData);
121
+ if (!result.success) {
122
+ throw new ValidationError("body", result.error.issues);
123
+ }
124
+ body = result.data;
125
+ }
126
+ return { params, query, headers, body, request, context };
127
+ }
128
+ async function parseSSERequest(request, endpoint, pathParams, context) {
129
+ const url = new URL(request.url);
130
+ let params = pathParams;
131
+ if (endpoint.params) {
132
+ const result = endpoint.params.safeParse(pathParams);
133
+ if (!result.success) {
134
+ throw new ValidationError("params", result.error.issues);
135
+ }
136
+ params = result.data;
137
+ }
138
+ let query = {};
139
+ if (endpoint.query) {
140
+ const queryData = parseQuery(url.searchParams);
141
+ const result = endpoint.query.safeParse(queryData);
142
+ if (!result.success) {
143
+ throw new ValidationError("query", result.error.issues);
144
+ }
145
+ query = result.data;
146
+ }
147
+ let headers = {};
148
+ if (endpoint.headers) {
149
+ const headersObj = {};
150
+ request.headers.forEach((value, key) => {
151
+ headersObj[key] = value;
152
+ });
153
+ const result = endpoint.headers.safeParse(headersObj);
154
+ if (!result.success) {
155
+ throw new ValidationError("headers", result.error.issues);
156
+ }
157
+ headers = result.data;
158
+ }
159
+ return { params, query, headers, request, context };
160
+ }
161
+ async function parseDownloadRequest(request, endpoint, pathParams, context) {
162
+ const url = new URL(request.url);
163
+ let params = pathParams;
164
+ if (endpoint.params) {
165
+ const result = endpoint.params.safeParse(pathParams);
166
+ if (!result.success) {
167
+ throw new ValidationError("params", result.error.issues);
168
+ }
169
+ params = result.data;
170
+ }
171
+ let query = {};
172
+ if (endpoint.query) {
173
+ const queryData = parseQuery(url.searchParams);
174
+ const result = endpoint.query.safeParse(queryData);
175
+ if (!result.success) {
176
+ throw new ValidationError("query", result.error.issues);
177
+ }
178
+ query = result.data;
179
+ }
180
+ let headers = {};
181
+ if (endpoint.headers) {
182
+ const headersObj = {};
183
+ request.headers.forEach((value, key) => {
184
+ headersObj[key] = value;
185
+ });
186
+ const result = endpoint.headers.safeParse(headersObj);
187
+ if (!result.success) {
188
+ throw new ValidationError("headers", result.error.issues);
189
+ }
190
+ headers = result.data;
191
+ }
192
+ return { params, query, headers, request, context };
193
+ }
76
194
  function createResponse(endpoint, handlerResponse) {
77
195
  const { status, body, headers: customHeaders } = handlerResponse;
78
196
  const responseSchema = endpoint.responses[status];
@@ -97,6 +215,122 @@ function createResponse(endpoint, handlerResponse) {
97
215
  headers: responseHeaders
98
216
  });
99
217
  }
218
+ function createDownloadResponse(_endpoint, handlerResponse) {
219
+ const { status, body, headers: customHeaders } = handlerResponse;
220
+ const responseHeaders = new Headers(customHeaders);
221
+ if (status === 200 && body instanceof File) {
222
+ if (!responseHeaders.has("content-type")) {
223
+ responseHeaders.set("content-type", body.type || "application/octet-stream");
224
+ }
225
+ responseHeaders.set("content-length", String(body.size));
226
+ if (!responseHeaders.has("content-disposition")) {
227
+ const filename = encodeURIComponent(body.name);
228
+ responseHeaders.set("content-disposition", `attachment; filename="${filename}"; filename*=UTF-8''${filename}`);
229
+ }
230
+ return new Response(body, { status: 200, headers: responseHeaders });
231
+ }
232
+ if (!responseHeaders.has("content-type")) {
233
+ responseHeaders.set("content-type", "application/json");
234
+ }
235
+ return new Response(JSON.stringify(body), {
236
+ status,
237
+ headers: responseHeaders
238
+ });
239
+ }
240
+ function createStreamingResponse(handler) {
241
+ const { readable, writable } = new TransformStream;
242
+ const writer = writable.getWriter();
243
+ const encoder = new TextEncoder;
244
+ let closed = false;
245
+ const stream = {
246
+ send(chunk) {
247
+ if (!closed) {
248
+ writer.write(encoder.encode(`${JSON.stringify(chunk)}
249
+ `));
250
+ }
251
+ },
252
+ close(final) {
253
+ if (!closed) {
254
+ closed = true;
255
+ if (final !== undefined) {
256
+ writer.write(encoder.encode(`${JSON.stringify({ __final__: true, data: final })}
257
+ `));
258
+ }
259
+ writer.close();
260
+ }
261
+ },
262
+ get isOpen() {
263
+ return !closed;
264
+ }
265
+ };
266
+ Promise.resolve(handler(stream)).catch((err) => {
267
+ console.error("Streaming handler error:", err);
268
+ stream.close();
269
+ });
270
+ return new Response(readable, {
271
+ headers: {
272
+ "Content-Type": "application/x-ndjson",
273
+ "Cache-Control": "no-cache",
274
+ Connection: "keep-alive"
275
+ }
276
+ });
277
+ }
278
+ function createSSEResponse(handler) {
279
+ const { readable, writable } = new TransformStream;
280
+ const writer = writable.getWriter();
281
+ const encoder = new TextEncoder;
282
+ const controller = new AbortController;
283
+ let closed = false;
284
+ let cleanup;
285
+ const emitter = {
286
+ send(event, data, options) {
287
+ if (!closed) {
288
+ let message = "";
289
+ if (options?.id) {
290
+ message += `id: ${options.id}
291
+ `;
292
+ }
293
+ message += `event: ${String(event)}
294
+ `;
295
+ message += `data: ${JSON.stringify(data)}
296
+
297
+ `;
298
+ writer.write(encoder.encode(message));
299
+ }
300
+ },
301
+ close() {
302
+ if (!closed) {
303
+ closed = true;
304
+ if (cleanup)
305
+ cleanup();
306
+ writer.close();
307
+ }
308
+ },
309
+ get isOpen() {
310
+ return !closed;
311
+ }
312
+ };
313
+ Promise.resolve(handler(emitter, controller.signal)).then((cleanupFn) => {
314
+ if (typeof cleanupFn === "function") {
315
+ cleanup = cleanupFn;
316
+ }
317
+ }).catch((err) => {
318
+ console.error("SSE handler error:", err);
319
+ emitter.close();
320
+ });
321
+ const [responseStream, detectStream] = readable.tee();
322
+ detectStream.pipeTo(new WritableStream).catch(() => {
323
+ controller.abort();
324
+ emitter.close();
325
+ });
326
+ return new Response(responseStream, {
327
+ headers: {
328
+ "Content-Type": "text/event-stream",
329
+ "Cache-Control": "no-cache",
330
+ Connection: "keep-alive"
331
+ }
332
+ });
333
+ }
100
334
 
101
335
  class Router {
102
336
  contract;
@@ -140,8 +374,34 @@ class Router {
140
374
  const { name, endpoint, params } = match;
141
375
  const handler = this.handlers[name];
142
376
  const context = this.contextFactory ? await this.contextFactory(request, String(name), endpoint) : undefined;
377
+ if (endpoint.type === "streaming") {
378
+ const input2 = await parseStreamingRequest(request, endpoint, params, context);
379
+ return createStreamingResponse((stream) => {
380
+ return handler({
381
+ ...input2,
382
+ stream
383
+ });
384
+ });
385
+ }
386
+ if (endpoint.type === "sse") {
387
+ const input2 = await parseSSERequest(request, endpoint, params, context);
388
+ return createSSEResponse((emitter, signal) => {
389
+ return handler({
390
+ ...input2,
391
+ emitter,
392
+ signal
393
+ });
394
+ });
395
+ }
396
+ if (endpoint.type === "download") {
397
+ const input2 = await parseDownloadRequest(request, endpoint, params, context);
398
+ const downloadHandler = handler;
399
+ const response = await downloadHandler(input2);
400
+ return createDownloadResponse(endpoint, response);
401
+ }
143
402
  const input = await parseRequest(request, endpoint, params, context);
144
- const handlerResponse = await handler(input);
403
+ const standardHandler = handler;
404
+ const handlerResponse = await standardHandler(input);
145
405
  return createResponse(endpoint, handlerResponse);
146
406
  }
147
407
  get fetch() {
@@ -152,6 +412,7 @@ function createRouter(contract, handlers, options) {
152
412
  return new Router(contract, handlers, options);
153
413
  }
154
414
  export {
415
+ createWebSocketRouter,
155
416
  createRouter,
156
417
  ValidationError,
157
418
  Status,
@@ -159,4 +420,4 @@ export {
159
420
  RouteNotFoundError
160
421
  };
161
422
 
162
- //# debugId=2A54C105B1B2019764756E2164756E21
423
+ //# debugId=997D001F075A95C564756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../index.ts"],
4
4
  "sourcesContent": [
5
- "import type {\n Contract,\n EndpointDefinition,\n ExtractBody,\n ExtractHeaders,\n ExtractParams,\n ExtractQuery,\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\nexport type HandlerInput<T extends EndpointDefinition, 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\nexport type HandlerResponse<T extends EndpointDefinition> = {\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\nexport type Handler<T extends EndpointDefinition, C = unknown> = (\n input: HandlerInput<T, C>,\n) => Promise<HandlerResponse<T>> | HandlerResponse<T>;\n\n// Contract handlers mapping\nexport type ContractHandlers<T extends Contract, C = unknown> = {\n [K in keyof T]: Handler<T[K], C>;\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 EndpointDefinition, 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 * Validate and create response\n */\nfunction createResponse<T extends EndpointDefinition>(\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 * 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 // Parse and validate request\n const input = await parseRequest(request, endpoint, params, context);\n\n // Call handler\n const handlerResponse = await handler(input as any);\n\n // Create and validate response\n return createResponse(endpoint as T[keyof T], handlerResponse);\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"
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';"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "2A54C105B1B2019764756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "997D001F075A95C564756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@richie-rpc/server",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "type": "module"
5
5
  }
@@ -1,8 +1,8 @@
1
- import type { Contract, EndpointDefinition, ExtractBody, ExtractHeaders, ExtractParams, ExtractQuery } from '@richie-rpc/core';
1
+ import type { Contract, DownloadEndpointDefinition, EndpointDefinition, ExtractBody, ExtractChunk, ExtractFinalResponse, ExtractHeaders, ExtractParams, ExtractQuery, ExtractSSEEventData, SSEEndpointDefinition, StandardEndpointDefinition, StreamingEndpointDefinition } from '@richie-rpc/core';
2
2
  import { Status } from '@richie-rpc/core';
3
3
  import type { z } from 'zod';
4
4
  export { Status };
5
- export type HandlerInput<T extends EndpointDefinition, C = unknown> = {
5
+ export type HandlerInput<T extends StandardEndpointDefinition, C = unknown> = {
6
6
  params: ExtractParams<T>;
7
7
  query: ExtractQuery<T>;
8
8
  headers: ExtractHeaders<T>;
@@ -10,16 +10,106 @@ export type HandlerInput<T extends EndpointDefinition, C = unknown> = {
10
10
  request: Request;
11
11
  context: C;
12
12
  };
13
- export type HandlerResponse<T extends EndpointDefinition> = {
13
+ export type HandlerResponse<T extends StandardEndpointDefinition> = {
14
14
  [Status in keyof T['responses']]: {
15
15
  status: Status;
16
16
  body: T['responses'][Status] extends z.ZodTypeAny ? z.infer<T['responses'][Status]> : never;
17
17
  headers?: Record<string, string>;
18
18
  };
19
19
  }[keyof T['responses']];
20
- export type Handler<T extends EndpointDefinition, C = unknown> = (input: HandlerInput<T, C>) => Promise<HandlerResponse<T>> | HandlerResponse<T>;
20
+ export type Handler<T extends StandardEndpointDefinition, C = unknown> = (input: HandlerInput<T, C>) => Promise<HandlerResponse<T>> | HandlerResponse<T>;
21
+ /**
22
+ * Emitter for streaming responses - push-based API
23
+ */
24
+ export interface StreamEmitter<T extends StreamingEndpointDefinition> {
25
+ /** Send a chunk to the client */
26
+ send(chunk: ExtractChunk<T>): void;
27
+ /** Close the stream with optional final response */
28
+ close(final?: ExtractFinalResponse<T>): void;
29
+ /** Check if stream is still open */
30
+ readonly isOpen: boolean;
31
+ }
32
+ /**
33
+ * Handler input for streaming endpoints
34
+ */
35
+ export type StreamingHandlerInput<T extends StreamingEndpointDefinition, C = unknown> = {
36
+ params: ExtractParams<T>;
37
+ query: ExtractQuery<T>;
38
+ headers: ExtractHeaders<T>;
39
+ body: ExtractBody<T>;
40
+ request: Request;
41
+ context: C;
42
+ stream: StreamEmitter<T>;
43
+ };
44
+ /**
45
+ * Handler function type for streaming endpoints
46
+ */
47
+ export type StreamingHandler<T extends StreamingEndpointDefinition, C = unknown> = (input: StreamingHandlerInput<T, C>) => void | Promise<void>;
48
+ /**
49
+ * Emitter for SSE responses
50
+ */
51
+ export interface SSEEmitter<T extends SSEEndpointDefinition> {
52
+ /** Send an event to the client */
53
+ send<K extends keyof T['events']>(event: K, data: ExtractSSEEventData<T, K>, options?: {
54
+ id?: string;
55
+ }): void;
56
+ /** Close the connection */
57
+ close(): void;
58
+ /** Check if connection is still open */
59
+ readonly isOpen: boolean;
60
+ }
61
+ /**
62
+ * Handler input for SSE endpoints
63
+ */
64
+ export type SSEHandlerInput<T extends SSEEndpointDefinition, C = unknown> = {
65
+ params: ExtractParams<T>;
66
+ query: ExtractQuery<T>;
67
+ headers: ExtractHeaders<T>;
68
+ request: Request;
69
+ context: C;
70
+ emitter: SSEEmitter<T>;
71
+ /** AbortSignal for detecting client disconnect */
72
+ signal: AbortSignal;
73
+ };
74
+ /**
75
+ * Handler function type for SSE endpoints
76
+ * Returns an optional cleanup function
77
+ */
78
+ export type SSEHandler<T extends SSEEndpointDefinition, C = unknown> = (input: SSEHandlerInput<T, C>) => void | (() => void) | Promise<void | (() => void)>;
79
+ /**
80
+ * Handler input for download endpoints
81
+ */
82
+ export type DownloadHandlerInput<T extends DownloadEndpointDefinition, C = unknown> = {
83
+ params: ExtractParams<T>;
84
+ query: ExtractQuery<T>;
85
+ headers: ExtractHeaders<T>;
86
+ request: Request;
87
+ context: C;
88
+ };
89
+ /**
90
+ * Handler response for download endpoints
91
+ * Success (200) returns File, errors return typed response
92
+ */
93
+ export type DownloadHandlerResponse<T extends DownloadEndpointDefinition> = {
94
+ status: 200;
95
+ body: File;
96
+ headers?: Record<string, string>;
97
+ } | (T['errorResponses'] extends Record<number, z.ZodTypeAny> ? {
98
+ [S in keyof T['errorResponses']]: {
99
+ status: S;
100
+ body: T['errorResponses'][S] extends z.ZodTypeAny ? z.infer<T['errorResponses'][S]> : never;
101
+ headers?: Record<string, string>;
102
+ };
103
+ }[keyof T['errorResponses']] : never);
104
+ /**
105
+ * Handler function type for download endpoints
106
+ */
107
+ export type DownloadHandler<T extends DownloadEndpointDefinition, C = unknown> = (input: DownloadHandlerInput<T, C>) => Promise<DownloadHandlerResponse<T>> | DownloadHandlerResponse<T>;
108
+ /**
109
+ * Contract handlers mapping - conditionally applies handler type based on endpoint type
110
+ */
21
111
  export type ContractHandlers<T extends Contract, C = unknown> = {
22
- [K in keyof T]: Handler<T[K], C>;
112
+ [K in keyof T]: T[K] extends StandardEndpointDefinition ? Handler<T[K], C> : T[K] extends StreamingEndpointDefinition ? StreamingHandler<T[K], C> : T[K] extends SSEEndpointDefinition ? SSEHandler<T[K], C> : T[K] extends DownloadEndpointDefinition ? DownloadHandler<T[K], C> : never;
23
113
  };
24
114
  export declare class ValidationError extends Error {
25
115
  field: string;
@@ -64,3 +154,4 @@ export declare class Router<T extends Contract, C = unknown> {
64
154
  * Create a router from a contract and handlers
65
155
  */
66
156
  export declare function createRouter<T extends Contract, C = unknown>(contract: T, handlers: ContractHandlers<T, C>, options?: RouterOptions<C>): Router<T, C>;
157
+ export { createWebSocketRouter } from './websocket';