@lark-apaas/devtool-kits 1.2.17-alpha.38 → 1.2.17-alpha.39
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/bin/generate-api-routes.cjs +7 -2
- package/dist/bin/generate-api-routes.cjs.map +1 -1
- package/dist/bin/generate-api-routes.js +7 -2
- package/dist/bin/generate-api-routes.js.map +1 -1
- package/dist/bin/generate-page-routes.cjs +7 -2
- package/dist/bin/generate-page-routes.cjs.map +1 -1
- package/dist/bin/generate-page-routes.js +7 -2
- package/dist/bin/generate-page-routes.js.map +1 -1
- package/dist/index.cjs +15 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -25
- package/dist/index.d.ts +1 -25
- package/dist/index.js +15 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -178,7 +178,12 @@ try {
|
|
|
178
178
|
import_fs.default.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
179
179
|
console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
180
180
|
} catch (error) {
|
|
181
|
-
console.
|
|
182
|
-
|
|
181
|
+
console.warn("[api-routes] Failed to generate api-routes.json, writing empty fallback:", error);
|
|
182
|
+
if (!import_fs.default.existsSync(outDir)) {
|
|
183
|
+
import_fs.default.mkdirSync(outDir, {
|
|
184
|
+
recursive: true
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
import_fs.default.writeFileSync(import_path.default.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
183
188
|
}
|
|
184
189
|
//# sourceMappingURL=generate-api-routes.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bin/generate-api-routes.ts","../../src/middlewares/openapi/services.ts","../../src/middlewares/openapi/utils.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parseApiRoutes } from '../middlewares/openapi/services';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst serverDir = path.resolve(process.cwd(), getArg('--server-dir', './server'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'api-routes.json');\n\ntry {\n const routes = parseApiRoutes(serverDir);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.error('[api-routes] Failed to generate api-routes.json:', error);\n process.exit(1);\n}\n","import { promises as fs } from 'node:fs';\nimport * as fsSync from 'node:fs';\nimport path from 'node:path';\nimport ts from 'typescript';\nimport type { SourceInfo, EnhanceOptions, EnhanceResult } from './types';\nimport { findControllerFiles, buildSourceMap, enhanceOpenApiPaths } from './utils';\n\n/**\n * Enhances OpenAPI JSON with source file location metadata\n * Can be called programmatically or run as a script\n */\nexport async function enhanceOpenApiWithSourceInfo(options: EnhanceOptions = {}): Promise<EnhanceResult> {\n const startTime = Date.now();\n\n const openapiPath = options.openapiPath || path.resolve(__dirname, '../client/src/api/gen/openapi.json');\n const serverDir = options.serverDir || path.resolve(__dirname, '../server');\n const writeFile = options.writeFile !== false;\n\n let openapi: any;\n if (options.openapiData) {\n // Use provided data (for in-memory enhancement)\n openapi = JSON.parse(JSON.stringify(options.openapiData)); // Deep clone\n } else {\n // Read from file\n const openapiContent = await fs.readFile(openapiPath, 'utf-8');\n openapi = JSON.parse(openapiContent);\n }\n\n const controllerFiles = await findControllerFiles(serverDir);\n const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);\n const enhanced = enhanceOpenApiPaths(openapi, sourceMap);\n\n if (writeFile) {\n await fs.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + '\\n', 'utf-8');\n }\n\n const duration = Date.now() - startTime;\n\n return {\n openapi,\n stats: {\n duration,\n controllersFound: controllerFiles.length,\n endpointsExtracted: sourceMap.size,\n endpointsEnhanced: enhanced,\n },\n };\n}\n\n/**\n * Process a single controller file\n */\nasync function processControllerFile(filePath: string): Promise<Map<string, SourceInfo>> {\n const relativePath = path.relative(process.cwd(), filePath);\n\n // Parse file\n const content = await fs.readFile(filePath, 'utf-8');\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n return extractControllerMetadata(sourceFile, relativePath);\n}\n\n/**\n * Extract controller metadata from TypeScript source file\n */\nfunction extractControllerMetadata(sourceFile: ts.SourceFile, filePath: string): Map<string, SourceInfo> {\n const metadata = new Map<string, SourceInfo>();\n let controllerPath = '';\n let className = '';\n\n // Helper function to get decorators from both old and new TypeScript APIs\n function getDecorators(node: ts.Node): readonly ts.Decorator[] {\n // TypeScript 5.x: decorators are in modifiers array\n if ('modifiers' in node && Array.isArray(node.modifiers)) {\n return (node.modifiers as ts.ModifierLike[]).filter(\n (mod): mod is ts.Decorator => mod.kind === ts.SyntaxKind.Decorator,\n );\n }\n // TypeScript 4.x: decorators are in decorators array\n if ('decorators' in node && Array.isArray(node.decorators)) {\n return node.decorators as readonly ts.Decorator[];\n }\n return [];\n }\n\n function visit(node: ts.Node): void {\n // Extract @Controller decorator and its path\n if (ts.isClassDeclaration(node)) {\n const decorators = getDecorators(node);\n\n // Extract class name\n if (node.name) {\n className = node.name.getText(sourceFile);\n }\n\n for (const decorator of decorators) {\n if (ts.isCallExpression(decorator.expression)) {\n const expression = decorator.expression;\n const decoratorName = expression.expression.getText(sourceFile);\n\n if (decoratorName === 'Controller') {\n if (expression.arguments.length > 0) {\n const arg = expression.arguments[0];\n if (ts.isStringLiteral(arg)) {\n controllerPath = arg.text;\n }\n }\n }\n }\n }\n }\n\n // Extract methods with HTTP decorators\n if (ts.isMethodDeclaration(node) && node.name) {\n const methodName = node.name.getText(sourceFile);\n let httpMethod = '';\n let routePath = '';\n const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n\n const decorators = getDecorators(node);\n\n for (const decorator of decorators) {\n if (ts.isCallExpression(decorator.expression)) {\n const decoratorName = decorator.expression.expression.getText(sourceFile);\n if (['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All'].includes(decoratorName)) {\n httpMethod = decoratorName.toLowerCase();\n if (decorator.expression.arguments.length > 0) {\n const arg = decorator.expression.arguments[0];\n if (ts.isStringLiteral(arg)) {\n routePath = arg.text;\n }\n }\n }\n }\n }\n\n if (httpMethod && methodName && className) {\n const operationId = `${className}_${methodName}`;\n metadata.set(operationId, {\n file: filePath,\n line: line + 1,\n method: httpMethod,\n controllerPath,\n routePath,\n });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return metadata;\n}\n\n// --- API Route Parsing (synchronous, for build-time injection) ---\n\nconst HTTP_DECORATOR_NAMES = ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All'];\n\nfunction findControllerFilesSync(dir: string): string[] {\n const files: string[] = [];\n function scan(currentDir: string): void {\n let entries: fsSync.Dirent[];\n try {\n entries = fsSync.readdirSync(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.controller.ts')) {\n files.push(fullPath);\n }\n }\n }\n scan(dir);\n return files;\n}\n\nfunction normalizeApiPath(controllerPath: string, routePath: string): string {\n const combined = routePath ? `${controllerPath}/${routePath}` : controllerPath;\n let normalized = combined.replace(/\\/+/g, '/');\n if (!normalized.startsWith('/')) normalized = `/${normalized}`;\n if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);\n return normalized;\n}\n\nfunction parseControllerRoutes(filePath: string): Array<{ method: string; path: string }> {\n let content: string;\n try {\n content = fsSync.readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n const routes: Array<{ method: string; path: string }> = [];\n\n function getDecoratorsCompat(node: ts.Node): readonly ts.Decorator[] {\n if ('modifiers' in node && Array.isArray(node.modifiers)) {\n return (node.modifiers as ts.ModifierLike[]).filter(\n (mod): mod is ts.Decorator => mod.kind === ts.SyntaxKind.Decorator,\n );\n }\n if ('decorators' in node && Array.isArray(node.decorators)) {\n return node.decorators as readonly ts.Decorator[];\n }\n return [];\n }\n\n function getStringArg(expr: ts.CallExpression): string {\n if (expr.arguments.length > 0 && ts.isStringLiteral(expr.arguments[0])) {\n return expr.arguments[0].text;\n }\n return '';\n }\n\n function visit(node: ts.Node): void {\n if (ts.isClassDeclaration(node)) {\n let controllerPath = '';\n for (const dec of getDecoratorsCompat(node)) {\n if (ts.isCallExpression(dec.expression)) {\n const name = dec.expression.expression.getText(sourceFile);\n if (name === 'Controller') {\n controllerPath = getStringArg(dec.expression);\n break;\n }\n }\n }\n\n for (const member of node.members) {\n if (!ts.isMethodDeclaration(member)) continue;\n for (const dec of getDecoratorsCompat(member)) {\n if (!ts.isCallExpression(dec.expression)) continue;\n const decoratorName = dec.expression.expression.getText(sourceFile);\n if (!HTTP_DECORATOR_NAMES.includes(decoratorName)) continue;\n\n const routePath = getStringArg(dec.expression);\n const fullPath = normalizeApiPath(controllerPath, routePath);\n const method = decoratorName === 'All' ? '*' : decoratorName.toUpperCase();\n routes.push({ method, path: fullPath });\n }\n }\n }\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return routes;\n}\n\n/**\n * Scan serverDir for NestJS controller files and extract all parameterized API routes.\n * Synchronous — safe to call at preset config time (before bundling).\n */\nexport function parseApiRoutes(serverDir: string): Array<{ method: string; path: string }> {\n const resolvedDir = path.resolve(serverDir);\n const controllerFiles = findControllerFilesSync(resolvedDir);\n const routes: Array<{ method: string; path: string }> = [];\n for (const filePath of controllerFiles) {\n routes.push(...parseControllerRoutes(filePath));\n }\n return routes;\n}\n","import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport type { SourceInfo } from './types';\n\n/**\n * Find all controller files in a directory\n */\nexport async function findControllerFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n\n async function scan(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n await scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.controller.ts')) {\n files.push(fullPath);\n }\n }\n }\n\n await scan(dir);\n return files;\n}\n\n/**\n * Build source map from controller files\n */\nexport async function buildSourceMap(\n controllerFiles: string[],\n processFile: (filePath: string) => Promise<Map<string, SourceInfo>>,\n): Promise<Map<string, SourceInfo>> {\n const sourceMap = new Map<string, SourceInfo>();\n\n // Process files in parallel with a concurrency limit\n const concurrency = 10;\n const results: Map<string, SourceInfo>[] = [];\n\n for (let i = 0; i < controllerFiles.length; i += concurrency) {\n const batch = controllerFiles.slice(i, i + concurrency);\n const batchResults = await Promise.all(batch.map((filePath) => processFile(filePath)));\n results.push(...batchResults);\n }\n\n // Merge results\n for (const metadata of results) {\n for (const [operationId, info] of metadata.entries()) {\n sourceMap.set(operationId, info);\n }\n }\n\n return sourceMap;\n}\n\n/**\n * Try to match operationId with different formats\n * Supports:\n * - Direct match: ClassName_methodName\n * - Camel case: classNameMethodName\n * - Method only: methodName\n */\nfunction findSourceInfo(operationId: string, sourceMap: Map<string, SourceInfo>): SourceInfo | undefined {\n // Try direct match first\n const directMatch = sourceMap.get(operationId);\n if (directMatch) {\n return directMatch;\n }\n\n // Try matching with different formats\n for (const [key, value] of sourceMap.entries()) {\n // key format: ClassName_methodName\n const [className, methodName] = key.split('_');\n if (!className || !methodName) continue;\n\n // Try camelCase format: classNameMethodName\n const camelCaseId = className.charAt(0).toLowerCase() + className.slice(1) + methodName.charAt(0).toUpperCase() + methodName.slice(1);\n if (operationId === camelCaseId) {\n return value;\n }\n\n // Try method name only\n if (operationId === methodName) {\n return value;\n }\n }\n\n return undefined;\n}\n\n/**\n * Enhance OpenAPI paths with source information\n */\nexport function enhanceOpenApiPaths(openapi: any, sourceMap: Map<string, SourceInfo>): number {\n let enhancedCount = 0;\n\n if (!openapi.paths) {\n return enhancedCount;\n }\n\n for (const pathItem of Object.values(openapi.paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue;\n\n for (const operation of Object.values(pathItem)) {\n if (operation && typeof operation === 'object' && 'operationId' in operation) {\n const sourceInfo = findSourceInfo(operation.operationId as string, sourceMap);\n if (sourceInfo) {\n operation['x-source'] = {\n file: sourceInfo.file,\n line: sourceInfo.line,\n };\n enhancedCount++;\n }\n }\n }\n }\n\n return enhancedCount;\n}\n\n/**\n * Transform OpenAPI paths by removing basePath prefix\n */\nexport function transformOpenapiPaths(openapi: any, basePath: string): any {\n if (basePath === '/' || !openapi.paths) {\n return openapi;\n }\n\n const newPaths: any = {};\n Object.keys(openapi.paths).forEach((key) => {\n const staticApiKey = key.startsWith(basePath) ? key.slice(basePath.length) : key;\n newPaths[staticApiKey] = openapi.paths[key];\n });\n\n return {\n ...openapi,\n paths: newPaths,\n basePath,\n };\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","HTTP_DECORATOR_NAMES","findControllerFilesSync","dir","files","scan","currentDir","entries","readdirSync","withFileTypes","entry","fullPath","path","join","name","isDirectory","isFile","endsWith","push","normalizeApiPath","controllerPath","routePath","combined","normalized","replace","startsWith","length","slice","parseControllerRoutes","filePath","content","readFileSync","sourceFile","ts","createSourceFile","ScriptTarget","Latest","routes","getDecoratorsCompat","node","Array","isArray","modifiers","filter","mod","kind","SyntaxKind","Decorator","decorators","getStringArg","expr","arguments","isStringLiteral","text","visit","isClassDeclaration","dec","isCallExpression","expression","getText","member","members","isMethodDeclaration","decoratorName","includes","method","toUpperCase","forEachChild","parseApiRoutes","serverDir","resolvedDir","resolve","controllerFiles","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","serverDir","path","resolve","cwd","outDir","filename","routes","parseApiRoutes","fs","existsSync","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","length","error","exit"]}
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-api-routes.ts","../../src/middlewares/openapi/services.ts","../../src/middlewares/openapi/utils.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parseApiRoutes } from '../middlewares/openapi/services';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst serverDir = path.resolve(process.cwd(), getArg('--server-dir', './server'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'api-routes.json');\n\ntry {\n const routes = parseApiRoutes(serverDir);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[api-routes] Failed to generate api-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n","import { promises as fs } from 'node:fs';\nimport * as fsSync from 'node:fs';\nimport path from 'node:path';\nimport ts from 'typescript';\nimport type { SourceInfo, EnhanceOptions, EnhanceResult } from './types';\nimport { findControllerFiles, buildSourceMap, enhanceOpenApiPaths } from './utils';\n\n/**\n * Enhances OpenAPI JSON with source file location metadata\n * Can be called programmatically or run as a script\n */\nexport async function enhanceOpenApiWithSourceInfo(options: EnhanceOptions = {}): Promise<EnhanceResult> {\n const startTime = Date.now();\n\n const openapiPath = options.openapiPath || path.resolve(__dirname, '../client/src/api/gen/openapi.json');\n const serverDir = options.serverDir || path.resolve(__dirname, '../server');\n const writeFile = options.writeFile !== false;\n\n let openapi: any;\n if (options.openapiData) {\n // Use provided data (for in-memory enhancement)\n openapi = JSON.parse(JSON.stringify(options.openapiData)); // Deep clone\n } else {\n // Read from file\n const openapiContent = await fs.readFile(openapiPath, 'utf-8');\n openapi = JSON.parse(openapiContent);\n }\n\n const controllerFiles = await findControllerFiles(serverDir);\n const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);\n const enhanced = enhanceOpenApiPaths(openapi, sourceMap);\n\n if (writeFile) {\n await fs.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + '\\n', 'utf-8');\n }\n\n const duration = Date.now() - startTime;\n\n return {\n openapi,\n stats: {\n duration,\n controllersFound: controllerFiles.length,\n endpointsExtracted: sourceMap.size,\n endpointsEnhanced: enhanced,\n },\n };\n}\n\n/**\n * Process a single controller file\n */\nasync function processControllerFile(filePath: string): Promise<Map<string, SourceInfo>> {\n const relativePath = path.relative(process.cwd(), filePath);\n\n // Parse file\n const content = await fs.readFile(filePath, 'utf-8');\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n\n return extractControllerMetadata(sourceFile, relativePath);\n}\n\n/**\n * Extract controller metadata from TypeScript source file\n */\nfunction extractControllerMetadata(sourceFile: ts.SourceFile, filePath: string): Map<string, SourceInfo> {\n const metadata = new Map<string, SourceInfo>();\n let controllerPath = '';\n let className = '';\n\n // Helper function to get decorators from both old and new TypeScript APIs\n function getDecorators(node: ts.Node): readonly ts.Decorator[] {\n // TypeScript 5.x: decorators are in modifiers array\n if ('modifiers' in node && Array.isArray(node.modifiers)) {\n return (node.modifiers as ts.ModifierLike[]).filter(\n (mod): mod is ts.Decorator => mod.kind === ts.SyntaxKind.Decorator,\n );\n }\n // TypeScript 4.x: decorators are in decorators array\n if ('decorators' in node && Array.isArray(node.decorators)) {\n return node.decorators as readonly ts.Decorator[];\n }\n return [];\n }\n\n function visit(node: ts.Node): void {\n // Extract @Controller decorator and its path\n if (ts.isClassDeclaration(node)) {\n const decorators = getDecorators(node);\n\n // Extract class name\n if (node.name) {\n className = node.name.getText(sourceFile);\n }\n\n for (const decorator of decorators) {\n if (ts.isCallExpression(decorator.expression)) {\n const expression = decorator.expression;\n const decoratorName = expression.expression.getText(sourceFile);\n\n if (decoratorName === 'Controller') {\n if (expression.arguments.length > 0) {\n const arg = expression.arguments[0];\n if (ts.isStringLiteral(arg)) {\n controllerPath = arg.text;\n }\n }\n }\n }\n }\n }\n\n // Extract methods with HTTP decorators\n if (ts.isMethodDeclaration(node) && node.name) {\n const methodName = node.name.getText(sourceFile);\n let httpMethod = '';\n let routePath = '';\n const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));\n\n const decorators = getDecorators(node);\n\n for (const decorator of decorators) {\n if (ts.isCallExpression(decorator.expression)) {\n const decoratorName = decorator.expression.expression.getText(sourceFile);\n if (['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All'].includes(decoratorName)) {\n httpMethod = decoratorName.toLowerCase();\n if (decorator.expression.arguments.length > 0) {\n const arg = decorator.expression.arguments[0];\n if (ts.isStringLiteral(arg)) {\n routePath = arg.text;\n }\n }\n }\n }\n }\n\n if (httpMethod && methodName && className) {\n const operationId = `${className}_${methodName}`;\n metadata.set(operationId, {\n file: filePath,\n line: line + 1,\n method: httpMethod,\n controllerPath,\n routePath,\n });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return metadata;\n}\n\n// --- API Route Parsing (synchronous, for build-time injection) ---\n\nconst HTTP_DECORATOR_NAMES = ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head', 'All'];\n\nfunction findControllerFilesSync(dir: string): string[] {\n const files: string[] = [];\n function scan(currentDir: string): void {\n let entries: fsSync.Dirent[];\n try {\n entries = fsSync.readdirSync(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.controller.ts')) {\n files.push(fullPath);\n }\n }\n }\n scan(dir);\n return files;\n}\n\nfunction normalizeApiPath(controllerPath: string, routePath: string): string {\n const combined = routePath ? `${controllerPath}/${routePath}` : controllerPath;\n let normalized = combined.replace(/\\/+/g, '/');\n if (!normalized.startsWith('/')) normalized = `/${normalized}`;\n if (normalized.length > 1 && normalized.endsWith('/')) normalized = normalized.slice(0, -1);\n return normalized;\n}\n\nfunction parseControllerRoutes(filePath: string): Array<{ method: string; path: string }> {\n let content: string;\n try {\n content = fsSync.readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n\n const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);\n const routes: Array<{ method: string; path: string }> = [];\n\n function getDecoratorsCompat(node: ts.Node): readonly ts.Decorator[] {\n if ('modifiers' in node && Array.isArray(node.modifiers)) {\n return (node.modifiers as ts.ModifierLike[]).filter(\n (mod): mod is ts.Decorator => mod.kind === ts.SyntaxKind.Decorator,\n );\n }\n if ('decorators' in node && Array.isArray(node.decorators)) {\n return node.decorators as readonly ts.Decorator[];\n }\n return [];\n }\n\n function getStringArg(expr: ts.CallExpression): string {\n if (expr.arguments.length > 0 && ts.isStringLiteral(expr.arguments[0])) {\n return expr.arguments[0].text;\n }\n return '';\n }\n\n function visit(node: ts.Node): void {\n if (ts.isClassDeclaration(node)) {\n let controllerPath = '';\n for (const dec of getDecoratorsCompat(node)) {\n if (ts.isCallExpression(dec.expression)) {\n const name = dec.expression.expression.getText(sourceFile);\n if (name === 'Controller') {\n controllerPath = getStringArg(dec.expression);\n break;\n }\n }\n }\n\n for (const member of node.members) {\n if (!ts.isMethodDeclaration(member)) continue;\n for (const dec of getDecoratorsCompat(member)) {\n if (!ts.isCallExpression(dec.expression)) continue;\n const decoratorName = dec.expression.expression.getText(sourceFile);\n if (!HTTP_DECORATOR_NAMES.includes(decoratorName)) continue;\n\n const routePath = getStringArg(dec.expression);\n const fullPath = normalizeApiPath(controllerPath, routePath);\n const method = decoratorName === 'All' ? '*' : decoratorName.toUpperCase();\n routes.push({ method, path: fullPath });\n }\n }\n }\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return routes;\n}\n\n/**\n * Scan serverDir for NestJS controller files and extract all parameterized API routes.\n * Synchronous — safe to call at preset config time (before bundling).\n */\nexport function parseApiRoutes(serverDir: string): Array<{ method: string; path: string }> {\n const resolvedDir = path.resolve(serverDir);\n const controllerFiles = findControllerFilesSync(resolvedDir);\n const routes: Array<{ method: string; path: string }> = [];\n for (const filePath of controllerFiles) {\n routes.push(...parseControllerRoutes(filePath));\n }\n return routes;\n}\n","import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport type { SourceInfo } from './types';\n\n/**\n * Find all controller files in a directory\n */\nexport async function findControllerFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n\n async function scan(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n await scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith('.controller.ts')) {\n files.push(fullPath);\n }\n }\n }\n\n await scan(dir);\n return files;\n}\n\n/**\n * Build source map from controller files\n */\nexport async function buildSourceMap(\n controllerFiles: string[],\n processFile: (filePath: string) => Promise<Map<string, SourceInfo>>,\n): Promise<Map<string, SourceInfo>> {\n const sourceMap = new Map<string, SourceInfo>();\n\n // Process files in parallel with a concurrency limit\n const concurrency = 10;\n const results: Map<string, SourceInfo>[] = [];\n\n for (let i = 0; i < controllerFiles.length; i += concurrency) {\n const batch = controllerFiles.slice(i, i + concurrency);\n const batchResults = await Promise.all(batch.map((filePath) => processFile(filePath)));\n results.push(...batchResults);\n }\n\n // Merge results\n for (const metadata of results) {\n for (const [operationId, info] of metadata.entries()) {\n sourceMap.set(operationId, info);\n }\n }\n\n return sourceMap;\n}\n\n/**\n * Try to match operationId with different formats\n * Supports:\n * - Direct match: ClassName_methodName\n * - Camel case: classNameMethodName\n * - Method only: methodName\n */\nfunction findSourceInfo(operationId: string, sourceMap: Map<string, SourceInfo>): SourceInfo | undefined {\n // Try direct match first\n const directMatch = sourceMap.get(operationId);\n if (directMatch) {\n return directMatch;\n }\n\n // Try matching with different formats\n for (const [key, value] of sourceMap.entries()) {\n // key format: ClassName_methodName\n const [className, methodName] = key.split('_');\n if (!className || !methodName) continue;\n\n // Try camelCase format: classNameMethodName\n const camelCaseId = className.charAt(0).toLowerCase() + className.slice(1) + methodName.charAt(0).toUpperCase() + methodName.slice(1);\n if (operationId === camelCaseId) {\n return value;\n }\n\n // Try method name only\n if (operationId === methodName) {\n return value;\n }\n }\n\n return undefined;\n}\n\n/**\n * Enhance OpenAPI paths with source information\n */\nexport function enhanceOpenApiPaths(openapi: any, sourceMap: Map<string, SourceInfo>): number {\n let enhancedCount = 0;\n\n if (!openapi.paths) {\n return enhancedCount;\n }\n\n for (const pathItem of Object.values(openapi.paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue;\n\n for (const operation of Object.values(pathItem)) {\n if (operation && typeof operation === 'object' && 'operationId' in operation) {\n const sourceInfo = findSourceInfo(operation.operationId as string, sourceMap);\n if (sourceInfo) {\n operation['x-source'] = {\n file: sourceInfo.file,\n line: sourceInfo.line,\n };\n enhancedCount++;\n }\n }\n }\n }\n\n return enhancedCount;\n}\n\n/**\n * Transform OpenAPI paths by removing basePath prefix\n */\nexport function transformOpenapiPaths(openapi: any, basePath: string): any {\n if (basePath === '/' || !openapi.paths) {\n return openapi;\n }\n\n const newPaths: any = {};\n Object.keys(openapi.paths).forEach((key) => {\n const staticApiKey = key.startsWith(basePath) ? key.slice(basePath.length) : key;\n newPaths[staticApiKey] = openapi.paths[key];\n });\n\n return {\n ...openapi,\n paths: newPaths,\n basePath,\n };\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","HTTP_DECORATOR_NAMES","findControllerFilesSync","dir","files","scan","currentDir","entries","readdirSync","withFileTypes","entry","fullPath","path","join","name","isDirectory","isFile","endsWith","push","normalizeApiPath","controllerPath","routePath","combined","normalized","replace","startsWith","length","slice","parseControllerRoutes","filePath","content","readFileSync","sourceFile","ts","createSourceFile","ScriptTarget","Latest","routes","getDecoratorsCompat","node","Array","isArray","modifiers","filter","mod","kind","SyntaxKind","Decorator","decorators","getStringArg","expr","arguments","isStringLiteral","text","visit","isClassDeclaration","dec","isCallExpression","expression","getText","member","members","isMethodDeclaration","decoratorName","includes","method","toUpperCase","forEachChild","parseApiRoutes","serverDir","resolvedDir","resolve","controllerFiles","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","serverDir","path","resolve","cwd","outDir","filename","routes","parseApiRoutes","fs","existsSync","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","length","error","warn"]}
|
|
@@ -32,7 +32,12 @@ try {
|
|
|
32
32
|
fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
33
33
|
console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
34
34
|
} catch (error) {
|
|
35
|
-
console.
|
|
36
|
-
|
|
35
|
+
console.warn("[api-routes] Failed to generate api-routes.json, writing empty fallback:", error);
|
|
36
|
+
if (!fs.existsSync(outDir)) {
|
|
37
|
+
fs.mkdirSync(outDir, {
|
|
38
|
+
recursive: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
37
42
|
}
|
|
38
43
|
//# sourceMappingURL=generate-api-routes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bin/generate-api-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parseApiRoutes } from '../middlewares/openapi/services';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst serverDir = path.resolve(process.cwd(), getArg('--server-dir', './server'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'api-routes.json');\n\ntry {\n const routes = parseApiRoutes(serverDir);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-api-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parseApiRoutes } from '../middlewares/openapi/services';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst serverDir = path.resolve(process.cwd(), getArg('--server-dir', './server'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'api-routes.json');\n\ntry {\n const routes = parseApiRoutes(serverDir);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[api-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[api-routes] Failed to generate api-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n"],"mappings":";;;;;;;;;AACA,OAAOA,QAAQ;AACf,OAAOC,UAAU;AAGjB,IAAMC,OAAOC,QAAQC,KAAKC,MAAM,CAAA;AAEhC,SAASC,OAAOC,MAAcC,cAAoB;AAChD,QAAMC,QAAQP,KAAKQ,QAAQH,IAAAA;AAC3B,MAAIE,UAAU,MAAMP,KAAKO,QAAQ,CAAA,GAAI;AACnC,WAAOP,KAAKO,QAAQ,CAAA;EACtB;AACA,SAAOD;AACT;AANSF;AAQT,IAAMK,YAAYC,KAAKC,QAAQV,QAAQW,IAAG,GAAIR,OAAO,gBAAgB,UAAA,CAAA;AACrE,IAAMS,SAASH,KAAKC,QAAQV,QAAQW,IAAG,GAAIR,OAAO,aAAa,QAAA,CAAA;AAC/D,IAAMU,WAAWV,OAAO,cAAc,iBAAA;AAEtC,IAAI;AACF,QAAMW,SAASC,eAAeP,SAAAA;AAC9B,MAAI,CAACQ,GAAGC,WAAWL,MAAAA,GAAS;AAC1BI,OAAGE,UAAUN,QAAQ;MAAEO,WAAW;IAAK,CAAA;EACzC;AACA,QAAMC,UAAUX,KAAKC,QAAQE,QAAQC,QAAAA;AACrCG,KAAGK,cAAcD,SAASE,KAAKC,UAAUT,QAAQ,MAAM,CAAA,CAAA;AACvDU,UAAQC,IAAI,0BAA0BL,OAAAA,KAAYN,OAAOY,MAAM,UAAU;AAC3E,SAASC,OAAO;AACdH,UAAQI,KAAK,4EAA4ED,KAAAA;AACzF,MAAI,CAACX,GAAGC,WAAWL,MAAAA,GAAS;AAC1BI,OAAGE,UAAUN,QAAQ;MAAEO,WAAW;IAAK,CAAA;EACzC;AACAH,KAAGK,cAAcZ,KAAKC,QAAQE,QAAQC,QAAAA,GAAWS,KAAKC,UAAU,CAAA,GAAI,MAAM,CAAA,CAAA;AAC5E;","names":["fs","path","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","serverDir","path","resolve","cwd","outDir","filename","routes","parseApiRoutes","fs","existsSync","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","length","error","warn"]}
|
|
@@ -180,7 +180,12 @@ try {
|
|
|
180
180
|
import_fs.default.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
181
181
|
console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
182
182
|
} catch (error) {
|
|
183
|
-
console.
|
|
184
|
-
|
|
183
|
+
console.warn("[page-routes] Failed to generate page-routes.json, writing empty fallback:", error);
|
|
184
|
+
if (!import_fs.default.existsSync(outDir)) {
|
|
185
|
+
import_fs.default.mkdirSync(outDir, {
|
|
186
|
+
recursive: true
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
import_fs.default.writeFileSync(import_path.default.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
185
190
|
}
|
|
186
191
|
//# sourceMappingURL=generate-page-routes.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bin/generate-page-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parse } from '@babel/parser';\nimport _traverse from '@babel/traverse';\n// Handle CJS default export interop in ESM context\nconst traverse = typeof _traverse === 'function' ? _traverse : (_traverse as any).default;\nimport * as t from '@babel/types';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst appPath = path.resolve(process.cwd(), getArg('--app-path', './client/src/app.tsx'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'page-routes.json');\n\ninterface RouteInfo {\n path?: string;\n index?: boolean;\n [key: string]: unknown;\n}\n\nfunction isRouteComponent(openingElement: t.JSXOpeningElement): boolean {\n return (\n t.isJSXIdentifier(openingElement.name) &&\n openingElement.name.name === 'Route'\n );\n}\n\nfunction evaluateTemplateLiteral(templateLiteral: t.TemplateLiteral): string {\n const quasis = templateLiteral.quasis;\n const expressions = templateLiteral.expressions;\n\n if (quasis.length === 1 && expressions.length === 0) {\n return quasis[0].value.raw;\n }\n\n return quasis.map((q) => q.value.raw).join('');\n}\n\nfunction extractRouteInfo(openingElement: t.JSXOpeningElement): RouteInfo {\n const routeInfo: RouteInfo = {};\n\n openingElement.attributes.forEach((attr) => {\n if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {\n const name = attr.name.name;\n let value: unknown;\n\n if (attr.value) {\n if (t.isStringLiteral(attr.value)) {\n value = attr.value.value;\n } else if (t.isJSXExpressionContainer(attr.value)) {\n const expression = attr.value.expression;\n if (t.isStringLiteral(expression)) {\n value = expression.value;\n } else if (t.isTemplateLiteral(expression)) {\n value = evaluateTemplateLiteral(expression);\n } else {\n value = true;\n }\n }\n } else {\n value = true;\n }\n\n routeInfo[name] = value;\n }\n });\n\n return routeInfo;\n}\n\nfunction buildFullPath(routeStack: RouteInfo[], currentRoute: RouteInfo): string | null {\n let fullPath = '';\n\n for (let i = 0; i < routeStack.length; i++) {\n if (routeStack[i].path) {\n let parentPath = routeStack[i].path!;\n if (!parentPath.startsWith('/')) parentPath = `/${parentPath}`;\n if (parentPath.endsWith('/') && parentPath !== '/') {\n parentPath = parentPath.slice(0, -1);\n }\n\n fullPath += parentPath === '/' ? '' : parentPath;\n }\n }\n\n if (currentRoute.index) {\n return fullPath || '/';\n } else if (currentRoute.path) {\n const routePath = currentRoute.path;\n\n if (routePath === '*') {\n return null;\n }\n\n if (!routePath.startsWith('/')) {\n fullPath = `${fullPath}/${routePath}`;\n } else {\n fullPath = routePath;\n }\n\n if (fullPath === '') fullPath = '/';\n if (!fullPath.startsWith('/')) fullPath = `/${fullPath}`;\n\n return fullPath;\n }\n\n return null;\n}\n\nfunction parsePageRoutes(appFilePath: string): Array<{ path: string }> {\n if (!fs.existsSync(appFilePath)) {\n throw new Error(`App file does not exist: ${appFilePath}`);\n }\n\n const sourceCode = fs.readFileSync(appFilePath, 'utf-8');\n\n const ast = parse(sourceCode, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'objectRestSpread',\n 'functionBind',\n 'exportDefaultFrom',\n 'exportNamespaceFrom',\n 'dynamicImport',\n 'nullishCoalescingOperator',\n 'optionalChaining',\n ],\n });\n\n const routeSet = new Set<string>();\n const routeStack: RouteInfo[] = [];\n\n traverse(ast, {\n JSXElement: {\n enter(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n routeStack.push(extractRouteInfo(openingElement));\n }\n },\n exit(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n const currentRoute = routeStack.pop();\n if (currentRoute && currentRoute.path === '*') return;\n if (currentRoute && (currentRoute.path || currentRoute.index)) {\n const fullPath = buildFullPath(routeStack, currentRoute);\n if (fullPath) routeSet.add(fullPath);\n }\n }\n },\n },\n });\n\n // page-routes 不带 basePath 前缀,供日志服务消费\n const routes = Array.from(routeSet).map((routePath) => ({ path: routePath }));\n return routes.length > 0 ? routes : [{ path: '/' }];\n}\n\ntry {\n const routes = parsePageRoutes(appPath);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.error('[page-routes] Failed to generate page-routes.json:', error);\n process.exit(1);\n}\n"],"mappings":"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","names":["traverse","_traverse","default","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","appPath","path","resolve","cwd","outDir","filename","isRouteComponent","openingElement","isJSXIdentifier","evaluateTemplateLiteral","templateLiteral","quasis","expressions","length","value","raw","map","q","join","extractRouteInfo","routeInfo","attributes","forEach","attr","isJSXAttribute","isStringLiteral","isJSXExpressionContainer","expression","isTemplateLiteral","buildFullPath","routeStack","currentRoute","fullPath","i","parentPath","startsWith","endsWith","routePath","parsePageRoutes","appFilePath","fs","existsSync","Error","sourceCode","readFileSync","ast","parse","sourceType","plugins","routeSet","Set","JSXElement","enter","nodePath","node","push","exit","pop","add","routes","Array","from","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","error"]}
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-page-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parse } from '@babel/parser';\nimport _traverse from '@babel/traverse';\n// Handle CJS default export interop in ESM context\nconst traverse = typeof _traverse === 'function' ? _traverse : (_traverse as any).default;\nimport * as t from '@babel/types';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst appPath = path.resolve(process.cwd(), getArg('--app-path', './client/src/app.tsx'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'page-routes.json');\n\ninterface RouteInfo {\n path?: string;\n index?: boolean;\n [key: string]: unknown;\n}\n\nfunction isRouteComponent(openingElement: t.JSXOpeningElement): boolean {\n return (\n t.isJSXIdentifier(openingElement.name) &&\n openingElement.name.name === 'Route'\n );\n}\n\nfunction evaluateTemplateLiteral(templateLiteral: t.TemplateLiteral): string {\n const quasis = templateLiteral.quasis;\n const expressions = templateLiteral.expressions;\n\n if (quasis.length === 1 && expressions.length === 0) {\n return quasis[0].value.raw;\n }\n\n return quasis.map((q) => q.value.raw).join('');\n}\n\nfunction extractRouteInfo(openingElement: t.JSXOpeningElement): RouteInfo {\n const routeInfo: RouteInfo = {};\n\n openingElement.attributes.forEach((attr) => {\n if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {\n const name = attr.name.name;\n let value: unknown;\n\n if (attr.value) {\n if (t.isStringLiteral(attr.value)) {\n value = attr.value.value;\n } else if (t.isJSXExpressionContainer(attr.value)) {\n const expression = attr.value.expression;\n if (t.isStringLiteral(expression)) {\n value = expression.value;\n } else if (t.isTemplateLiteral(expression)) {\n value = evaluateTemplateLiteral(expression);\n } else {\n value = true;\n }\n }\n } else {\n value = true;\n }\n\n routeInfo[name] = value;\n }\n });\n\n return routeInfo;\n}\n\nfunction buildFullPath(routeStack: RouteInfo[], currentRoute: RouteInfo): string | null {\n let fullPath = '';\n\n for (let i = 0; i < routeStack.length; i++) {\n if (routeStack[i].path) {\n let parentPath = routeStack[i].path!;\n if (!parentPath.startsWith('/')) parentPath = `/${parentPath}`;\n if (parentPath.endsWith('/') && parentPath !== '/') {\n parentPath = parentPath.slice(0, -1);\n }\n\n fullPath += parentPath === '/' ? '' : parentPath;\n }\n }\n\n if (currentRoute.index) {\n return fullPath || '/';\n } else if (currentRoute.path) {\n const routePath = currentRoute.path;\n\n if (routePath === '*') {\n return null;\n }\n\n if (!routePath.startsWith('/')) {\n fullPath = `${fullPath}/${routePath}`;\n } else {\n fullPath = routePath;\n }\n\n if (fullPath === '') fullPath = '/';\n if (!fullPath.startsWith('/')) fullPath = `/${fullPath}`;\n\n return fullPath;\n }\n\n return null;\n}\n\nfunction parsePageRoutes(appFilePath: string): Array<{ path: string }> {\n if (!fs.existsSync(appFilePath)) {\n throw new Error(`App file does not exist: ${appFilePath}`);\n }\n\n const sourceCode = fs.readFileSync(appFilePath, 'utf-8');\n\n const ast = parse(sourceCode, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'objectRestSpread',\n 'functionBind',\n 'exportDefaultFrom',\n 'exportNamespaceFrom',\n 'dynamicImport',\n 'nullishCoalescingOperator',\n 'optionalChaining',\n ],\n });\n\n const routeSet = new Set<string>();\n const routeStack: RouteInfo[] = [];\n\n traverse(ast, {\n JSXElement: {\n enter(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n routeStack.push(extractRouteInfo(openingElement));\n }\n },\n exit(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n const currentRoute = routeStack.pop();\n if (currentRoute && currentRoute.path === '*') return;\n if (currentRoute && (currentRoute.path || currentRoute.index)) {\n const fullPath = buildFullPath(routeStack, currentRoute);\n if (fullPath) routeSet.add(fullPath);\n }\n }\n },\n },\n });\n\n // page-routes 不带 basePath 前缀,供日志服务消费\n const routes = Array.from(routeSet).map((routePath) => ({ path: routePath }));\n return routes.length > 0 ? routes : [{ path: '/' }];\n}\n\ntry {\n const routes = parsePageRoutes(appPath);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[page-routes] Failed to generate page-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n"],"mappings":"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","names":["traverse","_traverse","default","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","appPath","path","resolve","cwd","outDir","filename","isRouteComponent","openingElement","isJSXIdentifier","evaluateTemplateLiteral","templateLiteral","quasis","expressions","length","value","raw","map","q","join","extractRouteInfo","routeInfo","attributes","forEach","attr","isJSXAttribute","isStringLiteral","isJSXExpressionContainer","expression","isTemplateLiteral","buildFullPath","routeStack","currentRoute","fullPath","i","parentPath","startsWith","endsWith","routePath","parsePageRoutes","appFilePath","fs","existsSync","Error","sourceCode","readFileSync","ast","parse","sourceType","plugins","routeSet","Set","JSXElement","enter","nodePath","node","push","exit","pop","add","routes","Array","from","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","error","warn"]}
|
|
@@ -159,7 +159,12 @@ try {
|
|
|
159
159
|
fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
160
160
|
console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);
|
|
161
161
|
} catch (error) {
|
|
162
|
-
console.
|
|
163
|
-
|
|
162
|
+
console.warn("[page-routes] Failed to generate page-routes.json, writing empty fallback:", error);
|
|
163
|
+
if (!fs.existsSync(outDir)) {
|
|
164
|
+
fs.mkdirSync(outDir, {
|
|
165
|
+
recursive: true
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));
|
|
164
169
|
}
|
|
165
170
|
//# sourceMappingURL=generate-page-routes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bin/generate-page-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parse } from '@babel/parser';\nimport _traverse from '@babel/traverse';\n// Handle CJS default export interop in ESM context\nconst traverse = typeof _traverse === 'function' ? _traverse : (_traverse as any).default;\nimport * as t from '@babel/types';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst appPath = path.resolve(process.cwd(), getArg('--app-path', './client/src/app.tsx'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'page-routes.json');\n\ninterface RouteInfo {\n path?: string;\n index?: boolean;\n [key: string]: unknown;\n}\n\nfunction isRouteComponent(openingElement: t.JSXOpeningElement): boolean {\n return (\n t.isJSXIdentifier(openingElement.name) &&\n openingElement.name.name === 'Route'\n );\n}\n\nfunction evaluateTemplateLiteral(templateLiteral: t.TemplateLiteral): string {\n const quasis = templateLiteral.quasis;\n const expressions = templateLiteral.expressions;\n\n if (quasis.length === 1 && expressions.length === 0) {\n return quasis[0].value.raw;\n }\n\n return quasis.map((q) => q.value.raw).join('');\n}\n\nfunction extractRouteInfo(openingElement: t.JSXOpeningElement): RouteInfo {\n const routeInfo: RouteInfo = {};\n\n openingElement.attributes.forEach((attr) => {\n if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {\n const name = attr.name.name;\n let value: unknown;\n\n if (attr.value) {\n if (t.isStringLiteral(attr.value)) {\n value = attr.value.value;\n } else if (t.isJSXExpressionContainer(attr.value)) {\n const expression = attr.value.expression;\n if (t.isStringLiteral(expression)) {\n value = expression.value;\n } else if (t.isTemplateLiteral(expression)) {\n value = evaluateTemplateLiteral(expression);\n } else {\n value = true;\n }\n }\n } else {\n value = true;\n }\n\n routeInfo[name] = value;\n }\n });\n\n return routeInfo;\n}\n\nfunction buildFullPath(routeStack: RouteInfo[], currentRoute: RouteInfo): string | null {\n let fullPath = '';\n\n for (let i = 0; i < routeStack.length; i++) {\n if (routeStack[i].path) {\n let parentPath = routeStack[i].path!;\n if (!parentPath.startsWith('/')) parentPath = `/${parentPath}`;\n if (parentPath.endsWith('/') && parentPath !== '/') {\n parentPath = parentPath.slice(0, -1);\n }\n\n fullPath += parentPath === '/' ? '' : parentPath;\n }\n }\n\n if (currentRoute.index) {\n return fullPath || '/';\n } else if (currentRoute.path) {\n const routePath = currentRoute.path;\n\n if (routePath === '*') {\n return null;\n }\n\n if (!routePath.startsWith('/')) {\n fullPath = `${fullPath}/${routePath}`;\n } else {\n fullPath = routePath;\n }\n\n if (fullPath === '') fullPath = '/';\n if (!fullPath.startsWith('/')) fullPath = `/${fullPath}`;\n\n return fullPath;\n }\n\n return null;\n}\n\nfunction parsePageRoutes(appFilePath: string): Array<{ path: string }> {\n if (!fs.existsSync(appFilePath)) {\n throw new Error(`App file does not exist: ${appFilePath}`);\n }\n\n const sourceCode = fs.readFileSync(appFilePath, 'utf-8');\n\n const ast = parse(sourceCode, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'objectRestSpread',\n 'functionBind',\n 'exportDefaultFrom',\n 'exportNamespaceFrom',\n 'dynamicImport',\n 'nullishCoalescingOperator',\n 'optionalChaining',\n ],\n });\n\n const routeSet = new Set<string>();\n const routeStack: RouteInfo[] = [];\n\n traverse(ast, {\n JSXElement: {\n enter(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n routeStack.push(extractRouteInfo(openingElement));\n }\n },\n exit(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n const currentRoute = routeStack.pop();\n if (currentRoute && currentRoute.path === '*') return;\n if (currentRoute && (currentRoute.path || currentRoute.index)) {\n const fullPath = buildFullPath(routeStack, currentRoute);\n if (fullPath) routeSet.add(fullPath);\n }\n }\n },\n },\n });\n\n // page-routes 不带 basePath 前缀,供日志服务消费\n const routes = Array.from(routeSet).map((routePath) => ({ path: routePath }));\n return routes.length > 0 ? routes : [{ path: '/' }];\n}\n\ntry {\n const routes = parsePageRoutes(appPath);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.error('[page-routes] Failed to generate page-routes.json:', error);\n process.exit(1);\n}\n"],"mappings":"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","names":["fs","path","parse","_traverse","t","traverse","_traverse","default","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","appPath","path","resolve","cwd","outDir","filename","isRouteComponent","openingElement","isJSXIdentifier","evaluateTemplateLiteral","templateLiteral","quasis","expressions","length","value","raw","map","q","join","extractRouteInfo","routeInfo","attributes","forEach","attr","isJSXAttribute","isStringLiteral","isJSXExpressionContainer","expression","isTemplateLiteral","buildFullPath","routeStack","currentRoute","fullPath","i","parentPath","startsWith","endsWith","routePath","parsePageRoutes","appFilePath","fs","existsSync","Error","sourceCode","readFileSync","ast","parse","sourceType","plugins","routeSet","Set","JSXElement","enter","nodePath","node","push","exit","pop","add","routes","Array","from","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","error"]}
|
|
1
|
+
{"version":3,"sources":["../../src/bin/generate-page-routes.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs';\nimport path from 'path';\nimport { parse } from '@babel/parser';\nimport _traverse from '@babel/traverse';\n// Handle CJS default export interop in ESM context\nconst traverse = typeof _traverse === 'function' ? _traverse : (_traverse as any).default;\nimport * as t from '@babel/types';\n\nconst args = process.argv.slice(2);\n\nfunction getArg(name: string, defaultValue: string): string {\n const index = args.indexOf(name);\n if (index !== -1 && args[index + 1]) {\n return args[index + 1];\n }\n return defaultValue;\n}\n\nconst appPath = path.resolve(process.cwd(), getArg('--app-path', './client/src/app.tsx'));\nconst outDir = path.resolve(process.cwd(), getArg('--out-dir', './dist'));\nconst filename = getArg('--filename', 'page-routes.json');\n\ninterface RouteInfo {\n path?: string;\n index?: boolean;\n [key: string]: unknown;\n}\n\nfunction isRouteComponent(openingElement: t.JSXOpeningElement): boolean {\n return (\n t.isJSXIdentifier(openingElement.name) &&\n openingElement.name.name === 'Route'\n );\n}\n\nfunction evaluateTemplateLiteral(templateLiteral: t.TemplateLiteral): string {\n const quasis = templateLiteral.quasis;\n const expressions = templateLiteral.expressions;\n\n if (quasis.length === 1 && expressions.length === 0) {\n return quasis[0].value.raw;\n }\n\n return quasis.map((q) => q.value.raw).join('');\n}\n\nfunction extractRouteInfo(openingElement: t.JSXOpeningElement): RouteInfo {\n const routeInfo: RouteInfo = {};\n\n openingElement.attributes.forEach((attr) => {\n if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {\n const name = attr.name.name;\n let value: unknown;\n\n if (attr.value) {\n if (t.isStringLiteral(attr.value)) {\n value = attr.value.value;\n } else if (t.isJSXExpressionContainer(attr.value)) {\n const expression = attr.value.expression;\n if (t.isStringLiteral(expression)) {\n value = expression.value;\n } else if (t.isTemplateLiteral(expression)) {\n value = evaluateTemplateLiteral(expression);\n } else {\n value = true;\n }\n }\n } else {\n value = true;\n }\n\n routeInfo[name] = value;\n }\n });\n\n return routeInfo;\n}\n\nfunction buildFullPath(routeStack: RouteInfo[], currentRoute: RouteInfo): string | null {\n let fullPath = '';\n\n for (let i = 0; i < routeStack.length; i++) {\n if (routeStack[i].path) {\n let parentPath = routeStack[i].path!;\n if (!parentPath.startsWith('/')) parentPath = `/${parentPath}`;\n if (parentPath.endsWith('/') && parentPath !== '/') {\n parentPath = parentPath.slice(0, -1);\n }\n\n fullPath += parentPath === '/' ? '' : parentPath;\n }\n }\n\n if (currentRoute.index) {\n return fullPath || '/';\n } else if (currentRoute.path) {\n const routePath = currentRoute.path;\n\n if (routePath === '*') {\n return null;\n }\n\n if (!routePath.startsWith('/')) {\n fullPath = `${fullPath}/${routePath}`;\n } else {\n fullPath = routePath;\n }\n\n if (fullPath === '') fullPath = '/';\n if (!fullPath.startsWith('/')) fullPath = `/${fullPath}`;\n\n return fullPath;\n }\n\n return null;\n}\n\nfunction parsePageRoutes(appFilePath: string): Array<{ path: string }> {\n if (!fs.existsSync(appFilePath)) {\n throw new Error(`App file does not exist: ${appFilePath}`);\n }\n\n const sourceCode = fs.readFileSync(appFilePath, 'utf-8');\n\n const ast = parse(sourceCode, {\n sourceType: 'module',\n plugins: [\n 'jsx',\n 'typescript',\n 'decorators-legacy',\n 'classProperties',\n 'objectRestSpread',\n 'functionBind',\n 'exportDefaultFrom',\n 'exportNamespaceFrom',\n 'dynamicImport',\n 'nullishCoalescingOperator',\n 'optionalChaining',\n ],\n });\n\n const routeSet = new Set<string>();\n const routeStack: RouteInfo[] = [];\n\n traverse(ast, {\n JSXElement: {\n enter(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n routeStack.push(extractRouteInfo(openingElement));\n }\n },\n exit(nodePath: any) {\n const { openingElement } = nodePath.node;\n if (isRouteComponent(openingElement)) {\n const currentRoute = routeStack.pop();\n if (currentRoute && currentRoute.path === '*') return;\n if (currentRoute && (currentRoute.path || currentRoute.index)) {\n const fullPath = buildFullPath(routeStack, currentRoute);\n if (fullPath) routeSet.add(fullPath);\n }\n }\n },\n },\n });\n\n // page-routes 不带 basePath 前缀,供日志服务消费\n const routes = Array.from(routeSet).map((routePath) => ({ path: routePath }));\n return routes.length > 0 ? routes : [{ path: '/' }];\n}\n\ntry {\n const routes = parsePageRoutes(appPath);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n const outPath = path.resolve(outDir, filename);\n fs.writeFileSync(outPath, JSON.stringify(routes, null, 2));\n console.log(`[page-routes] Generated ${outPath} (${routes.length} routes)`);\n} catch (error) {\n console.warn('[page-routes] Failed to generate page-routes.json, writing empty fallback:', error);\n if (!fs.existsSync(outDir)) {\n fs.mkdirSync(outDir, { recursive: true });\n }\n fs.writeFileSync(path.resolve(outDir, filename), JSON.stringify([], null, 2));\n}\n"],"mappings":"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","names":["fs","path","parse","_traverse","t","traverse","_traverse","default","args","process","argv","slice","getArg","name","defaultValue","index","indexOf","appPath","path","resolve","cwd","outDir","filename","isRouteComponent","openingElement","isJSXIdentifier","evaluateTemplateLiteral","templateLiteral","quasis","expressions","length","value","raw","map","q","join","extractRouteInfo","routeInfo","attributes","forEach","attr","isJSXAttribute","isStringLiteral","isJSXExpressionContainer","expression","isTemplateLiteral","buildFullPath","routeStack","currentRoute","fullPath","i","parentPath","startsWith","endsWith","routePath","parsePageRoutes","appFilePath","fs","existsSync","Error","sourceCode","readFileSync","ast","parse","sourceType","plugins","routeSet","Set","JSXElement","enter","nodePath","node","push","exit","pop","add","routes","Array","from","mkdirSync","recursive","outPath","writeFileSync","JSON","stringify","console","log","error","warn"]}
|
package/dist/index.cjs
CHANGED
|
@@ -31,7 +31,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
|
-
apiRoutesPlugin: () => apiRoutesPlugin,
|
|
35
34
|
createCollectLogsMiddleware: () => createCollectLogsMiddleware,
|
|
36
35
|
createDevLogsMiddleware: () => createDevLogsMiddleware,
|
|
37
36
|
createOpenapiMiddleware: () => createOpenapiMiddleware,
|
|
@@ -1836,8 +1835,8 @@ function hasSpecialPatterns(pattern) {
|
|
|
1836
1835
|
return /[{*]/.test(pattern);
|
|
1837
1836
|
}
|
|
1838
1837
|
__name(hasSpecialPatterns, "hasSpecialPatterns");
|
|
1839
|
-
function normalizePathForMatching(
|
|
1840
|
-
return
|
|
1838
|
+
function normalizePathForMatching(path7) {
|
|
1839
|
+
return path7.replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
1841
1840
|
}
|
|
1842
1841
|
__name(normalizePathForMatching, "normalizePathForMatching");
|
|
1843
1842
|
|
|
@@ -2386,7 +2385,7 @@ __name(readLogsBySource, "readLogsBySource");
|
|
|
2386
2385
|
// src/middlewares/dev-logs/services/trigger.service.ts
|
|
2387
2386
|
var import_node_fs9 = require("fs");
|
|
2388
2387
|
var import_node_readline3 = require("readline");
|
|
2389
|
-
async function readTriggerList(filePath, trigger,
|
|
2388
|
+
async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
2390
2389
|
if (!await fileExists(filePath)) {
|
|
2391
2390
|
return void 0;
|
|
2392
2391
|
}
|
|
@@ -2412,7 +2411,7 @@ async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
|
|
|
2412
2411
|
if (alreadyAdded) {
|
|
2413
2412
|
return false;
|
|
2414
2413
|
}
|
|
2415
|
-
const isAutomationTrigger = builder.path?.endsWith(
|
|
2414
|
+
const isAutomationTrigger = builder.path?.endsWith(path7);
|
|
2416
2415
|
if (!isAutomationTrigger) {
|
|
2417
2416
|
return false;
|
|
2418
2417
|
}
|
|
@@ -2495,7 +2494,7 @@ async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
|
|
|
2495
2494
|
};
|
|
2496
2495
|
}
|
|
2497
2496
|
__name(readTriggerList, "readTriggerList");
|
|
2498
|
-
async function readTriggerDetail(filePath,
|
|
2497
|
+
async function readTriggerDetail(filePath, path7, instanceID) {
|
|
2499
2498
|
const exists = await fileExists(filePath);
|
|
2500
2499
|
if (!exists) {
|
|
2501
2500
|
return void 0;
|
|
@@ -2511,7 +2510,7 @@ async function readTriggerDetail(filePath, path8, instanceID) {
|
|
|
2511
2510
|
for await (const line of rl) {
|
|
2512
2511
|
const entry = parseLogLine(line);
|
|
2513
2512
|
if (!entry) continue;
|
|
2514
|
-
const isAutomationTrigger = entry.path?.endsWith(
|
|
2513
|
+
const isAutomationTrigger = entry.path?.endsWith(path7);
|
|
2515
2514
|
const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
|
|
2516
2515
|
if (!isAutomationTrigger || !hasInstanceID) continue;
|
|
2517
2516
|
matches.push(entry);
|
|
@@ -2765,16 +2764,16 @@ function createGetTriggerListHandler(logDir) {
|
|
|
2765
2764
|
});
|
|
2766
2765
|
}
|
|
2767
2766
|
const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
|
|
2768
|
-
const
|
|
2767
|
+
const path7 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2769
2768
|
const limit = parseLimit(req.query.limit, 10, 200);
|
|
2770
2769
|
try {
|
|
2771
|
-
const result = await readTriggerList(traceLogPath, trigger,
|
|
2770
|
+
const result = await readTriggerList(traceLogPath, trigger, path7, limit, triggerID);
|
|
2772
2771
|
if (!result) {
|
|
2773
2772
|
return handleNotFound(res, traceLogPath);
|
|
2774
2773
|
}
|
|
2775
2774
|
res.json({
|
|
2776
2775
|
file: getRelativePath(traceLogPath),
|
|
2777
|
-
path:
|
|
2776
|
+
path: path7,
|
|
2778
2777
|
...result
|
|
2779
2778
|
});
|
|
2780
2779
|
} catch (error) {
|
|
@@ -2792,9 +2791,9 @@ function createGetTriggerDetailHandler(logDir) {
|
|
|
2792
2791
|
message: "instanceID is required"
|
|
2793
2792
|
});
|
|
2794
2793
|
}
|
|
2795
|
-
const
|
|
2794
|
+
const path7 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2796
2795
|
try {
|
|
2797
|
-
const result = await readTriggerDetail(traceLogPath,
|
|
2796
|
+
const result = await readTriggerDetail(traceLogPath, path7, instanceID);
|
|
2798
2797
|
if (!result) {
|
|
2799
2798
|
return handleNotFound(res, traceLogPath);
|
|
2800
2799
|
}
|
|
@@ -3420,8 +3419,8 @@ __name(createSSEHandler, "createSSEHandler");
|
|
|
3420
3419
|
|
|
3421
3420
|
// src/middlewares/dev-logs/api-list-handler.ts
|
|
3422
3421
|
var SERVER_PORT = process.env.SERVER_PORT || "3000";
|
|
3423
|
-
function extractModuleFromPath(
|
|
3424
|
-
const segments =
|
|
3422
|
+
function extractModuleFromPath(path7) {
|
|
3423
|
+
const segments = path7.split("/").filter(Boolean);
|
|
3425
3424
|
let startIndex = 0;
|
|
3426
3425
|
if (segments[0] === "api") {
|
|
3427
3426
|
startIndex = 1;
|
|
@@ -3436,8 +3435,8 @@ function extractModuleFromPath(path8) {
|
|
|
3436
3435
|
return moduleName;
|
|
3437
3436
|
}
|
|
3438
3437
|
__name(extractModuleFromPath, "extractModuleFromPath");
|
|
3439
|
-
function generateRouteId(method,
|
|
3440
|
-
const cleanPath =
|
|
3438
|
+
function generateRouteId(method, path7) {
|
|
3439
|
+
const cleanPath = path7.replace(/[/:]/g, "_").replace(/^_+|_+$/g, "");
|
|
3441
3440
|
return `${method.toLowerCase()}_${cleanPath}`;
|
|
3442
3441
|
}
|
|
3443
3442
|
__name(generateRouteId, "generateRouteId");
|
|
@@ -3911,38 +3910,8 @@ async function registerMiddlewares(server, middlewares, options) {
|
|
|
3911
3910
|
}
|
|
3912
3911
|
}
|
|
3913
3912
|
__name(registerMiddlewares, "registerMiddlewares");
|
|
3914
|
-
|
|
3915
|
-
// src/tsup-plugin-api-routes.ts
|
|
3916
|
-
var import_fs3 = __toESM(require("fs"), 1);
|
|
3917
|
-
var import_path3 = __toESM(require("path"), 1);
|
|
3918
|
-
function apiRoutesPlugin(options = {}) {
|
|
3919
|
-
const { serverDir = "./src", filename = "api-routes.json" } = options;
|
|
3920
|
-
return {
|
|
3921
|
-
name: "api-routes-generator",
|
|
3922
|
-
setup(build) {
|
|
3923
|
-
build.onEnd(() => {
|
|
3924
|
-
try {
|
|
3925
|
-
const resolvedServerDir = import_path3.default.resolve(process.cwd(), serverDir);
|
|
3926
|
-
const routes = parseApiRoutes(resolvedServerDir);
|
|
3927
|
-
const outdir = import_path3.default.resolve(process.cwd(), "dist");
|
|
3928
|
-
const outPath = import_path3.default.resolve(outdir, filename);
|
|
3929
|
-
if (!import_fs3.default.existsSync(outdir)) {
|
|
3930
|
-
import_fs3.default.mkdirSync(outdir, {
|
|
3931
|
-
recursive: true
|
|
3932
|
-
});
|
|
3933
|
-
}
|
|
3934
|
-
import_fs3.default.writeFileSync(outPath, JSON.stringify(routes, null, 2));
|
|
3935
|
-
} catch (error) {
|
|
3936
|
-
console.warn("[api-routes-plugin] Failed to generate api-routes.json:", error);
|
|
3937
|
-
}
|
|
3938
|
-
});
|
|
3939
|
-
}
|
|
3940
|
-
};
|
|
3941
|
-
}
|
|
3942
|
-
__name(apiRoutesPlugin, "apiRoutesPlugin");
|
|
3943
3913
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3944
3914
|
0 && (module.exports = {
|
|
3945
|
-
apiRoutesPlugin,
|
|
3946
3915
|
createCollectLogsMiddleware,
|
|
3947
3916
|
createDevLogsMiddleware,
|
|
3948
3917
|
createOpenapiMiddleware,
|