@longzai-intelligence-transport/http-adapter-bun 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,357 @@
1
+ import { ApiResponse, InferRouteBody, InferRouteParams, InferRouteQuery, InferRouteResponse, RouteDefinition } from "@longzai-intelligence-transport/http-core";
2
+
3
+ //#region src/index.d.ts
4
+ /**
5
+ * 验证错误详情
6
+ *
7
+ * 单个字段的验证错误信息
8
+ */
9
+ type ValidationErrorDetail = {
10
+ /**
11
+ * 字段路径
12
+ */
13
+ path: string;
14
+ /**
15
+ * 错误信息
16
+ */
17
+ message: string;
18
+ /**
19
+ * 接收到的值
20
+ */
21
+ received: unknown;
22
+ /**
23
+ * 期望的类型
24
+ */
25
+ expected: string;
26
+ };
27
+ /**
28
+ * 验证错误数据
29
+ *
30
+ * 请求数据验证失败时返回的错误结构
31
+ */
32
+ type ValidationErrorData = {
33
+ /**
34
+ * 错误代码,固定为 VALIDATION_ERROR
35
+ */
36
+ code: 'VALIDATION_ERROR';
37
+ /**
38
+ * 错误概述信息
39
+ */
40
+ message: string;
41
+ /**
42
+ * 各字段的具体验证错误列表
43
+ */
44
+ details: Array<ValidationErrorDetail>;
45
+ };
46
+ /**
47
+ * Bun 请求上下文
48
+ *
49
+ * 包含请求的基本信息和解析后的数据
50
+ *
51
+ * @typeParam TContext - 自定义上下文类型
52
+ */
53
+ type BunRequestContext<TContext = unknown> = {
54
+ /**
55
+ * 原始请求对象
56
+ */
57
+ request: Request;
58
+ /**
59
+ * 解析后的路径参数
60
+ */
61
+ params: Record<string, string>;
62
+ /**
63
+ * 解析后的查询参数
64
+ */
65
+ query: Record<string, string | string[]>;
66
+ /**
67
+ * 解析后的请求体
68
+ */
69
+ body: unknown;
70
+ /**
71
+ * 自定义上下文数据
72
+ */
73
+ context?: TContext;
74
+ };
75
+ /**
76
+ * Bun 路由处理器
77
+ *
78
+ * Bun 原生路由处理函数类型
79
+ *
80
+ * @typeParam _TContext - 自定义上下文类型(保留用于类型一致性)
81
+ */
82
+ type BunRouteHandler<_TContext = unknown> = (request: Request) => Promise<Response> | Response;
83
+ /**
84
+ * Bun 路由处理器选项
85
+ *
86
+ * @typeParam TContext - 自定义上下文类型
87
+ */
88
+ type BunRouteHandlerOptions<TContext = unknown> = {
89
+ /**
90
+ * 创建自定义上下文的工厂函数
91
+ *
92
+ * 可用于注入数据库连接、用户信息等
93
+ *
94
+ * @param ctx - 请求上下文
95
+ */
96
+ createContext?: (ctx: BunRequestContext) => Promise<TContext> | TContext;
97
+ /**
98
+ * 全局错误处理函数
99
+ *
100
+ * 当请求处理过程中发生未捕获的异常时调用
101
+ *
102
+ * @param error - 捕获的错误对象
103
+ * @param request - 原始请求对象
104
+ */
105
+ onError?: (error: unknown, request: Request) => Response | Promise<Response>;
106
+ /**
107
+ * 验证错误处理函数
108
+ *
109
+ * 当请求参数验证失败时调用,可用于自定义错误响应格式
110
+ *
111
+ * @param validationError - 验证错误详情
112
+ * @param request - 原始请求对象
113
+ */
114
+ onValidationError?: (validationError: ValidationErrorData, request: Request) => Response | Promise<Response>;
115
+ };
116
+ /**
117
+ * 已验证的路由上下文
118
+ *
119
+ * 包含经过验证后的强类型路由参数
120
+ *
121
+ * @typeParam TRoute - 路由定义类型
122
+ * @typeParam TContext - 自定义上下文类型
123
+ */
124
+ type ValidatedRouteContext<TRoute extends RouteDefinition, TContext = unknown> = {
125
+ /**
126
+ * 原始请求对象
127
+ */
128
+ request: Request;
129
+ /**
130
+ * 已验证的路径参数
131
+ */
132
+ params: InferRouteParams<TRoute>;
133
+ /**
134
+ * 已验证的查询参数
135
+ */
136
+ query: InferRouteQuery<TRoute>;
137
+ /**
138
+ * 已验证的请求体
139
+ */
140
+ body: InferRouteBody<TRoute>;
141
+ /**
142
+ * 自定义上下文数据
143
+ */
144
+ context?: TContext;
145
+ };
146
+ /**
147
+ * 路由处理器函数类型
148
+ *
149
+ * 用户实现的具体业务逻辑处理函数
150
+ *
151
+ * @typeParam TRoute - 路由定义类型
152
+ * @typeParam TContext - 自定义上下文类型
153
+ * @param ctx - 请求上下文,包含已验证的参数数据
154
+ */
155
+ type RouteHandlerFn<TRoute extends RouteDefinition, TContext = unknown> = (ctx: BunRequestContext<TContext>) => Promise<ApiResponse<InferRouteResponse<TRoute>>> | ApiResponse<InferRouteResponse<TRoute>>;
156
+ /**
157
+ * 路径转正则表达式结果
158
+ *
159
+ * 包含转换后的正则表达式和提取的参数名
160
+ */
161
+ type PathToRegexResult = {
162
+ /**
163
+ * 匹配路径的正则表达式
164
+ */
165
+ regex: RegExp;
166
+ /**
167
+ * 路径中提取的参数名称列表
168
+ */
169
+ paramNames: string[];
170
+ };
171
+ /**
172
+ * 路由映射条目
173
+ *
174
+ * 包含路由定义和对应的处理函数
175
+ *
176
+ * @typeParam TContext - 自定义上下文类型
177
+ */
178
+ type RouteMapEntry<TContext = unknown> = {
179
+ /**
180
+ * 路由定义
181
+ */
182
+ route: RouteDefinition;
183
+ /**
184
+ * 处理函数
185
+ */
186
+ handler: RouteHandlerFn<RouteDefinition, TContext>;
187
+ };
188
+ /**
189
+ * 路由映射类型
190
+ *
191
+ * 路由名称到路由条目的映射
192
+ *
193
+ * @typeParam TRoutes - 路由定义映射类型
194
+ * @typeParam TContext - 自定义上下文类型
195
+ */
196
+ type RoutesMap<TRoutes extends Record<string, RouteDefinition>, TContext = unknown> = { [K in keyof TRoutes]: RouteHandlerFn<TRoutes[K], TContext> };
197
+ /**
198
+ * Fetch 处理器选项
199
+ *
200
+ * 自定义 fetch 处理器的错误处理行为
201
+ */
202
+ type FetchHandlerOptions = {
203
+ /**
204
+ * 路由未找到时的处理函数
205
+ */
206
+ onNotFound?: (request: Request) => Response | Promise<Response>;
207
+ /**
208
+ * HTTP 方法不允许时的处理函数
209
+ */
210
+ onMethodNotAllowed?: (request: Request) => Response | Promise<Response>;
211
+ };
212
+ /**
213
+ * Fetch 处理器函数类型
214
+ *
215
+ * 接收 Request 并返回 Response 的异步函数
216
+ *
217
+ * @param request - HTTP 请求对象
218
+ */
219
+ type FetchHandlerFn = (request: Request) => Promise<Response>;
220
+ /**
221
+ * 将路径模式转换为正则表达式
222
+ *
223
+ * @param path - 路径模式,如 "/users/:id"
224
+ * @returns 正则表达式和参数名称数组
225
+ */
226
+ declare function pathToRegex(path: string): PathToRegexResult;
227
+ /**
228
+ * 从 URL 中提取路径参数
229
+ *
230
+ * @param url - 请求 URL
231
+ * @param pathPattern - 路径模式
232
+ * @returns 路径参数对象
233
+ */
234
+ declare function extractPathParams(url: string, pathPattern: string): Record<string, string>;
235
+ /**
236
+ * 从请求中提取查询参数
237
+ *
238
+ * @param request - 请求对象
239
+ * @returns 查询参数对象
240
+ */
241
+ declare function extractQueryParams(request: Request): Record<string, string | string[]>;
242
+ /**
243
+ * 解析请求体
244
+ *
245
+ * 根据 Content-Type 头部解析请求体内容
246
+ *
247
+ * @param request - 请求对象
248
+ * @returns 解析后的请求体
249
+ */
250
+ declare function parseRequestBody(request: Request): Promise<unknown>;
251
+ /**
252
+ * 创建路由处理器
253
+ *
254
+ * 将 RouteDefinition 转换为 Bun 原生路由处理函数
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * import {
259
+ * createRouteHandler,
260
+ * defineRoute,
261
+ * ApiResponseSchema,
262
+ * z,
263
+ * } from '@longzai-intelligence-transport/http';
264
+ *
265
+ * const userRoute = defineRoute({
266
+ * method: 'GET',
267
+ * path: '/users/:id',
268
+ * params: z.object({ id: z.string() }),
269
+ * response: ApiResponseSchema(z.object({ id: z.string(), name: z.string() })),
270
+ * });
271
+ *
272
+ * const handler = createRouteHandler(userRoute, async (ctx) => {
273
+ * const user = await getUser(ctx.params.id);
274
+ * return { success: true, data: user };
275
+ * });
276
+ *
277
+ * // 在 Bun 服务器中使用
278
+ * const server = Bun.serve({
279
+ * fetch: handler,
280
+ * });
281
+ * ```;
282
+ *
283
+ * @typeParam TRoute - 路由定义类型
284
+ * @typeParam TContext - 自定义上下文类型
285
+ * @param route - 路由定义
286
+ * @param handler - 业务逻辑处理函数
287
+ * @param options - 处理器选项
288
+ * @returns Bun 路由处理函数
289
+ */
290
+ declare function createRouteHandler<TRoute extends RouteDefinition, TContext = unknown>(route: TRoute, handler: RouteHandlerFn<TRoute, TContext>, options?: BunRouteHandlerOptions<TContext>): BunRouteHandler<TContext>;
291
+ /**
292
+ * 创建路由映射
293
+ *
294
+ * 批量将路由定义转换为 Bun 路由映射
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * import {
299
+ * createRouteMap,
300
+ * defineRoute,
301
+ * ApiResponseSchema,
302
+ * z,
303
+ * } from '@longzai-intelligence-transport/http';
304
+ *
305
+ * const routes = {
306
+ * list: defineRoute({
307
+ * method: 'GET',
308
+ * path: '/users',
309
+ * response: ApiResponseSchema(z.array(z.object({ id: z.string(), name: z.string() }))),
310
+ * }),
311
+ * detail: defineRoute({
312
+ * method: 'GET',
313
+ * path: '/users/:id',
314
+ * params: z.object({ id: z.string() }),
315
+ * response: ApiResponseSchema(z.object({ id: z.string(), name: z.string() })),
316
+ * }),
317
+ * };
318
+ *
319
+ * const handlers = {
320
+ * list: async () => ({ success: true, data: await getUsers() }),
321
+ * detail: async (ctx) => ({ success: true, data: await getUser(ctx.params.id) }),
322
+ * };
323
+ *
324
+ * const routeMap = createRouteMap(routes, handlers);
325
+ *
326
+ * // 在 Bun 服务器中使用
327
+ * const server = Bun.serve({
328
+ * fetch: (request) => {
329
+ * const url = new URL(request.url);
330
+ * const handler = routeMap[url.pathname]?.[request.method];
331
+ * if (handler) return handler(request);
332
+ * return new Response('Not Found', { status: 404 });
333
+ * },
334
+ * });
335
+ * ```;
336
+ *
337
+ * @typeParam TRoutes - 路由定义映射类型
338
+ * @typeParam TContext - 自定义上下文类型
339
+ * @param routes - 路由定义映射
340
+ * @param handlers - 处理函数映射
341
+ * @param options - 处理器选项
342
+ * @returns Bun 路由映射,可用于 Bun.serve 的 fetch 处理
343
+ */
344
+ declare function createRouteMap<TRoutes extends Record<string, RouteDefinition>, TContext = unknown>(routes: TRoutes, handlers: RoutesMap<TRoutes, TContext>, options?: BunRouteHandlerOptions<TContext>): Record<string, Record<string, BunRouteHandler<TContext>>>;
345
+ /**
346
+ * 创建 Bun 服务器 fetch 处理器
347
+ *
348
+ * 将路由映射转换为 Bun.serve 可用的 fetch 函数
349
+ *
350
+ * @typeParam TContext - 自定义上下文类型
351
+ * @param routeMap - 路由映射
352
+ * @param options - 额外选项
353
+ * @returns fetch 处理函数
354
+ */
355
+ declare function createFetchHandler<TContext = unknown>(routeMap: Record<string, Record<string, BunRouteHandler<TContext>>>, options?: FetchHandlerOptions): FetchHandlerFn;
356
+ //#endregion
357
+ export { type BunRequestContext, type BunRouteHandler, type BunRouteHandlerOptions, type FetchHandlerFn, type FetchHandlerOptions, type PathToRegexResult, type RouteHandlerFn, type RouteMapEntry, type RoutesMap, type ValidatedRouteContext, type ValidationErrorData, type ValidationErrorDetail, createFetchHandler, createRouteHandler, createRouteMap, extractPathParams, extractQueryParams, parseRequestBody, pathToRegex };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{createValidationErrorResponseFromError as e,validateRequest as t}from"@longzai-intelligence-transport/http-core";function n(e){let t=[],n=/:([^/]+)/g,r=n.exec(e);for(;r!==null;)r[1]&&t.push(r[1]),r=n.exec(e);return t}function r(e){let t=n(e),r=e.replace(/:([^/]+)/g,`(?<$1>[^/]+)`);return{regex:RegExp(`^${r}$`),paramNames:t}}function i(e,t){let{regex:n}=r(t),i=new URL(e).pathname,a=n.exec(i);return!a||!a.groups?{}:a.groups}function a(e){let t=new URL(e.url),n={};return t.searchParams.forEach((e,t)=>{let r=n[t];r===void 0?n[t]=e:typeof r==`string`?n[t]=[r,e]:r.push(e)}),n}async function o(e){let t=e.headers.get(`content-type`);if(t){if(t.includes(`application/json`))try{return await e.json()}catch{return}if(t.includes(`multipart/form-data`)){let t=await e.formData(),n={};return t.forEach((e,t)=>{n[t]=e}),n}if(t.includes(`application/x-www-form-urlencoded`)){let t=await e.formData(),n={};return t.forEach((e,t)=>{n[t]=e}),n}if(t.includes(`text/`))return await e.text()}}function s(e){let t=e instanceof Error?e.message:`服务器内部错误`;return new Response(JSON.stringify({success:!1,error:{code:`INTERNAL_ERROR`,message:t},message:t,timestamp:Date.now()}),{status:500,headers:{"Content-Type":`application/json`}})}async function c(e,t,n,r,i,a){let o={request:t,params:n,query:r,body:i};a?.createContext&&(o.context=await a.createContext(o));let s=await e(o);return new Response(JSON.stringify(s),{status:200,headers:{"Content-Type":`application/json`}})}function l(n,r,l){return async u=>{try{let d=t(n,{params:i(u.url,n.path),query:a(u),body:await o(u)});if(!d.success){if(!d.error)return s(Error(`验证失败但未提供错误详情`));if(l?.onValidationError)return await l.onValidationError(d.error,u);let t=e(d.error);return new Response(JSON.stringify(t),{status:400,headers:{"Content-Type":`application/json`}})}return await c(r,u,d.params??{},d.query??{},d.body,l)}catch(e){return l?.onError?l.onError(e,u):s(e)}}}function u(e,t,n){let r={};for(let[i,a]of Object.entries(e)){let e=t[i];if(e){let t=l(a,e,n),i=a.path;r[i]||(r[i]={}),r[i][a.method]=t}}return r}function d(e,t){return async n=>{let r=e[new URL(n.url).pathname];if(!r)return t?.onNotFound?t.onNotFound(n):new Response(JSON.stringify({success:!1,error:{code:`NOT_FOUND`,message:`资源不存在`},message:`资源不存在`,timestamp:Date.now()}),{status:404,headers:{"Content-Type":`application/json`}});let i=r[n.method];return i?i(n):t?.onMethodNotAllowed?t.onMethodNotAllowed(n):new Response(JSON.stringify({success:!1,error:{code:`METHOD_NOT_ALLOWED`,message:`方法不允许`},message:`方法不允许`,timestamp:Date.now()}),{status:405,headers:{"Content-Type":`application/json`}})}}export{d as createFetchHandler,l as createRouteHandler,u as createRouteMap,i as extractPathParams,a as extractQueryParams,o as parseRequestBody,r as pathToRegex};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@longzai-intelligence-transport/http-adapter-bun",
3
+ "version": "0.1.0",
4
+ "keywords": [
5
+ "adapter",
6
+ "bun",
7
+ "http",
8
+ "route-handler",
9
+ "server",
10
+ "utils"
11
+ ],
12
+ "license": "UNLICENSED",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "tsgo --build tsconfig/build.json && resolve-aliases -p tsconfig/build.json",
27
+ "build:prod": "NODE_ENV=production tsdown",
28
+ "prepublishOnly": "bun run build:prod",
29
+ "typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
30
+ "typecheck:app": "tsgo --noEmit -p tsconfig/app.json",
31
+ "typecheck:node": "tsgo --noEmit -p tsconfig/node.json",
32
+ "typecheck:test": "tsgo --noEmit -p tsconfig/test.json",
33
+ "lint": "oxlint && oxfmt --check",
34
+ "lint:fix": "oxlint --fix && oxfmt",
35
+ "test": "bun test",
36
+ "test:watch": "bun test --watch",
37
+ "test:coverage": "bun test --coverage",
38
+ "clean": "rimraf dist out .cache"
39
+ },
40
+ "dependencies": {
41
+ "@longzai-intelligence-transport/http-core": "0.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "zod": "^4.4.3"
45
+ },
46
+ "peerDependencies": {
47
+ "bun": ">=1.0.0",
48
+ "zod": "^4.4.3"
49
+ }
50
+ }