@mindbase/express-common 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/index.d.mts +375 -0
  2. package/dist/index.mjs +2635 -0
  3. package/dist/index.mjs.map +1 -0
  4. package/package.json +24 -6
  5. package/bin/mindbase.ts +0 -52
  6. package/commands/precache.ts +0 -54
  7. package/core/app.ts +0 -200
  8. package/core/module/CreateModule.ts +0 -38
  9. package/core/module/FindPackageRoot.ts +0 -58
  10. package/core/module/GetModulePath.ts +0 -58
  11. package/core/state.ts +0 -72
  12. package/feature/cron/CronManager.ts +0 -63
  13. package/feature/scanner/FileScanner.ts +0 -288
  14. package/index.ts +0 -10
  15. package/middleware/Cors.ts +0 -17
  16. package/middleware/IpParser.ts +0 -81
  17. package/middleware/UaParser.ts +0 -50
  18. package/routes/Doc.route.ts +0 -118
  19. package/tests/Cors.test.ts +0 -34
  20. package/tests/Dayjs.test.ts +0 -24
  21. package/tests/FileScanner.test.ts +0 -85
  22. package/tests/GetModulePath.test.ts +0 -32
  23. package/tests/IpParser.test.ts +0 -72
  24. package/tests/Logger.test.ts +0 -68
  25. package/tests/UaParser.test.ts +0 -41
  26. package/tsconfig.json +0 -9
  27. package/types/DocTypes.ts +0 -111
  28. package/types/index.ts +0 -19
  29. package/utils/ComponentRegistry.ts +0 -34
  30. package/utils/DatabaseMigration.ts +0 -121
  31. package/utils/Dayjs.ts +0 -16
  32. package/utils/DocManager.ts +0 -274
  33. package/utils/HttpServer.ts +0 -41
  34. package/utils/InitDatabase.ts +0 -149
  35. package/utils/InitErrorHandler.ts +0 -71
  36. package/utils/InitExpress.ts +0 -35
  37. package/utils/Logger.ts +0 -206
  38. package/utils/MiddlewareRegistry.ts +0 -14
  39. package/utils/ProjectInitializer.ts +0 -283
  40. package/utils/RouteParser.ts +0 -408
  41. package/utils/RouteRegistry.ts +0 -66
  42. package/utils/SchemaMigrate.ts +0 -73
  43. package/utils/SchemaSync.ts +0 -47
  44. package/utils/TSTypeParser.ts +0 -455
  45. package/utils/Validate.ts +0 -25
  46. package/utils/ZodSchemaParser.ts +0 -420
  47. package/vitest.config.ts +0 -18
  48. package/zod/Doc.schema.ts +0 -9
