@mindbase/express-common 1.0.6 → 1.0.8

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/dist/index.mjs DELETED
@@ -1,2468 +0,0 @@
1
- // core/state.ts
2
- import express from "express";
3
- function createState(options = {}) {
4
- return {
5
- options: {
6
- host: options.host || "127.0.0.1",
7
- port: options.port || 3e3,
8
- logging: options.logging !== false,
9
- staticPath: options.staticPath,
10
- userAgent: options.userAgent !== false,
11
- ip: options.ip !== false,
12
- cors: options.cors !== false,
13
- apiPrefix: options.apiPrefix,
14
- logLevel: options.logLevel || "info",
15
- authWhitelist: [],
16
- database: {
17
- path: options.database?.path || "./data/app.db"
18
- }
19
- },
20
- express: express(),
21
- schemas: {}
22
- };
23
- }
24
-
25
- // feature/scanner/FileScanner.ts
26
- import { glob } from "glob";
27
- import path from "path";
28
- import fs from "fs";
29
- import { pathToFileURL } from "url";
30
-
31
- // utils/Dayjs.ts
32
- import dayjs from "dayjs";
33
- import "dayjs/locale/zh-cn";
34
- import duration from "dayjs/plugin/duration";
35
- import relativeTime from "dayjs/plugin/relativeTime";
36
- import utc from "dayjs/plugin/utc";
37
- dayjs.locale("zh-cn");
38
- dayjs.extend(utc);
39
- dayjs.extend(duration);
40
- dayjs.extend(relativeTime);
41
- var Dayjs_default = dayjs;
42
-
43
- // utils/Logger.ts
44
- var LOG_LEVELS = {
45
- debug: 0,
46
- info: 1,
47
- warn: 2,
48
- error: 3,
49
- silent: 4
50
- };
51
- var currentLogLevel = "info";
52
- var COLORS = {
53
- debug: "\x1B[36m",
54
- info: "\x1B[32m",
55
- warn: "\x1B[33m",
56
- error: "\x1B[31m",
57
- reset: "\x1B[0m"
58
- };
59
- function getVisualWidth(str) {
60
- let width = 0;
61
- const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
62
- for (const { segment } of segmenter.segment(str)) {
63
- const charCode = segment.charCodeAt(0);
64
- if (charCode <= 255) {
65
- width += 1;
66
- continue;
67
- }
68
- const isCJK = charCode >= 19968 && charCode <= 40959 || // CJK Unified Ideographs
69
- charCode >= 12288 && charCode <= 12351 || // CJK Symbols and Punctuation
70
- charCode >= 65280 && charCode <= 65519;
71
- const isMultiByte = segment.length > 1;
72
- if (isCJK || isMultiByte) {
73
- width += 2;
74
- } else {
75
- width += 1;
76
- }
77
- }
78
- return width;
79
- }
80
- function truncateToWidth(str, maxWidth) {
81
- const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
82
- const segments = Array.from(segmenter.segment(str)).map((s) => s.segment);
83
- if (getVisualWidth(str) <= maxWidth) return str;
84
- let currentWidth = 3;
85
- let result = "";
86
- for (let i = segments.length - 1; i >= 0; i--) {
87
- const segment = segments[i];
88
- const segmentWidth = getVisualWidth(segment);
89
- if (currentWidth + segmentWidth > maxWidth) break;
90
- result = segment + result;
91
- currentWidth += segmentWidth;
92
- }
93
- return "..." + result;
94
- }
95
- function formatMessage(level, message) {
96
- const timestamp = Dayjs_default().format("YYYY-MM-DD HH:mm:ss");
97
- const terminalWidth = process.stdout.columns || 80;
98
- const timeStr = ` ${timestamp}`;
99
- const timeWidth = getVisualWidth(timeStr);
100
- const maxMessageWidth = terminalWidth - timeWidth - 2;
101
- let displayMessage = message;
102
- const messageWidth = getVisualWidth(displayMessage);
103
- if (messageWidth > maxMessageWidth) {
104
- displayMessage = truncateToWidth(displayMessage, maxMessageWidth);
105
- }
106
- const currentMsgWidth = getVisualWidth(displayMessage);
107
- const paddingCount = Math.max(0, terminalWidth - currentMsgWidth - timeWidth);
108
- const padding = " ".repeat(paddingCount);
109
- return `${COLORS[level]}${displayMessage}${padding}${timeStr}${COLORS.reset}`;
110
- }
111
- var startupTime = Date.now();
112
- function padTag(tag, targetWidth = 8) {
113
- const currentWidth = getVisualWidth(tag);
114
- const padding = Math.max(0, targetWidth - currentWidth);
115
- if (padding === 0) return tag;
116
- const chars = [...tag];
117
- const gapCount = chars.length + 1;
118
- const baseSpaces = Math.floor(padding / gapCount);
119
- const extraSpaces = padding % gapCount;
120
- let result = "";
121
- for (let i = 0; i < chars.length; i++) {
122
- const spaces = baseSpaces + (i < extraSpaces ? 1 : 0);
123
- result += " ".repeat(spaces) + chars[i];
124
- }
125
- result += " ".repeat(baseSpaces + (chars.length < extraSpaces ? 1 : 0));
126
- return result;
127
- }
128
- function startupMessage(message, tag) {
129
- const diff = Date.now() - startupTime;
130
- const timeStr = diff.toString().padStart(6, "0");
131
- const tagStr = tag ? `\u3010${padTag(tag)}\u3011` : "";
132
- return `${COLORS["warn"]}[${timeStr}] ${tagStr}${message}${COLORS["reset"]}`;
133
- }
134
- function shouldLog(level) {
135
- return LOG_LEVELS[level] >= LOG_LEVELS[currentLogLevel];
136
- }
137
- function formatArgs(args) {
138
- return args.map((arg) => {
139
- if (arg instanceof Error) {
140
- return arg.message;
141
- }
142
- if (typeof arg === "object") {
143
- return JSON.stringify(arg, null, 2);
144
- }
145
- return String(arg);
146
- }).join(" ");
147
- }
148
- var logger = {
149
- debug(...args) {
150
- if (shouldLog("debug")) {
151
- console.log(formatMessage("debug", formatArgs(args)));
152
- }
153
- },
154
- info(...args) {
155
- if (shouldLog("info")) {
156
- console.log(formatMessage("info", formatArgs(args)));
157
- }
158
- },
159
- warn(...args) {
160
- if (shouldLog("warn")) {
161
- console.warn(formatMessage("warn", formatArgs(args)));
162
- }
163
- },
164
- error(...args) {
165
- if (shouldLog("error")) {
166
- console.error(formatMessage("error", formatArgs(args)));
167
- const errorArg = args.find((arg) => arg instanceof Error);
168
- if (errorArg) {
169
- console.error(errorArg);
170
- }
171
- }
172
- },
173
- startup(tagOrMessage, ...args) {
174
- if (args.length > 0) {
175
- console.log(startupMessage(formatArgs(args), tagOrMessage));
176
- } else {
177
- console.log(startupMessage(tagOrMessage));
178
- }
179
- },
180
- setLevel(level) {
181
- currentLogLevel = level;
182
- },
183
- getLevel() {
184
- return currentLogLevel;
185
- }
186
- };
187
- var Logger_default = logger;
188
-
189
- // feature/scanner/FileScanner.ts
190
- var CACHE_VERSION = "2.0";
191
- var CACHE_DIR = path.join(process.cwd(), "node_modules/.cache/mindbase");
192
- var CACHE_FILE = path.join(CACHE_DIR, "startup-cache.json");
193
- var cacheData = null;
194
- var state = {
195
- scanPaths: [],
196
- baseDir: ""
197
- };
198
- function setBaseDir(baseDir) {
199
- state.baseDir = baseDir;
200
- }
201
- function addScanPath(dirPath) {
202
- if (!path.isAbsolute(dirPath)) {
203
- throw new Error(`\u8DEF\u5F84\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84: ${dirPath}`);
204
- }
205
- if (!fs.existsSync(dirPath)) {
206
- throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${dirPath}`);
207
- }
208
- if (!fs.statSync(dirPath).isDirectory()) {
209
- throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${dirPath}`);
210
- }
211
- if (state.scanPaths.includes(dirPath)) {
212
- return;
213
- }
214
- if (state.baseDir && dirPath.startsWith(state.baseDir)) {
215
- return;
216
- }
217
- for (const existingPath of state.scanPaths) {
218
- if (dirPath.startsWith(existingPath)) {
219
- return;
220
- }
221
- }
222
- state.scanPaths.push(dirPath);
223
- }
224
- function isValidDirectory(dirPath) {
225
- try {
226
- return fs.statSync(dirPath).isDirectory();
227
- } catch {
228
- return false;
229
- }
230
- }
231
- function extractFileName(filePath) {
232
- const fileName = path.basename(filePath);
233
- return fileName.replace(/\.(route|middleware|schema)\.ts$/, "");
234
- }
235
- async function loadCache() {
236
- try {
237
- if (!fs.existsSync(CACHE_FILE)) {
238
- cacheData = { version: CACHE_VERSION, entries: /* @__PURE__ */ new Map() };
239
- return;
240
- }
241
- const json = fs.readFileSync(CACHE_FILE, "utf-8");
242
- const data = JSON.parse(json);
243
- if (data.version !== CACHE_VERSION) {
244
- Logger_default.debug(`\u7F13\u5B58\u7248\u672C\u4E0D\u5339\u914D (${data.version} vs ${CACHE_VERSION})\uFF0C\u5C06\u91CD\u65B0\u6784\u5EFA`);
245
- cacheData = { version: CACHE_VERSION, entries: /* @__PURE__ */ new Map() };
246
- return;
247
- }
248
- cacheData = {
249
- version: data.version,
250
- entries: new Map(Object.entries(data.entries).map(([k, v]) => [k, v]))
251
- };
252
- Logger_default.debug(`\u5DF2\u52A0\u8F7D\u7F13\u5B58\uFF0C\u5305\u542B ${cacheData.entries.size} \u4E2A\u6587\u4EF6\u6761\u76EE`);
253
- } catch (error) {
254
- Logger_default.warn(`\u52A0\u8F7D\u7F13\u5B58\u5931\u8D25\uFF0C\u5C06\u91CD\u65B0\u6784\u5EFA: ${error}`);
255
- cacheData = { version: CACHE_VERSION, entries: /* @__PURE__ */ new Map() };
256
- }
257
- }
258
- async function saveCache() {
259
- try {
260
- if (!cacheData) return;
261
- if (!fs.existsSync(CACHE_DIR)) {
262
- fs.mkdirSync(CACHE_DIR, { recursive: true });
263
- }
264
- const data = {
265
- version: cacheData.version,
266
- entries: Object.fromEntries(cacheData.entries)
267
- };
268
- fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2), "utf-8");
269
- Logger_default.debug(`\u7F13\u5B58\u5DF2\u4FDD\u5B58\uFF0C\u5305\u542B ${cacheData.entries.size} \u4E2A\u6587\u4EF6\u6761\u76EE`);
270
- } catch (error) {
271
- Logger_default.warn(`\u4FDD\u5B58\u7F13\u5B58\u5931\u8D25: ${error}`);
272
- }
273
- }
274
- async function scan(onlyPaths = false) {
275
- await loadCache();
276
- const results = [];
277
- const allScanPaths = [...state.scanPaths];
278
- const processedFiles = /* @__PURE__ */ new Set();
279
- if (state.baseDir) {
280
- allScanPaths.push(state.baseDir);
281
- }
282
- for (const dirPath of allScanPaths) {
283
- if (!isValidDirectory(dirPath)) {
284
- Logger_default.warn(`\u8DF3\u8FC7\u65E0\u6548\u76EE\u5F55: ${dirPath}`);
285
- continue;
286
- }
287
- const patterns = [
288
- { type: "middleware", pattern: "**/*.middleware.ts" },
289
- { type: "route", pattern: "**/*.route.ts" },
290
- { type: "schema", pattern: "**/*.schema.ts" }
291
- ];
292
- for (const { type, pattern } of patterns) {
293
- const globPattern = path.join(dirPath, pattern).replace(/\\/g, "/");
294
- try {
295
- const files = await glob(globPattern, { absolute: true });
296
- for (const file of files) {
297
- if (processedFiles.has(file)) {
298
- continue;
299
- }
300
- processedFiles.add(file);
301
- try {
302
- const fileName = extractFileName(file);
303
- if (onlyPaths) {
304
- results.push({
305
- fileName,
306
- type,
307
- filePath: file,
308
- defaultExport: null,
309
- allExports: null
310
- });
311
- continue;
312
- }
313
- const stats = await fs.promises.stat(file);
314
- const mtime = stats.mtimeMs;
315
- const cached = cacheData?.entries.get(file);
316
- const cacheHit = cached && cached.mtime === mtime;
317
- if (cacheHit) {
318
- Logger_default.debug(`\u7F13\u5B58\u547D\u4E2D: ${file}`);
319
- }
320
- const module = await import(pathToFileURL(file).href);
321
- const scanResult = {
322
- fileName,
323
- defaultExport: module.default,
324
- allExports: module,
325
- type,
326
- filePath: file,
327
- cacheHit
328
- };
329
- results.push(scanResult);
330
- if (cacheData) {
331
- cacheData.entries.set(file, { filePath: file, mtime });
332
- }
333
- } catch (error) {
334
- Logger_default.warn(`\u5904\u7406\u6587\u4EF6\u5931\u8D25: ${file}`, error);
335
- }
336
- }
337
- } catch (error) {
338
- Logger_default.warn(`\u626B\u63CF\u76EE\u5F55\u5931\u8D25: ${dirPath} (Pattern: ${pattern})`, error);
339
- }
340
- }
341
- }
342
- await saveCache();
343
- return results;
344
- }
345
-
346
- // utils/InitExpress.ts
347
- import express2 from "express";
348
- import fs2 from "fs";
349
- import cookieParser from "cookie-parser";
350
-
351
- // middleware/Cors.ts
352
- function Cors_default(req, res, next) {
353
- res.header("Access-Control-Allow-Origin", "*");
354
- res.header("Access-Control-Allow-Credentials", "true");
355
- res.header("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
356
- res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
357
- if (req.method == "OPTIONS") {
358
- res.sendStatus(200);
359
- } else {
360
- next();
361
- }
362
- }
363
-
364
- // middleware/IpParser.ts
365
- import ipdb from "ipip-ipdb";
366
- import { join } from "path";
367
- var dbPath = join(process.cwd(), "ipipfree.ipdb");
368
- var cityInstance;
369
- try {
370
- cityInstance = new ipdb.City(dbPath);
371
- } catch (error) {
372
- Logger_default.error(`[IpParser] Failed to load IP database at: ${dbPath}`);
373
- }
374
- function IpParser_default(req, res, next) {
375
- const ip = req.headers["real-ip"] || req.headers["x-real-ip"] || req.ip;
376
- const info = {
377
- address: ip,
378
- location: "\u672A\u77E5",
379
- country: "",
380
- province: "",
381
- city: "",
382
- isp: "",
383
- latitude: "",
384
- longitude: "",
385
- isLocal: false
386
- };
387
- const localIdentifiers = ["127.0.0.1", "::1", "::ffff:127.0.0.1", "localhost"];
388
- if (localIdentifiers.includes(ip)) {
389
- info.location = "\u672C\u673A";
390
- info.isLocal = true;
391
- res.locals.ip = info;
392
- return next();
393
- }
394
- if (cityInstance) {
395
- try {
396
- const data = cityInstance.findMap(ip, "CN");
397
- if (data) {
398
- info.country = data.country_name || "";
399
- info.province = data.region_name || "";
400
- info.city = data.city_name || "";
401
- info.isp = data.isp_domain || "";
402
- info.latitude = data.latitude || "";
403
- info.longitude = data.longitude || "";
404
- const parts = [info.country, info.province, info.city].filter(Boolean);
405
- info.location = parts.join(" ") || "\u672A\u77E5\u4F4D\u7F6E";
406
- }
407
- } catch (err) {
408
- }
409
- }
410
- res.locals.ip = info;
411
- next();
412
- }
413
-
414
- // middleware/UaParser.ts
415
- import { UAParser } from "ua-parser-js";
416
- function UaParser_default(req, res, next) {
417
- const parser = new UAParser(req.headers["user-agent"]);
418
- const result = parser.getResult();
419
- const ua = {
420
- ua: result.ua,
421
- browser: result.browser,
422
- engine: result.engine,
423
- os: result.os,
424
- device: result.device,
425
- cpu: result.cpu,
426
- isMobile: result.device.type === "mobile"
427
- };
428
- res.locals.UA = ua;
429
- next();
430
- }
431
-
432
- // utils/InitExpress.ts
433
- function initExpress(mindBaseState) {
434
- const app = mindBaseState.express;
435
- const options = mindBaseState.options;
436
- app.getDB = () => mindBaseState.db;
437
- app.configs = mindBaseState.options;
438
- app.disable("x-powered-by");
439
- app.use(express2.urlencoded({ extended: false, limit: "10mb" }));
440
- app.use(express2.json({ limit: "10mb" }));
441
- app.use(cookieParser());
442
- if (options.staticPath) {
443
- if (!fs2.existsSync(options.staticPath)) {
444
- fs2.mkdirSync(options.staticPath, { recursive: true });
445
- }
446
- app.use(express2.static(options.staticPath));
447
- Logger_default.startup("\u8BBE\u7F6E", `\u9759\u6001\u76EE\u5F55: ${options.staticPath}`);
448
- }
449
- if (options.cors) {
450
- app.use(Cors_default);
451
- Logger_default.startup("\u8BBE\u7F6E", "\u8DE8\u57DF\u8BF7\u6C42\u5934");
452
- }
453
- if (options.ip) {
454
- Logger_default.startup("\u8BBE\u7F6E", "IP\u5730\u5740\u89E3\u6790");
455
- app.use(IpParser_default);
456
- }
457
- if (options.userAgent) {
458
- Logger_default.startup("\u8BBE\u7F6E", "\u7528\u6237\u4EE3\u7406\u89E3\u6790");
459
- app.use(UaParser_default);
460
- }
461
- }
462
-
463
- // utils/InitDatabase.ts
464
- import path2 from "path";
465
- import fs3 from "fs";
466
- import { drizzle } from "drizzle-orm/better-sqlite3";
467
- import Database from "better-sqlite3";
468
- function setupDatabase(state2, scannedResults) {
469
- try {
470
- if (!state2) {
471
- throw new Error("\u5E94\u7528\u72B6\u6001\u5BF9\u8C61\u4E0D\u80FD\u4E3A\u7A7A");
472
- }
473
- if (!Array.isArray(scannedResults)) {
474
- throw new Error("\u626B\u63CF\u7ED3\u679C\u5FC5\u987B\u662F\u4E00\u4E2A\u6570\u7EC4");
475
- }
476
- const schemas = {};
477
- for (const item of scannedResults) {
478
- if (item.type === "schema" && item.allExports) {
479
- try {
480
- Object.assign(schemas, item.allExports);
481
- } catch (e) {
482
- Logger_default.warn(`\u5904\u7406 Schema \u6587\u4EF6\u5931\u8D25: ${item.filePath}`, e);
483
- }
484
- }
485
- }
486
- state2.schemas = schemas;
487
- Logger_default.startup("\u626B\u63CF", `\u63D0\u53D6\u5230 ${Object.keys(schemas).length} \u4E2A Schema \u5B9A\u4E49`);
488
- return initDatabase({
489
- schemas,
490
- state: state2
491
- });
492
- } catch (error) {
493
- Logger_default.error("\u6570\u636E\u5E93\u8BBE\u7F6E\u5931\u8D25\uFF1A", error);
494
- throw new Error(`\u6570\u636E\u5E93\u8BBE\u7F6E\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
495
- }
496
- }
497
- function initDatabase(options) {
498
- if (!options) {
499
- throw new Error("\u521D\u59CB\u5316\u9009\u9879\u4E0D\u80FD\u4E3A\u7A7A");
500
- }
501
- try {
502
- const currentDir = process.cwd();
503
- const dbPath2 = options.dbPath || options.state?.options?.database?.path || "./data/app.db";
504
- const absoluteDbPath = path2.resolve(currentDir, dbPath2);
505
- const dbDir = path2.dirname(absoluteDbPath);
506
- Logger_default.startup("\u6570\u636E\u5E93", `\u521D\u59CB\u5316\uFF1A\u8DEF\u5F84=${absoluteDbPath}`);
507
- try {
508
- if (!fs3.existsSync(dbDir)) {
509
- Logger_default.startup("\u6570\u636E\u5E93", `\u521B\u5EFA\u76EE\u5F55\uFF1A${dbDir}`);
510
- fs3.mkdirSync(dbDir, { recursive: true });
511
- }
512
- } catch (e) {
513
- throw new Error(`\u521B\u5EFA\u6570\u636E\u5E93\u76EE\u5F55\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
514
- }
515
- let sqlite;
516
- try {
517
- sqlite = new Database(absoluteDbPath);
518
- } catch (e) {
519
- throw new Error(`\u8FDE\u63A5\u6570\u636E\u5E93\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
520
- }
521
- try {
522
- sqlite.pragma("journal_mode = WAL");
523
- sqlite.pragma("synchronous = NORMAL");
524
- sqlite.pragma("cache_size = -1048576");
525
- sqlite.pragma("temp_store = MEMORY");
526
- sqlite.pragma("page_size = 4096");
527
- sqlite.pragma("wal_autocheckpoint = 10000");
528
- sqlite.pragma("busy_timeout = 5000");
529
- } catch (e) {
530
- Logger_default.warn("\u8BBE\u7F6E SQLite \u6027\u80FD\u53C2\u6570\u5931\u8D25\uFF1A", e);
531
- }
532
- let db;
533
- try {
534
- db = drizzle(sqlite, { schema: options.schemas || {} });
535
- } catch (e) {
536
- try {
537
- sqlite.close();
538
- } catch (closeError) {
539
- Logger_default.warn("\u5173\u95ED\u6570\u636E\u5E93\u8FDE\u63A5\u5931\u8D25\uFF1A", closeError);
540
- }
541
- throw new Error(`\u521D\u59CB\u5316 Drizzle ORM \u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
542
- }
543
- if (options.state) {
544
- options.state.db = db;
545
- }
546
- return db;
547
- } catch (error) {
548
- Logger_default.error("\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25\uFF1A", error);
549
- throw new Error(`\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
550
- }
551
- }
552
-
553
- // utils/MiddlewareRegistry.ts
554
- async function registerMiddleware(app, config2) {
555
- const { fileName = "anonymous", defaultExport: handler } = config2;
556
- try {
557
- app.use(handler);
558
- Logger_default.startup("\u4E2D\u95F4\u4EF6", `\u6CE8\u518C: ${fileName}`);
559
- } catch (error) {
560
- Logger_default.error(`\u6CE8\u518C\u4E2D\u95F4\u4EF6\u5931\u8D25 ${fileName}:`, error);
561
- }
562
- }
563
-
564
- // utils/RouteParser.ts
565
- import {
566
- Project,
567
- SyntaxKind as SyntaxKind3,
568
- ModuleKind,
569
- ScriptTarget
570
- } from "ts-morph";
571
- import * as path3 from "path";
572
-
573
- // utils/ZodSchemaParser.ts
574
- import {
575
- Node
576
- } from "ts-morph";
577
- var ZodSchemaParser = class {
578
- /**
579
- * 解析 schema 变量,返回字段结构
580
- */
581
- parseSchemaVariable(sourceFile, schemaName) {
582
- try {
583
- const variableDecl = this.findVariableDeclaration(sourceFile, schemaName);
584
- if (!variableDecl) {
585
- Logger_default.warn(`\u672A\u627E\u5230 schema \u53D8\u91CF: ${schemaName}`);
586
- return null;
587
- }
588
- const initializer = variableDecl.getInitializer();
589
- if (!initializer) {
590
- Logger_default.warn(`schema \u53D8\u91CF ${schemaName} \u6CA1\u6709\u521D\u59CB\u5316\u8868\u8FBE\u5F0F`);
591
- return null;
592
- }
593
- return this.parseZodObject(initializer);
594
- } catch (error) {
595
- Logger_default.error(`\u89E3\u6790 schema \u53D8\u91CF\u5931\u8D25: ${schemaName}`, error);
596
- return null;
597
- }
598
- }
599
- /**
600
- * 查找变量声明
601
- */
602
- findVariableDeclaration(sourceFile, name) {
603
- for (const decl of sourceFile.getVariableDeclarations()) {
604
- if (decl.getName() === name) {
605
- return decl;
606
- }
607
- }
608
- return null;
609
- }
610
- /**
611
- * 解析 z.object({...}) 调用
612
- */
613
- parseZodObject(node) {
614
- let targetNode = node;
615
- if (Node.isAsExpression(node)) {
616
- targetNode = node.getExpression();
617
- }
618
- if (Node.isCallExpression(targetNode)) {
619
- const expr = targetNode.getExpression();
620
- if (Node.isPropertyAccessExpression(expr)) {
621
- const propText = expr.getText();
622
- if (propText === "z.object") {
623
- const args = targetNode.getArguments();
624
- if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
625
- return this.parseObjectLiteral(args[0]);
626
- }
627
- }
628
- }
629
- }
630
- return null;
631
- }
632
- /**
633
- * 解析对象字面量中的字段定义
634
- */
635
- parseObjectLiteral(objLit) {
636
- const fields = {};
637
- for (const prop of objLit.getProperties()) {
638
- if (Node.isPropertyAssignment(prop)) {
639
- const fieldName = prop.getName();
640
- const initializer = prop.getInitializer();
641
- if (initializer) {
642
- fields[fieldName] = this.parseZodField(initializer);
643
- }
644
- }
645
- }
646
- return fields;
647
- }
648
- /**
649
- * 解析单个 Zod 字段定义
650
- */
651
- parseZodField(node) {
652
- const field = {
653
- type: "any",
654
- required: true
655
- };
656
- let targetNode = node;
657
- if (Node.isAsExpression(node)) {
658
- targetNode = node.getExpression();
659
- }
660
- const callChain = this.collectCallChain(targetNode);
661
- for (const call of callChain) {
662
- this.applyZodMethod(field, call);
663
- }
664
- return field;
665
- }
666
- /**
667
- * 收集方法调用链
668
- * 例如: z.string().min(1).optional() -> [{method: "z.string", args: []}, {method: "min", args: [...]}, {method: "optional", args: []}]
669
- */
670
- collectCallChain(node) {
671
- const chain = [];
672
- let current = node;
673
- while (current && Node.isCallExpression(current)) {
674
- const expr = current.getExpression();
675
- const args = current.getArguments();
676
- if (Node.isPropertyAccessExpression(expr)) {
677
- const methodName = expr.getName();
678
- chain.unshift({ method: methodName, args: [...args] });
679
- current = expr.getExpression();
680
- } else if (Node.isIdentifier(expr)) {
681
- chain.unshift({ method: expr.getText(), args: [] });
682
- break;
683
- } else if (Node.isPropertyAccessExpression(expr)) {
684
- chain.unshift({
685
- method: expr.getName(),
686
- args: []
687
- });
688
- current = expr.getExpression();
689
- } else {
690
- break;
691
- }
692
- }
693
- return chain;
694
- }
695
- /**
696
- * 应用 Zod 方法到字段定义
697
- */
698
- applyZodMethod(field, call) {
699
- const { method, args } = call;
700
- switch (method) {
701
- // 类型定义
702
- case "z.string":
703
- case "string":
704
- field.type = "string";
705
- break;
706
- case "z.number":
707
- case "number":
708
- field.type = "number";
709
- break;
710
- case "z.boolean":
711
- case "boolean":
712
- field.type = "boolean";
713
- break;
714
- case "z.array":
715
- case "array":
716
- field.type = "array";
717
- if (args.length > 0) {
718
- field.items = this.parseZodField(args[0]);
719
- }
720
- break;
721
- case "z.object":
722
- case "object":
723
- field.type = "object";
724
- if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
725
- field.properties = this.parseObjectLiteral(args[0]);
726
- }
727
- break;
728
- case "z.date":
729
- case "date":
730
- field.type = "date";
731
- break;
732
- case "z.any":
733
- case "any":
734
- field.type = "any";
735
- break;
736
- case "z.unknown":
737
- case "unknown":
738
- field.type = "any";
739
- break;
740
- case "z.undefined":
741
- case "undefined":
742
- field.required = false;
743
- field.optional = true;
744
- break;
745
- case "z.null":
746
- case "null":
747
- field.nullable = true;
748
- break;
749
- case "z.void":
750
- case "void":
751
- field.type = "any";
752
- break;
753
- case "z.nativeEnum":
754
- case "nativeEnum":
755
- case "z.enum":
756
- case "enum":
757
- field.type = "string";
758
- break;
759
- case "coerce":
760
- break;
761
- // 可选性
762
- case "optional":
763
- field.required = false;
764
- field.optional = true;
765
- break;
766
- case "nullable":
767
- field.nullable = true;
768
- break;
769
- case "nullish":
770
- field.required = false;
771
- field.optional = true;
772
- field.nullable = true;
773
- break;
774
- // 默认值
775
- case "default":
776
- if (args.length > 0) {
777
- field.defaultValue = this.parseArgumentValue(args[0]);
778
- field.required = false;
779
- }
780
- break;
781
- // 约束 - 通用
782
- case "min":
783
- if (args.length > 0) {
784
- const minVal = this.parseArgumentValue(args[0]);
785
- field.constraints = field.constraints || {};
786
- if (field.type === "string") {
787
- field.constraints.minLength = minVal;
788
- } else if (field.type === "number") {
789
- field.constraints.min = minVal;
790
- }
791
- if (args.length > 1) {
792
- const msg = this.parseArgumentValue(args[1]);
793
- if (typeof msg === "string") {
794
- field.constraints.custom = field.constraints.custom || [];
795
- field.constraints.custom.push(msg);
796
- }
797
- }
798
- }
799
- break;
800
- case "max":
801
- if (args.length > 0) {
802
- const maxVal = this.parseArgumentValue(args[0]);
803
- field.constraints = field.constraints || {};
804
- if (field.type === "string") {
805
- field.constraints.maxLength = maxVal;
806
- } else if (field.type === "number") {
807
- field.constraints.max = maxVal;
808
- }
809
- }
810
- break;
811
- case "length":
812
- if (args.length > 0) {
813
- const lenVal = this.parseArgumentValue(args[0]);
814
- field.constraints = field.constraints || {};
815
- field.constraints.minLength = lenVal;
816
- field.constraints.maxLength = lenVal;
817
- }
818
- break;
819
- // 约束 - 字符串
820
- case "email":
821
- field.constraints = field.constraints || {};
822
- field.constraints.email = true;
823
- break;
824
- case "url":
825
- field.constraints = field.constraints || {};
826
- field.constraints.url = true;
827
- break;
828
- case "uuid":
829
- field.constraints = field.constraints || {};
830
- field.constraints.uuid = true;
831
- break;
832
- case "regex":
833
- case "pattern":
834
- if (args.length > 0) {
835
- field.constraints = field.constraints || {};
836
- field.constraints.pattern = args[0].getText();
837
- }
838
- break;
839
- case "startsWith":
840
- case "endsWith":
841
- case "includes":
842
- case "trim":
843
- case "toLowerCase":
844
- case "toUpperCase":
845
- break;
846
- // 约束 - 数字
847
- case "int":
848
- field.constraints = field.constraints || {};
849
- field.constraints.int = true;
850
- break;
851
- case "positive":
852
- field.constraints = field.constraints || {};
853
- field.constraints.positive = true;
854
- break;
855
- case "nonnegative":
856
- field.constraints = field.constraints || {};
857
- field.constraints.nonnegative = true;
858
- break;
859
- case "negative":
860
- field.constraints = field.constraints || {};
861
- field.constraints.max = -1;
862
- break;
863
- case "nonpositive":
864
- field.constraints = field.constraints || {};
865
- field.constraints.max = 0;
866
- break;
867
- case "finite":
868
- case "safe":
869
- break;
870
- // 约束 - 数组
871
- case "nonempty":
872
- field.constraints = field.constraints || {};
873
- field.constraints.minLength = 1;
874
- break;
875
- // 其他方法
876
- case "refine":
877
- case "superRefine":
878
- case "transform":
879
- case "pipe":
880
- case " preprocess":
881
- case "omit":
882
- case "pick":
883
- case "partial":
884
- case "required":
885
- case "strict":
886
- case "passthrough":
887
- case "extend":
888
- case "merge":
889
- case "keyof":
890
- break;
891
- }
892
- }
893
- /**
894
- * 解析参数值
895
- */
896
- parseArgumentValue(node) {
897
- if (Node.isStringLiteral(node)) {
898
- return node.getLiteralValue();
899
- } else if (Node.isNumericLiteral(node)) {
900
- return node.getLiteralValue();
901
- } else if (Node.isTrueLiteral(node)) {
902
- return true;
903
- } else if (Node.isFalseLiteral(node)) {
904
- return false;
905
- } else if (Node.isIdentifier(node)) {
906
- return node.getText();
907
- } else if (Node.isObjectLiteralExpression(node)) {
908
- return node.getText();
909
- } else if (Node.isArrayLiteralExpression(node)) {
910
- return node.getText();
911
- }
912
- return node.getText();
913
- }
914
- };
915
- var zodSchemaParser = new ZodSchemaParser();
916
-
917
- // utils/TSTypeParser.ts
918
- import {
919
- Node as Node2,
920
- SyntaxKind as SyntaxKind2
921
- } from "ts-morph";
922
- var TSTypeParser = class {
923
- /**
924
- * 解析类型引用字符串
925
- * 例如: "ApiResponse<User[]>" -> { typeName: "ApiResponse", typeArgs: ["User[]"] }
926
- */
927
- parseTypeRef(typeString) {
928
- typeString = typeString.trim();
929
- const genericMatch = typeString.match(/^(\w+)<(.+)>$/);
930
- if (genericMatch) {
931
- const typeName = genericMatch[1];
932
- const argsStr = genericMatch[2];
933
- const typeArgs = argsStr.split(",").map((s) => s.trim());
934
- return { typeName, typeArgs };
935
- }
936
- if (/^\w+$/.test(typeString)) {
937
- return { typeName: typeString, typeArgs: [] };
938
- }
939
- const arrayMatch = typeString.match(/^(\w+)\[\]$/);
940
- if (arrayMatch) {
941
- return { typeName: "Array", typeArgs: [arrayMatch[1]] };
942
- }
943
- return null;
944
- }
945
- /**
946
- * 解析响应类型
947
- * @param sourceFile 源文件
948
- * @param typeString 类型字符串如 "ApiResponse<User[]>"
949
- * @param imports 导入映射 (类型名 -> 文件路径)
950
- */
951
- parseResponseType(sourceFile, typeString, imports) {
952
- const typeRef = this.parseTypeRef(typeString);
953
- if (!typeRef) {
954
- Logger_default.debug(`\u65E0\u6CD5\u89E3\u6790\u7C7B\u578B\u5F15\u7528: ${typeString}`);
955
- return null;
956
- }
957
- if (typeRef.typeName === "ApiResponse") {
958
- return this.parseApiResponse(sourceFile, typeRef.typeArgs, imports);
959
- }
960
- const typeDecl = this.findTypeDeclaration(sourceFile, typeRef.typeName, imports);
961
- if (!typeDecl) {
962
- Logger_default.debug(`\u672A\u627E\u5230\u7C7B\u578B\u5B9A\u4E49: ${typeRef.typeName}`);
963
- return null;
964
- }
965
- const fields = this.extractFieldsFromType(typeDecl, typeRef.typeArgs, imports);
966
- return { fields };
967
- }
968
- /**
969
- * 解析 ApiResponse<T> 类型
970
- * ApiResponse 是标准的 API 响应包装器,包含 code, data, msg 字段
971
- */
972
- parseApiResponse(sourceFile, typeArgs, imports) {
973
- const fields = {
974
- code: { type: "number", required: true },
975
- msg: { type: "string", required: true }
976
- };
977
- if (typeArgs.length > 0) {
978
- const dataType = typeArgs[0];
979
- const dataField = this.parseDataType(sourceFile, dataType, imports);
980
- fields.data = dataField;
981
- } else {
982
- fields.data = { type: "any", required: true };
983
- }
984
- return { fields };
985
- }
986
- /**
987
- * 解析 data 字段的类型
988
- */
989
- parseDataType(sourceFile, typeString, imports) {
990
- if (typeString === "string") {
991
- return { type: "string", required: true };
992
- } else if (typeString === "number") {
993
- return { type: "number", required: true };
994
- } else if (typeString === "boolean") {
995
- return { type: "boolean", required: true };
996
- } else if (typeString === "any") {
997
- return { type: "any", required: true };
998
- }
999
- const arrayMatch = typeString.match(/^(\w+)\[\]$/);
1000
- if (arrayMatch) {
1001
- const elementType = arrayMatch[1];
1002
- const elementField = this.parseDataType(sourceFile, elementType, imports);
1003
- return {
1004
- type: "array",
1005
- required: true,
1006
- items: elementField
1007
- };
1008
- }
1009
- const typeDecl = this.findTypeDeclaration(sourceFile, typeString, imports);
1010
- if (typeDecl) {
1011
- const fields = this.extractFieldsFromType(typeDecl, [], imports);
1012
- return {
1013
- type: "object",
1014
- required: true,
1015
- properties: fields
1016
- };
1017
- }
1018
- return { type: "any", required: true };
1019
- }
1020
- /**
1021
- * 查找类型声明
1022
- */
1023
- findTypeDeclaration(sourceFile, typeName, imports) {
1024
- const interfaceDecl = sourceFile.getInterface(typeName);
1025
- if (interfaceDecl) return interfaceDecl;
1026
- const typeAlias = sourceFile.getTypeAlias(typeName);
1027
- if (typeAlias) return typeAlias;
1028
- const importPath = imports.get(typeName);
1029
- if (importPath) {
1030
- try {
1031
- const importedFile = sourceFile.getProject().addSourceFileAtPath(importPath);
1032
- const importedInterface = importedFile.getInterface(typeName);
1033
- if (importedInterface) return importedInterface;
1034
- const importedTypeAlias = importedFile.getTypeAlias(typeName);
1035
- if (importedTypeAlias) return importedTypeAlias;
1036
- } catch (error) {
1037
- Logger_default.warn(`\u65E0\u6CD5\u52A0\u8F7D\u5BFC\u5165\u6587\u4EF6: ${importPath}`);
1038
- }
1039
- }
1040
- return null;
1041
- }
1042
- /**
1043
- * 从类型声明中提取字段
1044
- */
1045
- extractFieldsFromType(typeDecl, typeArgs, imports) {
1046
- const fields = {};
1047
- let typeNode;
1048
- if (Node2.isInterfaceDeclaration(typeDecl)) {
1049
- for (const prop of typeDecl.getProperties()) {
1050
- const fieldName = prop.getName();
1051
- const fieldType = prop.getType();
1052
- fields[fieldName] = this.parsePropertyType(prop, typeArgs, imports);
1053
- }
1054
- return fields;
1055
- } else if (Node2.isTypeAliasDeclaration(typeDecl)) {
1056
- typeNode = typeDecl.getTypeNode();
1057
- }
1058
- if (!typeNode) return fields;
1059
- if (typeNode.getKind() === SyntaxKind2.TypeReference) {
1060
- return this.extractFieldsFromTypeReference(typeNode, typeArgs, imports);
1061
- } else if (typeNode.getKind() === SyntaxKind2.IntersectionType) {
1062
- for (const child of typeNode.getTypeNodes()) {
1063
- const childFields = this.extractFieldsFromTypeNode(child, typeArgs, imports);
1064
- Object.assign(fields, childFields);
1065
- }
1066
- } else if (typeNode.getKind() === SyntaxKind2.UnionType) {
1067
- const firstType = typeNode.getTypeNodes()[0];
1068
- if (firstType) {
1069
- return this.extractFieldsFromTypeNode(firstType, typeArgs, imports);
1070
- }
1071
- } else {
1072
- return this.extractFieldsFromTypeNode(typeNode, typeArgs, imports);
1073
- }
1074
- return fields;
1075
- }
1076
- /**
1077
- * 从类型节点提取字段
1078
- */
1079
- extractFieldsFromTypeNode(typeNode, typeArgs, imports) {
1080
- const fields = {};
1081
- if (typeNode.getKind() === SyntaxKind2.TypeReference) {
1082
- return this.extractFieldsFromTypeReference(typeNode, typeArgs, imports);
1083
- } else if (typeNode.getKind() === SyntaxKind2.IntersectionType) {
1084
- for (const child of typeNode.getTypeNodes()) {
1085
- const childFields = this.extractFieldsFromTypeNode(child, typeArgs, imports);
1086
- Object.assign(fields, childFields);
1087
- }
1088
- }
1089
- return fields;
1090
- }
1091
- /**
1092
- * 从类型引用提取字段
1093
- */
1094
- extractFieldsFromTypeReference(typeRefNode, typeArgs, imports) {
1095
- const fields = {};
1096
- const typeName = typeRefNode.getTypeName().getText();
1097
- if (typeName === "Array" || typeName === "ReadonlyArray") {
1098
- const typeArgsNodes = typeRefNode.getTypeArguments();
1099
- if (typeArgsNodes.length > 0) {
1100
- const elementType = typeArgsNodes[0];
1101
- fields["[]"] = {
1102
- type: "array",
1103
- required: true,
1104
- items: this.parseTypeNodeToFieldValidation(elementType, typeArgs, imports)
1105
- };
1106
- }
1107
- return fields;
1108
- }
1109
- if (typeName === "Promise") {
1110
- const typeArgsNodes = typeRefNode.getTypeArguments();
1111
- if (typeArgsNodes.length > 0) {
1112
- return this.extractFieldsFromTypeNode(typeArgsNodes[0], typeArgs, imports);
1113
- }
1114
- }
1115
- if (typeArgs.includes(typeName) && typeArgs.length > 0) {
1116
- const actualType = typeArgs[0];
1117
- }
1118
- const sourceFile = typeRefNode.getSourceFile();
1119
- const typeDecl = this.findTypeDeclarationInFile(sourceFile, typeName);
1120
- if (typeDecl) {
1121
- return this.extractFieldsFromType(typeDecl, typeArgs, imports);
1122
- }
1123
- return fields;
1124
- }
1125
- /**
1126
- * 在文件中查找类型声明
1127
- */
1128
- findTypeDeclarationInFile(sourceFile, typeName) {
1129
- const interfaceDecl = sourceFile.getInterface(typeName);
1130
- if (interfaceDecl) return interfaceDecl;
1131
- const typeAlias = sourceFile.getTypeAlias(typeName);
1132
- if (typeAlias) return typeAlias;
1133
- return null;
1134
- }
1135
- /**
1136
- * 解析属性类型
1137
- */
1138
- parsePropertyType(prop, typeArgs, imports) {
1139
- const type = prop.getType();
1140
- const isOptional = prop.hasQuestionToken();
1141
- const typeText = type.getText();
1142
- if (type.isString()) {
1143
- return { type: "string", required: !isOptional };
1144
- } else if (type.isNumber()) {
1145
- return { type: "number", required: !isOptional };
1146
- } else if (type.isBoolean()) {
1147
- return { type: "boolean", required: !isOptional };
1148
- } else if (type.isArray()) {
1149
- const elementType = type.getArrayElementType();
1150
- let items;
1151
- if (elementType) {
1152
- items = {
1153
- type: this.mapTypeToString(elementType),
1154
- required: !elementType.isUndefined() && !elementType.isNull()
1155
- };
1156
- }
1157
- return { type: "array", required: !isOptional, items };
1158
- } else if (type.isObject()) {
1159
- const properties = type.getProperties();
1160
- if (properties.length > 0) {
1161
- const props = {};
1162
- for (const prop2 of properties) {
1163
- const propName = prop2.getName();
1164
- const propType = prop2.getTypeAtLocation(prop2.getValueDeclaration() || prop2.getDeclarations()[0]);
1165
- props[propName] = {
1166
- type: this.mapTypeToString(propType),
1167
- required: !prop2.isOptional()
1168
- };
1169
- }
1170
- return { type: "object", required: !isOptional, properties: props };
1171
- }
1172
- return { type: "object", required: !isOptional };
1173
- } else if (type.isNull()) {
1174
- return { type: "any", required: false, nullable: true };
1175
- } else if (type.isUndefined()) {
1176
- return { type: "any", required: false, optional: true };
1177
- } else if (type.isAny()) {
1178
- return { type: "any", required: !isOptional };
1179
- }
1180
- return { type: "any", required: !isOptional };
1181
- }
1182
- /**
1183
- * 将类型映射为字符串
1184
- */
1185
- mapTypeToString(type) {
1186
- if (type.isString()) return "string";
1187
- if (type.isNumber()) return "number";
1188
- if (type.isBoolean()) return "boolean";
1189
- if (type.isArray()) return "array";
1190
- if (type.isObject()) return "object";
1191
- if (type.isDate()) return "date";
1192
- return "any";
1193
- }
1194
- /**
1195
- * 从类型节点创建 FieldValidation
1196
- */
1197
- parseTypeNodeToFieldValidation(typeNode, typeArgs, imports) {
1198
- if (Node2.isStringKeyword(typeNode)) {
1199
- return { type: "string", required: true };
1200
- } else if (Node2.isNumberKeyword(typeNode)) {
1201
- return { type: "number", required: true };
1202
- } else if (Node2.isBooleanKeyword(typeNode)) {
1203
- return { type: "boolean", required: true };
1204
- } else if (Node2.isArrayTypeNode(typeNode)) {
1205
- const elementType = typeNode.getElementTypeNode();
1206
- return {
1207
- type: "array",
1208
- required: true,
1209
- items: this.parseTypeNodeToFieldValidation(elementType, typeArgs, imports)
1210
- };
1211
- } else if (typeNode.getKind() === SyntaxKind2.TypeReference) {
1212
- const refNode = typeNode;
1213
- const typeName = refNode.getTypeName().getText();
1214
- const sourceFile = refNode.getSourceFile();
1215
- const typeDecl = this.findTypeDeclarationInFile(sourceFile, typeName);
1216
- if (typeDecl) {
1217
- const fields = this.extractFieldsFromType(typeDecl, typeArgs, imports);
1218
- return { type: "object", required: true, properties: fields };
1219
- }
1220
- return { type: "any", required: true };
1221
- }
1222
- return { type: "any", required: true };
1223
- }
1224
- };
1225
- var tsTypeParser = new TSTypeParser();
1226
-
1227
- // utils/RouteParser.ts
1228
- var RouteParser = class {
1229
- project;
1230
- schemaParser;
1231
- tsTypeParser;
1232
- constructor() {
1233
- this.project = new Project({
1234
- compilerOptions: {
1235
- module: ModuleKind.CommonJS,
1236
- target: ScriptTarget.ES2018,
1237
- allowJs: true
1238
- }
1239
- });
1240
- this.schemaParser = new ZodSchemaParser();
1241
- this.tsTypeParser = new TSTypeParser();
1242
- }
1243
- /**
1244
- * 解析单个路由文件
1245
- * @param filePath 路由文件路径
1246
- * @returns 路由信息数组
1247
- */
1248
- parseRouteFile(filePath) {
1249
- try {
1250
- const sourceFile = this.project.addSourceFileAtPath(filePath);
1251
- const routes = [];
1252
- const schemaFiles = this.getImportedSchemaFiles(sourceFile, filePath);
1253
- const typeImports = this.getTypeImports(sourceFile, filePath);
1254
- sourceFile.forEachDescendant((node) => {
1255
- const callExpr = node;
1256
- if (callExpr.isKind && callExpr.isKind(SyntaxKind3.CallExpression) && callExpr.getExpression && callExpr.getExpression().isKind(SyntaxKind3.PropertyAccessExpression)) {
1257
- const propAccess = callExpr.getExpression();
1258
- const object = propAccess.getExpression && propAccess.getExpression().getText();
1259
- const method = propAccess.getName && propAccess.getName();
1260
- if (object === "router" && ["get", "post", "put", "delete", "patch"].includes(method)) {
1261
- const args = callExpr.getArguments && callExpr.getArguments();
1262
- if (args && args.length > 0) {
1263
- const pathArg = args[0];
1264
- if (pathArg.isKind && pathArg.isKind(SyntaxKind3.StringLiteral)) {
1265
- const routePath = pathArg.getText && pathArg.getText().replace(/['"]/g, "");
1266
- const jsDoc = callExpr.getPreviousSiblingIfKind && callExpr.getPreviousSiblingIfKind(SyntaxKind3.JSDocComment);
1267
- let summary = "";
1268
- let description = "";
1269
- let requestSchema;
1270
- let responseSchema;
1271
- if (jsDoc) {
1272
- const commentText = jsDoc.getCommentText && jsDoc.getCommentText();
1273
- if (commentText) {
1274
- const lines = commentText.split("\n").map((line) => line.trim());
1275
- summary = lines[0] || "";
1276
- description = lines.slice(1).join("\n").trim() || summary;
1277
- }
1278
- try {
1279
- const fullCommentText = jsDoc.getFullText?.() || commentText || "";
1280
- const apiDocTags = this.parseApiDocTags(fullCommentText);
1281
- if (apiDocTags.params.length > 0) {
1282
- requestSchema = this.buildRequestSchemaFromTags(apiDocTags.params);
1283
- }
1284
- if (apiDocTags.success.length > 0) {
1285
- responseSchema = this.buildResponseSchemaFromTags(apiDocTags.success);
1286
- }
1287
- } catch (error) {
1288
- Logger_default.warn(`\u89E3\u6790 apidoc \u6CE8\u91CA\u5931\u8D25: ${filePath}:${routePath}`, error);
1289
- }
1290
- if (!responseSchema) {
1291
- responseSchema = this.extractResponseSchema(jsDoc, sourceFile, typeImports);
1292
- }
1293
- }
1294
- const middlewares = [];
1295
- for (let i = 1; i < args.length - 1; i++) {
1296
- const arg = args[i];
1297
- if (arg.isKind(SyntaxKind3.CallExpression)) {
1298
- const mwCallExpr = arg;
1299
- const expr = mwCallExpr.getExpression();
1300
- if (expr.isKind(SyntaxKind3.Identifier)) {
1301
- middlewares.push(expr.getText());
1302
- } else if (expr.isKind(SyntaxKind3.PropertyAccessExpression)) {
1303
- middlewares.push(expr.getText());
1304
- }
1305
- }
1306
- }
1307
- if (!requestSchema) {
1308
- requestSchema = this.extractRequestSchema(args, schemaFiles);
1309
- }
1310
- const routeInfo = {
1311
- module: path3.basename(path3.dirname(filePath)).toLowerCase(),
1312
- method: method.toUpperCase(),
1313
- path: routePath,
1314
- fullPath: "",
1315
- // 完整路径会在注册时填充
1316
- summary,
1317
- description,
1318
- requestSchema,
1319
- responseSchema,
1320
- middlewares,
1321
- filePath
1322
- };
1323
- routes.push(routeInfo);
1324
- }
1325
- }
1326
- }
1327
- }
1328
- });
1329
- return routes;
1330
- } catch (error) {
1331
- Logger_default.error(`\u89E3\u6790\u8DEF\u7531\u6587\u4EF6\u5931\u8D25: ${filePath}`, error);
1332
- return [];
1333
- }
1334
- }
1335
- /**
1336
- * 获取导入的 schema 文件映射
1337
- * @returns Map<变量名, 文件绝对路径>
1338
- */
1339
- getImportedSchemaFiles(sourceFile, currentFilePath) {
1340
- const schemaFiles = /* @__PURE__ */ new Map();
1341
- for (const importDecl of sourceFile.getImportDeclarations()) {
1342
- const source = importDecl.getModuleSpecifierValue();
1343
- if (source.includes(".schema") || source.includes("/zod/") || source.includes("\\zod\\")) {
1344
- const absolutePath = this.resolveImportPath(
1345
- currentFilePath,
1346
- source
1347
- );
1348
- if (absolutePath) {
1349
- for (const specifier of importDecl.getNamedImports()) {
1350
- Logger_default.debug(`\u53D1\u73B0 schema \u5BFC\u5165: ${specifier.getName()} -> ${absolutePath}`);
1351
- schemaFiles.set(specifier.getName(), absolutePath);
1352
- }
1353
- } else {
1354
- Logger_default.debug(`\u65E0\u6CD5\u89E3\u6790 schema \u5BFC\u5165\u8DEF\u5F84: ${source}`);
1355
- }
1356
- }
1357
- }
1358
- return schemaFiles;
1359
- }
1360
- /**
1361
- * 获取所有类型导入映射(用于响应类型解析)
1362
- * @returns Map<类型名, 文件绝对路径>
1363
- */
1364
- getTypeImports(sourceFile, currentFilePath) {
1365
- const typeImports = /* @__PURE__ */ new Map();
1366
- for (const importDecl of sourceFile.getImportDeclarations()) {
1367
- const source = importDecl.getModuleSpecifierValue();
1368
- const absolutePath = this.resolveImportPath(currentFilePath, source);
1369
- if (absolutePath) {
1370
- for (const specifier of importDecl.getNamedImports()) {
1371
- typeImports.set(specifier.getName(), absolutePath);
1372
- }
1373
- }
1374
- }
1375
- return typeImports;
1376
- }
1377
- /**
1378
- * 从 JSDoc 中提取响应 Schema
1379
- */
1380
- extractResponseSchema(jsDoc, sourceFile, typeImports) {
1381
- const tags = jsDoc.getTags();
1382
- for (const tag of tags) {
1383
- const tagName = tag.getTagName();
1384
- if (tagName === "returns" || tagName === "return") {
1385
- const tagText = tag.getText?.() || "";
1386
- Logger_default.debug(`@returns \u6807\u7B7E\u6587\u672C: ${tagText}`);
1387
- const typeMatch = tagText.match(/@\s*returns?\s*\{([^}]+)\}/i);
1388
- if (typeMatch) {
1389
- const typeString = typeMatch[1].trim();
1390
- Logger_default.debug(`\u4ECE\u6807\u7B7E\u6587\u672C\u63D0\u53D6\u7C7B\u578B: ${typeString}`);
1391
- const responseSchema = this.tsTypeParser.parseResponseType(
1392
- sourceFile,
1393
- typeString,
1394
- typeImports
1395
- );
1396
- if (responseSchema) {
1397
- return responseSchema;
1398
- }
1399
- }
1400
- const comment = tag.getCommentText?.();
1401
- if (comment) {
1402
- const commentMatch = comment.match(/^\{([^}]+)\}/);
1403
- if (commentMatch) {
1404
- const typeString = commentMatch[1].trim();
1405
- Logger_default.debug(`\u4ECE\u6CE8\u91CA\u63D0\u53D6\u7C7B\u578B: ${typeString}`);
1406
- const responseSchema = this.tsTypeParser.parseResponseType(
1407
- sourceFile,
1408
- typeString,
1409
- typeImports
1410
- );
1411
- if (responseSchema) {
1412
- return responseSchema;
1413
- }
1414
- }
1415
- }
1416
- const typeExpression = tag.getTypeExpression?.();
1417
- if (typeExpression) {
1418
- const typeNode = typeExpression.getType?.();
1419
- if (typeNode) {
1420
- const typeString = typeNode.getText();
1421
- Logger_default.debug(`\u4ECE\u7C7B\u578B\u8868\u8FBE\u5F0F\u63D0\u53D6\u7C7B\u578B: ${typeString}`);
1422
- if (typeString && typeString !== "any") {
1423
- const responseSchema = this.tsTypeParser.parseResponseType(
1424
- sourceFile,
1425
- typeString,
1426
- typeImports
1427
- );
1428
- if (responseSchema) {
1429
- return responseSchema;
1430
- }
1431
- }
1432
- }
1433
- }
1434
- }
1435
- }
1436
- return void 0;
1437
- }
1438
- /**
1439
- * 解析导入路径为绝对路径
1440
- */
1441
- resolveImportPath(currentFilePath, importPath) {
1442
- try {
1443
- if (importPath.startsWith(".")) {
1444
- const currentDir = path3.dirname(currentFilePath);
1445
- const absolutePath = path3.resolve(currentDir, importPath);
1446
- const tsPath = absolutePath.endsWith(".ts") ? absolutePath : `${absolutePath}.ts`;
1447
- return tsPath;
1448
- }
1449
- return null;
1450
- } catch (error) {
1451
- Logger_default.warn(`\u89E3\u6790\u5BFC\u5165\u8DEF\u5F84\u5931\u8D25: ${importPath}`);
1452
- return null;
1453
- }
1454
- }
1455
- /**
1456
- * 提取请求 Schema
1457
- */
1458
- extractRequestSchema(args, schemaFiles) {
1459
- for (const arg of args) {
1460
- let targetArg = arg;
1461
- if (arg.isKind && arg.isKind(SyntaxKind3.AsExpression)) {
1462
- targetArg = arg.getExpression();
1463
- }
1464
- if (targetArg.isKind && targetArg.isKind(SyntaxKind3.CallExpression) && targetArg.getExpression && targetArg.getExpression().isKind && targetArg.getExpression().isKind(SyntaxKind3.Identifier) && targetArg.getExpression().getText() === "validate") {
1465
- const validateArgs = targetArg.getArguments();
1466
- if (validateArgs.length === 0) continue;
1467
- const schemaArg = validateArgs[0];
1468
- let schemaName;
1469
- if (schemaArg.isKind && schemaArg.isKind(SyntaxKind3.AsExpression)) {
1470
- schemaName = schemaArg.getExpression().getText();
1471
- } else {
1472
- schemaName = schemaArg.getText();
1473
- }
1474
- let target = "body";
1475
- if (validateArgs.length > 1) {
1476
- const targetArgParam = validateArgs[1];
1477
- if (targetArgParam.isKind && targetArgParam.isKind(SyntaxKind3.StringLiteral)) {
1478
- target = targetArgParam.getLiteralValue();
1479
- }
1480
- }
1481
- const schemaFilePath = schemaFiles.get(schemaName);
1482
- if (schemaFilePath) {
1483
- try {
1484
- const schemaSourceFile = this.project.addSourceFileAtPath(schemaFilePath);
1485
- const fields = this.schemaParser.parseSchemaVariable(
1486
- schemaSourceFile,
1487
- schemaName
1488
- );
1489
- if (fields) {
1490
- return { target, fields };
1491
- }
1492
- } catch (error) {
1493
- Logger_default.warn(`\u89E3\u6790 schema \u6587\u4EF6\u5931\u8D25: ${schemaFilePath}`, error);
1494
- }
1495
- } else {
1496
- Logger_default.debug(`\u672A\u627E\u5230 schema \u6587\u4EF6\u6620\u5C04: ${schemaName}`);
1497
- }
1498
- }
1499
- }
1500
- return void 0;
1501
- }
1502
- /**
1503
- * 解析多个路由文件
1504
- * @param filePaths 路由文件路径数组
1505
- * @returns 路由信息数组
1506
- */
1507
- parseRouteFiles(filePaths) {
1508
- const allRoutes = [];
1509
- for (const filePath of filePaths) {
1510
- const routes = this.parseRouteFile(filePath);
1511
- allRoutes.push(...routes);
1512
- }
1513
- return allRoutes;
1514
- }
1515
- /**
1516
- * 解析 apidoc 格式的注释标签
1517
- * @param commentText JSDoc 注释文本
1518
- * @returns 解析出的标签信息
1519
- */
1520
- parseApiDocTags(commentText) {
1521
- const tags = { params: [], success: [] };
1522
- const paramRegex = /@apiParam\s+\{([^}]+)\}\s*(?:\[([^\]]+)\]|(\S+))\s*(.*)/g;
1523
- let match;
1524
- while ((match = paramRegex.exec(commentText)) !== null) {
1525
- const [, typeStr, optionalName, requiredName, description] = match;
1526
- const name = optionalName || requiredName;
1527
- const required = !optionalName;
1528
- const typeInfo = this.parseParamType(typeStr);
1529
- tags.params.push({
1530
- name,
1531
- type: typeInfo.type,
1532
- description: description.trim(),
1533
- required,
1534
- enumValues: typeInfo.enumValues
1535
- });
1536
- }
1537
- const successRegex = /@apiSuccess\s+\{([^}]+)\}\s+([^\s]+)\s*(.*)/g;
1538
- while ((match = successRegex.exec(commentText)) !== null) {
1539
- const [, typeStr, fieldPath, description] = match;
1540
- const parts = fieldPath.split(".");
1541
- const name = parts[parts.length - 1];
1542
- const parent = parts.length > 1 ? parts.slice(0, -1).join(".") : void 0;
1543
- const typeInfo = this.parseParamType(typeStr);
1544
- tags.success.push({
1545
- name,
1546
- type: typeInfo.type,
1547
- description: description.trim(),
1548
- parent
1549
- });
1550
- }
1551
- return tags;
1552
- }
1553
- /**
1554
- * 解析 apidoc 类型字符串
1555
- * @param typeStr 类型字符串,如 "string", "string=", "number[]", {string="a","b"}
1556
- * @returns 解析后的类型信息
1557
- */
1558
- parseParamType(typeStr) {
1559
- typeStr = typeStr.trim();
1560
- const enumMatch = typeStr.match(/^(\w+)=(.+)$/);
1561
- if (enumMatch) {
1562
- const baseType = enumMatch[1];
1563
- const enumValues = enumMatch[2].split(",").map((v) => v.trim().replace(/^["']|["']$/g, ""));
1564
- return {
1565
- type: this.mapTypeName(baseType),
1566
- enumValues
1567
- };
1568
- }
1569
- if (typeStr.endsWith("[]")) {
1570
- const baseType = typeStr.slice(0, -2);
1571
- return {
1572
- type: this.mapTypeName(baseType),
1573
- array: true
1574
- };
1575
- }
1576
- if (typeStr.endsWith("?")) {
1577
- const baseType = typeStr.slice(0, -1);
1578
- return {
1579
- type: this.mapTypeName(baseType)
1580
- };
1581
- }
1582
- return {
1583
- type: this.mapTypeName(typeStr)
1584
- };
1585
- }
1586
- /**
1587
- * 映射类型名称到 FieldValidation 类型
1588
- */
1589
- mapTypeName(typeName) {
1590
- const typeMap = {
1591
- string: "string",
1592
- number: "number",
1593
- integer: "number",
1594
- boolean: "boolean",
1595
- array: "array",
1596
- object: "object",
1597
- file: "file",
1598
- date: "date"
1599
- };
1600
- return typeMap[typeName.toLowerCase()] || "any";
1601
- }
1602
- /**
1603
- * 从 @apiParam 标签构建请求 Schema
1604
- */
1605
- buildRequestSchemaFromTags(params) {
1606
- const fields = {};
1607
- for (const param of params) {
1608
- fields[param.name] = {
1609
- type: param.type,
1610
- required: param.required,
1611
- constraints: param.enumValues ? { custom: [`\u53EF\u9009\u503C: ${param.enumValues.join(", ")}`] } : void 0
1612
- };
1613
- }
1614
- return { target: "body", fields };
1615
- }
1616
- /**
1617
- * 从 @apiSuccess 标签构建响应 Schema
1618
- */
1619
- buildResponseSchemaFromTags(success) {
1620
- const fields = {};
1621
- for (const field of success) {
1622
- if (!field.parent && !fields[field.name]) {
1623
- if (field.type === "object") {
1624
- fields[field.name] = {
1625
- type: "object",
1626
- required: true,
1627
- properties: {}
1628
- };
1629
- } else {
1630
- fields[field.name] = {
1631
- type: field.type,
1632
- required: true
1633
- };
1634
- }
1635
- }
1636
- }
1637
- for (const field of success) {
1638
- if (field.parent) {
1639
- const parts = field.parent.split(".");
1640
- let current = fields;
1641
- for (let i = 0; i < parts.length; i++) {
1642
- const part = parts[i];
1643
- if (!current[part]) {
1644
- current[part] = {
1645
- type: "object",
1646
- required: true,
1647
- properties: {}
1648
- };
1649
- } else if (!current[part].properties) {
1650
- current[part].properties = {};
1651
- }
1652
- current = current[part].properties;
1653
- }
1654
- current[field.name] = {
1655
- type: field.type,
1656
- required: true
1657
- };
1658
- }
1659
- }
1660
- return { fields };
1661
- }
1662
- };
1663
- var routeParser = new RouteParser();
1664
-
1665
- // utils/DocManager.ts
1666
- import Database2 from "better-sqlite3";
1667
- import * as path4 from "path";
1668
- import * as fs4 from "fs";
1669
- var DocManager = class {
1670
- db = null;
1671
- config;
1672
- constructor(config2) {
1673
- this.config = config2;
1674
- this.init();
1675
- }
1676
- /**
1677
- * 初始化数据库
1678
- */
1679
- init() {
1680
- try {
1681
- const dbDir = path4.dirname(this.config.path);
1682
- if (!fs4.existsSync(dbDir)) {
1683
- fs4.mkdirSync(dbDir, { recursive: true });
1684
- }
1685
- this.db = new Database2(this.config.path);
1686
- this.createTables();
1687
- } catch (error) {
1688
- Logger_default.error("\u6587\u6863\u6570\u636E\u5E93\u521D\u59CB\u5316\u5931\u8D25:", error);
1689
- this.db = null;
1690
- }
1691
- }
1692
- /**
1693
- * 创建表结构
1694
- */
1695
- createTables() {
1696
- if (!this.db) return;
1697
- this.db.exec(`
1698
- CREATE TABLE IF NOT EXISTS doc_routes (
1699
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1700
- module TEXT NOT NULL,
1701
- method TEXT NOT NULL,
1702
- path TEXT NOT NULL,
1703
- full_path TEXT NOT NULL,
1704
- summary TEXT,
1705
- description TEXT,
1706
- request_schema TEXT,
1707
- response_schema TEXT,
1708
- middlewares TEXT,
1709
- file_path TEXT NOT NULL,
1710
- created_at INTEGER NOT NULL,
1711
- updated_at INTEGER NOT NULL,
1712
- UNIQUE(module, method, path)
1713
- );
1714
- `);
1715
- this.db.exec(`
1716
- CREATE INDEX IF NOT EXISTS idx_doc_routes_module ON doc_routes(module);
1717
- CREATE INDEX IF NOT EXISTS idx_doc_routes_full_path ON doc_routes(full_path);
1718
- `);
1719
- }
1720
- /**
1721
- * 保存路由信息
1722
- * @param routeInfo 路由信息
1723
- * @param apiPrefix API 前缀
1724
- * @param moduleName 模块名称
1725
- */
1726
- saveRoute(routeInfo, apiPrefix = "/api", moduleName) {
1727
- if (!this.db) return;
1728
- try {
1729
- const fullPath = `${apiPrefix}/${moduleName}${routeInfo.path}`.replace(/\/+/g, "/");
1730
- const now = Date.now();
1731
- const data = {
1732
- module: moduleName,
1733
- method: routeInfo.method,
1734
- path: routeInfo.path,
1735
- full_path: fullPath,
1736
- summary: routeInfo.summary,
1737
- description: routeInfo.description,
1738
- request_schema: routeInfo.requestSchema ? JSON.stringify(routeInfo.requestSchema) : null,
1739
- response_schema: routeInfo.responseSchema ? JSON.stringify(routeInfo.responseSchema) : null,
1740
- middlewares: routeInfo.middlewares ? JSON.stringify(routeInfo.middlewares) : null,
1741
- file_path: routeInfo.filePath,
1742
- created_at: now,
1743
- updated_at: now
1744
- };
1745
- const stmt = this.db.prepare(`
1746
- INSERT INTO doc_routes (
1747
- module, method, path, full_path, summary, description,
1748
- request_schema, response_schema, middlewares, file_path, created_at, updated_at
1749
- ) VALUES (
1750
- @module, @method, @path, @full_path, @summary, @description,
1751
- @request_schema, @response_schema, @middlewares, @file_path, @created_at, @updated_at
1752
- ) ON CONFLICT(module, method, path) DO UPDATE SET
1753
- full_path = @full_path,
1754
- summary = @summary,
1755
- description = @description,
1756
- request_schema = @request_schema,
1757
- response_schema = @response_schema,
1758
- middlewares = @middlewares,
1759
- file_path = @file_path,
1760
- updated_at = @updated_at
1761
- `);
1762
- stmt.run(data);
1763
- } catch (error) {
1764
- Logger_default.error("\u4FDD\u5B58\u8DEF\u7531\u4FE1\u606F\u5931\u8D25:", error);
1765
- }
1766
- }
1767
- /**
1768
- * 获取模块列表
1769
- * @returns 模块列表
1770
- */
1771
- getModules() {
1772
- if (!this.db) return [];
1773
- try {
1774
- const stmt = this.db.prepare(`
1775
- SELECT module, COUNT(*) as count
1776
- FROM doc_routes
1777
- GROUP BY module
1778
- ORDER BY module
1779
- `);
1780
- return stmt.all();
1781
- } catch (error) {
1782
- Logger_default.error("\u83B7\u53D6\u6A21\u5757\u5217\u8868\u5931\u8D25:", error);
1783
- return [];
1784
- }
1785
- }
1786
- /**
1787
- * 获取路由列表
1788
- * @param module 模块名称(可选)
1789
- * @returns 路由列表
1790
- */
1791
- getRoutes(module) {
1792
- if (!this.db) return [];
1793
- try {
1794
- let query = `
1795
- SELECT id, module, method, path, full_path, summary, description,
1796
- request_schema, response_schema, middlewares, file_path, created_at, updated_at
1797
- FROM doc_routes
1798
- `;
1799
- const params = {};
1800
- if (module) {
1801
- query += " WHERE module = @module";
1802
- params.module = module;
1803
- }
1804
- query += " ORDER BY module, method, path";
1805
- const stmt = this.db.prepare(query);
1806
- const rows = stmt.all(params);
1807
- return rows.map((row) => ({
1808
- id: row.id,
1809
- module: row.module,
1810
- method: row.method,
1811
- path: row.path,
1812
- fullPath: row.full_path,
1813
- summary: row.summary,
1814
- description: row.description,
1815
- requestSchema: row.request_schema ? JSON.parse(row.request_schema) : void 0,
1816
- responseSchema: row.response_schema ? JSON.parse(row.response_schema) : void 0,
1817
- middlewares: row.middlewares ? JSON.parse(row.middlewares) : void 0,
1818
- filePath: row.file_path,
1819
- createdAt: row.created_at,
1820
- updatedAt: row.updated_at
1821
- }));
1822
- } catch (error) {
1823
- Logger_default.error("\u83B7\u53D6\u8DEF\u7531\u5217\u8868\u5931\u8D25:", error);
1824
- return [];
1825
- }
1826
- }
1827
- /**
1828
- * 获取路由详情
1829
- * @param id 路由 ID
1830
- * @returns 路由详情
1831
- */
1832
- getRouteById(id) {
1833
- if (!this.db) return null;
1834
- try {
1835
- const stmt = this.db.prepare(`
1836
- SELECT id, module, method, path, full_path, summary, description,
1837
- request_schema, response_schema, middlewares, file_path, created_at, updated_at
1838
- FROM doc_routes
1839
- WHERE id = @id
1840
- `);
1841
- const row = stmt.get({ id });
1842
- if (!row) return null;
1843
- return {
1844
- id: row.id,
1845
- module: row.module,
1846
- method: row.method,
1847
- path: row.path,
1848
- fullPath: row.full_path,
1849
- summary: row.summary,
1850
- description: row.description,
1851
- requestSchema: row.request_schema ? JSON.parse(row.request_schema) : void 0,
1852
- responseSchema: row.response_schema ? JSON.parse(row.response_schema) : void 0,
1853
- middlewares: row.middlewares ? JSON.parse(row.middlewares) : void 0,
1854
- filePath: row.file_path,
1855
- createdAt: row.created_at,
1856
- updatedAt: row.updated_at
1857
- };
1858
- } catch (error) {
1859
- Logger_default.error("\u83B7\u53D6\u8DEF\u7531\u8BE6\u60C5\u5931\u8D25:", error);
1860
- return null;
1861
- }
1862
- }
1863
- /**
1864
- * 清空所有路由数据
1865
- */
1866
- clearAllRoutes() {
1867
- if (!this.db) return;
1868
- try {
1869
- this.db.exec("DELETE FROM doc_routes");
1870
- } catch (error) {
1871
- Logger_default.error("\u6E05\u7A7A\u8DEF\u7531\u6570\u636E\u5931\u8D25:", error);
1872
- }
1873
- }
1874
- /**
1875
- * 关闭数据库连接
1876
- */
1877
- close() {
1878
- if (this.db) {
1879
- this.db.close();
1880
- this.db = null;
1881
- Logger_default.info("\u6587\u6863\u6570\u636E\u5E93\u8FDE\u63A5\u5DF2\u5173\u95ED");
1882
- }
1883
- }
1884
- };
1885
- var docManager;
1886
- function initDocManager(config2) {
1887
- docManager = new DocManager(config2);
1888
- return docManager;
1889
- }
1890
-
1891
- // utils/RouteRegistry.ts
1892
- import path5 from "path";
1893
- function getRoutes(router2, prefix = "") {
1894
- const routes = [];
1895
- if (router2 && router2.stack) {
1896
- router2.stack.forEach((layer) => {
1897
- if (layer.route) {
1898
- const path9 = (prefix + layer.route.path).replace(/\/+/g, "/");
1899
- const methods = Object.keys(layer.route.methods).map((m) => m.toUpperCase());
1900
- methods.forEach((method) => {
1901
- routes.push({ path: path9, method });
1902
- });
1903
- } else if (layer.name === "router" && layer.handle && layer.handle.stack) {
1904
- const newPrefix = (prefix + (layer.regexp.source.replace("^\\/", "").replace("\\/?(?=\\/|$)", "") || "")).replace(/\/+/g, "/");
1905
- routes.push(...getRoutes(layer.handle, newPrefix));
1906
- }
1907
- });
1908
- }
1909
- return routes;
1910
- }
1911
- async function registerRoute(app, config2, apiPrefix = "/api") {
1912
- const { fileName, defaultExport: handler, filePath, cacheHit } = config2;
1913
- try {
1914
- const lowercaseModuleName = fileName.toLowerCase();
1915
- if (!cacheHit) {
1916
- const routes = routeParser.parseRouteFile(filePath);
1917
- routes.forEach((route) => {
1918
- docManager.saveRoute(route, apiPrefix, lowercaseModuleName);
1919
- });
1920
- }
1921
- const filePathParts = filePath.split(path5.sep);
1922
- const routesIndex = filePathParts.lastIndexOf("routes");
1923
- if (routesIndex === -1) {
1924
- throw new Error(`\u8DEF\u7531\u6587\u4EF6\u5FC5\u987B\u5728 routes \u76EE\u5F55\u4E0B: ${filePath}`);
1925
- }
1926
- const routesDir = filePathParts.slice(0, routesIndex + 1).join(path5.sep);
1927
- const fileDir = path5.dirname(filePath);
1928
- const relativePath = path5.relative(routesDir, fileDir);
1929
- let pathParts = [];
1930
- if (relativePath && relativePath !== ".") {
1931
- pathParts = relativePath.split(path5.sep).map((p) => p.toLowerCase());
1932
- }
1933
- pathParts.push(lowercaseModuleName);
1934
- const baseRoutePath = `${apiPrefix}/${pathParts.join("/")}`.replace(/\/+/g, "/");
1935
- app.use(baseRoutePath, handler);
1936
- const subRoutes = getRoutes(handler);
1937
- if (subRoutes.length > 0) {
1938
- Logger_default.startup("\u8DEF\u7531", `${baseRoutePath} (${subRoutes.length} \u4E2A\u7AEF\u70B9)`);
1939
- subRoutes.forEach((route) => {
1940
- const fullPath = `${baseRoutePath}${route.path}`.replace(/\/+/g, "/");
1941
- const method = route.method.padEnd(6, " ");
1942
- Logger_default.startup(` \u2514\u2500 ${method} ${fullPath}`);
1943
- });
1944
- } else {
1945
- Logger_default.startup("\u8DEF\u7531", `${baseRoutePath} (\u65E0\u7AEF\u70B9)`);
1946
- }
1947
- } catch (error) {
1948
- Logger_default.error(`\u6CE8\u518C\u8DEF\u7531\u5931\u8D25[${fileName.toLowerCase()}]: ${error}`);
1949
- }
1950
- }
1951
-
1952
- // utils/ComponentRegistry.ts
1953
- async function registerComponents(state2, scannedResults) {
1954
- for (const item of scannedResults) {
1955
- if (item.type === "middleware") {
1956
- registerMiddleware(state2.express, item);
1957
- }
1958
- }
1959
- for (const item of scannedResults) {
1960
- if (item.type === "route") {
1961
- registerRoute(state2.express, item, state2.options.apiPrefix);
1962
- }
1963
- }
1964
- const syncAppRoutes = state2.express.get("syncAppRoutes");
1965
- if (syncAppRoutes && typeof syncAppRoutes === "function") {
1966
- try {
1967
- await syncAppRoutes();
1968
- } catch (error) {
1969
- Logger_default.error("[ComponentRegistry] \u8DEF\u7531\u8D44\u6E90\u540C\u6B65\u5931\u8D25:", error);
1970
- }
1971
- }
1972
- }
1973
-
1974
- // utils/InitErrorHandler.ts
1975
- import { ZodError } from "zod";
1976
- function isSQLError(err) {
1977
- if (!err) return false;
1978
- const msg = err.message || err.toString() || "";
1979
- return /no such table|table.*does not exist|column.*does not exist|database schema has changed/i.test(msg);
1980
- }
1981
- function setupErrorHandlers(app, logging) {
1982
- app.use((req, res) => {
1983
- res.status(404).json({
1984
- code: 404,
1985
- data: null,
1986
- msg: "\u8D44\u6E90\u4E0D\u5B58\u5728",
1987
- error: "Not Found",
1988
- fields: {}
1989
- });
1990
- });
1991
- app.use((err, req, res, next) => {
1992
- if (err.isAppError || err.statusCode && err.statusCode >= 400 && err.statusCode < 500) {
1993
- return res.status(err.statusCode).json({
1994
- code: err.statusCode,
1995
- data: null,
1996
- msg: err.message,
1997
- error: "BusinessError",
1998
- fields: {}
1999
- });
2000
- }
2001
- if (err instanceof ZodError) {
2002
- const fields = {};
2003
- err.errors.forEach((e) => {
2004
- const path9 = e.path.join(".");
2005
- fields[path9] = e.message;
2006
- });
2007
- return res.status(400).json({
2008
- code: 400,
2009
- data: null,
2010
- msg: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
2011
- error: "ZodValidationError",
2012
- fields
2013
- });
2014
- }
2015
- if (isSQLError(err)) {
2016
- Logger_default.error("");
2017
- Logger_default.error("\u274C \u6570\u636E\u5E93\u7ED3\u6784\u53EF\u80FD\u5DF2\u53D8\u5316\uFF0C\u8BF7\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u540C\u6B65\uFF1A");
2018
- Logger_default.error("");
2019
- Logger_default.error(" npm run db:migrate");
2020
- Logger_default.error("");
2021
- }
2022
- const statusCode = err.status || err.statusCode || 500;
2023
- if (!isSQLError(err)) {
2024
- Logger_default.error("\u7CFB\u7EDF\u9519\u8BEF:", err);
2025
- }
2026
- res.status(statusCode).json({
2027
- code: statusCode,
2028
- data: null,
2029
- msg: err.message || "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF",
2030
- error: logging ? err.stack : "InternalServerError",
2031
- fields: {}
2032
- });
2033
- });
2034
- }
2035
-
2036
- // utils/HttpServer.ts
2037
- async function startServer(app, port) {
2038
- if (!app) {
2039
- throw new Error("Express \u5B9E\u4F8B\u4E0D\u80FD\u4E3A\u7A7A");
2040
- }
2041
- if (!port || typeof port !== "number" || port <= 0 || port > 65535) {
2042
- throw new Error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${port}`);
2043
- }
2044
- return new Promise((resolve3, reject) => {
2045
- try {
2046
- const server = app.listen(port, () => {
2047
- resolve3();
2048
- });
2049
- server.on("error", (error) => {
2050
- Logger_default.error("\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25\uFF1A", error);
2051
- reject(new Error(`\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`));
2052
- });
2053
- server.on("close", () => {
2054
- Logger_default.info("\u670D\u52A1\u5668\u5DF2\u5173\u95ED");
2055
- });
2056
- } catch (error) {
2057
- Logger_default.error("\u670D\u52A1\u5668\u542F\u52A8\u5F02\u5E38\uFF1A", error);
2058
- reject(new Error(`\u670D\u52A1\u5668\u542F\u52A8\u5F02\u5E38\uFF1A${error instanceof Error ? error.message : String(error)}`));
2059
- }
2060
- });
2061
- }
2062
-
2063
- // routes/Doc.route.ts
2064
- import { Router } from "express";
2065
-
2066
- // utils/Validate.ts
2067
- var validate = (schema, target = "body") => {
2068
- return async (req, res, next) => {
2069
- try {
2070
- await schema.parseAsync(req[target]);
2071
- next();
2072
- } catch (error) {
2073
- next(error);
2074
- }
2075
- };
2076
- };
2077
-
2078
- // zod/Doc.schema.ts
2079
- import { z } from "zod";
2080
- var routeIdParamsSchema = z.object({
2081
- id: z.coerce.number().int().positive("\u65E0\u6548\u7684\u8DEF\u7531ID")
2082
- });
2083
-
2084
- // routes/Doc.route.ts
2085
- var router = Router();
2086
- router.get("/modules", (req, res) => {
2087
- try {
2088
- const modules = docManager.getModules();
2089
- const response = {
2090
- code: 200,
2091
- data: modules,
2092
- msg: "success"
2093
- };
2094
- res.json(response);
2095
- } catch (error) {
2096
- const response = {
2097
- code: 500,
2098
- data: null,
2099
- msg: "\u83B7\u53D6\u6A21\u5757\u5217\u8868\u5931\u8D25"
2100
- };
2101
- res.json(response);
2102
- }
2103
- });
2104
- router.get("/routes", (req, res) => {
2105
- try {
2106
- const module = req.query.module;
2107
- const routes = docManager.getRoutes(module);
2108
- const response = {
2109
- code: 200,
2110
- data: routes,
2111
- msg: "success"
2112
- };
2113
- res.json(response);
2114
- } catch (error) {
2115
- const response = {
2116
- code: 500,
2117
- data: null,
2118
- msg: "\u83B7\u53D6\u8DEF\u7531\u5217\u8868\u5931\u8D25"
2119
- };
2120
- res.json(response);
2121
- }
2122
- });
2123
- router.get("/routes/:id", validate(routeIdParamsSchema, "params"), (req, res) => {
2124
- try {
2125
- const { id } = req.params;
2126
- const route = docManager.getRouteById(id);
2127
- if (!route) {
2128
- const response2 = {
2129
- code: 404,
2130
- data: null,
2131
- msg: "\u8DEF\u7531\u4E0D\u5B58\u5728"
2132
- };
2133
- return res.json(response2);
2134
- }
2135
- const response = {
2136
- code: 200,
2137
- data: route,
2138
- msg: "success"
2139
- };
2140
- res.json(response);
2141
- } catch (error) {
2142
- const response = {
2143
- code: 500,
2144
- data: null,
2145
- msg: "\u83B7\u53D6\u8DEF\u7531\u8BE6\u60C5\u5931\u8D25"
2146
- };
2147
- res.json(response);
2148
- }
2149
- });
2150
- router.post("/sync", (req, res) => {
2151
- try {
2152
- const response = {
2153
- code: 200,
2154
- data: true,
2155
- msg: "\u540C\u6B65\u6210\u529F"
2156
- };
2157
- res.json(response);
2158
- } catch (error) {
2159
- const response = {
2160
- code: 500,
2161
- data: null,
2162
- msg: "\u540C\u6B65\u5931\u8D25"
2163
- };
2164
- res.json(response);
2165
- }
2166
- });
2167
- var Doc_route_default = router;
2168
-
2169
- // core/app.ts
2170
- function createApp(options = {}) {
2171
- Logger_default.startup("\u521D\u59CB\u5316", "\u5E94\u7528\u521D\u59CB\u5316\u2026\u2026");
2172
- const stateInstance = createState(options);
2173
- const plugins = [];
2174
- if (stateInstance.options.logLevel) {
2175
- Logger_default.setLevel(stateInstance.options.logLevel);
2176
- }
2177
- const app = {
2178
- use: async (plugin) => {
2179
- if (typeof plugin.install === "function") {
2180
- plugins.push(plugin);
2181
- }
2182
- if (typeof plugin.__modulePath === "string") {
2183
- Logger_default.startup("\u6A21\u5757", `\u6CE8\u518C\uFF1A${plugin.__modulePath}`);
2184
- addScanPath(plugin.__modulePath);
2185
- }
2186
- },
2187
- startup: async () => {
2188
- initExpress(stateInstance);
2189
- await prepareScanEnvironment();
2190
- const scannedResults = await scanComponents();
2191
- await initializeDataStorage(scannedResults);
2192
- await installPlugins();
2193
- registerComponents(stateInstance, scannedResults);
2194
- registerDocRoutes();
2195
- setupErrorHandlers(stateInstance.express, stateInstance.options.logging);
2196
- await startHttpServer();
2197
- Logger_default.startup("\u521D\u59CB\u5316", "\u2705 \u5E94\u7528\u542F\u52A8\u5B8C\u6210");
2198
- },
2199
- getApp: () => stateInstance.express,
2200
- getDB: () => stateInstance.db,
2201
- getOptions: () => stateInstance.options
2202
- };
2203
- async function prepareScanEnvironment() {
2204
- setBaseDir(process.cwd());
2205
- Logger_default.startup("\u626B\u63CF", `\u57FA\u51C6\u76EE\u5F55: ${process.cwd()}`);
2206
- }
2207
- async function scanComponents() {
2208
- const scannedResults = await scan();
2209
- const routeFiles = scannedResults.filter((r) => r.type === "route");
2210
- const cacheHits = routeFiles.filter((r) => r.cacheHit).length;
2211
- const total = routeFiles.length;
2212
- if (cacheHits > 0) {
2213
- Logger_default.startup("\u626B\u63CF", `\u53D1\u73B0 ${scannedResults.length} \u4E2A\u7EC4\u4EF6\u6587\u4EF6 (\u7F13\u5B58\u547D\u4E2D: ${cacheHits}/${total})`);
2214
- } else {
2215
- Logger_default.startup("\u626B\u63CF", `\u53D1\u73B0 ${scannedResults.length} \u4E2A\u7EC4\u4EF6\u6587\u4EF6`);
2216
- }
2217
- return scannedResults;
2218
- }
2219
- async function initializeDataStorage(scannedResults) {
2220
- setupDatabase(stateInstance, scannedResults);
2221
- stateInstance.express.set("db", stateInstance.db);
2222
- if (stateInstance.db) {
2223
- const dbPath2 = stateInstance.options.database?.path || "./data/app.db";
2224
- Logger_default.startup("\u6570\u636E\u5E93", `\u5DF2\u8FDE\u63A5: SQLite (${dbPath2})`);
2225
- }
2226
- const docDbPath = stateInstance.options.database?.path ? stateInstance.options.database.path.replace(/app\.db$/, "doc.db") : "./data/doc.db";
2227
- initDocManager({ path: docDbPath });
2228
- Logger_default.startup("\u6570\u636E\u5E93", `\u6587\u6863\u5E93\u5DF2\u521D\u59CB\u5316: ${docDbPath}`);
2229
- }
2230
- async function installPlugins() {
2231
- for (const plugin of plugins) {
2232
- await plugin.install(app);
2233
- }
2234
- }
2235
- async function startHttpServer() {
2236
- const listenPort = stateInstance.options.port || 3e3;
2237
- await startServer(stateInstance.express, listenPort);
2238
- const serverUrl = `http://${stateInstance.options.host}:${listenPort}`;
2239
- Logger_default.startup("\u670D\u52A1", `\u8BBF\u95EE\u5730\u5740: ${serverUrl}`);
2240
- }
2241
- function registerDocRoutes() {
2242
- const apiPrefix = stateInstance.options.apiPrefix || "/api";
2243
- const docRoutePath = `${apiPrefix}/doc`.replace(/\/+/g, "/");
2244
- stateInstance.express.use(docRoutePath, Doc_route_default);
2245
- stateInstance.options.authWhitelist.push(`${docRoutePath}/modules`);
2246
- Logger_default.startup("\u8DEF\u7531", `${docRoutePath}/modules (GET)`);
2247
- stateInstance.options.authWhitelist.push(`${docRoutePath}/routes`);
2248
- Logger_default.startup("\u8DEF\u7531", `${docRoutePath}/routes (GET)`);
2249
- stateInstance.options.authWhitelist.push(new RegExp(`^${docRoutePath}/routes/[^/]+$`));
2250
- Logger_default.startup("\u8DEF\u7531", `${docRoutePath}/routes/:id (GET)`);
2251
- stateInstance.options.authWhitelist.push(`${docRoutePath}/sync`);
2252
- Logger_default.startup("\u8DEF\u7531", `${docRoutePath}/sync (POST)`);
2253
- }
2254
- return app;
2255
- }
2256
-
2257
- // commands/prepare.ts
2258
- import fs5 from "fs";
2259
- import path6 from "path";
2260
- async function prepare(cwd = process.cwd()) {
2261
- console.log("\u{1F680} MindBase \u73AF\u5883\u51C6\u5907\u5DE5\u5177");
2262
- console.log(`\u5DE5\u4F5C\u76EE\u5F55: ${cwd}
2263
- `);
2264
- setBaseDir(cwd);
2265
- try {
2266
- console.log("\u{1F4C2} \u626B\u63CF\u7EC4\u4EF6\u6587\u4EF6...");
2267
- const results = await scan();
2268
- const routeFiles = results.filter((r) => r.type === "route");
2269
- const middlewareFiles = results.filter((r) => r.type === "middleware");
2270
- const schemaFiles = results.filter((r) => r.type === "schema");
2271
- console.log(` \u53D1\u73B0 ${results.length} \u4E2A\u7EC4\u4EF6\u6587\u4EF6`);
2272
- console.log(` - \u8DEF\u7531: ${routeFiles.length}`);
2273
- console.log(` - \u4E2D\u95F4\u4EF6: ${middlewareFiles.length}`);
2274
- console.log(` - Schema: ${schemaFiles.length}`);
2275
- if (schemaFiles.length > 0) {
2276
- const schemaJsonPath = path6.join(cwd, ".drizzle-schemas.json");
2277
- const relativeSchemaPaths = schemaFiles.map(
2278
- (f) => path6.relative(cwd, f.filePath).replace(/\\/g, "/")
2279
- );
2280
- fs5.writeFileSync(schemaJsonPath, JSON.stringify(relativeSchemaPaths, null, 2));
2281
- console.log(` \u5DF2\u751F\u6210 schema \u6E05\u5355: .drizzle-schemas.json
2282
- `);
2283
- } else {
2284
- console.log(" \u8B66\u544A: \u672A\u627E\u5230 schema \u6587\u4EF6\n");
2285
- }
2286
- const schemas = {};
2287
- for (const item of results) {
2288
- if (item.type === "schema" && item.allExports) {
2289
- try {
2290
- Object.assign(schemas, item.allExports);
2291
- } catch (e) {
2292
- Logger_default.warn(`\u5904\u7406 Schema \u6587\u4EF6\u5931\u8D25: ${item.filePath}`, e);
2293
- }
2294
- }
2295
- }
2296
- console.log("\u{1F4BE} \u521D\u59CB\u5316\u6570\u636E\u5E93...");
2297
- const db = initDatabase({
2298
- dbPath: process.env.DB_PATH || "./data/app.db",
2299
- schemas
2300
- });
2301
- console.log(` \u6570\u636E\u5E93\u5DF2\u521D\u59CB\u5316
2302
- `);
2303
- const cacheFile = path6.join(cwd, "node_modules/.cache/mindbase/startup-cache.json");
2304
- console.log("\u2705 \u73AF\u5883\u51C6\u5907\u5B8C\u6210\uFF01");
2305
- console.log(`
2306
- \u{1F4E6} \u7F13\u5B58\u6587\u4EF6: ${cacheFile}`);
2307
- console.log(`\u{1F4BE} \u6570\u636E\u5E93: ${process.env.DB_PATH || "./data/app.db"}`);
2308
- console.log("\n\u{1F4A1} \u63D0\u793A: \u73B0\u5728\u53EF\u4EE5\u8FD0\u884C db push\u3001init-admin \u7B49\u547D\u4EE4\u4E86");
2309
- } catch (error) {
2310
- console.error("\n\u274C \u51C6\u5907\u5931\u8D25:", error);
2311
- process.exit(1);
2312
- }
2313
- }
2314
- async function precache(cwd) {
2315
- return prepare(cwd);
2316
- }
2317
-
2318
- // feature/cron/CronManager.ts
2319
- import { CronJob } from "cron";
2320
- import { randomUUID } from "crypto";
2321
- var jobs = {};
2322
- var config = {
2323
- timezone: "Asia/Shanghai"
2324
- };
2325
- function addCron(pattern, handler, customId) {
2326
- const id = customId || generateId();
2327
- if (jobs[id]) {
2328
- jobs[id].stop();
2329
- }
2330
- const job = new CronJob(pattern, handler, null, true, config.timezone);
2331
- jobs[id] = job;
2332
- return id;
2333
- }
2334
- function stopCron(id) {
2335
- const job = jobs[id];
2336
- if (job) {
2337
- job.stop();
2338
- delete jobs[id];
2339
- return true;
2340
- }
2341
- return false;
2342
- }
2343
- function stopAllCron() {
2344
- Object.values(jobs).forEach((job) => {
2345
- job.stop();
2346
- });
2347
- jobs = {};
2348
- }
2349
- function generateId() {
2350
- return randomUUID();
2351
- }
2352
-
2353
- // core/module/FindPackageRoot.ts
2354
- import path7 from "path";
2355
- import fs6 from "fs";
2356
- function findPackageRoot(startPath) {
2357
- if (!startPath || typeof startPath !== "string") {
2358
- throw new Error("\u65E0\u6548\u7684\u8D77\u59CB\u8DEF\u5F84\uFF1A\u8DEF\u5F84\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
2359
- }
2360
- let currentPath;
2361
- try {
2362
- currentPath = path7.isAbsolute(startPath) ? startPath : path7.resolve(startPath);
2363
- } catch (e) {
2364
- throw new Error(`\u65E0\u6CD5\u89E3\u6790\u8DEF\u5F84\uFF1A${startPath}`);
2365
- }
2366
- try {
2367
- if (!fs6.existsSync(currentPath)) {
2368
- throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF1A${currentPath}`);
2369
- }
2370
- const stat = fs6.statSync(currentPath);
2371
- if (stat.isFile()) {
2372
- currentPath = path7.dirname(currentPath);
2373
- }
2374
- } catch (e) {
2375
- throw new Error(`\u8DEF\u5F84\u9A8C\u8BC1\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
2376
- }
2377
- while (currentPath !== path7.parse(currentPath).root) {
2378
- try {
2379
- const pkgJsonPath = path7.join(currentPath, "package.json");
2380
- if (fs6.existsSync(pkgJsonPath)) {
2381
- const pkgStat = fs6.statSync(pkgJsonPath);
2382
- if (pkgStat.isFile()) {
2383
- return currentPath;
2384
- }
2385
- }
2386
- } catch (e) {
2387
- }
2388
- currentPath = path7.dirname(currentPath);
2389
- }
2390
- return path7.dirname(startPath);
2391
- }
2392
-
2393
- // core/module/GetModulePath.ts
2394
- import * as path8 from "path";
2395
- function getModulePath() {
2396
- try {
2397
- const error = new Error();
2398
- if (error.stack) {
2399
- console.log("DEBUG STACK:", error.stack);
2400
- const stackLines = error.stack.split("\n");
2401
- for (const line of stackLines) {
2402
- if (!line.includes("at") || line.includes("getModulePath") || line.includes("createModule") || line.includes("CreateModule.ts") || line.includes("ts-node") || line.includes("node:internal")) {
2403
- continue;
2404
- }
2405
- const match = line.match(/\(([^:]+\.ts)/) || line.match(/at\s+([^:]+\.ts)/);
2406
- if (match && match[1]) {
2407
- const filePath = match[1];
2408
- console.log("\u63D0\u53D6\u5230\u7684\u8DEF\u5F84:", filePath);
2409
- return path8.resolve(filePath);
2410
- }
2411
- }
2412
- }
2413
- } catch (e) {
2414
- }
2415
- throw new Error("\u65E0\u6CD5\u786E\u5B9A\u6A21\u5757\u8DEF\u5F84\uFF1A\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 __filename \u4E14\u65E0\u6CD5\u4ECE\u8C03\u7528\u6808\u4E2D\u63D0\u53D6\u8DEF\u5F84");
2416
- }
2417
-
2418
- // core/module/CreateModule.ts
2419
- function createModule(install, callerPath) {
2420
- if (typeof install !== "function") {
2421
- throw new Error("\u6A21\u5757\u5B89\u88C5\u51FD\u6570\u5FC5\u987B\u662F\u4E00\u4E2A\u51FD\u6570");
2422
- }
2423
- try {
2424
- const modulePath = callerPath || getModulePath();
2425
- const packageRoot = findPackageRoot(modulePath);
2426
- Logger_default.debug(`\u521B\u5EFA\u6A21\u5757\uFF1A\u8DEF\u5F84=${modulePath}\uFF0C\u5305\u6839\u76EE\u5F55=${packageRoot}`);
2427
- return {
2428
- install,
2429
- __modulePath: packageRoot
2430
- };
2431
- } catch (error) {
2432
- Logger_default.error("\u6A21\u5757\u521B\u5EFA\u5931\u8D25\uFF1A", error);
2433
- throw new Error(`\u6A21\u5757\u521B\u5EFA\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
2434
- }
2435
- }
2436
-
2437
- // utils/AppError.ts
2438
- function createAppError(code, msg) {
2439
- const err = new Error(msg);
2440
- err.statusCode = code;
2441
- err.isAppError = true;
2442
- return err;
2443
- }
2444
- var Errors = {
2445
- badRequest: (msg) => createAppError(400, msg),
2446
- unauthorized: (msg) => createAppError(401, msg),
2447
- forbidden: (msg) => createAppError(403, msg),
2448
- notFound: (msg) => createAppError(404, msg)
2449
- };
2450
- export {
2451
- DocManager,
2452
- Errors,
2453
- addCron,
2454
- createApp,
2455
- createAppError,
2456
- createModule,
2457
- dayjs,
2458
- docManager,
2459
- initDatabase,
2460
- initDocManager,
2461
- logger,
2462
- precache,
2463
- prepare,
2464
- stopAllCron,
2465
- stopCron,
2466
- validate
2467
- };
2468
- //# sourceMappingURL=index.mjs.map