@longzai-intelligence-transport/http-adapter-nestjs 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.
- package/README.md +73 -0
- package/dist/index.d.ts +429 -0
- package/dist/index.js +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @longzai-intelligence-transport/http-adapter-nestjs
|
|
2
|
+
|
|
3
|
+
NestJS Swagger 集成适配器,提供路由装饰器和 Zod 到 OpenAPI 的转换工具。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @longzai-intelligence-transport/http-adapter-nestjs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 功能特性
|
|
12
|
+
|
|
13
|
+
- **路由装饰器** - `@ApiRoute` 装饰器,自动生成 Swagger 文档
|
|
14
|
+
- **认证装饰器** - `@RequireAuth` 和 `@PublicRoute` 装饰器
|
|
15
|
+
- **Zod 转 OpenAPI** - 将 Zod Schema 转换为 OpenAPI Schema
|
|
16
|
+
- **类型提取** - 从 Zod Schema 提取类型描述
|
|
17
|
+
|
|
18
|
+
## 依赖
|
|
19
|
+
|
|
20
|
+
- `@nestjs/common` - NestJS 核心模块(peer dependency)
|
|
21
|
+
- `@nestjs/swagger` - Swagger 集成模块(peer dependency)
|
|
22
|
+
- `zod` - Schema 验证(peer dependency)
|
|
23
|
+
- `@longzai-intelligence-transport/http-core` - 核心模块
|
|
24
|
+
|
|
25
|
+
## 使用示例
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import {
|
|
29
|
+
ApiRoute,
|
|
30
|
+
RequireAuth,
|
|
31
|
+
zodToOpenApi,
|
|
32
|
+
} from '@longzai-intelligence-transport/http-adapter-nestjs';
|
|
33
|
+
import { Controller, Get } from '@nestjs/common';
|
|
34
|
+
|
|
35
|
+
@Controller('users')
|
|
36
|
+
@RequireAuth()
|
|
37
|
+
export class UserController {
|
|
38
|
+
@Get(':id')
|
|
39
|
+
@ApiRoute({
|
|
40
|
+
summary: '获取用户信息',
|
|
41
|
+
response: UserSchema,
|
|
42
|
+
})
|
|
43
|
+
async getUser() {
|
|
44
|
+
// ...
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 导出模块
|
|
50
|
+
|
|
51
|
+
### 装饰器
|
|
52
|
+
|
|
53
|
+
- `ApiRoute` - 路由装饰器,自动生成 Swagger 文档
|
|
54
|
+
- `RequireAuth` - 标记需要认证的路由
|
|
55
|
+
- `PublicRoute` - 标记公开路由
|
|
56
|
+
- `ROUTE_DEFINITION_KEY` - 路由定义元数据键
|
|
57
|
+
|
|
58
|
+
### 工具函数
|
|
59
|
+
|
|
60
|
+
- `zodToOpenApi` - 将 Zod Schema 转换为 OpenAPI Schema
|
|
61
|
+
- `extractZodDescriptions` - 提取 Zod Schema 字段描述
|
|
62
|
+
- `getTypeName` - 获取类型名称
|
|
63
|
+
- `getDef` - 获取 Zod 定义
|
|
64
|
+
|
|
65
|
+
### 类型
|
|
66
|
+
|
|
67
|
+
- `OpenApiSchema` - OpenAPI Schema 类型
|
|
68
|
+
- `RouteDefinition` - 路由定义类型(从 core 重新导出)
|
|
69
|
+
- `HttpMethod` - HTTP 方法类型(从 core 重新导出)
|
|
70
|
+
|
|
71
|
+
## 许可证
|
|
72
|
+
|
|
73
|
+
UNLICENSED
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { ApiResponse, HttpMethod, HttpMethod as HttpMethod$1, InferRouteBody, InferRouteFullResponse, InferRouteParams, InferRouteQuery, InferRouteResponse, InferRouteResponse as InferRouteResponse$1, InferRouteReturn, RouteDefinition, RouteDefinition as RouteDefinition$1, RouteGroup, RouteGroup as RouteGroup$1, createTypedEmpty, createTypedResponse } from "@longzai-intelligence-transport/http-core";
|
|
2
|
+
import { ZodType, core } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/decorators/api-route.decorator.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 任意路由定义类型
|
|
7
|
+
*
|
|
8
|
+
* 用于装饰器内部,放宽泛型约束以便统一处理
|
|
9
|
+
*/
|
|
10
|
+
type AnyRouteDefinition = RouteDefinition$1<HttpMethod$1, string, ZodType | undefined, ZodType | undefined, ZodType | undefined, ZodType>;
|
|
11
|
+
/**
|
|
12
|
+
* 路由元数据 Key
|
|
13
|
+
*
|
|
14
|
+
* 用于在反射元数据中存储路由定义信息的键名
|
|
15
|
+
*/
|
|
16
|
+
declare const ROUTE_DEFINITION_KEY = "route_definition";
|
|
17
|
+
/**
|
|
18
|
+
* 错误响应配置项
|
|
19
|
+
*/
|
|
20
|
+
type ErrorResponseBody = {
|
|
21
|
+
/**
|
|
22
|
+
* HTTP 状态码
|
|
23
|
+
*/
|
|
24
|
+
status: number;
|
|
25
|
+
/**
|
|
26
|
+
* 错误描述文本
|
|
27
|
+
*/
|
|
28
|
+
description: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* API 路由装饰器可选项
|
|
32
|
+
*/
|
|
33
|
+
type ApiRouteOptions$1 = {
|
|
34
|
+
/**
|
|
35
|
+
* 是否需要认证,默认为 true
|
|
36
|
+
*/
|
|
37
|
+
requireAuth?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* 成功响应的描述文本,默认为"成功"
|
|
40
|
+
*/
|
|
41
|
+
successDescription?: string;
|
|
42
|
+
/**
|
|
43
|
+
* 错误响应配置数组
|
|
44
|
+
*/
|
|
45
|
+
errorResponses?: Array<ErrorResponseBody>;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* 根据路由定义自动生成 Swagger 文档的装饰器
|
|
49
|
+
*
|
|
50
|
+
* 集成路由定义与 NestJS Swagger,自动添加认证标记、API 操作描述、
|
|
51
|
+
* 响应模式、路径参数、查询参数和请求体。
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* @ApiRoute(userRoutes.getProfile, { requireAuth: true })
|
|
56
|
+
* async getProfile() { ... }
|
|
57
|
+
* ```;
|
|
58
|
+
*
|
|
59
|
+
* @typeParam T - 路由定义类型
|
|
60
|
+
* @param route - 路由定义对象,包含请求和响应的 Zod schema
|
|
61
|
+
* @param options - 可选配置项
|
|
62
|
+
* @param options.requireAuth - 是否需要认证,默认为 true
|
|
63
|
+
* @param options.successDescription - 成功响应的描述文本,默认为"成功"
|
|
64
|
+
* @param options.errorResponses - 错误响应配置数组
|
|
65
|
+
* @returns 组合装饰器
|
|
66
|
+
*/
|
|
67
|
+
declare function ApiRoute<T extends AnyRouteDefinition>(route: T, options?: ApiRouteOptions$1): <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
68
|
+
/**
|
|
69
|
+
* 标记路由需要认证
|
|
70
|
+
*
|
|
71
|
+
* 便捷装饰器,用于显式声明接口需要 Bearer Token 认证。等同于直接使用 @ApiBearerAuth()。
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* @RequireAuth()
|
|
76
|
+
* async protectedEndpoint() { ... }
|
|
77
|
+
* ```;
|
|
78
|
+
*
|
|
79
|
+
* @returns Bearer Auth 装饰器
|
|
80
|
+
*/
|
|
81
|
+
declare function RequireAuth(): ClassDecorator & MethodDecorator;
|
|
82
|
+
/**
|
|
83
|
+
* 标记路由为公开接口
|
|
84
|
+
*
|
|
85
|
+
* 空装饰器,用于显式声明接口不需要认证。实际的公开访问控制需要在全局 Guard 中配合 SkipAuth 装饰器实现。
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* 此装饰器本身不实现任何功能,仅作为代码可读性的标记。
|
|
89
|
+
* 如需跳过认证,请配合使用 @SetMetadata('isPublic', true) 或自定义装饰器。
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* @PublicRoute()
|
|
93
|
+
* async publicEndpoint() { ... }
|
|
94
|
+
* ```;
|
|
95
|
+
*
|
|
96
|
+
* @returns 空组合装饰器
|
|
97
|
+
*/
|
|
98
|
+
declare function PublicRoute(): <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/decorators/typed-controller.decorator.d.ts
|
|
101
|
+
/**
|
|
102
|
+
* 可用于 TypedController 的路由类型
|
|
103
|
+
*
|
|
104
|
+
* 包含 prefix 和可选的 tags、auth 属性
|
|
105
|
+
*/
|
|
106
|
+
type TypedControllerRoute = {
|
|
107
|
+
/**
|
|
108
|
+
* 路径前缀
|
|
109
|
+
*/
|
|
110
|
+
readonly prefix: string;
|
|
111
|
+
/**
|
|
112
|
+
* 认证策略
|
|
113
|
+
*/
|
|
114
|
+
readonly auth?: string;
|
|
115
|
+
/**
|
|
116
|
+
* API 标签
|
|
117
|
+
*/
|
|
118
|
+
readonly tags?: string[];
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* 带有路由定义的路由分组
|
|
122
|
+
*
|
|
123
|
+
* 扩展 RouteGroup,额外包含 routes 属性用于存放路由定义集合
|
|
124
|
+
*
|
|
125
|
+
* @typeParam TPrefix - 路径前缀类型
|
|
126
|
+
*/
|
|
127
|
+
type RouteGroupWithRoutes<TPrefix extends string> = RouteGroup$1<TPrefix> & {
|
|
128
|
+
/**
|
|
129
|
+
* 路由定义集合
|
|
130
|
+
*/
|
|
131
|
+
readonly routes: unknown;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* TypedController 可接受的输入类型
|
|
135
|
+
*
|
|
136
|
+
* 支持路由分组、带路由定义的路由分组,以及手动构造的路由类型
|
|
137
|
+
*
|
|
138
|
+
* @typeParam TPrefix - 路径前缀类型
|
|
139
|
+
*/
|
|
140
|
+
type TypedControllerInput<TPrefix extends string> = RouteGroup$1<TPrefix> | RouteGroupWithRoutes<TPrefix> | TypedControllerRoute;
|
|
141
|
+
/**
|
|
142
|
+
* 从路由分组自动生成 Controller 和 Swagger 文档标签的装饰器
|
|
143
|
+
*
|
|
144
|
+
* 集成路由分组与 NestJS Controller,自动提取分组的 prefix 设置 @Controller(),
|
|
145
|
+
* 提取 tags 设置 @ApiTags(),替代手动组合 @Controller() + @ApiTags() 的写法。
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* // 使用 RouteGroup
|
|
150
|
+
* @TypedController(clientApi)
|
|
151
|
+
* export class ActivityController {}
|
|
152
|
+
*
|
|
153
|
+
* // 使用带 routes 的路由分组
|
|
154
|
+
* @TypedController(authRoutes)
|
|
155
|
+
* export class AuthController {}
|
|
156
|
+
* ```;
|
|
157
|
+
*
|
|
158
|
+
* @typeParam TPrefix - 路径前缀类型
|
|
159
|
+
* @param route - 路由分组或路由模块对象,包含 prefix 和可选的 tags
|
|
160
|
+
* @returns 组合的类装饰器
|
|
161
|
+
*/
|
|
162
|
+
declare function TypedController<TPrefix extends string>(route: TypedControllerInput<TPrefix>): ClassDecorator;
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/decorators/create-typed-handler.decorator.d.ts
|
|
165
|
+
/**
|
|
166
|
+
* Handler 路由类型
|
|
167
|
+
*
|
|
168
|
+
* 包含 prefix 和可选的 tags、auth 属性
|
|
169
|
+
*/
|
|
170
|
+
type TypedHandlerRoute = {
|
|
171
|
+
/**
|
|
172
|
+
* 路径前缀
|
|
173
|
+
*/
|
|
174
|
+
readonly prefix: string;
|
|
175
|
+
/**
|
|
176
|
+
* 认证策略
|
|
177
|
+
*/
|
|
178
|
+
readonly auth?: string;
|
|
179
|
+
/**
|
|
180
|
+
* API 标签
|
|
181
|
+
*/
|
|
182
|
+
readonly tags?: string[];
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Handler 装饰器工厂返回类型
|
|
186
|
+
*
|
|
187
|
+
* 无参类装饰器工厂函数
|
|
188
|
+
*/
|
|
189
|
+
type TypedHandlerDecoratorFactory = () => ClassDecorator;
|
|
190
|
+
/**
|
|
191
|
+
* Handler 模式的类装饰器工厂
|
|
192
|
+
*
|
|
193
|
+
* 绑定路由后返回类装饰器,语义上用于 handler 模式(一个类对应一个路由操作)。
|
|
194
|
+
* 从路由定义自动提取 prefix 和 tags,组合 @Controller() + @ApiTags()。
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const { TypedHandler } = defineHandler(clientRoutes.activities.list);
|
|
199
|
+
*
|
|
200
|
+
* @TypedHandler()
|
|
201
|
+
* export class ListActivitiesHandler {
|
|
202
|
+
* @TypedHandle()
|
|
203
|
+
* async handle() {}
|
|
204
|
+
* }
|
|
205
|
+
* ```;
|
|
206
|
+
*
|
|
207
|
+
* @param route - 路由定义或路由分组对象
|
|
208
|
+
* @returns 类装饰器工厂函数
|
|
209
|
+
*/
|
|
210
|
+
declare function createTypedHandler(route: RouteDefinition$1 | RouteGroup$1<string> | TypedHandlerRoute): TypedHandlerDecoratorFactory;
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/decorators/typed-route.decorator.d.ts
|
|
213
|
+
/**
|
|
214
|
+
* ApiRoute 的可选项类型
|
|
215
|
+
*/
|
|
216
|
+
type ApiRouteOptions = Parameters<typeof ApiRoute>[1];
|
|
217
|
+
/**
|
|
218
|
+
* 从路由定义自动生成 NestJS 方法装饰器和 Swagger 文档的装饰器
|
|
219
|
+
*
|
|
220
|
+
* 集成路由定义与 NestJS HTTP 方法装饰器,自动提取 method 设置对应的方法装饰器,
|
|
221
|
+
* 优先使用 basePath(分组路由的相对路径),复用 ApiRoute 生成 Swagger 文档。
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* @TypedController(clientRoutes.activities)
|
|
226
|
+
* export class ActivityController {
|
|
227
|
+
* @TypedRoute(clientRoutes.activities.detail)
|
|
228
|
+
* async getDetail() {}
|
|
229
|
+
* }
|
|
230
|
+
* ```;
|
|
231
|
+
*
|
|
232
|
+
* @typeParam T - 路由定义类型
|
|
233
|
+
* @param route - 路由定义对象
|
|
234
|
+
* @param options - 可选配置项,透传给 ApiRoute
|
|
235
|
+
* @returns 组合的方法装饰器
|
|
236
|
+
*/
|
|
237
|
+
declare function TypedRoute<T extends AnyRouteDefinition>(route: T, options?: ApiRouteOptions): MethodDecorator;
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/utils/zod-to-openapi.types.d.ts
|
|
240
|
+
/**
|
|
241
|
+
* OpenAPI Schema 定义
|
|
242
|
+
*
|
|
243
|
+
* 描述 OpenAPI 3.0 规范中的 Schema 对象结构
|
|
244
|
+
*/
|
|
245
|
+
type OpenApiSchema = {
|
|
246
|
+
/**
|
|
247
|
+
* 数据类型
|
|
248
|
+
*/
|
|
249
|
+
type?: string;
|
|
250
|
+
/**
|
|
251
|
+
* 对象属性
|
|
252
|
+
*/
|
|
253
|
+
properties?: Record<string, OpenApiSchema>;
|
|
254
|
+
/**
|
|
255
|
+
* 数组项 Schema
|
|
256
|
+
*/
|
|
257
|
+
items?: OpenApiSchema;
|
|
258
|
+
/**
|
|
259
|
+
* 枚举值
|
|
260
|
+
*/
|
|
261
|
+
enum?: (string | number | boolean)[];
|
|
262
|
+
/**
|
|
263
|
+
* 格式修饰符
|
|
264
|
+
*/
|
|
265
|
+
format?: string;
|
|
266
|
+
/**
|
|
267
|
+
* 最小值
|
|
268
|
+
*/
|
|
269
|
+
minimum?: number;
|
|
270
|
+
/**
|
|
271
|
+
* 最大值
|
|
272
|
+
*/
|
|
273
|
+
maximum?: number;
|
|
274
|
+
/**
|
|
275
|
+
* 最小长度
|
|
276
|
+
*/
|
|
277
|
+
minLength?: number;
|
|
278
|
+
/**
|
|
279
|
+
* 最大长度
|
|
280
|
+
*/
|
|
281
|
+
maxLength?: number;
|
|
282
|
+
/**
|
|
283
|
+
* 最小项数
|
|
284
|
+
*/
|
|
285
|
+
minItems?: number;
|
|
286
|
+
/**
|
|
287
|
+
* 最大项数
|
|
288
|
+
*/
|
|
289
|
+
maxItems?: number;
|
|
290
|
+
/**
|
|
291
|
+
* 必需字段列表
|
|
292
|
+
*/
|
|
293
|
+
required?: string[];
|
|
294
|
+
/**
|
|
295
|
+
* 默认值
|
|
296
|
+
*/
|
|
297
|
+
default?: unknown;
|
|
298
|
+
/**
|
|
299
|
+
* 描述文本
|
|
300
|
+
*/
|
|
301
|
+
description?: string;
|
|
302
|
+
/**
|
|
303
|
+
* 额外属性 Schema
|
|
304
|
+
*/
|
|
305
|
+
additionalProperties?: OpenApiSchema | boolean;
|
|
306
|
+
/**
|
|
307
|
+
* 是否可空
|
|
308
|
+
*/
|
|
309
|
+
nullable?: boolean;
|
|
310
|
+
/**
|
|
311
|
+
* 联合类型(oneOf)
|
|
312
|
+
*/
|
|
313
|
+
oneOf?: OpenApiSchema[];
|
|
314
|
+
/**
|
|
315
|
+
* 联合类型(anyOf)
|
|
316
|
+
*/
|
|
317
|
+
anyOf?: OpenApiSchema[];
|
|
318
|
+
/**
|
|
319
|
+
* 交叉类型(allOf)
|
|
320
|
+
*/
|
|
321
|
+
allOf?: OpenApiSchema[];
|
|
322
|
+
};
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/utils/zod-to-openapi.converter.d.ts
|
|
325
|
+
/**
|
|
326
|
+
* 将 Zod Schema 转换为 OpenAPI Schema
|
|
327
|
+
*
|
|
328
|
+
* 用于生成 Swagger 文档。这是一个简化实现,支持常用的 Zod 类型。
|
|
329
|
+
*
|
|
330
|
+
* @remarks
|
|
331
|
+
* 为什么使用自定义实现而不是第三方库:
|
|
332
|
+
* 1. 项目使用 Zod v4,而大多数第三方库(如 zod-openapi)主要支持 Zod v3
|
|
333
|
+
* 2. 自定义实现可以更好地控制输出格式,满足项目特定需求
|
|
334
|
+
* 3. 避免引入额外依赖,减少包大小
|
|
335
|
+
* 当前支持的 Zod 类型:
|
|
336
|
+
* - 基础类型:string, number, boolean, date
|
|
337
|
+
* - 复合类型:array, object, record
|
|
338
|
+
* - 修饰类型:optional, nullable, default
|
|
339
|
+
* - 字面量类型:enum, literal
|
|
340
|
+
* - 联合类型:union, intersection
|
|
341
|
+
*
|
|
342
|
+
* @param schema - Zod Schema 对象(支持 core.$ZodType 及其子类型 ZodType)
|
|
343
|
+
* @returns OpenAPI Schema 对象
|
|
344
|
+
*/
|
|
345
|
+
declare function zodToOpenApi(schema: core.$ZodType): OpenApiSchema;
|
|
346
|
+
/**
|
|
347
|
+
* 从 Zod Schema 提取字段描述
|
|
348
|
+
*
|
|
349
|
+
* 遍历 ZodObject 的 shape,提取每个字段的 description 属性
|
|
350
|
+
*
|
|
351
|
+
* @param schema - Zod Schema 对象(支持 core.$ZodType 及其子类型 ZodType)
|
|
352
|
+
* @returns 字段名到描述文本的映射
|
|
353
|
+
*/
|
|
354
|
+
declare function extractZodDescriptions(schema: core.$ZodType): Record<string, string>;
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/utils/zod-to-openapi.utils.d.ts
|
|
357
|
+
/**
|
|
358
|
+
* 获取 Zod 类型的类型名称
|
|
359
|
+
*
|
|
360
|
+
* Zod v4 使用 def.type 作为类型标识
|
|
361
|
+
*
|
|
362
|
+
* @param def - Zod 定义对象
|
|
363
|
+
* @returns 类型名称字符串
|
|
364
|
+
*/
|
|
365
|
+
declare function getTypeName(def: core.$ZodTypeDef): string | undefined;
|
|
366
|
+
/**
|
|
367
|
+
* 获取 Zod Schema 的定义对象
|
|
368
|
+
*
|
|
369
|
+
* Zod v4 使用 _zod.def 作为内部结构
|
|
370
|
+
*
|
|
371
|
+
* @param schema - Zod Schema 对象(支持 core.$ZodType 及其子类型)
|
|
372
|
+
* @returns 定义对象
|
|
373
|
+
*/
|
|
374
|
+
declare function getDef(schema: core.$ZodType): core.$ZodTypeDef;
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/utils/handler.utils.d.ts
|
|
377
|
+
/**
|
|
378
|
+
* defineHandler 返回值类型
|
|
379
|
+
*
|
|
380
|
+
* @typeParam R - 路由定义类型
|
|
381
|
+
*/
|
|
382
|
+
type HandlerBindings<R extends RouteDefinition$1> = {
|
|
383
|
+
/**
|
|
384
|
+
* Handler 类装饰器工厂函数
|
|
385
|
+
*/
|
|
386
|
+
readonly TypedHandler: () => ClassDecorator;
|
|
387
|
+
/**
|
|
388
|
+
* 路由方法装饰器
|
|
389
|
+
*/
|
|
390
|
+
readonly TypedHandle: () => MethodDecorator;
|
|
391
|
+
/**
|
|
392
|
+
* 类型安全响应函数
|
|
393
|
+
*/
|
|
394
|
+
readonly typedResponse: (data: InferRouteResponse$1<R>) => ApiResponse<InferRouteResponse$1<R>>;
|
|
395
|
+
/**
|
|
396
|
+
* 类型安全空响应函数
|
|
397
|
+
*/
|
|
398
|
+
readonly typedEmpty: () => ApiResponse<void>;
|
|
399
|
+
};
|
|
400
|
+
/**
|
|
401
|
+
* 一次性合成 Handler 所需的全部绑定
|
|
402
|
+
*
|
|
403
|
+
* 输入路由定义,返回类装饰器、方法装饰器和响应函数。
|
|
404
|
+
* 路由类型通过独立的 route 变量配合 InferRouteParams / InferRouteReturn 等类型工具使用。
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```typescript
|
|
408
|
+
* const route = api.client.order.routes.detail;
|
|
409
|
+
* const { TypedHandler, TypedHandle, typedResponse } = defineHandler(route);
|
|
410
|
+
*
|
|
411
|
+
* @TypedHandler()
|
|
412
|
+
* export class DetailHandler {
|
|
413
|
+
* @TypedHandle()
|
|
414
|
+
* async handle(
|
|
415
|
+
* @Param('id') id: InferRouteParams<typeof route>['id'],
|
|
416
|
+
* ): InferRouteReturn<typeof route> {
|
|
417
|
+
* const data = await this.orderService.findById(id);
|
|
418
|
+
* return typedResponse(data);
|
|
419
|
+
* }
|
|
420
|
+
* }
|
|
421
|
+
* ```;
|
|
422
|
+
*
|
|
423
|
+
* @typeParam R - 路由定义类型
|
|
424
|
+
* @param route - 路由定义
|
|
425
|
+
* @returns Handler 绑定对象
|
|
426
|
+
*/
|
|
427
|
+
declare function defineHandler<R extends RouteDefinition$1>(route: R): HandlerBindings<R>;
|
|
428
|
+
//#endregion
|
|
429
|
+
export { type AnyRouteDefinition, ApiRoute, type HandlerBindings, type HttpMethod, type InferRouteBody, type InferRouteFullResponse, type InferRouteParams, type InferRouteQuery, type InferRouteResponse, type InferRouteReturn, type OpenApiSchema, PublicRoute, ROUTE_DEFINITION_KEY, RequireAuth, type RouteDefinition, type RouteGroup, TypedController, TypedRoute, createTypedEmpty, createTypedHandler, createTypedResponse, defineHandler, extractZodDescriptions, getDef, getTypeName, zodToOpenApi };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Controller as e,Delete as t,Get as n,Patch as r,Post as i,Put as a,applyDecorators as o}from"@nestjs/common";import{ApiBearerAuth as s,ApiBody as c,ApiOperation as l,ApiParam as u,ApiQuery as d,ApiResponse as f,ApiTags as p}from"@nestjs/swagger";import{createTypedEmpty as m,createTypedEmpty as h,createTypedResponse as g,createTypedResponse as ee}from"@longzai-intelligence-transport/http-core";function _(e){return e.type}function v(e){return e._zod.def}function y(e,t){return t===`array`}function b(e,t){return t===`object`}function x(e,t){return t===`record`}function S(e,t){return t===`optional`||t===`nullable`}function C(e,t){return t==="default"}function w(e,t){return t===`enum`}function T(e,t){return t===`literal`}function E(e,t){return t===`union`}function D(e,t){return t===`intersection`}function O(e,t){return{type:`array`,items:e.element?t(e.element):{type:`object`}}}function k(e,t){let n={},r=[],i=e.shape;if(i)for(let[e,a]of Object.entries(i)){n[e]=t(a);let i=_(v(a));i!==`optional`&&i!==`nullable`&&r.push(e)}let a={type:`object`,properties:n};return r.length>0&&(a.required=r),a}function A(e,t){return{type:`object`,additionalProperties:e.valueType?t(e.valueType):!0}}function j(e,t){return{...e.innerType?t(e.innerType):{type:`object`},nullable:!0}}function M(e,t){let n=e.defaultValue;return{...e.innerType?t(e.innerType):{type:`object`},default:typeof n==`function`?n():n}}function N(e){return{type:`string`,enum:e.entries?Object.values(e.entries):[]}}function P(e){let t=e.values?.[0];return typeof t==`string`?{type:`string`,enum:[t]}:typeof t==`number`?{type:`number`,enum:[t]}:typeof t==`boolean`?{type:`boolean`,enum:[t]}:{type:`string`}}function F(e,t){return{oneOf:e.options?.map(e=>t(e))??[]}}function I(e,t){let n=[];return e.left&&n.push(t(e.left)),e.right&&n.push(t(e.right)),{allOf:n}}const te={string:{type:`string`},number:{type:`number`},int:{type:`number`},boolean:{type:`boolean`},date:{type:`string`,format:`date-time`}};function L(e){let t=v(e),n=_(t);return n?te[n]||(y(t,n)?O(t,L):b(t,n)?k(t,L):x(t,n)?A(t,L):S(t,n)?j(t,L):C(t,n)?M(t,L):w(t,n)?N(t):T(t,n)?P(t):E(t,n)?F(t,L):D(t,n)?I(t,L):{type:`object`}):{type:`object`}}function R(e){let t={},n=v(e),r=_(n);if(r&&b(n,r)&&n.shape)for(let[e,r]of Object.entries(n.shape)){let n=`description`in r&&typeof r.description==`string`?r.description:void 0;n&&(t[e]=n)}return t}const z=`route_definition`;function B(e,t){t!==!1&&e.push(s())}function V(e,t,n){let r=L(t);e.push(f({status:200,description:n??`成功`,schema:r}))}function H(e,t){if(t)for(let n of t)e.push(f({status:n.status,description:n.description,schema:{type:`object`,properties:{code:{type:`string`},message:{type:`string`},data:{type:`object`,nullable:!0}}}}))}function U(e,t){if(!t)return;let n=L(t);if(n.properties)for(let[t,r]of Object.entries(n.properties))e.push(u({name:t,schema:r,required:!0}))}function W(e,t){if(!t)return;let n=L(t);if(n.properties){let t=n.required||[];for(let[r,i]of Object.entries(n.properties))e.push(d({name:r,schema:i,required:t.includes(r)}))}}function G(e,t){t&&e.push(c({schema:L(t)}))}function K(e,t){let n=[];return B(n,t?.requireAuth),n.push(l({summary:e.summary,tags:e.tags})),V(n,e.response,t?.successDescription),H(n,t?.errorResponses),U(n,e.params),W(n,e.query),G(n,e.body),o(...n)}function q(){return s()}function J(){return o()}function Y(t){let n=[];return n.push(e(t.prefix)),t.tags&&t.tags.length>0&&n.push(p(...t.tags)),o(...n)}function X(e){if(e.basePath){let t=e.basePath.startsWith(`/`)?e.basePath:`/${e.basePath}`;return(e.path.endsWith(t)?e.path.slice(0,-t.length):e.path)||`/`}let t=e.path.lastIndexOf(`/`);return t>0?e.path.slice(0,t):e.path}function Z(e){return`prefix`in e&&typeof e.prefix==`string`}function Q(t){return()=>{let n=[],r=Z(t)?t.prefix:X(t);return n.push(e(r)),t.tags&&t.tags.length>0&&n.push(p(...t.tags)),o(...n)}}function ne(e){switch(e){case`GET`:return n;case`POST`:return i;case`PUT`:return a;case`PATCH`:return r;case`DELETE`:return t}}function $(e,t){return o(ne(e.method)(e.basePath??e.path),K(e,t))}function re(e){return{TypedHandler:Q(e),TypedHandle:()=>$(e),typedResponse:ee(e),typedEmpty:h(e)}}export{K as ApiRoute,J as PublicRoute,z as ROUTE_DEFINITION_KEY,q as RequireAuth,Y as TypedController,$ as TypedRoute,m as createTypedEmpty,Q as createTypedHandler,g as createTypedResponse,re as defineHandler,R as extractZodDescriptions,v as getDef,_ as getTypeName,L as zodToOpenApi};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-transport/http-adapter-nestjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"adapter",
|
|
6
|
+
"decorator",
|
|
7
|
+
"http",
|
|
8
|
+
"nestjs",
|
|
9
|
+
"swagger",
|
|
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
|
+
"@nestjs/common": "^11.1.26",
|
|
45
|
+
"@nestjs/swagger": "^11.4.4",
|
|
46
|
+
"zod": "^4.4.3"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@nestjs/common": "^11.0.0",
|
|
50
|
+
"@nestjs/swagger": "^11.0.0",
|
|
51
|
+
"zod": "^4.4.3"
|
|
52
|
+
}
|
|
53
|
+
}
|