@@ -1,455 +0,0 @@
1
- import {
2
- Node,
3
- SyntaxKind,
4
- SourceFile,
5
- InterfaceDeclaration,
6
- TypeAliasDeclaration,
7
- PropertySignature,
8
- TypeReferenceNode,
9
- ArrayTypeNode,
10
- UnionTypeNode,
11
- IntersectionTypeNode,
12
- LiteralTypeNode,
13
- Identifier,
14
- } from "ts-morph";
15
- import { FieldValidation, ResponseSchema } from "../types/DocTypes";
16
- import logger from "./Logger";
17
-
18
- /**
19
- * 类型引用解析结果
20
- */
21
- interface TypeRef {
22
- typeName: string;
23
- typeArgs: string[]; // 泛型参数
24
- }
25
-
26
- /**
27
- * TypeScript 类型解析器
28
- * 从 interface/type 定义中提取字段结构
29
- */
30
- export class TSTypeParser {
31
- /**
32
- * 解析类型引用字符串
33
- * 例如: "ApiResponse<User[]>" -> { typeName: "ApiResponse", typeArgs: ["User[]"] }
34
- */
35
- public parseTypeRef(typeString: string): TypeRef | null {
36
- // 移除花括号内的内容(如果有)
37
- typeString = typeString.trim();
38
-
39
- // 匹配泛型: TypeName<Arg1, Arg2>
40
- const genericMatch = typeString.match(/^(\w+)<(.+)>$/);
41
- if (genericMatch) {
42
- const typeName = genericMatch[1];
43
- const argsStr = genericMatch[2];
44
- // 简单分割泛型参数(不支持嵌套泛型)
45
- const typeArgs = argsStr.split(",").map((s) => s.trim());
46
- return { typeName, typeArgs };
47
- }
48
-
49
- // 简单类型引用
50
- if (/^\w+$/.test(typeString)) {
51
- return { typeName: typeString, typeArgs: [] };
52
- }
53
-
54
- // 数组类型: User[]
55
- const arrayMatch = typeString.match(/^(\w+)\[\]$/);
56
- if (arrayMatch) {
57
- return { typeName: "Array", typeArgs: [arrayMatch[1]] };
58
- }
59
-
60
- return null;
61
- }
62
-
63
- /**
64
- * 解析响应类型
65
- * @param sourceFile 源文件
66
- * @param typeString 类型字符串如 "ApiResponse<User[]>"
67
- * @param imports 导入映射 (类型名 -> 文件路径)
68
- */
69
- public parseResponseType(
70
- sourceFile: SourceFile,
71
- typeString: string,
72
- imports: Map<string, string>
73
- ): ResponseSchema | null {
74
- const typeRef = this.parseTypeRef(typeString);
75
- if (!typeRef) {
76
- logger.debug(`无法解析类型引用: ${typeString}`);
77
- return null;
78
- }
79
-
80
- // 内置类型处理: ApiResponse<T>
81
- if (typeRef.typeName === "ApiResponse") {
82
- return this.parseApiResponse(sourceFile, typeRef.typeArgs, imports);
83
- }
84
-
85
- // 查找类型定义
86
- const typeDecl = this.findTypeDeclaration(sourceFile, typeRef.typeName, imports);
87
- if (!typeDecl) {
88
- logger.debug(`未找到类型定义: ${typeRef.typeName}`);
89
- return null;
90
- }
91
-
92
- // 解析字段
93
- const fields = this.extractFieldsFromType(typeDecl, typeRef.typeArgs, imports);
94
-
95
- return { fields };
96
- }
97
-
98
- /**
99
- * 解析 ApiResponse<T> 类型
100
- * ApiResponse 是标准的 API 响应包装器,包含 code, data, msg 字段
101
- */
102
- private parseApiResponse(
103
- sourceFile: SourceFile,
104
- typeArgs: string[],
105
- imports: Map<string, string>
106
- ): ResponseSchema {
107
- const fields: Record<string, FieldValidation> = {
108
- code: { type: "number", required: true },
109
- msg: { type: "string", required: true },
110
- };
111
-
112
- // 解析 data 字段的类型
113
- if (typeArgs.length > 0) {
114
- const dataType = typeArgs[0];
115
- const dataField = this.parseDataType(sourceFile, dataType, imports);
116
- fields.data = dataField;
117
- } else {
118
- fields.data = { type: "any", required: true };
119
- }
120
-
121
- return { fields };
122
- }
123
-
124
- /**
125
- * 解析 data 字段的类型
126
- */
127
- private parseDataType(
128
- sourceFile: SourceFile,
129
- typeString: string,
130
- imports: Map<string, string>
131
- ): FieldValidation {
132
- // 基础类型
133
- if (typeString === "string") {
134
- return { type: "string", required: true };
135
- } else if (typeString === "number") {
136
- return { type: "number", required: true };
137
- } else if (typeString === "boolean") {
138
- return { type: "boolean", required: true };
139
- } else if (typeString === "any") {
140
- return { type: "any", required: true };
141
- }
142
-
143
- // 数组类型: User[]
144
- const arrayMatch = typeString.match(/^(\w+)\[\]$/);
145
- if (arrayMatch) {
146
- const elementType = arrayMatch[1];
147
- const elementField = this.parseDataType(sourceFile, elementType, imports);
148
- return {
149
- type: "array",
150
- required: true,
151
- items: elementField,
152
- };
153
- }
154
-
155
- // 类型引用: LoginResult
156
- const typeDecl = this.findTypeDeclaration(sourceFile, typeString, imports);
157
- if (typeDecl) {
158
- const fields = this.extractFieldsFromType(typeDecl, [], imports);
159
- return {
160
- type: "object",
161
- required: true,
162
- properties: fields,
163
- };
164
- }
165
-
166
- // 默认返回 any
167
- return { type: "any", required: true };
168
- }
169
-
170
- /**
171
- * 查找类型声明
172
- */
173
- private findTypeDeclaration(
174
- sourceFile: SourceFile,
175
- typeName: string,
176
- imports: Map<string, string>
177
- ): InterfaceDeclaration | TypeAliasDeclaration | null {
178
- // 先在当前文件中查找
179
- const interfaceDecl = sourceFile.getInterface(typeName);
180
- if (interfaceDecl) return interfaceDecl;
181
-
182
- const typeAlias = sourceFile.getTypeAlias(typeName);
183
- if (typeAlias) return typeAlias;
184
-
185
- // 在导入的文件中查找
186
- const importPath = imports.get(typeName);
187
- if (importPath) {
188
- try {
189
- const importedFile = sourceFile.getProject().addSourceFileAtPath(importPath);
190
-
191
- const importedInterface = importedFile.getInterface(typeName);
192
- if (importedInterface) return importedInterface;
193
-
194
- const importedTypeAlias = importedFile.getTypeAlias(typeName);
195
- if (importedTypeAlias) return importedTypeAlias;
196
- } catch (error) {
197
- logger.warn(`无法加载导入文件: ${importPath}`);
198
- }
199
- }
200
-
201
- return null;
202
- }
203
-
204
- /**
205
- * 从类型声明中提取字段
206
- */
207
- private extractFieldsFromType(
208
- typeDecl: InterfaceDeclaration | TypeAliasDeclaration,
209
- typeArgs: string[],
210
- imports: Map<string, string>
211
- ): Record<string, FieldValidation> {
212
- const fields: Record<string, FieldValidation> = {};
213
-
214
- // 获取类型节点
215
- let typeNode: Node | undefined;
216
-
217
- if (Node.isInterfaceDeclaration(typeDecl)) {
218
- // 接口: 直接遍历属性
219
- for (const prop of typeDecl.getProperties()) {
220
- const fieldName = prop.getName();
221
- const fieldType = prop.getType();
222
- fields[fieldName] = this.parsePropertyType(prop, typeArgs, imports);
223
- }
224
- return fields;
225
- } else if (Node.isTypeAliasDeclaration(typeDecl)) {
226
- typeNode = typeDecl.getTypeNode();
227
- }
228
-
229
- if (!typeNode) return fields;
230
-
231
- // 处理类型别名
232
- if (Node.isTypeReferenceNode(typeNode)) {
233
- // 如果是类型引用,可能需要展开
234
- return this.extractFieldsFromTypeReference(typeNode, typeArgs, imports);
235
- } else if (Node.isIntersectionTypeNode(typeNode)) {
236
- // 交叉类型: 合并所有类型的字段
237
- for (const child of typeNode.getTypeNodes()) {
238
- const childFields = this.extractFieldsFromTypeNode(child, typeArgs, imports);
239
- Object.assign(fields, childFields);
240
- }
241
- } else if (Node.isUnionTypeNode(typeNode)) {
242
- // 联合类型: 取第一个类型的字段(简化处理)
243
- const firstType = typeNode.getTypeNodes()[0];
244
- if (firstType) {
245
- return this.extractFieldsFromTypeNode(firstType, typeArgs, imports);
246
- }
247
- } else {
248
- return this.extractFieldsFromTypeNode(typeNode, typeArgs, imports);
249
- }
250
-
251
- return fields;
252
- }
253
-
254
- /**
255
- * 从类型节点提取字段
256
- */
257
- private extractFieldsFromTypeNode(
258
- typeNode: Node,
259
- typeArgs: string[],
260
- imports: Map<string, string>
261
- ): Record<string, FieldValidation> {
262
- const fields: Record<string, FieldValidation> = {};
263
-
264
- if (Node.isTypeReferenceNode(typeNode)) {
265
- return this.extractFieldsFromTypeReference(typeNode, typeArgs, imports);
266
- } else if (Node.isIntersectionTypeNode(typeNode)) {
267
- for (const child of (typeNode as IntersectionTypeNode).getTypeNodes()) {
268
- const childFields = this.extractFieldsFromTypeNode(child, typeArgs, imports);
269
- Object.assign(fields, childFields);
270
- }
271
- }
272
-
273
- return fields;
274
- }
275
-
276
- /**
277
- * 从类型引用提取字段
278
- */
279
- private extractFieldsFromTypeReference(
280
- typeRefNode: TypeReferenceNode,
281
- typeArgs: string[],
282
- imports: Map<string, string>
283
- ): Record<string, FieldValidation> {
284
- const fields: Record<string, FieldValidation> = {};
285
- const typeName = typeRefNode.getTypeName().getText();
286
-
287
- // 特殊处理泛型参数
288
- if (typeName === "Array" || typeName === "ReadonlyArray") {
289
- // 数组类型,返回 items 信息
290
- const typeArgsNodes = typeRefNode.getTypeArguments();
291
- if (typeArgsNodes.length > 0) {
292
- const elementType = typeArgsNodes[0];
293
- fields["[]"] = {
294
- type: "array",
295
- required: true,
296
- items: this.parseTypeNodeToFieldValidation(elementType, typeArgs, imports),
297
- };
298
- }
299
- return fields;
300
- }
301
-
302
- // 处理 Promise<T> - 提取 T
303
- if (typeName === "Promise") {
304
- const typeArgsNodes = typeRefNode.getTypeArguments();
305
- if (typeArgsNodes.length > 0) {
306
- return this.extractFieldsFromTypeNode(typeArgsNodes[0], typeArgs, imports);
307
- }
308
- }
309
-
310
- // 处理泛型参数 T, K 等
311
- if (typeArgs.includes(typeName) && typeArgs.length > 0) {
312
- // 使用实际类型参数
313
- const actualType = typeArgs[0]; // 简化处理,取第一个
314
- // 这里可以递归解析实际类型
315
- }
316
-
317
- // 解析类型定义
318
- const sourceFile = typeRefNode.getSourceFile();
319
- const typeDecl = this.findTypeDeclarationInFile(sourceFile, typeName);
320
-
321
- if (typeDecl) {
322
- return this.extractFieldsFromType(typeDecl, typeArgs, imports);
323
- }
324
-
325
- return fields;
326
- }
327
-
328
- /**
329
- * 在文件中查找类型声明
330
- */
331
- private findTypeDeclarationInFile(
332
- sourceFile: SourceFile,
333
- typeName: string
334
- ): InterfaceDeclaration | TypeAliasDeclaration | null {
335
- const interfaceDecl = sourceFile.getInterface(typeName);
336
- if (interfaceDecl) return interfaceDecl;
337
-
338
- const typeAlias = sourceFile.getTypeAlias(typeName);
339
- if (typeAlias) return typeAlias;
340
-
341
- return null;
342
- }
343
-
344
- /**
345
- * 解析属性类型
346
- */
347
- private parsePropertyType(
348
- prop: PropertySignature,
349
- typeArgs: string[],
350
- imports: Map<string, string>
351
- ): FieldValidation {
352
- const type = prop.getType();
353
- const isOptional = prop.hasQuestionToken();
354
-
355
- // 获取类型文本
356
- const typeText = type.getText();
357
-
358
- // 基础类型判断
359
- if (type.isString()) {
360
- return { type: "string", required: !isOptional };
361
- } else if (type.isNumber()) {
362
- return { type: "number", required: !isOptional };
363
- } else if (type.isBoolean()) {
364
- return { type: "boolean", required: !isOptional };
365
- } else if (type.isArray()) {
366
- const elementType = type.getArrayElementType();
367
- let items: FieldValidation | undefined;
368
- if (elementType) {
369
- items = {
370
- type: this.mapTypeToString(elementType),
371
- required: !elementType.isUndefined() && !elementType.isNull(),
372
- };
373
- }
374
- return { type: "array", required: !isOptional, items };
375
- } else if (type.isObject()) {
376
- // 尝试获取对象属性
377
- const properties = type.getProperties();
378
- if (properties.length > 0) {
379
- const props: Record<string, FieldValidation> = {};
380
- for (const prop of properties) {
381
- const propName = prop.getName();
382
- const propType = prop.getTypeAtLocation(prop.getValueDeclaration() || prop.getDeclarations()[0]);
383
- props[propName] = {
384
- type: this.mapTypeToString(propType),
385
- required: !prop.isOptional(),
386
- };
387
- }
388
- return { type: "object", required: !isOptional, properties: props };
389
- }
390
- return { type: "object", required: !isOptional };
391
- } else if (type.isNull()) {
392
- return { type: "any", required: false, nullable: true };
393
- } else if (type.isUndefined()) {
394
- return { type: "any", required: false, optional: true };
395
- } else if (type.isAny()) {
396
- return { type: "any", required: !isOptional };
397
- }
398
-
399
- // 其他类型,使用类型文本
400
- return { type: "any", required: !isOptional };
401
- }
402
-
403
- /**
404
- * 将类型映射为字符串
405
- */
406
- private mapTypeToString(type: any): FieldValidation["type"] {
407
- if (type.isString()) return "string";
408
- if (type.isNumber()) return "number";
409
- if (type.isBoolean()) return "boolean";
410
- if (type.isArray()) return "array";
411
- if (type.isObject()) return "object";
412
- if (type.isDate()) return "date";
413
- return "any";
414
- }
415
-
416
- /**
417
- * 从类型节点创建 FieldValidation
418
- */
419
- private parseTypeNodeToFieldValidation(
420
- typeNode: Node,
421
- typeArgs: string[],
422
- imports: Map<string, string>
423
- ): FieldValidation {
424
- if (Node.isStringKeyword(typeNode)) {
425
- return { type: "string", required: true };
426
- } else if (Node.isNumberKeyword(typeNode)) {
427
- return { type: "number", required: true };
428
- } else if (Node.isBooleanKeyword(typeNode)) {
429
- return { type: "boolean", required: true };
430
- } else if (Node.isArrayTypeNode(typeNode)) {
431
- const elementType = (typeNode as ArrayTypeNode).getElementTypeNode();
432
- return {
433
- type: "array",
434
- required: true,
435
- items: this.parseTypeNodeToFieldValidation(elementType, typeArgs, imports),
436
- };
437
- } else if (Node.isTypeReferenceNode(typeNode)) {
438
- const typeName = typeNode.getTypeName().getText();
439
- const sourceFile = typeNode.getSourceFile();
440
- const typeDecl = this.findTypeDeclarationInFile(sourceFile, typeName);
441
-
442
- if (typeDecl) {
443
- const fields = this.extractFieldsFromType(typeDecl, typeArgs, imports);
444
- return { type: "object", required: true, properties: fields };
445
- }
446
-
447
- return { type: "any", required: true };
448
- }
449
-
450
- return { type: "any", required: true };
451
- }
452
- }
453
-
454
- // 导出单例实例
455
- export const tsTypeParser = new TSTypeParser();
package/utils/Validate.ts DELETED
@@ -1,25 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- import { AnyZodObject, ZodError, ZodSchema } from "zod";
3
-
4
- /**
5
- * 验证位置类型
6
- */
7
- export type ValidationTarget = "body" | "query" | "params";
8
-
9
- /**
10
- * Zod 验证中间件
11
- * @param schema Zod Schema
12
- * @param target 验证目标 (默认 body)
13
- */
14
- export const validate = (schema: ZodSchema, target: ValidationTarget = "body") => {
15
- return async (req: Request, res: Response, next: NextFunction) => {
16
- try {
17
- const data = await schema.parseAsync(req[target]);
18
- // 将校验并转换后的数据重新赋值回 req,确保后续拿到的是正确类型的数据
19
- req[target] = data;
20
- next();
21
- } catch (error) {
22
- next(error);
23
- }
24
- };
25
- };