@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,408 +0,0 @@
1
- import {
2
- Project,
3
- SyntaxKind,
4
- Node,
5
- SourceFile,
6
- StringLiteral,
7
- ModuleKind,
8
- ScriptTarget,
9
- JSDoc,
10
- } from "ts-morph";
11
- import * as path from "path";
12
- import { RouteInfo, RequestSchema, ResponseSchema } from "../types/DocTypes";
13
- import logger from "./Logger";
14
- import { ZodSchemaParser } from "./ZodSchemaParser";
15
- import { TSTypeParser } from "./TSTypeParser";
16
-
17
- /**
18
- * 路由解析器
19
- * 使用 ts-morph 解析路由文件,提取路由信息
20
- */
21
- export class RouteParser {
22
- private project: Project;
23
- private schemaParser: ZodSchemaParser;
24
- private tsTypeParser: TSTypeParser;
25
-
26
- constructor() {
27
- this.project = new Project({
28
- compilerOptions: {
29
- module: ModuleKind.CommonJS,
30
- target: ScriptTarget.ES2018,
31
- allowJs: true,
32
- },
33
- });
34
- this.schemaParser = new ZodSchemaParser();
35
- this.tsTypeParser = new TSTypeParser();
36
- }
37
-
38
- /**
39
- * 解析单个路由文件
40
- * @param filePath 路由文件路径
41
- * @returns 路由信息数组
42
- */
43
- public parseRouteFile(filePath: string): RouteInfo[] {
44
- try {
45
- const sourceFile = this.project.addSourceFileAtPath(filePath);
46
- const routes: RouteInfo[] = [];
47
-
48
- // 获取所有导入的 schema 文件映射
49
- const schemaFiles = this.getImportedSchemaFiles(sourceFile, filePath);
50
-
51
- // 获取所有类型导入映射(用于响应类型解析)
52
- const typeImports = this.getTypeImports(sourceFile, filePath);
53
-
54
- // 查找所有路由调用
55
- sourceFile.forEachDescendant((node) => {
56
- const callExpr = node as any;
57
- if (callExpr.isKind && callExpr.isKind(SyntaxKind.CallExpression) && callExpr.getExpression && callExpr.getExpression().isKind(SyntaxKind.PropertyAccessExpression)) {
58
- const propAccess = callExpr.getExpression();
59
- const object = propAccess.getExpression && propAccess.getExpression().getText();
60
- const method = propAccess.getName && propAccess.getName();
61
-
62
- // 检查是否是 router 的 HTTP 方法调用
63
- if (object === "router" && ["get", "post", "put", "delete", "patch"].includes(method)) {
64
- // 提取路由路径
65
- const args = callExpr.getArguments && callExpr.getArguments();
66
- if (args && args.length > 0) {
67
- const pathArg = args[0];
68
- if (pathArg.isKind && pathArg.isKind(SyntaxKind.StringLiteral)) {
69
- const routePath = pathArg.getText && pathArg.getText().replace(/['"]/g, "");
70
-
71
- // 提取 JSDoc 注释
72
- const jsDoc = callExpr.getPreviousSiblingIfKind && callExpr.getPreviousSiblingIfKind(SyntaxKind.JSDocComment);
73
- let summary = "";
74
- let description = "";
75
- let responseSchema: ResponseSchema | undefined;
76
-
77
- if (jsDoc) {
78
- const commentText = jsDoc.getCommentText && jsDoc.getCommentText();
79
- if (commentText) {
80
- const lines = commentText.split("\n").map((line) => line.trim());
81
- summary = lines[0] || "";
82
- description = lines.slice(1).join("\n").trim() || summary;
83
- }
84
-
85
- // 提取 @returns 标签中的响应类型
86
- responseSchema = this.extractResponseSchema(jsDoc, sourceFile, typeImports);
87
- }
88
-
89
- // 提取中间件信息
90
- const middlewares: string[] = [];
91
- for (let i = 1; i < args.length - 1; i++) {
92
- const arg = args[i];
93
- if (arg.isKind(SyntaxKind.CallExpression)) {
94
- const mwCallExpr = arg as any;
95
- const expr = mwCallExpr.getExpression();
96
- if (expr.isKind(SyntaxKind.Identifier)) {
97
- middlewares.push(expr.getText());
98
- } else if (expr.isKind(SyntaxKind.PropertyAccessExpression)) {
99
- middlewares.push(expr.getText());
100
- }
101
- }
102
- }
103
-
104
- // 提取请求参数 schema (通过 validate 中间件)
105
- const requestSchema = this.extractRequestSchema(args, schemaFiles);
106
-
107
- const routeInfo: RouteInfo = {
108
- module: path.basename(path.dirname(filePath)).toLowerCase(),
109
- method: method.toUpperCase(),
110
- path: routePath,
111
- fullPath: "", // 完整路径会在注册时填充
112
- summary,
113
- description,
114
- requestSchema,
115
- responseSchema,
116
- middlewares,
117
- filePath,
118
- };
119
-
120
- routes.push(routeInfo);
121
- }
122
- }
123
- }
124
- }
125
- });
126
-
127
- return routes;
128
- } catch (error) {
129
- logger.error(`解析路由文件失败: ${filePath}`, error);
130
- return [];
131
- }
132
- }
133
-
134
- /**
135
- * 获取导入的 schema 文件映射
136
- * @returns Map<变量名, 文件绝对路径>
137
- */
138
- private getImportedSchemaFiles(
139
- sourceFile: SourceFile,
140
- currentFilePath: string
141
- ): Map<string, string> {
142
- const schemaFiles = new Map<string, string>();
143
-
144
- for (const importDecl of sourceFile.getImportDeclarations()) {
145
- const source = importDecl.getModuleSpecifierValue();
146
-
147
- // 检查是否是 schema 相关的导入
148
- if (source.includes(".schema") || source.includes("/zod/") || source.includes("\\zod\\")) {
149
- // 解析相对路径为绝对路径
150
- const absolutePath = this.resolveImportPath(
151
- currentFilePath,
152
- source
153
- );
154
- if (absolutePath) {
155
- // 记录导入的变量名 -> 文件路径映射
156
- for (const specifier of importDecl.getNamedImports()) {
157
- logger.debug(`发现 schema 导入: ${specifier.getName()} -> ${absolutePath}`);
158
- schemaFiles.set(specifier.getName(), absolutePath);
159
- }
160
- } else {
161
- logger.debug(`无法解析 schema 导入路径: ${source}`);
162
- }
163
- }
164
- }
165
-
166
- return schemaFiles;
167
- }
168
-
169
- /**
170
- * 获取所有类型导入映射(用于响应类型解析)
171
- * @returns Map<类型名, 文件绝对路径>
172
- */
173
- private getTypeImports(
174
- sourceFile: SourceFile,
175
- currentFilePath: string
176
- ): Map<string, string> {
177
- const typeImports = new Map<string, string>();
178
-
179
- for (const importDecl of sourceFile.getImportDeclarations()) {
180
- const source = importDecl.getModuleSpecifierValue();
181
-
182
- // 解析相对路径为绝对路径
183
- const absolutePath = this.resolveImportPath(currentFilePath, source);
184
- if (absolutePath) {
185
- // 记录所有命名导入
186
- for (const specifier of importDecl.getNamedImports()) {
187
- typeImports.set(specifier.getName(), absolutePath);
188
- }
189
- }
190
- }
191
-
192
- return typeImports;
193
- }
194
-
195
- /**
196
- * 从 JSDoc 中提取响应 Schema
197
- */
198
- private extractResponseSchema(
199
- jsDoc: JSDoc,
200
- sourceFile: SourceFile,
201
- typeImports: Map<string, string>
202
- ): ResponseSchema | undefined {
203
- // 获取所有 JSDoc 标签
204
- const tags = jsDoc.getTags();
205
-
206
- for (const tag of tags) {
207
- const tagName = tag.getTagName();
208
-
209
- // 检查 @returns 或 @return 标签
210
- if (tagName === "returns" || tagName === "return") {
211
- // 获取标签的完整文本
212
- const tagText = tag.getText?.() || "";
213
- logger.debug(`@returns 标签文本: ${tagText}`);
214
-
215
- // 尝试从标签文本中提取类型 {TypeName}
216
- const typeMatch = tagText.match(/@\s*returns?\s*\{([^}]+)\}/i);
217
- if (typeMatch) {
218
- const typeString = typeMatch[1].trim();
219
- logger.debug(`从标签文本提取类型: ${typeString}`);
220
-
221
- const responseSchema = this.tsTypeParser.parseResponseType(
222
- sourceFile,
223
- typeString,
224
- typeImports
225
- );
226
-
227
- if (responseSchema) {
228
- return responseSchema;
229
- }
230
- }
231
-
232
- // 尝试从注释中提取类型
233
- const comment = tag.getCommentText?.();
234
- if (comment) {
235
- const commentMatch = comment.match(/^\{([^}]+)\}/);
236
- if (commentMatch) {
237
- const typeString = commentMatch[1].trim();
238
- logger.debug(`从注释提取类型: ${typeString}`);
239
-
240
- const responseSchema = this.tsTypeParser.parseResponseType(
241
- sourceFile,
242
- typeString,
243
- typeImports
244
- );
245
-
246
- if (responseSchema) {
247
- return responseSchema;
248
- }
249
- }
250
- }
251
-
252
- // 尝试使用 ts-morph 的类型表达式
253
- const typeExpression = (tag as any).getTypeExpression?.();
254
- if (typeExpression) {
255
- const typeNode = typeExpression.getType?.();
256
- if (typeNode) {
257
- const typeString = typeNode.getText();
258
- logger.debug(`从类型表达式提取类型: ${typeString}`);
259
-
260
- if (typeString && typeString !== "any") {
261
- const responseSchema = this.tsTypeParser.parseResponseType(
262
- sourceFile,
263
- typeString,
264
- typeImports
265
- );
266
-
267
- if (responseSchema) {
268
- return responseSchema;
269
- }
270
- }
271
- }
272
- }
273
- }
274
- }
275
-
276
- return undefined;
277
- }
278
-
279
- /**
280
- * 解析导入路径为绝对路径
281
- */
282
- private resolveImportPath(
283
- currentFilePath: string,
284
- importPath: string
285
- ): string | null {
286
- try {
287
- // 处理相对路径
288
- if (importPath.startsWith(".")) {
289
- const currentDir = path.dirname(currentFilePath);
290
- const absolutePath = path.resolve(currentDir, importPath);
291
-
292
- // 尝试添加 .ts 扩展名
293
- const tsPath = absolutePath.endsWith(".ts")
294
- ? absolutePath
295
- : `${absolutePath}.ts`;
296
- return tsPath;
297
- }
298
-
299
- // 非相对路径暂不处理
300
- return null;
301
- } catch (error) {
302
- logger.warn(`解析导入路径失败: ${importPath}`);
303
- return null;
304
- }
305
- }
306
-
307
- /**
308
- * 提取请求 Schema
309
- */
310
- private extractRequestSchema(
311
- args: any[],
312
- schemaFiles: Map<string, string>
313
- ): RequestSchema | undefined {
314
- for (const arg of args) {
315
- // 路由参数可能是 validate(...) 或 validate(...) as any
316
- // 需要先处理 as any 类型断言
317
- let targetArg = arg;
318
- if (
319
- arg.isKind &&
320
- arg.isKind(SyntaxKind.AsExpression)
321
- ) {
322
- targetArg = arg.getExpression();
323
- }
324
-
325
- // 检查是否是 validate 调用
326
- if (
327
- targetArg.isKind &&
328
- targetArg.isKind(SyntaxKind.CallExpression) &&
329
- targetArg.getExpression &&
330
- targetArg.getExpression().isKind &&
331
- targetArg.getExpression().isKind(SyntaxKind.Identifier) &&
332
- targetArg.getExpression().getText() === "validate"
333
- ) {
334
- const validateArgs = targetArg.getArguments();
335
- if (validateArgs.length === 0) continue;
336
-
337
- // 获取 schema 变量名
338
- const schemaArg = validateArgs[0];
339
- let schemaName: string;
340
-
341
- // 处理 schema 参数中的 as any 类型断言
342
- if (
343
- schemaArg.isKind &&
344
- schemaArg.isKind(SyntaxKind.AsExpression)
345
- ) {
346
- schemaName = schemaArg.getExpression().getText();
347
- } else {
348
- schemaName = schemaArg.getText();
349
- }
350
-
351
- // 获取 target 参数
352
- let target: "body" | "query" | "params" = "body";
353
- if (validateArgs.length > 1) {
354
- const targetArgParam = validateArgs[1];
355
- if (
356
- targetArgParam.isKind &&
357
- targetArgParam.isKind(SyntaxKind.StringLiteral)
358
- ) {
359
- target = (targetArgParam as StringLiteral).getLiteralValue() as
360
- | "body"
361
- | "query"
362
- | "params";
363
- }
364
- }
365
-
366
- // 解析 schema 文件
367
- const schemaFilePath = schemaFiles.get(schemaName);
368
- if (schemaFilePath) {
369
- try {
370
- const schemaSourceFile =
371
- this.project.addSourceFileAtPath(schemaFilePath);
372
- const fields = this.schemaParser.parseSchemaVariable(
373
- schemaSourceFile,
374
- schemaName
375
- );
376
-
377
- if (fields) {
378
- return { target, fields };
379
- }
380
- } catch (error) {
381
- logger.warn(`解析 schema 文件失败: ${schemaFilePath}`, error);
382
- }
383
- } else {
384
- logger.debug(`未找到 schema 文件映射: ${schemaName}`);
385
- }
386
- }
387
- }
388
-
389
- return undefined;
390
- }
391
-
392
- /**
393
- * 解析多个路由文件
394
- * @param filePaths 路由文件路径数组
395
- * @returns 路由信息数组
396
- */
397
- public parseRouteFiles(filePaths: string[]): RouteInfo[] {
398
- const allRoutes: RouteInfo[] = [];
399
- for (const filePath of filePaths) {
400
- const routes = this.parseRouteFile(filePath);
401
- allRoutes.push(...routes);
402
- }
403
- return allRoutes;
404
- }
405
- }
406
-
407
- // 导出单例实例
408
- export const routeParser = new RouteParser();
@@ -1,66 +0,0 @@
1
- import { Express, Router } from "express";
2
- import { ScanResult } from "../types/Index";
3
- import logger from "./Logger";
4
- import { routeParser } from "./RouteParser";
5
- import { docManager } from "./DocManager";
6
-
7
- /**
8
- * 递归提取 Router 中注册的所有路由路径
9
- */
10
- function getRoutes(router: any, prefix: string = ""): { path: string; method: string }[] {
11
- const routes: { path: string; method: string }[] = [];
12
-
13
- if (router && router.stack) {
14
- router.stack.forEach((layer: any) => {
15
- if (layer.route) {
16
- // 处理直接定义的路由 (如 router.get('/hello'))
17
- const path = (prefix + layer.route.path).replace(/\/+/g, "/");
18
- const methods = Object.keys(layer.route.methods).map((m) => m.toUpperCase());
19
- methods.forEach((method) => {
20
- routes.push({ path, method });
21
- });
22
- } else if (layer.name === "router" && layer.handle && layer.handle.stack) {
23
- // 处理嵌套的 Router (如 router.use('/sub', subRouter))
24
- const newPrefix = (prefix + (layer.regexp.source.replace("^\\/", "").replace("\\/?(?=\\/|$)", "") || "")).replace(/\/+/g, "/");
25
- routes.push(...getRoutes(layer.handle, newPrefix));
26
- }
27
- });
28
- }
29
-
30
- return routes;
31
- }
32
-
33
- export async function registerRoute(app: Express, config: ScanResult, apiPrefix: string = "/api"): Promise<void> {
34
- const { fileName, defaultExport: handler, filePath, cacheHit } = config;
35
-
36
- try {
37
- const lowercaseModuleName = fileName.toLowerCase();
38
-
39
- // 缓存未命中时才解析和保存文档
40
- if (!cacheHit) {
41
- const routes = routeParser.parseRouteFile(filePath);
42
- routes.forEach((route) => {
43
- docManager.saveRoute(route, apiPrefix, lowercaseModuleName);
44
- });
45
- }
46
-
47
- const baseRoutePath = `${apiPrefix}/${lowercaseModuleName}`.replace(/\/+/g, "/");
48
- app.use(baseRoutePath, handler);
49
-
50
- // 提取并打印所有子路由
51
- const subRoutes = getRoutes(handler);
52
- if (subRoutes.length > 0) {
53
- logger.startup("路由", `${baseRoutePath} (${subRoutes.length} 个端点)`);
54
- subRoutes.forEach((route) => {
55
- const fullPath = `${baseRoutePath}${route.path}`.replace(/\/+/g, "/");
56
- // Method 填充到 6 字符宽度,保证 path 对齐
57
- const method = route.method.padEnd(6, " ");
58
- logger.startup(` └─ ${method} ${fullPath}`);
59
- });
60
- } else {
61
- logger.startup("路由", `${baseRoutePath} (无端点)`);
62
- }
63
- } catch (error) {
64
- logger.error(`注册路由失败[${fileName.toLowerCase()}]: ${error}`);
65
- }
66
- }
@@ -1,73 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { setBaseDir, scan } from "../feature/scanner/FileScanner";
4
- import { initDatabase } from "./InitDatabase";
5
- import { executeDatabaseMigration } from "./DatabaseMigration";
6
- import logger from "./Logger";
7
-
8
- /**
9
- * 处理数据库迁移(Schema 同步 + Drizzle Push)
10
- * 这是 migrate 模式的核心逻辑
11
- */
12
- export async function handleSchemaMigrate() {
13
- logger.startup("迁移", "🚀 开始数据库迁移流程...");
14
-
15
- try {
16
- // 1. 执行扫描并收集 Schema 文件路径
17
- try {
18
- setBaseDir(process.cwd());
19
- const scannedResults = await scan(true);
20
-
21
- logger.startup("迁移", `扫描完成,发现 ${scannedResults.length} 个文件`);
22
-
23
- const schemaFiles = scannedResults.filter((item) => item.type === "schema").map((item) => item.filePath);
24
-
25
- logger.startup("迁移", `提取到 ${schemaFiles.length} 个 Schema 文件路径`);
26
-
27
- // 2. 写入 .drizzle-schemas.json 清单文件(供 drizzle-kit 使用)
28
- const schemaListPath = path.join(process.cwd(), ".drizzle-schemas.json");
29
- try {
30
- fs.writeFileSync(schemaListPath, JSON.stringify(schemaFiles, null, 2));
31
- logger.startup("迁移", `已生成清单文件:${schemaListPath}`);
32
- } catch (e) {
33
- throw new Error(`写入清单文件失败:${e instanceof Error ? e.message : String(e)}`);
34
- }
35
-
36
- // 3. 动态导入 Schema 文件并加载到内存
37
- const schemas: Record<string, any> = {};
38
- for (const schemaPath of schemaFiles) {
39
- try {
40
- const module = await import(schemaPath);
41
- Object.assign(schemas, module);
42
- } catch (e) {
43
- logger.warn(`加载 Schema 文件失败: ${schemaPath}`, e);
44
- }
45
- }
46
-
47
- logger.startup("迁移", `已加载 ${Object.keys(schemas).length} 个 Schema 定义`);
48
-
49
- // 4. 初始化数据库连接
50
- const db = initDatabase({ schemas });
51
- logger.startup("迁移", "数据库连接已建立");
52
-
53
- // 5. 执行 drizzle-kit push
54
- logger.startup("迁移", "正在执行数据库结构同步...");
55
- const success = await executeDatabaseMigration(db, schemas);
56
-
57
- if (success) {
58
- logger.startup("迁移", "✅ 数据库迁移完成");
59
- process.exit(0);
60
- } else {
61
- logger.error("❌ 数据库迁移失败");
62
- logger.info("💡 提示:请检查数据库连接或手动运行 'npx drizzle-kit push'");
63
- process.exit(1);
64
- }
65
- } catch (e) {
66
- throw new Error(`Schema 扫描或数据库迁移失败:${e instanceof Error ? e.message : String(e)}`);
67
- }
68
- } catch (error) {
69
- logger.error("数据库迁移失败:", error);
70
- logger.error(`错误:${error instanceof Error ? error.message : String(error)}`);
71
- process.exit(1);
72
- }
73
- }
@@ -1,47 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { setBaseDir, scan } from "../feature/scanner/FileScanner";
4
- import logger from "./Logger";
5
-
6
- /**
7
- * 处理 Schema 同步逻辑
8
- * 扫描项目中的所有 Schema 文件并生成 .drizzle-schemas.json 清单
9
- * @throws 如果 Schema 同步失败
10
- */
11
- export async function handleSchemaSync() {
12
- logger.info("🚀 检测到同步模式:正在收集 Schema 路径...");
13
-
14
- try {
15
- // 1. 执行静默扫描 (onlyPaths = true)
16
- try {
17
- setBaseDir(process.cwd());
18
- const scannedResults = await scan(true);
19
-
20
- logger.startup(`扫描完成,发现 ${scannedResults.length} 个文件`);
21
-
22
- const schemas = scannedResults.filter((item) => item.type === "schema").map((item) => item.filePath);
23
-
24
- logger.startup(`提取到 ${schemas.length} 个 Schema 文件路径`);
25
-
26
- // 2. 写入清单文件
27
- const outputPath = path.join(process.cwd(), ".drizzle-schemas.json");
28
- try {
29
- fs.writeFileSync(outputPath, JSON.stringify(schemas, null, 2));
30
- logger.startup(`写入清单文件:${outputPath}`);
31
- } catch (e) {
32
- throw new Error(`写入清单文件失败:${e instanceof Error ? e.message : String(e)}`);
33
- }
34
-
35
- logger.startup(`✅ 已发现 ${schemas.length} 个 Schema,清单已更新至 .drizzle-schemas.json`);
36
- } catch (e) {
37
- throw new Error(`Schema 扫描失败:${e instanceof Error ? e.message : String(e)}`);
38
- }
39
-
40
- // 3. 同步模式下直接退出
41
- process.exit(0);
42
- } catch (error) {
43
- logger.error("Schema 同步失败:", error);
44
- logger.error(`错误:${error instanceof Error ? error.message : String(error)}`);
45
- process.exit(1);
46
- }
47
- }