shokupan 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,5 +3,7 @@ import { Middleware, NextFn } from './types';
3
3
  /**
4
4
  * Composes a list of middleware into a single function.
5
5
  * This is the onion model (Koa-style).
6
+ *
7
+ * CRITICAL: This must build the chain ONCE, not rebuild it on every request.
6
8
  */
7
9
  export declare const compose: (middleware: Middleware[]) => (context: ShokupanContext<unknown>, next?: NextFn) => Promise<any>;
@@ -401,6 +401,9 @@ class OpenAPIAnalyzer {
401
401
  if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {
402
402
  analyzeReturnExpression(node.body);
403
403
  }
404
+ if (ts.isExpressionStatement(node)) {
405
+ analyzeReturnExpression(node.expression);
406
+ }
404
407
  ts.forEachChild(node, visit);
405
408
  };
406
409
  if (ts.isBlock(body)) {
@@ -766,4 +769,4 @@ async function analyzeDirectory(directory) {
766
769
  }
767
770
  exports.OpenAPIAnalyzer = OpenAPIAnalyzer;
768
771
  exports.analyzeDirectory = analyzeDirectory;
769
- //# sourceMappingURL=openapi-analyzer-CFqgSLNK.cjs.map
772
+ //# sourceMappingURL=openapi-analyzer-BN0wFCML.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-analyzer-BN0wFCML.cjs","sources":["../src/analysis/openapi-analyzer.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport ts from 'typescript';\n\n/**\n * File information collected during scan\n */\ninterface CollectedFile {\n path: string;\n type: 'ts' | 'js' | 'map';\n content?: string;\n}\n\n/**\n * Route information extracted from AST\n */\nexport interface RouteInfo {\n method: string;\n path: string;\n handlerName?: string;\n handlerSource?: string;\n requestTypes?: {\n body?: any;\n query?: Record<string, string>;\n params?: Record<string, string>;\n headers?: Record<string, string>;\n };\n responseType?: string;\n responseSchema?: any;\n summary?: string;\n description?: string;\n tags?: string[];\n operationId?: string;\n}\n\n/**\n * Dependency information\n */\ninterface DependencyInfo {\n packageName: string;\n version?: string;\n importPath: string;\n isExternal: boolean;\n}\n\n/**\n * Application/Router instance found in code\n */\nexport interface ApplicationInstance {\n name: string;\n filePath: string;\n className: 'Shokupan' | 'ShokupanRouter' | 'Controller';\n routes: RouteInfo[];\n mounted: MountInfo[];\n}\n\ninterface MountInfo {\n prefix: string;\n target: string; // Controller/Router name or file path\n dependency?: DependencyInfo;\n}\n\n/**\n * Main analyzer class\n */\nexport class OpenAPIAnalyzer {\n private files: CollectedFile[] = [];\n private applications: ApplicationInstance[] = [];\n private program?: ts.Program;\n\n private entrypoint?: string;\n\n constructor(private rootDir: string, entrypoint?: string) {\n if (entrypoint) {\n this.entrypoint = path.resolve(entrypoint);\n }\n }\n\n /**\n * Main analysis entry point\n */\n /**\n * Main analysis entry point\n */\n public async analyze(): Promise<{ applications: ApplicationInstance[]; }> {\n // console.log(`Analyzing directory: ${this.rootDir}`);\n\n // Step 1: Parse TypeScript files (which might involve scanning or using entrypoint)\n await this.parseTypeScriptFiles();\n\n // Step 2: Process source maps if needed\n await this.processSourceMaps();\n\n // Step 3: Find Shokupan applications\n await this.findApplications();\n\n // Step 4: Extract route information\n await this.extractRoutes();\n\n // Return the raw application data for further processing\n return { applications: this.applications };\n }\n\n /**\n * Recursively scan directory for TypeScript/JavaScript files\n */\n private async scanDirectory(dir: string): Promise<void> {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n // Skip node_modules for source files (we'll handle deps separately)\n if (entry.isDirectory()) {\n if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') {\n continue;\n }\n await this.scanDirectory(fullPath);\n } else {\n const ext = path.extname(entry.name);\n if (ext === '.ts') {\n this.files.push({ path: fullPath, type: 'ts' });\n } else if (ext === '.js') {\n this.files.push({ path: fullPath, type: 'js' });\n } else if (ext === '.map') {\n this.files.push({ path: fullPath, type: 'map' });\n }\n }\n }\n } catch (error: any) {\n // Silently skip directories that don't exist or can't be read\n if (error.code !== 'ENOENT' && error.code !== 'EACCES') {\n throw error;\n }\n }\n }\n\n /**\n * Process source maps to reconstruct TypeScript\n */\n private async processSourceMaps(): Promise<void> {\n // Find JS files that have corresponding .map files\n const jsFiles = this.files.filter(f => f.type === 'js');\n const mapFiles = this.files.filter(f => f.type === 'map');\n\n for (const jsFile of jsFiles) {\n const mapFile = mapFiles.find(m => m.path === jsFile.path + '.map');\n\n if (mapFile && !this.files.some(f => f.path === jsFile.path.replace(/\\.js$/, '.ts'))) {\n // We have .js + .map but no .ts file\n // For now, we'll just parse the JS file directly\n // Full source map reconstruction would require the 'source-map' library\n console.log(`Note: Found ${jsFile.path} with source map but no .ts file. Will parse JS directly.`);\n }\n }\n }\n\n /**\n * Parse TypeScript files and create AST\n */\n private async parseTypeScriptFiles(): Promise<void> {\n let fileNames: string[] = [];\n\n if (this.entrypoint) {\n // If entrypoint is provided, let TypeScript resolve dependencies\n fileNames = [this.entrypoint];\n // console.log(`[Analyzer] Using entrypoint: ${this.entrypoint}`);\n } else {\n // Otherwise, scan the directory manually\n await this.scanDirectory(this.rootDir);\n const tsFiles = this.files.filter(f => f.type === 'ts' || f.type === 'js');\n fileNames = tsFiles.map(f => f.path);\n // console.log(`[Analyzer] Scanning directory, found ${fileNames.length} files`);\n }\n\n // Create TypeScript program\n this.program = ts.createProgram(fileNames, {\n target: ts.ScriptTarget.ESNext,\n module: ts.ModuleKind.ESNext,\n allowJs: true,\n moduleResolution: ts.ModuleResolutionKind.Node10,\n rootDir: this.rootDir,\n skipLibCheck: true,\n skipDefaultLibCheck: true,\n });\n\n // If using entrypoint, update this.files with what TS found in the project\n if (this.entrypoint) {\n this.files = this.program.getSourceFiles()\n .filter(sf => !sf.fileName.includes('node_modules'))\n .map(sf => ({ path: sf.fileName, type: sf.fileName.endsWith('.js') ? 'js' : 'ts' }));\n }\n }\n\n /**\n * Find all Shokupan/ShokupanRouter instances\n */\n private async findApplications(): Promise<void> {\n if (!this.program) return;\n\n const typeChecker = this.program.getTypeChecker();\n\n for (const sourceFile of this.program.getSourceFiles()) {\n // Skip node_modules and declaration files/tests\n if (sourceFile.fileName.includes('node_modules')) continue;\n if (sourceFile.isDeclarationFile) continue;\n if (sourceFile.fileName.includes('.test.ts') || sourceFile.fileName.includes('.spec.ts')) continue;\n\n // console.log(`[Analyzer] Visiting file: ${sourceFile.fileName}`);\n ts.forEachChild(sourceFile, (node) => {\n this.visitNode(node, sourceFile, typeChecker);\n });\n }\n }\n\n /**\n * Visit AST node to find application instances\n */\n private visitNode(node: ts.Node, sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker): void {\n // Look for: class FooController ... @Controller(...)\n if (ts.isClassDeclaration(node)) {\n // Check for @Controller decorator\n let isController = false;\n let className = node.name?.getText(sourceFile);\n\n if ((node as any).decorators) {\n const controllerDecorator = (node as any).decorators.find((d: any) => {\n const expr = d.expression;\n if (ts.isCallExpression(expr)) {\n const identifier = expr.expression.getText(sourceFile);\n return identifier === 'Controller';\n }\n return false;\n });\n if (controllerDecorator) isController = true;\n }\n\n // Fallback: Check for method decorators (@Get, @Post, etc.)\n if (!isController) {\n const hasRouteDecorators = node.members.some(m => {\n // Trust 175 as MethodDeclaration if TS matches mostly, or just check members\n if (ts.isMethodDeclaration(m) || m.kind === 175 || m.kind === 170 || m.kind === 171) {\n // Note: 171 is standard MethodDeclaration in some versions, 175 in others?\n // Or maybe 171 is Decorator.\n const decs = (m as any).decorators || (m as any).modifiers?.filter((mod: any) => ts.isDecorator(mod));\n if (decs) {\n return decs.some((d: any) => {\n const expr = d.expression;\n if (ts.isCallExpression(expr)) {\n const identifier = expr.expression.getText(sourceFile);\n return ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head'].includes(identifier);\n }\n return false;\n });\n }\n }\n return false;\n });\n if (hasRouteDecorators) {\n isController = true;\n }\n }\n\n if (isController && className) {\n this.applications.push({\n name: className,\n filePath: sourceFile.fileName,\n className: 'Controller',\n routes: [],\n mounted: []\n });\n }\n }\n\n // Look for: new Shokupan() or new ShokupanRouter()\n if (ts.isVariableDeclaration(node) && node.initializer) {\n if (ts.isNewExpression(node.initializer)) {\n const expr = node.initializer;\n const className = expr.expression.getText(sourceFile);\n\n if (className === 'Shokupan' || className === 'ShokupanRouter') {\n const varName = node.name.getText(sourceFile);\n\n this.applications.push({\n name: varName,\n filePath: sourceFile.fileName,\n className: className as 'Shokupan' | 'ShokupanRouter',\n routes: [],\n mounted: []\n });\n }\n }\n }\n\n // Recursively visit children\n ts.forEachChild(node, (child) => this.visitNode(child, sourceFile, typeChecker));\n }\n\n /**\n * Extract route information from applications\n */\n private async extractRoutes(): Promise<void> {\n if (!this.program) return;\n\n for (const app of this.applications) {\n const sourceFile = this.program.getSourceFile(app.filePath);\n if (!sourceFile) continue;\n\n this.extractRoutesFromFile(app, sourceFile);\n }\n }\n\n /**\n * Extract routes from a Controller class\n */\n private extractRoutesFromController(app: ApplicationInstance, classNode: ts.ClassDeclaration, sourceFile: ts.SourceFile): void {\n const methods = classNode.members.filter(m => ts.isMethodDeclaration(m) || m.kind === 175);\n\n for (const method of methods) {\n const methodNode = method as any; // Cast to any to access decorators in newer/older TS mix\n if (!methodNode.decorators && !methodNode.modifiers) continue;\n\n const decorators = methodNode.decorators || methodNode.modifiers?.filter((m: any) => ts.isDecorator(m));\n if (!decorators) continue;\n\n // Find route decorators: @Get, @Post, etc.\n const routeDecorator = decorators.find((d: any) => {\n const expr = d.expression;\n if (ts.isCallExpression(expr)) {\n const identifier = expr.expression.getText(sourceFile);\n return ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head'].includes(identifier);\n }\n return false;\n });\n\n if (routeDecorator && ts.isCallExpression(routeDecorator.expression)) {\n const decoratorName = routeDecorator.expression.expression.getText(sourceFile);\n const httpMethod = decoratorName.toUpperCase();\n let routePath = '/';\n\n // Get path\n const pathArg = routeDecorator.expression.arguments[0];\n if (pathArg && ts.isStringLiteral(pathArg)) {\n routePath = pathArg.text;\n }\n\n // Normalize path params: /users/:id -> /users/{id}\n routePath = routePath.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');\n\n // Handler Name (Class.method)\n const handlerName = `${app.name}.${methodNode.name.getText(sourceFile)}`;\n\n // Analyze the method body\n const analysis = this.analyzeHandler(methodNode, sourceFile);\n\n app.routes.push({\n method: httpMethod,\n path: routePath,\n handlerName: handlerName,\n handlerSource: methodNode.getText(sourceFile),\n requestTypes: analysis.requestTypes,\n responseType: analysis.responseType,\n responseSchema: analysis.responseSchema\n });\n }\n }\n };\n\n /**\n * Extract routes from a specific file\n */\n private extractRoutesFromFile(app: ApplicationInstance, sourceFile: ts.SourceFile): void {\n if (app.className === 'Controller') {\n const classNode = sourceFile.statements.find(s => ts.isClassDeclaration(s) && s.name?.getText(sourceFile) === app.name) as ts.ClassDeclaration;\n if (classNode) {\n this.extractRoutesFromController(app, classNode, sourceFile);\n }\n } else {\n // Existing logic for app/router instances\n const visit = (node: ts.Node) => {\n // ... (rest of the existing logic)\n // Look for method calls: app.get(...), app.post(...), app.mount(...)\n if (ts.isCallExpression(node)) {\n const expr = node.expression;\n\n if (ts.isPropertyAccessExpression(expr)) {\n const objName = expr.expression.getText(sourceFile);\n const methodName = expr.name.getText(sourceFile);\n\n // Check if this is our application instance\n if (objName === app.name) {\n if (['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(methodName)) {\n // Extract route info\n const route = this.extractRouteFromCall(node, sourceFile, methodName.toUpperCase());\n if (route) {\n app.routes.push(route);\n }\n } else if (methodName === 'mount') {\n // Extract mount info\n const mount = this.extractMountFromCall(node, sourceFile);\n if (mount) {\n app.mounted.push(mount);\n }\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n }\n }\n\n /**\n * Extract route information from a route call (e.g., app.get('/path', handler))\n */\n private extractRouteFromCall(node: ts.CallExpression, sourceFile: ts.SourceFile, method: string): RouteInfo | null {\n const args = node.arguments;\n\n if (args.length < 2) return null;\n\n const pathArg = args[0];\n let routePath = '/';\n\n if (ts.isStringLiteral(pathArg)) {\n routePath = pathArg.text;\n }\n\n // Normalize path params: /users/:id -> /users/{id}\n // This ensures matching with runtime-generated keys\n const normalizedPath = routePath.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');\n\n let metadata: any = {};\n\n // Check for metadata argument (3 args: path, metadata, handler)\n if (args.length >= 3 && ts.isObjectLiteralExpression(args[1])) {\n const metaObj = args[1];\n // Extract summary, description, tags, etc.\n const rawMeta = this.convertExpressionToSchema(metaObj, sourceFile, new Map());\n\n // convertExpressionToSchema returns a schema-like object { type: 'object', properties: {...} }\n // But we want the actual values if they are literals.\n // convertExpressionToSchema is designed for SCHEMAS, not values.\n // We need a simpler value extractor or just parse the props directly for this specific case.\n\n for (const prop of metaObj.properties) {\n if (ts.isPropertyAssignment(prop) && prop.name) {\n const name = prop.name.getText(sourceFile);\n const val = prop.initializer;\n\n if (ts.isStringLiteral(val)) {\n metadata[name] = val.text;\n } else if (ts.isArrayLiteralExpression(val) && name === 'tags') {\n metadata.tags = val.elements\n .filter(e => ts.isStringLiteral(e))\n .map(e => (e as ts.StringLiteral).text);\n } else if (name === 'operationId' && ts.isStringLiteral(val)) {\n metadata.operationId = val.text;\n }\n }\n }\n }\n\n // Extract handler information\n const handlerArg = args[args.length - 1];\n const handlerInfo = this.analyzeHandler(handlerArg, sourceFile);\n\n return {\n method,\n path: normalizedPath,\n handlerName: handlerArg.getText(sourceFile).substring(0, 50), // Truncate for display\n handlerSource: handlerArg.getText(sourceFile),\n requestTypes: handlerInfo.requestTypes,\n responseType: handlerInfo.responseType,\n responseSchema: handlerInfo.responseSchema,\n ...metadata\n };\n }\n\n /**\n * Analyze a route handler to extract type information\n */\n private analyzeHandler(handler: ts.Node, sourceFile: ts.SourceFile): {\n requestTypes?: RouteInfo['requestTypes'];\n responseType?: string;\n responseSchema?: any;\n } {\n const requestTypes: RouteInfo['requestTypes'] = {};\n let responseType: string | undefined;\n let responseSchema: any | undefined;\n\n // Simple scope to track variable types (name -> schema)\n const scope = new Map<string, any>();\n\n // Pre-populate scope with function parameters\n if (ts.isFunctionLike(handler)) {\n handler.parameters.forEach(param => {\n if (ts.isIdentifier(param.name) && param.type) {\n const paramName = param.name.getText(sourceFile);\n // Resolving TypeReference for parameters (e.g. User) would require partial type checker or expanded scope logic\n // For now, we rely on basic types\n const paramType = this.convertTypeNodeToSchema(param.type, sourceFile);\n if (paramType) {\n scope.set(paramName, paramType);\n }\n }\n });\n }\n\n // Helper to analyze an expression that is being returned (either explicitly or implicitly)\n const analyzeReturnExpression = (expr: ts.Expression) => {\n let node = expr;\n // Unwrap await\n if (ts.isAwaitExpression(node)) {\n node = node.expression;\n }\n\n // Case 1: return ctx.json(...)\n if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {\n const callObj = node.expression.expression.getText(sourceFile);\n const callProp = node.expression.name.getText(sourceFile);\n\n if ((callObj === 'ctx' || callObj.endsWith('.ctx')) && callProp === 'json') {\n if (node.arguments.length > 0) {\n responseSchema = this.convertExpressionToSchema(node.arguments[0], sourceFile, scope);\n responseType = 'object';\n }\n return;\n }\n }\n\n // Case 2: Direct object return\n // Only use this if we haven't found a better schema yet, or if it looks specific\n if (!responseSchema || responseSchema.type === 'object') {\n const schema = this.convertExpressionToSchema(node, sourceFile, scope);\n if (schema && (schema.type !== 'object' || Object.keys(schema.properties || {}).length > 0)) {\n responseSchema = schema;\n responseType = schema.type;\n }\n }\n\n // Fallback to text matching if schema inference failed and we still don't have a type\n if (!responseSchema && !responseType) {\n const returnText = node.getText(sourceFile);\n if (returnText.startsWith('{')) {\n responseType = 'object';\n } else if (returnText.startsWith('[')) {\n responseType = 'array';\n } else if (returnText.startsWith('\"') || returnText.startsWith(\"'\")) {\n responseType = 'string';\n }\n }\n };\n\n // Handle arrow functions\n let body: ts.Block | undefined;\n // Also check for Kind 175 (Method/Accessor in some TS versions)\n if (ts.isArrowFunction(handler) || ts.isFunctionExpression(handler) || ts.isMethodDeclaration(handler) || handler.kind === 175) {\n // TS method has .body which is FunctionBody (Block) or undefined\n body = (handler as any).body;\n\n // Visit the handler body to find ctx usage\n const visit = (node: ts.Node) => {\n // Check for type assertions on ctx.body()\n if (ts.isAsExpression(node)) {\n if (this.isCtxBodyCall(node.expression, sourceFile)) {\n const schema = this.convertTypeNodeToSchema(node.type, sourceFile);\n if (schema) {\n requestTypes.body = schema;\n\n // Track variables assigned to this body\n if (ts.isVariableDeclaration(node.parent)) {\n const varName = node.parent.name.getText(sourceFile);\n scope.set(varName, schema);\n }\n }\n }\n }\n\n // Look for ctx usage\n if (ts.isPropertyAccessExpression(node)) {\n const objText = node.expression.getText(sourceFile);\n const propText = node.name.getText(sourceFile);\n\n if (objText === 'ctx' || objText.endsWith('.ctx')) {\n if (propText === 'body') {\n if (!requestTypes.body) {\n requestTypes.body = { type: 'object' };\n }\n } else if (propText === 'query') {\n if (!requestTypes.query) requestTypes.query = {};\n } else if (propText === 'params') {\n if (!requestTypes.params) requestTypes.params = {};\n } else if (propText === 'headers') {\n if (!requestTypes.headers) requestTypes.headers = {};\n }\n }\n }\n\n // Explicit Return\n if (ts.isReturnStatement(node) && node.expression) {\n analyzeReturnExpression(node.expression);\n }\n\n // Implicit Return (Concise Arrow Function)\n // e.g. (ctx) => ctx.json(...) or .then(res => ctx.json(res))\n if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {\n analyzeReturnExpression(node.body as ts.Expression);\n }\n\n // Implicit Return call (e.g. ctx.json(...) as a statement without return)\n if (ts.isExpressionStatement(node)) {\n analyzeReturnExpression(node.expression);\n }\n\n ts.forEachChild(node, visit);\n };\n\n if (ts.isBlock(body)) {\n ts.forEachChild(body, visit);\n } else {\n // Main handler is an implicit return: app.get('/', (ctx) => ctx.json(...))\n analyzeReturnExpression(body);\n // Also verify children (e.g. if body is a CallExpression, verify args??)\n // analyzeReturnExpression analyzes the expression itself.\n // But we ALSO want to visit children to find other usages (request types)\n // or nested arrow functions in a call chain (e.g. chaining .then())\n ts.forEachChild(body, visit);\n }\n }\n\n return { requestTypes, responseType, responseSchema };\n }\n\n /**\n * Convert an Expression node to an OpenAPI schema (best effort)\n */\n private convertExpressionToSchema(node: ts.Expression, sourceFile: ts.SourceFile, scope: Map<string, any>): any {\n // Object Literal: { a: 1, b: \"text\" }\n if (ts.isObjectLiteralExpression(node)) {\n const schema: any = {\n type: 'object',\n properties: {},\n required: []\n };\n\n for (const prop of node.properties) {\n if (ts.isPropertyAssignment(prop)) {\n const name = prop.name.getText(sourceFile);\n const valueSchema = this.convertExpressionToSchema(prop.initializer, sourceFile, scope);\n\n schema.properties[name] = valueSchema;\n schema.required.push(name); // Properties in literal return are required\n }\n else if (ts.isShorthandPropertyAssignment(prop)) {\n const name = prop.name.getText(sourceFile);\n // Check scope for variable\n const scopedSchema = scope.get(name);\n schema.properties[name] = scopedSchema || { type: 'object' };\n schema.required.push(name);\n }\n }\n if (schema.required.length === 0) {\n delete schema.required;\n }\n return schema;\n }\n\n // Array Literal: [1, 2]\n if (ts.isArrayLiteralExpression(node)) {\n const schema: any = { type: 'array' };\n if (node.elements.length > 0) {\n // Infer item type from first element\n schema.items = this.convertExpressionToSchema(node.elements[0], sourceFile, scope);\n } else {\n schema.items = {};\n }\n return schema;\n }\n\n // Conditional (Ternary) Expression: cond ? trueVal : falseVal\n if (ts.isConditionalExpression(node)) {\n const trueSchema = this.convertExpressionToSchema(node.whenTrue, sourceFile, scope);\n // const falseSchema = this.convertExpressionToSchema(node.whenFalse, sourceFile, scope);\n\n // Simplified: return true branch schema. Ideally we'd do oneOf\n return trueSchema;\n }\n\n // Template Expression: `Hello ${name}`\n if (ts.isTemplateExpression(node)) {\n return { type: 'string' };\n }\n\n // Identifier (Variable reference)\n if (ts.isIdentifier(node)) {\n const name = node.getText(sourceFile);\n const scopedSchema = scope.get(name);\n if (scopedSchema) return scopedSchema;\n return { type: 'object' }; // Unknown reference\n }\n\n // Literals\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return { type: 'string' };\n if (ts.isNumericLiteral(node)) return { type: 'number' };\n if (node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword) return { type: 'boolean' };\n\n // Unknown\n return { type: 'object' };\n }\n\n /**\n * Check if an expression is a call to ctx.body()\n */\n private isCtxBodyCall(node: ts.Expression, sourceFile: ts.SourceFile): boolean {\n // Handle await ctx.body()\n if (ts.isAwaitExpression(node)) {\n return this.isCtxBodyCall(node.expression, sourceFile);\n }\n\n // Handle ctx.body()\n if (ts.isCallExpression(node)) {\n // Check expression: ctx.body\n if (ts.isPropertyAccessExpression(node.expression)) {\n const objText = node.expression.expression.getText(sourceFile);\n const propText = node.expression.name.getText(sourceFile);\n return (objText === 'ctx' || objText.endsWith('.ctx')) && propText === 'body';\n }\n }\n\n return false;\n }\n\n /**\n * Convert a TypeScript TypeNode to an OpenAPI schema\n */\n private convertTypeNodeToSchema(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): any {\n switch (typeNode.kind) {\n case ts.SyntaxKind.StringKeyword:\n return { type: 'string' };\n case ts.SyntaxKind.NumberKeyword:\n return { type: 'number' };\n case ts.SyntaxKind.BooleanKeyword:\n return { type: 'boolean' };\n case ts.SyntaxKind.AnyKeyword:\n case ts.SyntaxKind.UnknownKeyword:\n return {}; // Any/Unknown -> empty schema (accepts anything)\n\n case ts.SyntaxKind.TypeLiteral: {\n const literal = typeNode as ts.TypeLiteralNode;\n const schema: any = {\n type: 'object',\n properties: {},\n required: []\n };\n\n for (const member of literal.members) {\n if (ts.isPropertySignature(member) && member.type) {\n const name = member.name.getText(sourceFile);\n const propSchema = this.convertTypeNodeToSchema(member.type, sourceFile);\n\n schema.properties[name] = propSchema;\n\n // Property is required unless it has a question token\n if (!member.questionToken) {\n schema.required.push(name);\n }\n }\n }\n\n if (schema.required.length === 0) {\n delete schema.required;\n }\n\n return schema;\n }\n\n case ts.SyntaxKind.ArrayType: {\n const arrayType = typeNode as ts.ArrayTypeNode;\n return {\n type: 'array',\n items: this.convertTypeNodeToSchema(arrayType.elementType, sourceFile)\n };\n }\n\n // Handle Type References (e.g. Array<string>)\n case ts.SyntaxKind.TypeReference: {\n const typeRef = typeNode as ts.TypeReferenceNode;\n const typeName = typeRef.typeName.getText(sourceFile);\n\n if (typeName === 'Array' && typeRef.typeArguments && typeRef.typeArguments.length > 0) {\n return {\n type: 'array',\n items: this.convertTypeNodeToSchema(typeRef.typeArguments[0], sourceFile)\n };\n }\n\n // For other references, we default to string or object as fallback\n // A fuller implementation would resolve the reference\n return { type: 'object', description: `Ref: ${typeName}` };\n }\n\n default:\n return { type: 'object' };\n }\n }\n\n /**\n * Extract mount information from mount call\n */\n private extractMountFromCall(node: ts.CallExpression, sourceFile: ts.SourceFile): MountInfo | null {\n const args = node.arguments;\n\n if (args.length < 2) return null;\n\n const pathArg = args[0];\n const targetArg = args[1];\n\n let prefix = '/';\n if (ts.isStringLiteral(pathArg)) {\n prefix = pathArg.text;\n }\n\n const target = targetArg.getText(sourceFile);\n\n // Check if target is from node_modules\n const dependency = this.checkIfExternalDependency(target, sourceFile);\n\n return {\n prefix,\n target,\n dependency\n };\n }\n\n /**\n * Check if a reference is to an external dependency\n */\n private checkIfExternalDependency(identifier: string, sourceFile: ts.SourceFile): DependencyInfo | undefined {\n // This is a simplified check - in a full implementation, \n // we'd track imports and resolve them\n\n // For now, check if there's an import statement for this identifier\n const imports: ts.ImportDeclaration[] = [];\n\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isImportDeclaration(node)) {\n imports.push(node);\n }\n });\n\n for (const imp of imports) {\n const moduleSpecifier = imp.moduleSpecifier;\n if (ts.isStringLiteral(moduleSpecifier)) {\n const modulePath = moduleSpecifier.text;\n\n // Check if it's a node_modules import (no relative path)\n if (!modulePath.startsWith('.') && !modulePath.startsWith('/')) {\n const namedBindings = imp.importClause?.namedBindings;\n\n if (namedBindings && ts.isNamedImports(namedBindings)) {\n for (const element of namedBindings.elements) {\n if (element.name.text === identifier) {\n // Try to read package version\n const version = this.getPackageVersion(modulePath);\n\n return {\n packageName: modulePath,\n version,\n importPath: modulePath,\n isExternal: true\n };\n }\n }\n }\n }\n }\n }\n\n return undefined;\n }\n\n /**\n * Get package version from package.json\n */\n private getPackageVersion(packageName: string): string | undefined {\n try {\n const packageJsonPath = path.join(this.rootDir, 'node_modules', packageName, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n return packageJson.version;\n }\n } catch (e) {\n // Ignore\n }\n return undefined;\n }\n\n /**\n * Generate OpenAPI specification\n */\n public generateOpenAPISpec(): any {\n const paths: Record<string, any> = {};\n\n const collectRoutes = (app: ApplicationInstance, prefix: string = '') => {\n // Add direct routes\n for (const route of app.routes) {\n // Ensure prefix handles slashes correctly\n const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const cleanPath = route.path.startsWith('/') ? route.path : '/' + route.path;\n const fullPath = (cleanPrefix + cleanPath) || '/';\n\n // Normalization is already done in extractRouteFromCall, but double check\n const pathKey = fullPath.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');\n\n if (!paths[pathKey]) {\n paths[pathKey] = {};\n }\n\n const method = route.method.toLowerCase();\n const operation: any = {\n summary: route.summary || `${route.method.toUpperCase()} ${pathKey}`,\n description: route.description,\n tags: route.tags,\n operationId: route.operationId,\n responses: {\n '200': {\n description: 'Successful response'\n }\n }\n };\n\n // Clean up undefined\n if (!operation.description) delete operation.description;\n if (!operation.tags) delete operation.tags;\n if (!operation.operationId) delete operation.operationId;\n\n // Add inferred response schema\n if (route.responseSchema) {\n operation.responses['200'].content = {\n 'application/json': {\n schema: route.responseSchema\n }\n };\n } else if (route.responseType) {\n // Fallback to basic type\n const contentType = route.responseType === 'string' ? 'text/plain' : 'application/json';\n operation.responses['200'].content = {\n [contentType]: {\n schema: { type: route.responseType }\n }\n };\n } else {\n // Default object\n operation.responses['200'].content = {\n 'application/json': {\n schema: { type: 'object' }\n }\n };\n }\n\n // Add request body schema if available\n if (route.requestTypes?.body) {\n operation.requestBody = {\n content: {\n 'application/json': {\n schema: route.requestTypes.body\n }\n }\n };\n }\n\n // Add query/path parameters\n const parameters: any[] = [];\n\n if (route.requestTypes?.query) {\n for (const [key] of Object.entries(route.requestTypes.query)) {\n parameters.push({\n name: key,\n in: 'query',\n schema: { type: 'string' }\n });\n }\n }\n\n if (route.requestTypes?.params) {\n // Also check for path params implied by the URL {param}\n // But assume extractRouteFromCall handled explicit ones?\n // Let's just trust requestTypes for now\n for (const [key] of Object.entries(route.requestTypes.params)) {\n parameters.push({\n name: key,\n in: 'path',\n required: true,\n schema: { type: 'string' }\n });\n }\n }\n\n // Extract params from path if not explicitly typed but present in URL\n // This backfills untyped params so they appear in spec\n const pathParams = pathKey.match(/{([^}]+)}/g);\n if (pathParams) {\n pathParams.forEach(p => {\n const name = p.slice(1, -1);\n if (!parameters.some(param => param.name === name && param.in === 'path')) {\n parameters.push({\n name,\n in: 'path',\n required: true,\n schema: { type: 'string' } // Default to string\n });\n }\n });\n }\n\n if (parameters.length > 0) {\n operation.parameters = parameters;\n }\n\n paths[pathKey][method] = operation;\n }\n\n // Recurse into mounted apps\n for (const mount of app.mounted) {\n // We need to resolve the ApplicationInstance for the target\n // The 'mounted' array currently only stores { prefix, target, dependency }\n // We need to find the AppInstance that matches 'target' class name\n\n // Note: This simple matching by class name might be brittle if multiple files have same class name\n // In a full implementation we would resolve the file path.\n const mountedApp = this.applications.find(a => a.name === mount.target || a.className === mount.target);\n\n if (mountedApp) {\n // Prevent infinite recursion\n if (mountedApp === app) continue;\n\n const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const mountPrefix = mount.prefix.startsWith('/') ? mount.prefix : '/' + mount.prefix;\n const nextPrefix = cleanPrefix + mountPrefix;\n\n collectRoutes(mountedApp, nextPrefix);\n }\n }\n };\n\n for (const app of this.applications) {\n // We only want to start collection from \"root\" apps? \n // Or just collect everything? \n // If we collect everything, we might duplicate routes if they are mounted.\n // Current findApplications finds ALL instances.\n\n // Heuristic: If this app is mounted by another app, don't collect it as root.\n // But we don't have back-references easily.\n // Simplified: Just collect everything for now, but realize that un-mounted routers might show up as root.\n // Ideally we need to find the \"Main\" app (root).\n\n // For now, let's collect all, but we might have duplicates if we traverse mounts.\n // Let's rely on the user to only have one Main entrypoint usually.\n // Or we can try to detect if it is mounted.\n\n const isMounted = this.applications.some(parent =>\n parent.mounted.some(m => m.target === app.name || m.target === app.className)\n );\n\n if (!isMounted) {\n collectRoutes(app);\n }\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: 'Shokupan API',\n version: '1.0.0',\n description: 'Auto-generated from Shokupan application analysis'\n },\n paths,\n components: {\n schemas: {}\n }\n };\n }\n\n /**\n * Convert a type string to an OpenAPI schema\n */\n private typeToSchema(type: string): any {\n switch (type) {\n case 'string':\n return { type: 'string' };\n case 'number':\n return { type: 'number' };\n case 'boolean':\n return { type: 'boolean' };\n case 'array':\n return { type: 'array', items: {} };\n case 'object':\n default:\n return { type: 'object' };\n }\n }\n}\n\n/**\n * Analyze a directory and generate OpenAPI spec\n */\nexport async function analyzeDirectory(directory: string): Promise<any> {\n const analyzer = new OpenAPIAnalyzer(directory);\n return await analyzer.analyze();\n}\n"],"names":[],"mappings":"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"}
@@ -399,6 +399,9 @@ class OpenAPIAnalyzer {
399
399
  if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {
400
400
  analyzeReturnExpression(node.body);
401
401
  }
402
+ if (ts.isExpressionStatement(node)) {
403
+ analyzeReturnExpression(node.expression);
404
+ }
402
405
  ts.forEachChild(node, visit);
403
406
  };
404
407
  if (ts.isBlock(body)) {
@@ -766,4 +769,4 @@ export {
766
769
  OpenAPIAnalyzer,
767
770
  analyzeDirectory
768
771
  };
769
- //# sourceMappingURL=openapi-analyzer-cjdGeQ5a.js.map
772
+ //# sourceMappingURL=openapi-analyzer-BTExMLX4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-analyzer-BTExMLX4.js","sources":["../src/analysis/openapi-analyzer.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport ts from 'typescript';\n\n/**\n * File information collected during scan\n */\ninterface CollectedFile {\n path: string;\n type: 'ts' | 'js' | 'map';\n content?: string;\n}\n\n/**\n * Route information extracted from AST\n */\nexport interface RouteInfo {\n method: string;\n path: string;\n handlerName?: string;\n handlerSource?: string;\n requestTypes?: {\n body?: any;\n query?: Record<string, string>;\n params?: Record<string, string>;\n headers?: Record<string, string>;\n };\n responseType?: string;\n responseSchema?: any;\n summary?: string;\n description?: string;\n tags?: string[];\n operationId?: string;\n}\n\n/**\n * Dependency information\n */\ninterface DependencyInfo {\n packageName: string;\n version?: string;\n importPath: string;\n isExternal: boolean;\n}\n\n/**\n * Application/Router instance found in code\n */\nexport interface ApplicationInstance {\n name: string;\n filePath: string;\n className: 'Shokupan' | 'ShokupanRouter' | 'Controller';\n routes: RouteInfo[];\n mounted: MountInfo[];\n}\n\ninterface MountInfo {\n prefix: string;\n target: string; // Controller/Router name or file path\n dependency?: DependencyInfo;\n}\n\n/**\n * Main analyzer class\n */\nexport class OpenAPIAnalyzer {\n private files: CollectedFile[] = [];\n private applications: ApplicationInstance[] = [];\n private program?: ts.Program;\n\n private entrypoint?: string;\n\n constructor(private rootDir: string, entrypoint?: string) {\n if (entrypoint) {\n this.entrypoint = path.resolve(entrypoint);\n }\n }\n\n /**\n * Main analysis entry point\n */\n /**\n * Main analysis entry point\n */\n public async analyze(): Promise<{ applications: ApplicationInstance[]; }> {\n // console.log(`Analyzing directory: ${this.rootDir}`);\n\n // Step 1: Parse TypeScript files (which might involve scanning or using entrypoint)\n await this.parseTypeScriptFiles();\n\n // Step 2: Process source maps if needed\n await this.processSourceMaps();\n\n // Step 3: Find Shokupan applications\n await this.findApplications();\n\n // Step 4: Extract route information\n await this.extractRoutes();\n\n // Return the raw application data for further processing\n return { applications: this.applications };\n }\n\n /**\n * Recursively scan directory for TypeScript/JavaScript files\n */\n private async scanDirectory(dir: string): Promise<void> {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n // Skip node_modules for source files (we'll handle deps separately)\n if (entry.isDirectory()) {\n if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') {\n continue;\n }\n await this.scanDirectory(fullPath);\n } else {\n const ext = path.extname(entry.name);\n if (ext === '.ts') {\n this.files.push({ path: fullPath, type: 'ts' });\n } else if (ext === '.js') {\n this.files.push({ path: fullPath, type: 'js' });\n } else if (ext === '.map') {\n this.files.push({ path: fullPath, type: 'map' });\n }\n }\n }\n } catch (error: any) {\n // Silently skip directories that don't exist or can't be read\n if (error.code !== 'ENOENT' && error.code !== 'EACCES') {\n throw error;\n }\n }\n }\n\n /**\n * Process source maps to reconstruct TypeScript\n */\n private async processSourceMaps(): Promise<void> {\n // Find JS files that have corresponding .map files\n const jsFiles = this.files.filter(f => f.type === 'js');\n const mapFiles = this.files.filter(f => f.type === 'map');\n\n for (const jsFile of jsFiles) {\n const mapFile = mapFiles.find(m => m.path === jsFile.path + '.map');\n\n if (mapFile && !this.files.some(f => f.path === jsFile.path.replace(/\\.js$/, '.ts'))) {\n // We have .js + .map but no .ts file\n // For now, we'll just parse the JS file directly\n // Full source map reconstruction would require the 'source-map' library\n console.log(`Note: Found ${jsFile.path} with source map but no .ts file. Will parse JS directly.`);\n }\n }\n }\n\n /**\n * Parse TypeScript files and create AST\n */\n private async parseTypeScriptFiles(): Promise<void> {\n let fileNames: string[] = [];\n\n if (this.entrypoint) {\n // If entrypoint is provided, let TypeScript resolve dependencies\n fileNames = [this.entrypoint];\n // console.log(`[Analyzer] Using entrypoint: ${this.entrypoint}`);\n } else {\n // Otherwise, scan the directory manually\n await this.scanDirectory(this.rootDir);\n const tsFiles = this.files.filter(f => f.type === 'ts' || f.type === 'js');\n fileNames = tsFiles.map(f => f.path);\n // console.log(`[Analyzer] Scanning directory, found ${fileNames.length} files`);\n }\n\n // Create TypeScript program\n this.program = ts.createProgram(fileNames, {\n target: ts.ScriptTarget.ESNext,\n module: ts.ModuleKind.ESNext,\n allowJs: true,\n moduleResolution: ts.ModuleResolutionKind.Node10,\n rootDir: this.rootDir,\n skipLibCheck: true,\n skipDefaultLibCheck: true,\n });\n\n // If using entrypoint, update this.files with what TS found in the project\n if (this.entrypoint) {\n this.files = this.program.getSourceFiles()\n .filter(sf => !sf.fileName.includes('node_modules'))\n .map(sf => ({ path: sf.fileName, type: sf.fileName.endsWith('.js') ? 'js' : 'ts' }));\n }\n }\n\n /**\n * Find all Shokupan/ShokupanRouter instances\n */\n private async findApplications(): Promise<void> {\n if (!this.program) return;\n\n const typeChecker = this.program.getTypeChecker();\n\n for (const sourceFile of this.program.getSourceFiles()) {\n // Skip node_modules and declaration files/tests\n if (sourceFile.fileName.includes('node_modules')) continue;\n if (sourceFile.isDeclarationFile) continue;\n if (sourceFile.fileName.includes('.test.ts') || sourceFile.fileName.includes('.spec.ts')) continue;\n\n // console.log(`[Analyzer] Visiting file: ${sourceFile.fileName}`);\n ts.forEachChild(sourceFile, (node) => {\n this.visitNode(node, sourceFile, typeChecker);\n });\n }\n }\n\n /**\n * Visit AST node to find application instances\n */\n private visitNode(node: ts.Node, sourceFile: ts.SourceFile, typeChecker: ts.TypeChecker): void {\n // Look for: class FooController ... @Controller(...)\n if (ts.isClassDeclaration(node)) {\n // Check for @Controller decorator\n let isController = false;\n let className = node.name?.getText(sourceFile);\n\n if ((node as any).decorators) {\n const controllerDecorator = (node as any).decorators.find((d: any) => {\n const expr = d.expression;\n if (ts.isCallExpression(expr)) {\n const identifier = expr.expression.getText(sourceFile);\n return identifier === 'Controller';\n }\n return false;\n });\n if (controllerDecorator) isController = true;\n }\n\n // Fallback: Check for method decorators (@Get, @Post, etc.)\n if (!isController) {\n const hasRouteDecorators = node.members.some(m => {\n // Trust 175 as MethodDeclaration if TS matches mostly, or just check members\n if (ts.isMethodDeclaration(m) || m.kind === 175 || m.kind === 170 || m.kind === 171) {\n // Note: 171 is standard MethodDeclaration in some versions, 175 in others?\n // Or maybe 171 is Decorator.\n const decs = (m as any).decorators || (m as any).modifiers?.filter((mod: any) => ts.isDecorator(mod));\n if (decs) {\n return decs.some((d: any) => {\n const expr = d.expression;\n if (ts.isCallExpression(expr)) {\n const identifier = expr.expression.getText(sourceFile);\n return ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head'].includes(identifier);\n }\n return false;\n });\n }\n }\n return false;\n });\n if (hasRouteDecorators) {\n isController = true;\n }\n }\n\n if (isController && className) {\n this.applications.push({\n name: className,\n filePath: sourceFile.fileName,\n className: 'Controller',\n routes: [],\n mounted: []\n });\n }\n }\n\n // Look for: new Shokupan() or new ShokupanRouter()\n if (ts.isVariableDeclaration(node) && node.initializer) {\n if (ts.isNewExpression(node.initializer)) {\n const expr = node.initializer;\n const className = expr.expression.getText(sourceFile);\n\n if (className === 'Shokupan' || className === 'ShokupanRouter') {\n const varName = node.name.getText(sourceFile);\n\n this.applications.push({\n name: varName,\n filePath: sourceFile.fileName,\n className: className as 'Shokupan' | 'ShokupanRouter',\n routes: [],\n mounted: []\n });\n }\n }\n }\n\n // Recursively visit children\n ts.forEachChild(node, (child) => this.visitNode(child, sourceFile, typeChecker));\n }\n\n /**\n * Extract route information from applications\n */\n private async extractRoutes(): Promise<void> {\n if (!this.program) return;\n\n for (const app of this.applications) {\n const sourceFile = this.program.getSourceFile(app.filePath);\n if (!sourceFile) continue;\n\n this.extractRoutesFromFile(app, sourceFile);\n }\n }\n\n /**\n * Extract routes from a Controller class\n */\n private extractRoutesFromController(app: ApplicationInstance, classNode: ts.ClassDeclaration, sourceFile: ts.SourceFile): void {\n const methods = classNode.members.filter(m => ts.isMethodDeclaration(m) || m.kind === 175);\n\n for (const method of methods) {\n const methodNode = method as any; // Cast to any to access decorators in newer/older TS mix\n if (!methodNode.decorators && !methodNode.modifiers) continue;\n\n const decorators = methodNode.decorators || methodNode.modifiers?.filter((m: any) => ts.isDecorator(m));\n if (!decorators) continue;\n\n // Find route decorators: @Get, @Post, etc.\n const routeDecorator = decorators.find((d: any) => {\n const expr = d.expression;\n if (ts.isCallExpression(expr)) {\n const identifier = expr.expression.getText(sourceFile);\n return ['Get', 'Post', 'Put', 'Delete', 'Patch', 'Options', 'Head'].includes(identifier);\n }\n return false;\n });\n\n if (routeDecorator && ts.isCallExpression(routeDecorator.expression)) {\n const decoratorName = routeDecorator.expression.expression.getText(sourceFile);\n const httpMethod = decoratorName.toUpperCase();\n let routePath = '/';\n\n // Get path\n const pathArg = routeDecorator.expression.arguments[0];\n if (pathArg && ts.isStringLiteral(pathArg)) {\n routePath = pathArg.text;\n }\n\n // Normalize path params: /users/:id -> /users/{id}\n routePath = routePath.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');\n\n // Handler Name (Class.method)\n const handlerName = `${app.name}.${methodNode.name.getText(sourceFile)}`;\n\n // Analyze the method body\n const analysis = this.analyzeHandler(methodNode, sourceFile);\n\n app.routes.push({\n method: httpMethod,\n path: routePath,\n handlerName: handlerName,\n handlerSource: methodNode.getText(sourceFile),\n requestTypes: analysis.requestTypes,\n responseType: analysis.responseType,\n responseSchema: analysis.responseSchema\n });\n }\n }\n };\n\n /**\n * Extract routes from a specific file\n */\n private extractRoutesFromFile(app: ApplicationInstance, sourceFile: ts.SourceFile): void {\n if (app.className === 'Controller') {\n const classNode = sourceFile.statements.find(s => ts.isClassDeclaration(s) && s.name?.getText(sourceFile) === app.name) as ts.ClassDeclaration;\n if (classNode) {\n this.extractRoutesFromController(app, classNode, sourceFile);\n }\n } else {\n // Existing logic for app/router instances\n const visit = (node: ts.Node) => {\n // ... (rest of the existing logic)\n // Look for method calls: app.get(...), app.post(...), app.mount(...)\n if (ts.isCallExpression(node)) {\n const expr = node.expression;\n\n if (ts.isPropertyAccessExpression(expr)) {\n const objName = expr.expression.getText(sourceFile);\n const methodName = expr.name.getText(sourceFile);\n\n // Check if this is our application instance\n if (objName === app.name) {\n if (['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(methodName)) {\n // Extract route info\n const route = this.extractRouteFromCall(node, sourceFile, methodName.toUpperCase());\n if (route) {\n app.routes.push(route);\n }\n } else if (methodName === 'mount') {\n // Extract mount info\n const mount = this.extractMountFromCall(node, sourceFile);\n if (mount) {\n app.mounted.push(mount);\n }\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n ts.forEachChild(sourceFile, visit);\n }\n }\n\n /**\n * Extract route information from a route call (e.g., app.get('/path', handler))\n */\n private extractRouteFromCall(node: ts.CallExpression, sourceFile: ts.SourceFile, method: string): RouteInfo | null {\n const args = node.arguments;\n\n if (args.length < 2) return null;\n\n const pathArg = args[0];\n let routePath = '/';\n\n if (ts.isStringLiteral(pathArg)) {\n routePath = pathArg.text;\n }\n\n // Normalize path params: /users/:id -> /users/{id}\n // This ensures matching with runtime-generated keys\n const normalizedPath = routePath.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');\n\n let metadata: any = {};\n\n // Check for metadata argument (3 args: path, metadata, handler)\n if (args.length >= 3 && ts.isObjectLiteralExpression(args[1])) {\n const metaObj = args[1];\n // Extract summary, description, tags, etc.\n const rawMeta = this.convertExpressionToSchema(metaObj, sourceFile, new Map());\n\n // convertExpressionToSchema returns a schema-like object { type: 'object', properties: {...} }\n // But we want the actual values if they are literals.\n // convertExpressionToSchema is designed for SCHEMAS, not values.\n // We need a simpler value extractor or just parse the props directly for this specific case.\n\n for (const prop of metaObj.properties) {\n if (ts.isPropertyAssignment(prop) && prop.name) {\n const name = prop.name.getText(sourceFile);\n const val = prop.initializer;\n\n if (ts.isStringLiteral(val)) {\n metadata[name] = val.text;\n } else if (ts.isArrayLiteralExpression(val) && name === 'tags') {\n metadata.tags = val.elements\n .filter(e => ts.isStringLiteral(e))\n .map(e => (e as ts.StringLiteral).text);\n } else if (name === 'operationId' && ts.isStringLiteral(val)) {\n metadata.operationId = val.text;\n }\n }\n }\n }\n\n // Extract handler information\n const handlerArg = args[args.length - 1];\n const handlerInfo = this.analyzeHandler(handlerArg, sourceFile);\n\n return {\n method,\n path: normalizedPath,\n handlerName: handlerArg.getText(sourceFile).substring(0, 50), // Truncate for display\n handlerSource: handlerArg.getText(sourceFile),\n requestTypes: handlerInfo.requestTypes,\n responseType: handlerInfo.responseType,\n responseSchema: handlerInfo.responseSchema,\n ...metadata\n };\n }\n\n /**\n * Analyze a route handler to extract type information\n */\n private analyzeHandler(handler: ts.Node, sourceFile: ts.SourceFile): {\n requestTypes?: RouteInfo['requestTypes'];\n responseType?: string;\n responseSchema?: any;\n } {\n const requestTypes: RouteInfo['requestTypes'] = {};\n let responseType: string | undefined;\n let responseSchema: any | undefined;\n\n // Simple scope to track variable types (name -> schema)\n const scope = new Map<string, any>();\n\n // Pre-populate scope with function parameters\n if (ts.isFunctionLike(handler)) {\n handler.parameters.forEach(param => {\n if (ts.isIdentifier(param.name) && param.type) {\n const paramName = param.name.getText(sourceFile);\n // Resolving TypeReference for parameters (e.g. User) would require partial type checker or expanded scope logic\n // For now, we rely on basic types\n const paramType = this.convertTypeNodeToSchema(param.type, sourceFile);\n if (paramType) {\n scope.set(paramName, paramType);\n }\n }\n });\n }\n\n // Helper to analyze an expression that is being returned (either explicitly or implicitly)\n const analyzeReturnExpression = (expr: ts.Expression) => {\n let node = expr;\n // Unwrap await\n if (ts.isAwaitExpression(node)) {\n node = node.expression;\n }\n\n // Case 1: return ctx.json(...)\n if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {\n const callObj = node.expression.expression.getText(sourceFile);\n const callProp = node.expression.name.getText(sourceFile);\n\n if ((callObj === 'ctx' || callObj.endsWith('.ctx')) && callProp === 'json') {\n if (node.arguments.length > 0) {\n responseSchema = this.convertExpressionToSchema(node.arguments[0], sourceFile, scope);\n responseType = 'object';\n }\n return;\n }\n }\n\n // Case 2: Direct object return\n // Only use this if we haven't found a better schema yet, or if it looks specific\n if (!responseSchema || responseSchema.type === 'object') {\n const schema = this.convertExpressionToSchema(node, sourceFile, scope);\n if (schema && (schema.type !== 'object' || Object.keys(schema.properties || {}).length > 0)) {\n responseSchema = schema;\n responseType = schema.type;\n }\n }\n\n // Fallback to text matching if schema inference failed and we still don't have a type\n if (!responseSchema && !responseType) {\n const returnText = node.getText(sourceFile);\n if (returnText.startsWith('{')) {\n responseType = 'object';\n } else if (returnText.startsWith('[')) {\n responseType = 'array';\n } else if (returnText.startsWith('\"') || returnText.startsWith(\"'\")) {\n responseType = 'string';\n }\n }\n };\n\n // Handle arrow functions\n let body: ts.Block | undefined;\n // Also check for Kind 175 (Method/Accessor in some TS versions)\n if (ts.isArrowFunction(handler) || ts.isFunctionExpression(handler) || ts.isMethodDeclaration(handler) || handler.kind === 175) {\n // TS method has .body which is FunctionBody (Block) or undefined\n body = (handler as any).body;\n\n // Visit the handler body to find ctx usage\n const visit = (node: ts.Node) => {\n // Check for type assertions on ctx.body()\n if (ts.isAsExpression(node)) {\n if (this.isCtxBodyCall(node.expression, sourceFile)) {\n const schema = this.convertTypeNodeToSchema(node.type, sourceFile);\n if (schema) {\n requestTypes.body = schema;\n\n // Track variables assigned to this body\n if (ts.isVariableDeclaration(node.parent)) {\n const varName = node.parent.name.getText(sourceFile);\n scope.set(varName, schema);\n }\n }\n }\n }\n\n // Look for ctx usage\n if (ts.isPropertyAccessExpression(node)) {\n const objText = node.expression.getText(sourceFile);\n const propText = node.name.getText(sourceFile);\n\n if (objText === 'ctx' || objText.endsWith('.ctx')) {\n if (propText === 'body') {\n if (!requestTypes.body) {\n requestTypes.body = { type: 'object' };\n }\n } else if (propText === 'query') {\n if (!requestTypes.query) requestTypes.query = {};\n } else if (propText === 'params') {\n if (!requestTypes.params) requestTypes.params = {};\n } else if (propText === 'headers') {\n if (!requestTypes.headers) requestTypes.headers = {};\n }\n }\n }\n\n // Explicit Return\n if (ts.isReturnStatement(node) && node.expression) {\n analyzeReturnExpression(node.expression);\n }\n\n // Implicit Return (Concise Arrow Function)\n // e.g. (ctx) => ctx.json(...) or .then(res => ctx.json(res))\n if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {\n analyzeReturnExpression(node.body as ts.Expression);\n }\n\n // Implicit Return call (e.g. ctx.json(...) as a statement without return)\n if (ts.isExpressionStatement(node)) {\n analyzeReturnExpression(node.expression);\n }\n\n ts.forEachChild(node, visit);\n };\n\n if (ts.isBlock(body)) {\n ts.forEachChild(body, visit);\n } else {\n // Main handler is an implicit return: app.get('/', (ctx) => ctx.json(...))\n analyzeReturnExpression(body);\n // Also verify children (e.g. if body is a CallExpression, verify args??)\n // analyzeReturnExpression analyzes the expression itself.\n // But we ALSO want to visit children to find other usages (request types)\n // or nested arrow functions in a call chain (e.g. chaining .then())\n ts.forEachChild(body, visit);\n }\n }\n\n return { requestTypes, responseType, responseSchema };\n }\n\n /**\n * Convert an Expression node to an OpenAPI schema (best effort)\n */\n private convertExpressionToSchema(node: ts.Expression, sourceFile: ts.SourceFile, scope: Map<string, any>): any {\n // Object Literal: { a: 1, b: \"text\" }\n if (ts.isObjectLiteralExpression(node)) {\n const schema: any = {\n type: 'object',\n properties: {},\n required: []\n };\n\n for (const prop of node.properties) {\n if (ts.isPropertyAssignment(prop)) {\n const name = prop.name.getText(sourceFile);\n const valueSchema = this.convertExpressionToSchema(prop.initializer, sourceFile, scope);\n\n schema.properties[name] = valueSchema;\n schema.required.push(name); // Properties in literal return are required\n }\n else if (ts.isShorthandPropertyAssignment(prop)) {\n const name = prop.name.getText(sourceFile);\n // Check scope for variable\n const scopedSchema = scope.get(name);\n schema.properties[name] = scopedSchema || { type: 'object' };\n schema.required.push(name);\n }\n }\n if (schema.required.length === 0) {\n delete schema.required;\n }\n return schema;\n }\n\n // Array Literal: [1, 2]\n if (ts.isArrayLiteralExpression(node)) {\n const schema: any = { type: 'array' };\n if (node.elements.length > 0) {\n // Infer item type from first element\n schema.items = this.convertExpressionToSchema(node.elements[0], sourceFile, scope);\n } else {\n schema.items = {};\n }\n return schema;\n }\n\n // Conditional (Ternary) Expression: cond ? trueVal : falseVal\n if (ts.isConditionalExpression(node)) {\n const trueSchema = this.convertExpressionToSchema(node.whenTrue, sourceFile, scope);\n // const falseSchema = this.convertExpressionToSchema(node.whenFalse, sourceFile, scope);\n\n // Simplified: return true branch schema. Ideally we'd do oneOf\n return trueSchema;\n }\n\n // Template Expression: `Hello ${name}`\n if (ts.isTemplateExpression(node)) {\n return { type: 'string' };\n }\n\n // Identifier (Variable reference)\n if (ts.isIdentifier(node)) {\n const name = node.getText(sourceFile);\n const scopedSchema = scope.get(name);\n if (scopedSchema) return scopedSchema;\n return { type: 'object' }; // Unknown reference\n }\n\n // Literals\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return { type: 'string' };\n if (ts.isNumericLiteral(node)) return { type: 'number' };\n if (node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword) return { type: 'boolean' };\n\n // Unknown\n return { type: 'object' };\n }\n\n /**\n * Check if an expression is a call to ctx.body()\n */\n private isCtxBodyCall(node: ts.Expression, sourceFile: ts.SourceFile): boolean {\n // Handle await ctx.body()\n if (ts.isAwaitExpression(node)) {\n return this.isCtxBodyCall(node.expression, sourceFile);\n }\n\n // Handle ctx.body()\n if (ts.isCallExpression(node)) {\n // Check expression: ctx.body\n if (ts.isPropertyAccessExpression(node.expression)) {\n const objText = node.expression.expression.getText(sourceFile);\n const propText = node.expression.name.getText(sourceFile);\n return (objText === 'ctx' || objText.endsWith('.ctx')) && propText === 'body';\n }\n }\n\n return false;\n }\n\n /**\n * Convert a TypeScript TypeNode to an OpenAPI schema\n */\n private convertTypeNodeToSchema(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): any {\n switch (typeNode.kind) {\n case ts.SyntaxKind.StringKeyword:\n return { type: 'string' };\n case ts.SyntaxKind.NumberKeyword:\n return { type: 'number' };\n case ts.SyntaxKind.BooleanKeyword:\n return { type: 'boolean' };\n case ts.SyntaxKind.AnyKeyword:\n case ts.SyntaxKind.UnknownKeyword:\n return {}; // Any/Unknown -> empty schema (accepts anything)\n\n case ts.SyntaxKind.TypeLiteral: {\n const literal = typeNode as ts.TypeLiteralNode;\n const schema: any = {\n type: 'object',\n properties: {},\n required: []\n };\n\n for (const member of literal.members) {\n if (ts.isPropertySignature(member) && member.type) {\n const name = member.name.getText(sourceFile);\n const propSchema = this.convertTypeNodeToSchema(member.type, sourceFile);\n\n schema.properties[name] = propSchema;\n\n // Property is required unless it has a question token\n if (!member.questionToken) {\n schema.required.push(name);\n }\n }\n }\n\n if (schema.required.length === 0) {\n delete schema.required;\n }\n\n return schema;\n }\n\n case ts.SyntaxKind.ArrayType: {\n const arrayType = typeNode as ts.ArrayTypeNode;\n return {\n type: 'array',\n items: this.convertTypeNodeToSchema(arrayType.elementType, sourceFile)\n };\n }\n\n // Handle Type References (e.g. Array<string>)\n case ts.SyntaxKind.TypeReference: {\n const typeRef = typeNode as ts.TypeReferenceNode;\n const typeName = typeRef.typeName.getText(sourceFile);\n\n if (typeName === 'Array' && typeRef.typeArguments && typeRef.typeArguments.length > 0) {\n return {\n type: 'array',\n items: this.convertTypeNodeToSchema(typeRef.typeArguments[0], sourceFile)\n };\n }\n\n // For other references, we default to string or object as fallback\n // A fuller implementation would resolve the reference\n return { type: 'object', description: `Ref: ${typeName}` };\n }\n\n default:\n return { type: 'object' };\n }\n }\n\n /**\n * Extract mount information from mount call\n */\n private extractMountFromCall(node: ts.CallExpression, sourceFile: ts.SourceFile): MountInfo | null {\n const args = node.arguments;\n\n if (args.length < 2) return null;\n\n const pathArg = args[0];\n const targetArg = args[1];\n\n let prefix = '/';\n if (ts.isStringLiteral(pathArg)) {\n prefix = pathArg.text;\n }\n\n const target = targetArg.getText(sourceFile);\n\n // Check if target is from node_modules\n const dependency = this.checkIfExternalDependency(target, sourceFile);\n\n return {\n prefix,\n target,\n dependency\n };\n }\n\n /**\n * Check if a reference is to an external dependency\n */\n private checkIfExternalDependency(identifier: string, sourceFile: ts.SourceFile): DependencyInfo | undefined {\n // This is a simplified check - in a full implementation, \n // we'd track imports and resolve them\n\n // For now, check if there's an import statement for this identifier\n const imports: ts.ImportDeclaration[] = [];\n\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isImportDeclaration(node)) {\n imports.push(node);\n }\n });\n\n for (const imp of imports) {\n const moduleSpecifier = imp.moduleSpecifier;\n if (ts.isStringLiteral(moduleSpecifier)) {\n const modulePath = moduleSpecifier.text;\n\n // Check if it's a node_modules import (no relative path)\n if (!modulePath.startsWith('.') && !modulePath.startsWith('/')) {\n const namedBindings = imp.importClause?.namedBindings;\n\n if (namedBindings && ts.isNamedImports(namedBindings)) {\n for (const element of namedBindings.elements) {\n if (element.name.text === identifier) {\n // Try to read package version\n const version = this.getPackageVersion(modulePath);\n\n return {\n packageName: modulePath,\n version,\n importPath: modulePath,\n isExternal: true\n };\n }\n }\n }\n }\n }\n }\n\n return undefined;\n }\n\n /**\n * Get package version from package.json\n */\n private getPackageVersion(packageName: string): string | undefined {\n try {\n const packageJsonPath = path.join(this.rootDir, 'node_modules', packageName, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n return packageJson.version;\n }\n } catch (e) {\n // Ignore\n }\n return undefined;\n }\n\n /**\n * Generate OpenAPI specification\n */\n public generateOpenAPISpec(): any {\n const paths: Record<string, any> = {};\n\n const collectRoutes = (app: ApplicationInstance, prefix: string = '') => {\n // Add direct routes\n for (const route of app.routes) {\n // Ensure prefix handles slashes correctly\n const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const cleanPath = route.path.startsWith('/') ? route.path : '/' + route.path;\n const fullPath = (cleanPrefix + cleanPath) || '/';\n\n // Normalization is already done in extractRouteFromCall, but double check\n const pathKey = fullPath.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');\n\n if (!paths[pathKey]) {\n paths[pathKey] = {};\n }\n\n const method = route.method.toLowerCase();\n const operation: any = {\n summary: route.summary || `${route.method.toUpperCase()} ${pathKey}`,\n description: route.description,\n tags: route.tags,\n operationId: route.operationId,\n responses: {\n '200': {\n description: 'Successful response'\n }\n }\n };\n\n // Clean up undefined\n if (!operation.description) delete operation.description;\n if (!operation.tags) delete operation.tags;\n if (!operation.operationId) delete operation.operationId;\n\n // Add inferred response schema\n if (route.responseSchema) {\n operation.responses['200'].content = {\n 'application/json': {\n schema: route.responseSchema\n }\n };\n } else if (route.responseType) {\n // Fallback to basic type\n const contentType = route.responseType === 'string' ? 'text/plain' : 'application/json';\n operation.responses['200'].content = {\n [contentType]: {\n schema: { type: route.responseType }\n }\n };\n } else {\n // Default object\n operation.responses['200'].content = {\n 'application/json': {\n schema: { type: 'object' }\n }\n };\n }\n\n // Add request body schema if available\n if (route.requestTypes?.body) {\n operation.requestBody = {\n content: {\n 'application/json': {\n schema: route.requestTypes.body\n }\n }\n };\n }\n\n // Add query/path parameters\n const parameters: any[] = [];\n\n if (route.requestTypes?.query) {\n for (const [key] of Object.entries(route.requestTypes.query)) {\n parameters.push({\n name: key,\n in: 'query',\n schema: { type: 'string' }\n });\n }\n }\n\n if (route.requestTypes?.params) {\n // Also check for path params implied by the URL {param}\n // But assume extractRouteFromCall handled explicit ones?\n // Let's just trust requestTypes for now\n for (const [key] of Object.entries(route.requestTypes.params)) {\n parameters.push({\n name: key,\n in: 'path',\n required: true,\n schema: { type: 'string' }\n });\n }\n }\n\n // Extract params from path if not explicitly typed but present in URL\n // This backfills untyped params so they appear in spec\n const pathParams = pathKey.match(/{([^}]+)}/g);\n if (pathParams) {\n pathParams.forEach(p => {\n const name = p.slice(1, -1);\n if (!parameters.some(param => param.name === name && param.in === 'path')) {\n parameters.push({\n name,\n in: 'path',\n required: true,\n schema: { type: 'string' } // Default to string\n });\n }\n });\n }\n\n if (parameters.length > 0) {\n operation.parameters = parameters;\n }\n\n paths[pathKey][method] = operation;\n }\n\n // Recurse into mounted apps\n for (const mount of app.mounted) {\n // We need to resolve the ApplicationInstance for the target\n // The 'mounted' array currently only stores { prefix, target, dependency }\n // We need to find the AppInstance that matches 'target' class name\n\n // Note: This simple matching by class name might be brittle if multiple files have same class name\n // In a full implementation we would resolve the file path.\n const mountedApp = this.applications.find(a => a.name === mount.target || a.className === mount.target);\n\n if (mountedApp) {\n // Prevent infinite recursion\n if (mountedApp === app) continue;\n\n const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const mountPrefix = mount.prefix.startsWith('/') ? mount.prefix : '/' + mount.prefix;\n const nextPrefix = cleanPrefix + mountPrefix;\n\n collectRoutes(mountedApp, nextPrefix);\n }\n }\n };\n\n for (const app of this.applications) {\n // We only want to start collection from \"root\" apps? \n // Or just collect everything? \n // If we collect everything, we might duplicate routes if they are mounted.\n // Current findApplications finds ALL instances.\n\n // Heuristic: If this app is mounted by another app, don't collect it as root.\n // But we don't have back-references easily.\n // Simplified: Just collect everything for now, but realize that un-mounted routers might show up as root.\n // Ideally we need to find the \"Main\" app (root).\n\n // For now, let's collect all, but we might have duplicates if we traverse mounts.\n // Let's rely on the user to only have one Main entrypoint usually.\n // Or we can try to detect if it is mounted.\n\n const isMounted = this.applications.some(parent =>\n parent.mounted.some(m => m.target === app.name || m.target === app.className)\n );\n\n if (!isMounted) {\n collectRoutes(app);\n }\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: 'Shokupan API',\n version: '1.0.0',\n description: 'Auto-generated from Shokupan application analysis'\n },\n paths,\n components: {\n schemas: {}\n }\n };\n }\n\n /**\n * Convert a type string to an OpenAPI schema\n */\n private typeToSchema(type: string): any {\n switch (type) {\n case 'string':\n return { type: 'string' };\n case 'number':\n return { type: 'number' };\n case 'boolean':\n return { type: 'boolean' };\n case 'array':\n return { type: 'array', items: {} };\n case 'object':\n default:\n return { type: 'object' };\n }\n }\n}\n\n/**\n * Analyze a directory and generate OpenAPI spec\n */\nexport async function analyzeDirectory(directory: string): Promise<any> {\n const analyzer = new OpenAPIAnalyzer(directory);\n return await analyzer.analyze();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { ShokupanRouter } from '../../router';
2
+ import { ShokupanHooks } from '../../types';
3
+ export interface RequestLog {
4
+ method: string;
5
+ url: string;
6
+ status: number;
7
+ duration: number;
8
+ timestamp: number;
9
+ }
10
+ export interface DebugDashboardConfig {
11
+ /**
12
+ * Function to generate headers for the dashboard fetch requests.
13
+ * This function will be serialized and executed in the browser.
14
+ */
15
+ getRequestHeaders?: () => Record<string, string>;
16
+ /**
17
+ * How long to keep request logs in milliseconds.
18
+ * @default 7200000 (2 hours)
19
+ */
20
+ retentionMs?: number;
21
+ }
22
+ export declare class DebugDashboard extends ShokupanRouter {
23
+ private readonly dashboardConfig;
24
+ private metrics;
25
+ private eta;
26
+ private startTime;
27
+ constructor(dashboardConfig?: DebugDashboardConfig);
28
+ /**
29
+ * Returns the hooks needed to collect metrics using Shokupan's lifecycle events.
30
+ * Add this spread to your application hooks.
31
+ */
32
+ getHooks(): ShokupanHooks;
33
+ private updateTiming;
34
+ }
@@ -0,0 +1,2 @@
1
+ import { Middleware } from '../types';
2
+ export declare function openApiValidator(): Middleware;
@@ -0,0 +1,9 @@
1
+ import { Middleware } from '../types';
2
+ export interface ProxyOptions {
3
+ target: string;
4
+ pathRewrite?: (path: string) => string;
5
+ changeOrigin?: boolean;
6
+ ws?: boolean;
7
+ headers?: Record<string, string>;
8
+ }
9
+ export declare function Proxy(options: ProxyOptions): Middleware;
@@ -39,4 +39,8 @@ export declare class ShokupanResponse {
39
39
  * @param key Header name
40
40
  */
41
41
  has(key: string): boolean;
42
+ /**
43
+ * Internal: check if headers have been initialized/modified
44
+ */
45
+ get hasPopulatedHeaders(): boolean;
42
46
  }
package/dist/router.d.ts CHANGED
@@ -24,6 +24,8 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
24
24
  enableOpenApiGen?: boolean;
25
25
  reusePort?: boolean;
26
26
  controllersOnly?: boolean;
27
+ enableTracing?: boolean;
28
+ enableMiddlewareTracking?: boolean;
27
29
  httpLogger?: (ctx: ShokupanContext<Record<string, any>>) => void;
28
30
  logger?: {
29
31
  verbose?: boolean;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const http = require("node:http");
4
+ require("node:https");
5
+ function _interopNamespaceDefault(e) {
6
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
7
+ if (e) {
8
+ for (const k in e) {
9
+ if (k !== "default") {
10
+ const d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: () => e[k]
14
+ });
15
+ }
16
+ }
17
+ }
18
+ n.default = e;
19
+ return Object.freeze(n);
20
+ }
21
+ const http__namespace = /* @__PURE__ */ _interopNamespaceDefault(http);
22
+ function createHttpServer() {
23
+ return async (options) => {
24
+ const server = http__namespace.createServer(async (req, res) => {
25
+ const url = new URL(req.url, `http://${req.headers.host}`);
26
+ const request = new Request(url.toString(), {
27
+ method: req.method,
28
+ headers: req.headers,
29
+ body: ["GET", "HEAD"].includes(req.method) ? void 0 : new ReadableStream({
30
+ start(controller) {
31
+ req.on("data", (chunk) => controller.enqueue(chunk));
32
+ req.on("end", () => controller.close());
33
+ req.on("error", (err) => controller.error(err));
34
+ }
35
+ })
36
+ });
37
+ const response = await options.fetch(request, fauxServer);
38
+ res.statusCode = response.status;
39
+ response.headers.forEach((v, k) => res.setHeader(k, v));
40
+ if (response.body) {
41
+ for await (const chunk of response.body) {
42
+ res.write(chunk);
43
+ }
44
+ }
45
+ res.end();
46
+ });
47
+ const fauxServer = {
48
+ stop: () => {
49
+ server.close();
50
+ return Promise.resolve();
51
+ },
52
+ upgrade(req, options2) {
53
+ return false;
54
+ },
55
+ reload(options2) {
56
+ return fauxServer;
57
+ },
58
+ get port() {
59
+ const addr = server.address();
60
+ if (typeof addr === "object" && addr !== null) {
61
+ return addr.port;
62
+ }
63
+ return options.port;
64
+ },
65
+ hostname: options.hostname,
66
+ development: options.development,
67
+ pendingRequests: 0,
68
+ requestIP: (req) => null,
69
+ publish: () => 0,
70
+ subscriberCount: () => 0,
71
+ url: new URL(`http://${options.hostname}:${options.port}`)
72
+ };
73
+ return new Promise((resolve) => {
74
+ server.listen(options.port, options.hostname, () => {
75
+ resolve(fauxServer);
76
+ });
77
+ });
78
+ };
79
+ }
80
+ exports.createHttpServer = createHttpServer;
81
+ //# sourceMappingURL=server-adapter-BD6oKEto.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-adapter-BD6oKEto.cjs","sources":["../src/plugins/server-adapter.ts"],"sourcesContent":["import type { Server } from \"bun\";\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport type { ServerFactory } from \"../types\";\n\n/**\n * Creates a server factory that uses the standard Node.js `http` module.\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpServer(): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any\n });\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // @ts-ignore\n for await (const chunk of response.body) {\n res.write(chunk);\n }\n }\n res.end();\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n return Promise.resolve(); // Bun.Server stop usually returns void but in type definition it might vary.\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`http://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n\n/**\n * Creates a server factory that uses the standard Node.js `https` module.\n * @param sslOptions - Node.js HTTPS options (key, cert, etc.)\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpsServer(sslOptions: https.ServerOptions): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = https.createServer(sslOptions, async (req, res) => {\n const url = new URL(req.url!, `https://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any\n });\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // @ts-ignore\n for await (const chunk of response.body) {\n res.write(chunk);\n }\n }\n res.end();\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`https://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n"],"names":["http","options"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASO,SAAS,mBAAkC;AAC9C,SAAO,OAAO,YAAkC;AAC5C,UAAM,SAASA,gBAAK,aAAa,OAAO,KAAK,QAAQ;AACjD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC1D,YAAM,UAAU,IAAI,QAAQ,IAAI,YAAY;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,MAAM,CAAC,OAAO,MAAM,EAAE,SAAS,IAAI,MAAO,IAAI,SAAY,IAAI,eAAe;AAAA,UACzE,MAAM,YAAY;AACd,gBAAI,GAAG,QAAQ,CAAA,UAAS,WAAW,QAAQ,KAAK,CAAC;AACjD,gBAAI,GAAG,OAAO,MAAM,WAAW,OAAO;AACtC,gBAAI,GAAG,SAAS,CAAA,QAAO,WAAW,MAAM,GAAG,CAAC;AAAA,UAChD;AAAA,QAAA,CACH;AAAA,MAAA,CACJ;AAED,YAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,UAAU;AAExD,UAAI,aAAa,SAAS;AAC1B,eAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AAEtD,UAAI,SAAS,MAAM;AAEf,yBAAiB,SAAS,SAAS,MAAM;AACrC,cAAI,MAAM,KAAK;AAAA,QACnB;AAAA,MACJ;AACA,UAAI,IAAA;AAAA,IACR,CAAC;AAED,UAAM,aAAqB;AAAA,MACvB,MAAM,MAAM;AACR,eAAO,MAAA;AACP,eAAO,QAAQ,QAAA;AAAA,MACnB;AAAA,MACA,QAAQ,KAAKC,UAAS;AAClB,eAAO;AAAA,MACX;AAAA,MACA,OAAOA,UAAS;AACZ,eAAO;AAAA,MACX;AAAA,MACA,IAAI,OAAO;AACP,cAAM,OAAO,OAAO,QAAA;AACpB,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC3C,iBAAO,KAAK;AAAA,QAChB;AACA,eAAO,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,iBAAiB;AAAA,MACjB,WAAW,CAAC,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,KAAK,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE;AAAA,IAAA;AAG7D,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,aAAO,OAAO,QAAQ,MAAM,QAAQ,UAAU,MAAM;AAChD,gBAAQ,UAAU;AAAA,MACtB,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;;"}
@@ -0,0 +1,64 @@
1
+ import * as http from "node:http";
2
+ import "node:https";
3
+ function createHttpServer() {
4
+ return async (options) => {
5
+ const server = http.createServer(async (req, res) => {
6
+ const url = new URL(req.url, `http://${req.headers.host}`);
7
+ const request = new Request(url.toString(), {
8
+ method: req.method,
9
+ headers: req.headers,
10
+ body: ["GET", "HEAD"].includes(req.method) ? void 0 : new ReadableStream({
11
+ start(controller) {
12
+ req.on("data", (chunk) => controller.enqueue(chunk));
13
+ req.on("end", () => controller.close());
14
+ req.on("error", (err) => controller.error(err));
15
+ }
16
+ })
17
+ });
18
+ const response = await options.fetch(request, fauxServer);
19
+ res.statusCode = response.status;
20
+ response.headers.forEach((v, k) => res.setHeader(k, v));
21
+ if (response.body) {
22
+ for await (const chunk of response.body) {
23
+ res.write(chunk);
24
+ }
25
+ }
26
+ res.end();
27
+ });
28
+ const fauxServer = {
29
+ stop: () => {
30
+ server.close();
31
+ return Promise.resolve();
32
+ },
33
+ upgrade(req, options2) {
34
+ return false;
35
+ },
36
+ reload(options2) {
37
+ return fauxServer;
38
+ },
39
+ get port() {
40
+ const addr = server.address();
41
+ if (typeof addr === "object" && addr !== null) {
42
+ return addr.port;
43
+ }
44
+ return options.port;
45
+ },
46
+ hostname: options.hostname,
47
+ development: options.development,
48
+ pendingRequests: 0,
49
+ requestIP: (req) => null,
50
+ publish: () => 0,
51
+ subscriberCount: () => 0,
52
+ url: new URL(`http://${options.hostname}:${options.port}`)
53
+ };
54
+ return new Promise((resolve) => {
55
+ server.listen(options.port, options.hostname, () => {
56
+ resolve(fauxServer);
57
+ });
58
+ });
59
+ };
60
+ }
61
+ export {
62
+ createHttpServer
63
+ };
64
+ //# sourceMappingURL=server-adapter-CnQFr4P7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-adapter-CnQFr4P7.js","sources":["../src/plugins/server-adapter.ts"],"sourcesContent":["import type { Server } from \"bun\";\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport type { ServerFactory } from \"../types\";\n\n/**\n * Creates a server factory that uses the standard Node.js `http` module.\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpServer(): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any\n });\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // @ts-ignore\n for await (const chunk of response.body) {\n res.write(chunk);\n }\n }\n res.end();\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n return Promise.resolve(); // Bun.Server stop usually returns void but in type definition it might vary.\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`http://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n\n/**\n * Creates a server factory that uses the standard Node.js `https` module.\n * @param sslOptions - Node.js HTTPS options (key, cert, etc.)\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpsServer(sslOptions: https.ServerOptions): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = https.createServer(sslOptions, async (req, res) => {\n const url = new URL(req.url!, `https://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any\n });\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // @ts-ignore\n for await (const chunk of response.body) {\n res.write(chunk);\n }\n }\n res.end();\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`https://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n"],"names":["options"],"mappings":";;AASO,SAAS,mBAAkC;AAC9C,SAAO,OAAO,YAAkC;AAC5C,UAAM,SAAS,KAAK,aAAa,OAAO,KAAK,QAAQ;AACjD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC1D,YAAM,UAAU,IAAI,QAAQ,IAAI,YAAY;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,MAAM,CAAC,OAAO,MAAM,EAAE,SAAS,IAAI,MAAO,IAAI,SAAY,IAAI,eAAe;AAAA,UACzE,MAAM,YAAY;AACd,gBAAI,GAAG,QAAQ,CAAA,UAAS,WAAW,QAAQ,KAAK,CAAC;AACjD,gBAAI,GAAG,OAAO,MAAM,WAAW,OAAO;AACtC,gBAAI,GAAG,SAAS,CAAA,QAAO,WAAW,MAAM,GAAG,CAAC;AAAA,UAChD;AAAA,QAAA,CACH;AAAA,MAAA,CACJ;AAED,YAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,UAAU;AAExD,UAAI,aAAa,SAAS;AAC1B,eAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AAEtD,UAAI,SAAS,MAAM;AAEf,yBAAiB,SAAS,SAAS,MAAM;AACrC,cAAI,MAAM,KAAK;AAAA,QACnB;AAAA,MACJ;AACA,UAAI,IAAA;AAAA,IACR,CAAC;AAED,UAAM,aAAqB;AAAA,MACvB,MAAM,MAAM;AACR,eAAO,MAAA;AACP,eAAO,QAAQ,QAAA;AAAA,MACnB;AAAA,MACA,QAAQ,KAAKA,UAAS;AAClB,eAAO;AAAA,MACX;AAAA,MACA,OAAOA,UAAS;AACZ,eAAO;AAAA,MACX;AAAA,MACA,IAAI,OAAO;AACP,cAAM,OAAO,OAAO,QAAA;AACpB,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC3C,iBAAO,KAAK;AAAA,QAChB;AACA,eAAO,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,iBAAiB;AAAA,MACjB,WAAW,CAAC,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,KAAK,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE;AAAA,IAAA;AAG7D,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,aAAO,OAAO,QAAQ,MAAM,QAAQ,UAAU,MAAM;AAChD,gBAAQ,UAAU;AAAA,MACtB,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;"}
@@ -6,6 +6,7 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
6
6
  readonly applicationConfig: ShokupanConfig;
7
7
  openApiSpec?: any;
8
8
  private middleware;
9
+ private composedMiddleware?;
9
10
  get logger(): {
10
11
  verbose?: boolean;
11
12
  info?: (msg: string, props: Record<string, any>) => void;
@@ -45,4 +46,5 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
45
46
  * @returns The response to send.
46
47
  */
47
48
  fetch(req: Request, server?: import('bun').Server): Promise<Response>;
49
+ private handleRequest;
48
50
  }