opencroc 1.3.0 → 1.4.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/cli/commands/init.ts","../../src/cli/load-config.ts","../../src/parsers/model-parser.ts","../../src/parsers/controller-parser.ts","../../src/parsers/association-parser.ts","../../src/analyzers/api-chain-analyzer.ts","../../src/generators/er-diagram-generator.ts","../../src/generators/test-code-generator.ts","../../src/validators/schema-validator.ts","../../src/validators/semantic-validator.ts","../../src/validators/dryrun-validator.ts","../../src/validators/config-validator.ts","../../src/pipeline/index.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/test.ts","../../src/cli/commands/validate.ts","../../src/llm/openai.ts","../../src/llm/ollama.ts","../../src/llm/index.ts","../../src/self-healing/dialog-loop-runner.ts","../../src/self-healing/controlled-fixer.ts","../../src/self-healing/auto-fix-generator.ts","../../src/self-healing/index.ts","../../src/cli/commands/heal.ts","../../src/ci/index.ts","../../src/cli/commands/ci.ts","../../src/reporters/checklist-reporter.ts","../../src/reporters/workorder-reporter.ts","../../src/reporters/token-reporter.ts","../../src/reporters/index.ts","../../src/cli/commands/report.ts","../../src/dashboard/index.ts","../../src/cli/commands/dashboard.ts","../../src/runtime/playwright-config-generator.ts","../../src/runtime/global-setup-generator.ts","../../src/runtime/global-teardown-generator.ts","../../src/runtime/auth-setup-generator.ts","../../src/cli/commands/init-runtime.ts","../../src/orchestrator/index.ts","../../src/orchestrator/reporter.ts","../../src/cli/commands/run.ts","../../src/server/routes/project.ts","../../src/server/routes/agents.ts","../../src/server/croc-office.ts","../../src/server/index.ts","../../src/cli/commands/serve.ts","../../src/cli/index.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import chalk from 'chalk';\r\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\r\nimport { join } from 'node:path';\r\nimport { createInterface } from 'node:readline/promises';\r\nimport { stdin, stdout } from 'node:process';\r\n\r\nconst ADAPTERS = ['sequelize', 'typeorm', 'prisma'] as const;\r\nconst LLM_PROVIDERS = ['openai', 'zhipu', 'ollama', 'none'] as const;\r\n\r\nexport interface InitAnswers {\r\n backendRoot: string;\r\n adapter: string;\r\n llmProvider: string;\r\n outDir: string;\r\n}\r\n\r\nconst DEFAULTS: InitAnswers = {\r\n backendRoot: './backend',\r\n adapter: 'sequelize',\r\n llmProvider: 'openai',\r\n outDir: './opencroc-output',\r\n};\r\n\r\nexport function buildConfigContent(answers: InitAnswers): string {\r\n const llmBlock =\r\n answers.llmProvider === 'none'\r\n ? ''\r\n : `\r\n llm: {\r\n provider: '${answers.llmProvider}',${answers.llmProvider === 'ollama' ? '' : \"\\n // apiKey: process.env.OPENCROC_LLM_API_KEY,\"}\r\n model: '${answers.llmProvider === 'zhipu' ? 'glm-4' : answers.llmProvider === 'ollama' ? 'llama3' : 'gpt-4o-mini'}',\r\n },`;\r\n\r\n return `import { defineConfig } from 'opencroc';\r\n\r\nexport default defineConfig({\r\n backendRoot: '${answers.backendRoot}',\r\n adapter: '${answers.adapter}',${llmBlock}\r\n outDir: '${answers.outDir}',\r\n selfHealing: {\r\n enabled: true,\r\n maxIterations: 3,\r\n },\r\n});\r\n`;\r\n}\r\n\r\nasync function prompt(\r\n rl: ReturnType<typeof createInterface>,\r\n question: string,\r\n defaultValue: string,\r\n): Promise<string> {\r\n const answer = await rl.question(` ${question} ${chalk.gray(`(${defaultValue})`)}: `);\r\n return answer.trim() || defaultValue;\r\n}\r\n\r\nasync function promptChoice(\r\n rl: ReturnType<typeof createInterface>,\r\n question: string,\r\n choices: readonly string[],\r\n defaultValue: string,\r\n): Promise<string> {\r\n const list = choices\r\n .map((c) => (c === defaultValue ? chalk.underline(c) : c))\r\n .join(' / ');\r\n const answer = await rl.question(` ${question} [${list}]: `);\r\n const trimmed = answer.trim().toLowerCase();\r\n if (!trimmed) return defaultValue;\r\n return choices.find((c) => c.toLowerCase() === trimmed) || defaultValue;\r\n}\r\n\r\nasync function collectAnswers(): Promise<InitAnswers> {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n try {\r\n const backendRoot = await prompt(rl, 'Backend source root', DEFAULTS.backendRoot);\r\n const adapter = await promptChoice(rl, 'ORM adapter', ADAPTERS, DEFAULTS.adapter);\r\n const llmProvider = await promptChoice(rl, 'LLM provider', LLM_PROVIDERS, DEFAULTS.llmProvider);\r\n const outDir = await prompt(rl, 'Test output directory', DEFAULTS.outDir);\r\n return { backendRoot, adapter, llmProvider, outDir };\r\n } finally {\r\n rl.close();\r\n }\r\n}\r\n\r\nfunction writeProject(cwd: string, answers: InitAnswers): void {\r\n const configPath = join(cwd, 'opencroc.config.ts');\r\n writeFileSync(configPath, buildConfigContent(answers), 'utf-8');\r\n console.log(chalk.green(' ✓ Created opencroc.config.ts'));\r\n\r\n const outputDir = join(cwd, answers.outDir);\r\n if (!existsSync(outputDir)) {\r\n mkdirSync(outputDir, { recursive: true });\r\n console.log(chalk.green(` ✓ Created ${answers.outDir}/`));\r\n }\r\n}\r\n\r\nfunction printNextSteps(answers: InitAnswers): void {\r\n const needsKey = answers.llmProvider !== 'none' && answers.llmProvider !== 'ollama';\r\n console.log('');\r\n console.log(chalk.cyan(' Next steps:'));\r\n let step = 1;\r\n console.log(` ${step++}. Review opencroc.config.ts`);\r\n if (needsKey) {\r\n console.log(` ${step++}. Set OPENCROC_LLM_API_KEY environment variable`);\r\n }\r\n console.log(` ${step++}. npx opencroc generate --all`);\r\n console.log(` ${step}. npx opencroc test`);\r\n}\r\n\r\nexport async function initProject(opts?: { yes?: boolean }): Promise<void> {\r\n const cwd = process.cwd();\r\n const configPath = join(cwd, 'opencroc.config.ts');\r\n\r\n if (existsSync(configPath)) {\r\n console.log(chalk.yellow('\\n ⚠ opencroc.config.ts already exists. Skipping.\\n'));\r\n return;\r\n }\r\n\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Project Setup\\n'));\r\n\r\n const answers = opts?.yes ? { ...DEFAULTS } : await collectAnswers();\r\n\r\n console.log('');\r\n writeProject(cwd, answers);\r\n printNextSteps(answers);\r\n console.log('');\r\n}\r\n","import { cosmiconfig } from 'cosmiconfig';\r\nimport type { OpenCrocConfig } from '../types.js';\r\n\r\nconst MODULE_NAME = 'opencroc';\r\n\r\nconst SEARCH_PLACES = [\r\n 'opencroc.config.ts',\r\n 'opencroc.config.js',\r\n 'opencroc.config.json',\r\n '.opencrocrc.json',\r\n 'package.json',\r\n];\r\n\r\nexport interface LoadConfigResult {\r\n config: OpenCrocConfig;\r\n filepath: string;\r\n}\r\n\r\nexport async function loadConfig(cwd?: string): Promise<LoadConfigResult> {\r\n const explorer = cosmiconfig(MODULE_NAME, {\r\n searchPlaces: SEARCH_PLACES,\r\n ...(cwd ? { stopDir: cwd } : {}),\r\n });\r\n\r\n const result = cwd ? await explorer.search(cwd) : await explorer.search();\r\n\r\n if (!result || result.isEmpty) {\r\n throw new Error(\r\n 'No opencroc config found. Run `opencroc init` to create one.',\r\n );\r\n }\r\n\r\n const config: OpenCrocConfig =\r\n result.config?.default ?? result.config;\r\n\r\n if (!config.backendRoot) {\r\n throw new Error(\r\n `Invalid config in ${result.filepath}: \"backendRoot\" is required.`,\r\n );\r\n }\r\n\r\n return { config, filepath: result.filepath };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport {\r\n Project,\r\n SyntaxKind,\r\n type CallExpression,\r\n type ObjectLiteralExpression,\r\n type PropertyAssignment,\r\n type Node,\r\n} from 'ts-morph';\r\nimport type { TableSchema, FieldSchema, IndexSchema } from '../types.js';\r\n\r\nexport interface ModelParser {\r\n parseFile(filePath: string): Promise<TableSchema | null>;\r\n parseDirectory(dirPath: string): Promise<TableSchema[]>;\r\n}\r\n\r\n/**\r\n * Parse a single Sequelize Model file and extract TableSchema.\r\n */\r\nexport function parseModelFile(filePath: string): TableSchema | null {\r\n const absolutePath = path.resolve(filePath);\r\n if (!fs.existsSync(absolutePath)) return null;\r\n\r\n const project = new Project({ compilerOptions: { strict: false } });\r\n const sourceFile = project.addSourceFileAtPath(absolutePath);\r\n\r\n const initCall = findInitCall(sourceFile);\r\n if (!initCall) return null;\r\n\r\n const args = initCall.getArguments();\r\n if (args.length < 2) return null;\r\n\r\n const fields = parseFieldDefinitions(args[0]);\r\n const { tableName, indexes } = parseOptions(args[1]);\r\n\r\n if (!tableName) return null;\r\n\r\n return { tableName, fields, indexes };\r\n}\r\n\r\n/**\r\n * Batch parse all Model files in a directory.\r\n */\r\nexport function parseModuleModels(modelDir: string): TableSchema[] {\r\n const absoluteDir = path.resolve(modelDir);\r\n if (!fs.existsSync(absoluteDir)) return [];\r\n\r\n const files = fs.readdirSync(absoluteDir).filter((f) =>\r\n f.endsWith('.ts') &&\r\n !f.endsWith('.test.ts') &&\r\n !f.endsWith('.spec.ts') &&\r\n f !== 'index.ts' &&\r\n f !== 'associations.ts',\r\n );\r\n\r\n const schemas: TableSchema[] = [];\r\n for (const file of files) {\r\n try {\r\n const schema = parseModelFile(path.join(absoluteDir, file));\r\n if (schema) schemas.push(schema);\r\n } catch {\r\n // skip unparseable files\r\n }\r\n }\r\n return schemas;\r\n}\r\n\r\nfunction findInitCall(sourceFile: Node): CallExpression | null {\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n for (const call of calls) {\r\n const expr = call.getExpression();\r\n if (expr.getKind() === SyntaxKind.PropertyAccessExpression) {\r\n const propAccess = expr.asKindOrThrow(SyntaxKind.PropertyAccessExpression);\r\n if (propAccess.getName() === 'init') return call;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction parseFieldDefinitions(fieldsNode: Node): FieldSchema[] {\r\n const fields: FieldSchema[] = [];\r\n if (fieldsNode.getKind() !== SyntaxKind.ObjectLiteralExpression) return fields;\r\n\r\n const objLiteral = fieldsNode as ObjectLiteralExpression;\r\n for (const prop of objLiteral.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const propAssign = prop as PropertyAssignment;\r\n const initializer = propAssign.getInitializer();\r\n if (!initializer || initializer.getKind() !== SyntaxKind.ObjectLiteralExpression) continue;\r\n fields.push(parseFieldObject(propAssign.getName(), initializer as ObjectLiteralExpression));\r\n }\r\n return fields;\r\n}\r\n\r\nfunction parseFieldObject(fieldName: string, fieldObj: ObjectLiteralExpression): FieldSchema {\r\n const field: FieldSchema = { name: fieldName, type: 'STRING', allowNull: true, primaryKey: false };\r\n\r\n for (const prop of fieldObj.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const propAssign = prop as PropertyAssignment;\r\n const key = propAssign.getName();\r\n const init = propAssign.getInitializer();\r\n if (!init) continue;\r\n\r\n switch (key) {\r\n case 'type': field.type = extractDataType(init); break;\r\n case 'allowNull': field.allowNull = init.getText().trim() === 'true'; break;\r\n case 'primaryKey': field.primaryKey = init.getText().trim() === 'true'; break;\r\n case 'defaultValue': field.defaultValue = extractDefaultValue(init); break;\r\n }\r\n }\r\n return field;\r\n}\r\n\r\nfunction extractDataType(node: Node): string {\r\n const text = node.getText().trim();\r\n const callMatch = text.match(/^DataTypes\\.(\\w+)\\((.+)\\)$/);\r\n if (callMatch) return `${callMatch[1]}(${callMatch[2]})`;\r\n const propMatch = text.match(/^DataTypes\\.(\\w+)$/);\r\n if (propMatch) return propMatch[1];\r\n return text;\r\n}\r\n\r\nfunction extractDefaultValue(node: Node): unknown {\r\n const text = node.getText().trim();\r\n if (text === 'DataTypes.NOW') return 'DataTypes.NOW';\r\n if ((text.startsWith(\"'\") && text.endsWith(\"'\")) || (text.startsWith('\"') && text.endsWith('\"')))\r\n return text.slice(1, -1);\r\n if (/^-?\\d+(\\.\\d+)?$/.test(text)) return Number(text);\r\n if (text === 'true') return true;\r\n if (text === 'false') return false;\r\n if (text === 'null') return null;\r\n return text;\r\n}\r\n\r\nfunction parseOptions(optionsNode: Node): { tableName: string | null; indexes: IndexSchema[] } {\r\n let tableName: string | null = null;\r\n let indexes: IndexSchema[] = [];\r\n\r\n if (optionsNode.getKind() !== SyntaxKind.ObjectLiteralExpression) return { tableName, indexes };\r\n\r\n const objLiteral = optionsNode as ObjectLiteralExpression;\r\n for (const prop of objLiteral.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const propAssign = prop as PropertyAssignment;\r\n const key = propAssign.getName();\r\n const init = propAssign.getInitializer();\r\n if (!init) continue;\r\n\r\n if (key === 'tableName') tableName = extractStringValue(init);\r\n if (key === 'indexes') indexes = parseIndexes(init);\r\n }\r\n return { tableName, indexes };\r\n}\r\n\r\nfunction extractStringValue(node: Node): string | null {\r\n const text = node.getText().trim();\r\n if ((text.startsWith(\"'\") && text.endsWith(\"'\")) || (text.startsWith('\"') && text.endsWith('\"')))\r\n return text.slice(1, -1);\r\n return null;\r\n}\r\n\r\nfunction parseIndexes(node: Node): IndexSchema[] {\r\n if (node.getKind() !== SyntaxKind.ArrayLiteralExpression) return [];\r\n const arr = node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);\r\n const indexes: IndexSchema[] = [];\r\n for (const el of arr.getElements()) {\r\n if (el.getKind() !== SyntaxKind.ObjectLiteralExpression) continue;\r\n const idx = parseIndexObject(el as ObjectLiteralExpression);\r\n if (idx) indexes.push(idx);\r\n }\r\n return indexes;\r\n}\r\n\r\nfunction parseIndexObject(obj: ObjectLiteralExpression): IndexSchema | null {\r\n let name = '';\r\n let fields: string[] = [];\r\n let unique = false;\r\n\r\n for (const prop of obj.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const pa = prop as PropertyAssignment;\r\n const init = pa.getInitializer();\r\n if (!init) continue;\r\n switch (pa.getName()) {\r\n case 'name': name = extractStringValue(init) || ''; break;\r\n case 'fields': fields = extractStringArray(init); break;\r\n case 'unique': unique = init.getText().trim() === 'true'; break;\r\n }\r\n }\r\n if (!name || fields.length === 0) return null;\r\n return { name, fields, unique };\r\n}\r\n\r\nfunction extractStringArray(node: Node): string[] {\r\n if (node.getKind() !== SyntaxKind.ArrayLiteralExpression) return [];\r\n const arr = node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);\r\n return arr.getElements()\r\n .map((el) => el.getText().trim())\r\n .filter((t) => (t.startsWith(\"'\") || t.startsWith('\"')))\r\n .map((t) => t.slice(1, -1));\r\n}\r\n\r\nexport function createModelParser(): ModelParser {\r\n return {\r\n async parseFile(filePath: string) {\r\n return parseModelFile(filePath);\r\n },\r\n async parseDirectory(dirPath: string) {\r\n return parseModuleModels(dirPath);\r\n },\r\n };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport {\r\n Project,\r\n SyntaxKind,\r\n type CallExpression,\r\n type SourceFile,\r\n Node,\r\n type PropertyAccessExpression,\r\n type Decorator,\r\n type MethodDeclaration,\r\n type ObjectLiteralExpression,\r\n} from 'ts-morph';\r\nimport type { ApiEndpoint } from '../types.js';\r\n\r\nexport interface ControllerParser {\r\n parseFile(filePath: string): Promise<ApiEndpoint[]>;\r\n parseDirectory(dirPath: string): Promise<ApiEndpoint[]>;\r\n}\r\n\r\nconst HTTP_METHODS = new Set(['get', 'post', 'put', 'delete', 'patch']);\r\n\r\nconst METHOD_MAP: Record<string, string> = {\r\n get: 'GET', post: 'POST', put: 'PUT', delete: 'DELETE', patch: 'PATCH',\r\n};\r\n\r\nconst NEST_HTTP_DECORATORS = new Set(['get', 'post', 'put', 'delete', 'patch']);\r\n\r\n/**\r\n * Parse a single Controller file and extract API endpoints.\r\n */\r\nexport function parseControllerFile(filePath: string): ApiEndpoint[] {\r\n const absolutePath = path.resolve(filePath);\r\n if (!fs.existsSync(absolutePath)) return [];\r\n\r\n try {\r\n const project = new Project({ compilerOptions: { strict: false } });\r\n const sourceFile = project.addSourceFileAtPath(absolutePath);\r\n\r\n const endpoints: ApiEndpoint[] = [];\r\n endpoints.push(...extractRouterCalls(sourceFile));\r\n endpoints.push(...extractBaseCrudRoutes(sourceFile));\r\n endpoints.push(...extractNestControllerRoutes(sourceFile));\r\n\r\n return deduplicateEndpoints(endpoints);\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Parse all Controller files in a directory.\r\n */\r\nexport function parseControllerDirectory(dirPath: string): ApiEndpoint[] {\r\n const absoluteDir = path.resolve(dirPath);\r\n if (!fs.existsSync(absoluteDir)) return [];\r\n\r\n const files = fs.readdirSync(absoluteDir).filter((f) =>\r\n f.endsWith('.ts') &&\r\n !f.endsWith('.test.ts') &&\r\n !f.endsWith('.spec.ts') &&\r\n f !== 'index.ts',\r\n );\r\n\r\n const endpoints: ApiEndpoint[] = [];\r\n for (const file of files) {\r\n endpoints.push(...parseControllerFile(path.join(absoluteDir, file)));\r\n }\r\n return deduplicateEndpoints(endpoints);\r\n}\r\n\r\nfunction extractRouterCalls(sourceFile: SourceFile): ApiEndpoint[] {\r\n const endpoints: ApiEndpoint[] = [];\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expr = call.getExpression();\r\n if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) continue;\r\n\r\n const propAccess = expr as PropertyAccessExpression;\r\n const methodName = propAccess.getName().toLowerCase();\r\n if (!HTTP_METHODS.has(methodName)) continue;\r\n\r\n const objectText = propAccess.getExpression().getText().trim();\r\n if (!isRouterLike(objectText)) continue;\r\n\r\n const args = call.getArguments();\r\n if (args.length === 0) continue;\r\n\r\n const routePath = resolveRoutePath(args[0], sourceFile);\r\n if (!routePath) continue;\r\n\r\n endpoints.push({\r\n method: METHOD_MAP[methodName],\r\n path: routePath,\r\n pathParams: extractPathParams(routePath),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: extractDescription(call),\r\n });\r\n }\r\n return endpoints;\r\n}\r\n\r\nfunction isRouterLike(text: string): boolean {\r\n return text === 'router' || text === 'this.router';\r\n}\r\n\r\nfunction extractBaseCrudRoutes(sourceFile: SourceFile): ApiEndpoint[] {\r\n const endpoints: ApiEndpoint[] = [];\r\n\r\n let isBaseCrud = false;\r\n for (const cls of sourceFile.getClasses()) {\r\n const heritage = cls.getExtends();\r\n if (heritage?.getText().includes('BaseCrudController')) {\r\n isBaseCrud = true;\r\n break;\r\n }\r\n }\r\n if (!isBaseCrud) return endpoints;\r\n\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n let resourcePath: string | null = null;\r\n\r\n for (const call of calls) {\r\n const exprText = call.getExpression().getText();\r\n if (\r\n (exprText === 'super.registerRoutes' || exprText.endsWith('.registerRoutes')) &&\r\n !exprText.includes('Custom')\r\n ) {\r\n const args = call.getArguments();\r\n if (args.length >= 2) resourcePath = extractStringLiteral(args[1]);\r\n }\r\n }\r\n if (!resourcePath) return endpoints;\r\n\r\n const basePath = `/v1/:tenantId/${resourcePath}`;\r\n const crudRoutes: Array<{ method: string; path: string; desc: string }> = [\r\n { method: 'GET', path: basePath, desc: `List ${resourcePath}` },\r\n { method: 'GET', path: `${basePath}/:id`, desc: `Get ${resourcePath} by ID` },\r\n { method: 'POST', path: basePath, desc: `Create ${resourcePath}` },\r\n { method: 'PUT', path: `${basePath}/:id`, desc: `Update ${resourcePath}` },\r\n { method: 'DELETE', path: `${basePath}/:id`, desc: `Delete ${resourcePath}` },\r\n { method: 'POST', path: `${basePath}/batch-delete`, desc: `Batch delete ${resourcePath}` },\r\n ];\r\n\r\n for (const route of crudRoutes) {\r\n endpoints.push({\r\n method: route.method,\r\n path: route.path,\r\n pathParams: extractPathParams(route.path),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: route.desc,\r\n });\r\n }\r\n return endpoints;\r\n}\r\n\r\nfunction extractNestControllerRoutes(sourceFile: SourceFile): ApiEndpoint[] {\r\n const endpoints: ApiEndpoint[] = [];\r\n\r\n for (const cls of sourceFile.getClasses()) {\r\n const controllerDecorator = cls.getDecorators().find((d) => d.getName().toLowerCase() === 'controller');\r\n if (!controllerDecorator) continue;\r\n\r\n const controllerBasePath = normalizeRoutePath(extractDecoratorPath(controllerDecorator, sourceFile) ?? '');\r\n\r\n for (const methodDecl of cls.getMethods()) {\r\n const requestMapping = extractRequestMapping(methodDecl, sourceFile);\r\n if (requestMapping) {\r\n const fullPath = joinRoutePath(controllerBasePath, normalizeRoutePath(requestMapping.path));\r\n endpoints.push({\r\n method: requestMapping.method,\r\n path: fullPath,\r\n pathParams: extractPathParams(fullPath),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: extractMethodDescription(methodDecl),\r\n });\r\n continue;\r\n }\r\n\r\n const httpDecorator = methodDecl.getDecorators().find((d) => NEST_HTTP_DECORATORS.has(d.getName().toLowerCase()));\r\n if (!httpDecorator) continue;\r\n\r\n const methodName = httpDecorator.getName().toLowerCase();\r\n const method = METHOD_MAP[methodName];\r\n if (!method) continue;\r\n\r\n const methodPath = normalizeRoutePath(extractDecoratorPath(httpDecorator, sourceFile) ?? '');\r\n const fullPath = joinRoutePath(controllerBasePath, methodPath);\r\n\r\n endpoints.push({\r\n method,\r\n path: fullPath,\r\n pathParams: extractPathParams(fullPath),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: extractMethodDescription(methodDecl),\r\n });\r\n }\r\n }\r\n\r\n return endpoints;\r\n}\r\n\r\n/**\r\n * Infer related database table names from Service file imports.\r\n */\r\nexport function inferRelatedTables(servicePaths: string[]): string[] {\r\n const tables = new Set<string>();\r\n for (const sp of servicePaths) {\r\n const absolutePath = path.resolve(sp);\r\n if (!fs.existsSync(absolutePath)) continue;\r\n try {\r\n const content = fs.readFileSync(absolutePath, 'utf-8');\r\n const importRegex = /import\\s*\\{([^}]+)\\}\\s*from\\s*['\"][^'\"]*models[^'\"]*['\"]/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = importRegex.exec(content)) !== null) {\r\n const names = match[1].split(',').map((s) => s.trim());\r\n for (const name of names) {\r\n const cleanName = name.replace(/\\s+as\\s+\\w+/, '').trim();\r\n if (cleanName) tables.add(pascalToSnake(cleanName));\r\n }\r\n }\r\n } catch {\r\n // skip\r\n }\r\n }\r\n return Array.from(tables);\r\n}\r\n\r\nfunction resolveRoutePath(node: Node, sourceFile: SourceFile): string | null {\r\n const kind = node.getKind();\r\n if (kind === SyntaxKind.StringLiteral) return node.getText().slice(1, -1);\r\n if (kind === SyntaxKind.TemplateExpression || kind === SyntaxKind.NoSubstitutionTemplateLiteral) {\r\n return resolveTemplateLiteral(node, sourceFile);\r\n }\r\n if (kind === SyntaxKind.Identifier) {\r\n return resolveVariableValue(sourceFile, node.getText().trim());\r\n }\r\n return null;\r\n}\r\n\r\nfunction extractDecoratorPath(decorator: Decorator, sourceFile: SourceFile): string | null {\r\n const args = decorator.getArguments();\r\n if (args.length === 0) return '';\r\n\r\n const firstArg = args[0];\r\n if (firstArg.getKind() === SyntaxKind.ObjectLiteralExpression) {\r\n return extractPathFromObjectLiteral(firstArg as ObjectLiteralExpression, sourceFile);\r\n }\r\n\r\n return resolveRoutePath(firstArg, sourceFile);\r\n}\r\n\r\nfunction extractPathFromObjectLiteral(node: ObjectLiteralExpression, sourceFile: SourceFile): string | null {\r\n const pathProp = node.getProperty('path');\r\n if (!pathProp || !Node.isPropertyAssignment(pathProp)) return null;\r\n const initializer = pathProp.getInitializer();\r\n if (!initializer) return null;\r\n return resolveRoutePath(initializer, sourceFile);\r\n}\r\n\r\nfunction extractRequestMapping(\r\n methodDecl: MethodDeclaration,\r\n sourceFile: SourceFile,\r\n): { method: string; path: string } | null {\r\n const decorator = methodDecl.getDecorators().find((d) => d.getName().toLowerCase() === 'requestmapping');\r\n if (!decorator) return null;\r\n\r\n const args = decorator.getArguments();\r\n if (args.length === 0) return null;\r\n const firstArg = args[0];\r\n if (firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) return null;\r\n\r\n const obj = firstArg as ObjectLiteralExpression;\r\n const methodProp = obj.getProperty('method');\r\n let method = 'GET';\r\n if (methodProp && Node.isPropertyAssignment(methodProp)) {\r\n const init = methodProp.getInitializer();\r\n const methodText = init?.getText() || '';\r\n const normalized = methodText\r\n .replace(/['\"`]/g, '')\r\n .split('.')\r\n .pop()\r\n ?.toUpperCase();\r\n if (normalized && ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(normalized)) {\r\n method = normalized;\r\n }\r\n }\r\n\r\n const pathValue = extractPathFromObjectLiteral(obj, sourceFile) ?? '';\r\n return { method, path: pathValue };\r\n}\r\n\r\nfunction normalizeRoutePath(routePath: string): string {\r\n const cleaned = routePath.trim();\r\n if (!cleaned || cleaned === '/') return '';\r\n return `/${cleaned.replace(/^\\/+|\\/+$/g, '')}`;\r\n}\r\n\r\nfunction joinRoutePath(basePath: string, childPath: string): string {\r\n const joined = `${basePath}${childPath}`.replace(/\\/+/g, '/');\r\n return joined || '/';\r\n}\r\n\r\nfunction extractMethodDescription(methodDecl: MethodDeclaration): string {\r\n const docs = methodDecl.getJsDocs();\r\n if (docs.length > 0) {\r\n const desc = docs[0].getDescription().trim();\r\n if (desc) return desc;\r\n }\r\n return '';\r\n}\r\n\r\nfunction resolveTemplateLiteral(node: Node, sourceFile: SourceFile): string {\r\n let result = node.getText().slice(1, -1);\r\n result = result.replace(/\\$\\{([^}]+)\\}/g, (_match, expr: string) => {\r\n const resolved = resolveVariableValue(sourceFile, expr.trim());\r\n return resolved || `{${expr.trim()}}`;\r\n });\r\n return result;\r\n}\r\n\r\nfunction resolveVariableValue(sourceFile: SourceFile, varName: string): string | null {\r\n for (const decl of sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {\r\n if (decl.getName() === varName) {\r\n const init = decl.getInitializer();\r\n if (!init) continue;\r\n const t = init.getText().trim();\r\n if ((t.startsWith(\"'\") && t.endsWith(\"'\")) || (t.startsWith('\"') && t.endsWith('\"')))\r\n return t.slice(1, -1);\r\n if (t.startsWith('`') && t.endsWith('`'))\r\n return resolveTemplateLiteral(init, sourceFile);\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction extractPathParams(routePath: string): string[] {\r\n const params: string[] = [];\r\n const regex = /:(\\w+)/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = regex.exec(routePath)) !== null) params.push(match[1]);\r\n return params;\r\n}\r\n\r\nfunction extractDescription(call: CallExpression): string {\r\n let current: Node = call;\r\n while (\r\n current.getParent() &&\r\n current.getParent()!.getKind() !== SyntaxKind.SourceFile &&\r\n current.getParent()!.getKind() !== SyntaxKind.Block\r\n ) {\r\n current = current.getParent()!;\r\n }\r\n const fullText = current.getFullText();\r\n const leadingText = fullText.substring(0, fullText.indexOf(current.getText()));\r\n const jsdocMatch = leadingText.match(/\\/\\*\\*[\\s\\S]*?\\*\\s+(.+?)(?:\\n|\\*\\/)/);\r\n if (jsdocMatch) return jsdocMatch[1].replace(/^\\*\\s*/, '').trim();\r\n const lineMatch = leadingText.match(/\\/\\/\\s*(.+)/);\r\n if (lineMatch) return lineMatch[1].trim();\r\n return '';\r\n}\r\n\r\nfunction extractStringLiteral(node: Node): string | null {\r\n const t = node.getText().trim();\r\n if ((t.startsWith(\"'\") && t.endsWith(\"'\")) || (t.startsWith('\"') && t.endsWith('\"')))\r\n return t.slice(1, -1);\r\n return null;\r\n}\r\n\r\nfunction pascalToSnake(name: string): string {\r\n return name.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');\r\n}\r\n\r\nfunction deduplicateEndpoints(endpoints: ApiEndpoint[]): ApiEndpoint[] {\r\n const seen = new Map<string, ApiEndpoint>();\r\n for (const ep of endpoints) {\r\n const key = `${ep.method}:${ep.path}`;\r\n if (!seen.has(key)) {\r\n seen.set(key, ep);\r\n } else {\r\n const existing = seen.get(key)!;\r\n const merged = new Set([...existing.relatedTables, ...ep.relatedTables]);\r\n existing.relatedTables = Array.from(merged);\r\n if (!existing.description && ep.description) existing.description = ep.description;\r\n }\r\n }\r\n return Array.from(seen.values());\r\n}\r\n\r\nexport function createControllerParser(): ControllerParser {\r\n return {\r\n async parseFile(filePath: string) {\r\n return parseControllerFile(filePath);\r\n },\r\n async parseDirectory(dirPath: string) {\r\n return parseControllerDirectory(dirPath);\r\n },\r\n };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport {\r\n Project,\r\n SyntaxKind,\r\n type ObjectLiteralExpression,\r\n type PropertyAssignment,\r\n type SourceFile,\r\n} from 'ts-morph';\r\nimport type { ForeignKeyRelation } from '../types.js';\r\nimport { parseModelFile } from './model-parser.js';\r\n\r\nexport interface AssociationParser {\r\n parseFile(filePath: string): Promise<ForeignKeyRelation[]>;\r\n}\r\n\r\ninterface RawAssociation {\r\n sourceClass: string;\r\n targetClass: string;\r\n foreignKey: string;\r\n type: 'hasMany' | 'belongsTo' | 'hasOne';\r\n importPath?: string;\r\n}\r\n\r\n/**\r\n * Parse an associations.ts file to extract all foreign key relations.\r\n */\r\nexport function parseAssociationFile(\r\n filePath: string,\r\n classToTableMap?: Map<string, string>,\r\n moduleTablePrefix?: string,\r\n): ForeignKeyRelation[] {\r\n const absolutePath = path.resolve(filePath);\r\n if (!fs.existsSync(absolutePath)) return [];\r\n\r\n const project = new Project({ compilerOptions: { strict: false } });\r\n const sourceFile = project.addSourceFileAtPath(absolutePath);\r\n\r\n const importPathMap = collectImportPaths(sourceFile);\r\n const rawAssociations = extractAssociationCalls(sourceFile, importPathMap);\r\n if (rawAssociations.length === 0) return [];\r\n\r\n return deduplicateRelations(rawAssociations, classToTableMap, moduleTablePrefix);\r\n}\r\n\r\n/**\r\n * Build className → tableName map from Model files in a directory.\r\n */\r\nexport function buildClassToTableMap(modelDir: string): Map<string, string> {\r\n const map = new Map<string, string>();\r\n const absoluteDir = path.resolve(modelDir);\r\n if (!fs.existsSync(absoluteDir)) return map;\r\n\r\n const files = fs.readdirSync(absoluteDir).filter((f) =>\r\n f.endsWith('.ts') &&\r\n !f.endsWith('.test.ts') &&\r\n !f.endsWith('.spec.ts') &&\r\n f !== 'index.ts' &&\r\n f !== 'associations.ts',\r\n );\r\n\r\n for (const file of files) {\r\n try {\r\n const schema = parseModelFile(path.join(absoluteDir, file));\r\n if (schema) {\r\n const className = file.replace('.ts', '');\r\n map.set(className, schema.tableName);\r\n }\r\n } catch {\r\n // skip\r\n }\r\n }\r\n return map;\r\n}\r\n\r\nfunction collectImportPaths(sourceFile: SourceFile): Map<string, string> {\r\n const map = new Map<string, string>();\r\n for (const decl of sourceFile.getImportDeclarations()) {\r\n const moduleSpecifier = decl.getModuleSpecifierValue();\r\n for (const named of decl.getNamedImports()) {\r\n map.set(named.getName(), moduleSpecifier);\r\n }\r\n }\r\n return map;\r\n}\r\n\r\nfunction extractAssociationCalls(\r\n sourceFile: SourceFile,\r\n importPathMap: Map<string, string>,\r\n): RawAssociation[] {\r\n const associations: RawAssociation[] = [];\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expr = call.getExpression();\r\n if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) continue;\r\n\r\n const propAccess = expr.asKindOrThrow(SyntaxKind.PropertyAccessExpression);\r\n const methodName = propAccess.getName();\r\n if (methodName !== 'hasMany' && methodName !== 'belongsTo' && methodName !== 'hasOne') continue;\r\n\r\n const sourceClass = propAccess.getExpression().getText().trim();\r\n const args = call.getArguments();\r\n if (args.length < 1) continue;\r\n\r\n const targetClass = args[0].getText().trim();\r\n let foreignKey = '';\r\n\r\n if (args.length >= 2 && args[1].getKind() === SyntaxKind.ObjectLiteralExpression) {\r\n foreignKey = extractStringProperty(args[1] as ObjectLiteralExpression, 'foreignKey');\r\n }\r\n\r\n associations.push({\r\n sourceClass,\r\n targetClass,\r\n foreignKey,\r\n type: methodName as RawAssociation['type'],\r\n importPath: importPathMap.get(targetClass),\r\n });\r\n }\r\n return associations;\r\n}\r\n\r\nfunction extractStringProperty(obj: ObjectLiteralExpression, propertyName: string): string {\r\n for (const prop of obj.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const pa = prop as PropertyAssignment;\r\n if (pa.getName() !== propertyName) continue;\r\n const init = pa.getInitializer();\r\n if (!init) continue;\r\n const text = init.getText().trim();\r\n if ((text.startsWith(\"'\") && text.endsWith(\"'\")) || (text.startsWith('\"') && text.endsWith('\"')))\r\n return text.slice(1, -1);\r\n return text;\r\n }\r\n return '';\r\n}\r\n\r\nexport function classNameToTableName(className: string): string {\r\n return className.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');\r\n}\r\n\r\nfunction resolveTableName(className: string, classToTableMap?: Map<string, string>): string {\r\n if (classToTableMap?.has(className)) return classToTableMap.get(className)!;\r\n return classNameToTableName(className);\r\n}\r\n\r\nfunction isCrossModuleRef(\r\n targetTableName: string,\r\n importPath: string | undefined,\r\n moduleTablePrefix?: string,\r\n): boolean {\r\n if (moduleTablePrefix) return !targetTableName.startsWith(moduleTablePrefix);\r\n if (importPath) {\r\n const upLevels = (importPath.match(/\\.\\.\\//g) || []).length;\r\n return upLevels >= 2;\r\n }\r\n return false;\r\n}\r\n\r\nfunction deduplicateRelations(\r\n rawAssociations: RawAssociation[],\r\n classToTableMap?: Map<string, string>,\r\n moduleTablePrefix?: string,\r\n): ForeignKeyRelation[] {\r\n const seen = new Map<string, ForeignKeyRelation>();\r\n\r\n for (const raw of rawAssociations) {\r\n const sourceTable = resolveTableName(raw.sourceClass, classToTableMap);\r\n const targetTable = resolveTableName(raw.targetClass, classToTableMap);\r\n const crossModule = isCrossModuleRef(targetTable, raw.importPath, moduleTablePrefix);\r\n\r\n let parentTable: string;\r\n let childTable: string;\r\n let cardinality: ForeignKeyRelation['cardinality'];\r\n\r\n switch (raw.type) {\r\n case 'hasMany':\r\n parentTable = sourceTable; childTable = targetTable; cardinality = '1:N'; break;\r\n case 'belongsTo':\r\n parentTable = targetTable; childTable = sourceTable; cardinality = 'N:1'; break;\r\n case 'hasOne':\r\n parentTable = sourceTable; childTable = targetTable; cardinality = '1:1'; break;\r\n }\r\n\r\n const dedupeKey = `${parentTable}|${childTable}|${raw.foreignKey}`;\r\n if (seen.has(dedupeKey)) {\r\n const existing = seen.get(dedupeKey)!;\r\n if (existing.cardinality === 'N:1' && (cardinality === '1:N' || cardinality === '1:1')) {\r\n seen.set(dedupeKey, {\r\n sourceTable: parentTable, sourceField: 'id',\r\n targetTable: childTable, targetField: raw.foreignKey,\r\n cardinality, isCrossModule: crossModule || existing.isCrossModule,\r\n });\r\n }\r\n } else {\r\n seen.set(dedupeKey, {\r\n sourceTable: parentTable, sourceField: 'id',\r\n targetTable: childTable, targetField: raw.foreignKey,\r\n cardinality, isCrossModule: crossModule,\r\n });\r\n }\r\n }\r\n return Array.from(seen.values());\r\n}\r\n\r\nexport function createAssociationParser(): AssociationParser {\r\n return {\r\n async parseFile(filePath: string) {\r\n return parseAssociationFile(filePath);\r\n },\r\n };\r\n}\r\n","import type {\r\n ApiEndpoint,\r\n ApiDependency,\r\n ApiChainAnalysisResult,\r\n DirectedAcyclicGraph,\r\n} from '../types.js';\r\n\r\nconst EXCLUDED_PARAMS = new Set(['tenantId']);\r\n\r\nconst enum Color { WHITE = 0, GRAY = 1, BLACK = 2 }\r\n\r\nfunction toNodeKey(endpoint: ApiEndpoint): string {\r\n return `${endpoint.method} ${endpoint.path}`;\r\n}\r\n\r\nfunction paramToResourceHint(param: string): string {\r\n const stripped = param.endsWith('Id') ? param.slice(0, -2) : param;\r\n return stripped.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');\r\n}\r\n\r\nfunction postProducesResource(postEndpoint: ApiEndpoint, resourceHint: string): boolean {\r\n const segments = postEndpoint.path.split('/').filter((s) => s && !s.startsWith(':'));\r\n if (segments.length === 0) return false;\r\n\r\n const lastSegment = segments[segments.length - 1].toLowerCase();\r\n if (lastSegment.includes(resourceHint)) return true;\r\n\r\n const parts = lastSegment.split('-');\r\n if (parts.some((p) => p === resourceHint || p.startsWith(resourceHint))) return true;\r\n\r\n if (resourceHint.length <= 4) {\r\n const abbreviation = parts.map((p) => p[0]).join('');\r\n if (abbreviation.startsWith(resourceHint)) return true;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Infer API dependencies via path parameter matching.\r\n * POST endpoints produce IDs; GET/PUT/DELETE endpoints consume them.\r\n */\r\nexport function inferDependencies(endpoints: ApiEndpoint[]): ApiDependency[] {\r\n const dependencies: ApiDependency[] = [];\r\n const postEndpoints = endpoints.filter((ep) => ep.method === 'POST');\r\n\r\n for (const consumer of endpoints) {\r\n const consumedParams = consumer.pathParams.filter((p) => !EXCLUDED_PARAMS.has(p));\r\n if (consumedParams.length === 0) continue;\r\n\r\n for (const param of consumedParams) {\r\n if (param === 'id') {\r\n const basePath = consumer.path.replace(/\\/:id(\\/.*)?$/, '');\r\n const producer = postEndpoints.find((ep) => ep.path === basePath);\r\n if (producer && toNodeKey(producer) !== toNodeKey(consumer)) {\r\n dependencies.push({ from: consumer, to: producer, paramMapping: { [`:${param}`]: 'response.data.id' } });\r\n }\r\n continue;\r\n }\r\n\r\n const resourceHint = paramToResourceHint(param);\r\n if (!resourceHint) continue;\r\n\r\n const producer = postEndpoints.find((ep) => postProducesResource(ep, resourceHint));\r\n if (producer && toNodeKey(producer) !== toNodeKey(consumer)) {\r\n dependencies.push({ from: consumer, to: producer, paramMapping: { [`:${param}`]: 'response.data.id' } });\r\n }\r\n }\r\n }\r\n return deduplicateDependencies(dependencies);\r\n}\r\n\r\nfunction deduplicateDependencies(deps: ApiDependency[]): ApiDependency[] {\r\n const map = new Map<string, ApiDependency>();\r\n for (const dep of deps) {\r\n const key = `${toNodeKey(dep.from)}→${toNodeKey(dep.to)}`;\r\n if (map.has(key)) {\r\n Object.assign(map.get(key)!.paramMapping, dep.paramMapping);\r\n } else {\r\n map.set(key, { ...dep, paramMapping: { ...dep.paramMapping } });\r\n }\r\n }\r\n return Array.from(map.values());\r\n}\r\n\r\n/**\r\n * Build a directed graph from endpoints and their dependencies.\r\n */\r\nexport function buildGraph(\r\n endpoints: ApiEndpoint[],\r\n dependencies: ApiDependency[],\r\n): DirectedAcyclicGraph {\r\n const nodeSet = new Set<string>();\r\n for (const ep of endpoints) nodeSet.add(toNodeKey(ep));\r\n\r\n const edges: Array<{ from: string; to: string; label?: string }> = [];\r\n for (const dep of dependencies) {\r\n edges.push({\r\n from: toNodeKey(dep.from),\r\n to: toNodeKey(dep.to),\r\n label: Object.keys(dep.paramMapping).join(', ') || undefined,\r\n });\r\n }\r\n return { nodes: Array.from(nodeSet), edges };\r\n}\r\n\r\n/**\r\n * Detect cycles in a directed graph using DFS coloring.\r\n */\r\nexport function detectCycles(dag: DirectedAcyclicGraph): string[] {\r\n const adjacency = new Map<string, string[]>();\r\n for (const node of dag.nodes) adjacency.set(node, []);\r\n for (const edge of dag.edges) adjacency.get(edge.from)?.push(edge.to);\r\n\r\n const color = new Map<string, Color>();\r\n for (const node of dag.nodes) color.set(node, Color.WHITE);\r\n\r\n const warnings: string[] = [];\r\n const path: string[] = [];\r\n\r\n function dfs(node: string): void {\r\n color.set(node, Color.GRAY);\r\n path.push(node);\r\n for (const neighbor of adjacency.get(node) || []) {\r\n const nc = color.get(neighbor);\r\n if (nc === Color.GRAY) {\r\n const cycleStart = path.indexOf(neighbor);\r\n warnings.push(`Cycle detected: ${path.slice(cycleStart).concat(neighbor).join(' → ')}`);\r\n } else if (nc === Color.WHITE) {\r\n dfs(neighbor);\r\n }\r\n }\r\n path.pop();\r\n color.set(node, Color.BLACK);\r\n }\r\n\r\n for (const node of dag.nodes) {\r\n if (color.get(node) === Color.WHITE) dfs(node);\r\n }\r\n return warnings;\r\n}\r\n\r\n/**\r\n * Topological sort using Kahn's algorithm.\r\n */\r\nexport function topologicalSort(dag: DirectedAcyclicGraph): string[] {\r\n const inDegree = new Map<string, number>();\r\n const adjacency = new Map<string, string[]>();\r\n\r\n for (const node of dag.nodes) { inDegree.set(node, 0); adjacency.set(node, []); }\r\n for (const edge of dag.edges) {\r\n adjacency.get(edge.from)?.push(edge.to);\r\n inDegree.set(edge.to, (inDegree.get(edge.to) || 0) + 1);\r\n }\r\n\r\n const queue: string[] = [];\r\n for (const [node, degree] of inDegree) {\r\n if (degree === 0) queue.push(node);\r\n }\r\n\r\n const sorted: string[] = [];\r\n while (queue.length > 0) {\r\n const node = queue.shift()!;\r\n sorted.push(node);\r\n for (const neighbor of adjacency.get(node) || []) {\r\n const nd = (inDegree.get(neighbor) || 1) - 1;\r\n inDegree.set(neighbor, nd);\r\n if (nd === 0) queue.push(neighbor);\r\n }\r\n }\r\n return sorted;\r\n}\r\n\r\nexport interface ApiChainAnalyzer {\r\n analyze(endpoints: ApiEndpoint[]): ApiChainAnalysisResult;\r\n}\r\n\r\n/**\r\n * Analyze API endpoints: infer dependencies, build DAG, detect cycles, topological sort.\r\n */\r\nexport function createApiChainAnalyzer(): ApiChainAnalyzer {\r\n return {\r\n analyze(endpoints: ApiEndpoint[]): ApiChainAnalysisResult {\r\n const dependencies = inferDependencies(endpoints);\r\n const dag = buildGraph(endpoints, dependencies);\r\n const cycleWarnings = detectCycles(dag);\r\n\r\n return {\r\n moduleName: '',\r\n endpoints,\r\n dependencies,\r\n dag,\r\n hasCycles: cycleWarnings.length > 0,\r\n cycleWarnings,\r\n };\r\n },\r\n };\r\n}\r\n","import type { ERDiagramResult, TableSchema, ForeignKeyRelation } from '../types.js';\r\n\r\nexport interface ERDiagramGenerator {\r\n generate(tables: TableSchema[], relations: ForeignKeyRelation[]): ERDiagramResult;\r\n}\r\n\r\n/**\r\n * Map field type string to a short Mermaid ER type label.\r\n */\r\nfunction toMermaidType(fieldType: string): string {\r\n const upper = fieldType.toUpperCase();\r\n if (upper.startsWith('STRING')) return 'string';\r\n if (upper === 'BIGINT' || upper === 'INTEGER') return 'bigint';\r\n if (upper === 'BOOLEAN') return 'boolean';\r\n if (upper.startsWith('DATE') || upper === 'NOW') return 'datetime';\r\n if (upper === 'JSON' || upper === 'JSONB') return 'json';\r\n if (upper === 'TEXT') return 'text';\r\n if (upper === 'FLOAT' || upper === 'DOUBLE' || upper === 'DECIMAL') return 'float';\r\n if (upper === 'UUID') return 'uuid';\r\n if (upper.startsWith('ENUM')) return 'enum';\r\n return 'string';\r\n}\r\n\r\n/**\r\n * Mermaid requires entity names without special characters.\r\n */\r\nfunction sanitizeEntityName(name: string): string {\r\n return name.replace(/[^a-zA-Z0-9_]/g, '_');\r\n}\r\n\r\n/**\r\n * Generate Mermaid ER diagram syntax from parsed schemas and relations.\r\n */\r\nfunction generateMermaidER(tables: TableSchema[], relations: ForeignKeyRelation[]): string {\r\n const lines: string[] = ['erDiagram'];\r\n\r\n // Entity blocks\r\n for (const table of tables) {\r\n const entityName = sanitizeEntityName(table.tableName);\r\n lines.push(` ${entityName} {`);\r\n for (const field of table.fields) {\r\n const mType = toMermaidType(field.type);\r\n const pk = field.primaryKey ? 'PK' : '';\r\n const comment = field.comment ? ` \"${field.comment}\"` : '';\r\n lines.push(` ${mType} ${field.name}${pk ? ' ' + pk : ''}${comment}`);\r\n }\r\n lines.push(' }');\r\n }\r\n\r\n // Relationships\r\n const tableNames = new Set(tables.map((t) => t.tableName));\r\n for (const rel of relations) {\r\n if (!tableNames.has(rel.sourceTable) || !tableNames.has(rel.targetTable)) continue;\r\n\r\n const src = sanitizeEntityName(rel.sourceTable);\r\n const tgt = sanitizeEntityName(rel.targetTable);\r\n const linkStyle = rel.isCrossModule ? '..' : '--';\r\n\r\n let cardinality: string;\r\n switch (rel.cardinality) {\r\n case '1:N': cardinality = `||${linkStyle}o{`; break;\r\n case 'N:1': cardinality = `}o${linkStyle}||`; break;\r\n case '1:1': cardinality = `||${linkStyle}||`; break;\r\n default: cardinality = `||${linkStyle}o{`;\r\n }\r\n\r\n lines.push(` ${src} ${cardinality} ${tgt} : \"${rel.targetField}\"`);\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\nexport function createERDiagramGenerator(): ERDiagramGenerator {\r\n return {\r\n generate(tables: TableSchema[], relations: ForeignKeyRelation[]): ERDiagramResult {\r\n const mermaidText = generateMermaidER(tables, relations);\r\n return { tables, relations, mermaidText };\r\n },\r\n };\r\n}\r\n","import type { GeneratedTestFile, TestChain, TestStep } from '../types.js';\r\n\r\nexport interface TestCodeGenerator {\r\n generate(chains: TestChain[]): GeneratedTestFile[];\r\n}\r\n\r\n/**\r\n * Resolve a path parameter from the available createdIds.\r\n */\r\nfunction resolvePathParam(param: string, ids: string[]): string {\r\n // Try direct match (e.g., 'kbId' → look for 'kbId' in ids)\r\n if (ids.includes(param)) return `createdIds['${param}']`;\r\n // Try with 'Id' suffix stripped\r\n const stripped = param.endsWith('Id') ? param.slice(0, -2) : param;\r\n if (ids.includes(stripped)) return `createdIds['${stripped}']`;\r\n // Generic id\r\n if (param === 'id') return `createdIds['id']`;\r\n return `createdIds['${param}'] || '1'`;\r\n}\r\n\r\n/**\r\n * Generate URL building code for a test step.\r\n */\r\nfunction buildUrlCode(step: TestStep): string {\r\n const pathParams = step.endpoint.pathParams;\r\n if (pathParams.length === 0) return `const url = '${step.endpoint.path}';`;\r\n\r\n let urlTemplate = step.endpoint.path;\r\n const replacements: string[] = [];\r\n for (const param of pathParams) {\r\n urlTemplate = urlTemplate.replace(`:${param}`, `\\${${resolvePathParam(param, pathParams)}}`);\r\n replacements.push(param);\r\n }\r\n return `const url = \\`${urlTemplate}\\`;`;\r\n}\r\n\r\n/**\r\n * Generate assertion code for a test step.\r\n */\r\nfunction generateAssertions(step: TestStep): string[] {\r\n const lines: string[] = [];\r\n if (step.assertions.length > 0) {\r\n for (const assertion of step.assertions) {\r\n lines.push(` expect(${assertion}).toBeTruthy();`);\r\n }\r\n } else {\r\n // Default assertions\r\n if (step.endpoint.method === 'POST') {\r\n lines.push(' expect(response.status()).toBeLessThan(400);');\r\n lines.push(' const body = await response.json();');\r\n lines.push(\" if (body.data?.id) createdIds['id'] = body.data.id;\");\r\n } else if (step.endpoint.method === 'GET') {\r\n lines.push(' expect(response.ok()).toBeTruthy();');\r\n } else if (step.endpoint.method === 'DELETE') {\r\n lines.push(' expect(response.status()).toBeLessThan(400);');\r\n } else {\r\n lines.push(' expect(response.status()).toBeLessThan(400);');\r\n }\r\n }\r\n return lines;\r\n}\r\n\r\n/**\r\n * Generate a single Playwright test file from a test chain.\r\n */\r\nfunction generateTestFile(chain: TestChain): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`import { test, expect } from '@playwright/test';`);\r\n lines.push('');\r\n lines.push(`test.describe('${chain.name}', () => {`);\r\n lines.push(\" const createdIds: Record<string, string> = {};\");\r\n lines.push('');\r\n\r\n for (const step of chain.steps) {\r\n lines.push(` test('Step ${step.order}: ${step.description}', async ({ request }) => {`);\r\n lines.push(` // ${step.action}: ${step.endpoint.method} ${step.endpoint.path}`);\r\n lines.push(` ${buildUrlCode(step)}`);\r\n lines.push('');\r\n\r\n if (step.endpoint.method === 'GET') {\r\n lines.push(' const response = await request.get(url);');\r\n } else if (step.endpoint.method === 'POST') {\r\n lines.push(' const response = await request.post(url, { data: {} });');\r\n } else if (step.endpoint.method === 'PUT') {\r\n lines.push(' const response = await request.put(url, { data: {} });');\r\n } else if (step.endpoint.method === 'DELETE') {\r\n lines.push(' const response = await request.delete(url);');\r\n } else if (step.endpoint.method === 'PATCH') {\r\n lines.push(' const response = await request.patch(url, { data: {} });');\r\n }\r\n\r\n lines.push('');\r\n lines.push(...generateAssertions(step));\r\n lines.push(' });');\r\n lines.push('');\r\n }\r\n\r\n lines.push('});');\r\n return lines.join('\\n');\r\n}\r\n\r\nexport function createTestCodeGenerator(): TestCodeGenerator {\r\n return {\r\n generate(chains: TestChain[]): GeneratedTestFile[] {\r\n return chains.map((chain) => ({\r\n filePath: `${chain.module}/${chain.name.replace(/\\s+/g, '-').toLowerCase()}.spec.ts`,\r\n content: generateTestFile(chain),\r\n module: chain.module,\r\n chain: chain.name,\r\n }));\r\n },\r\n };\r\n}\r\n","/**\r\n * Schema Validator — Layer 1 of three-layer module config validation.\r\n * Checks structural integrity, field types, format conventions.\r\n */\r\n\r\nimport type {\r\n LayerValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\n\r\nconst VALID_HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as const;\r\n\r\nexport function validateSchema(config: unknown): LayerValidationResult {\r\n const errors: ModuleConfigValidationError[] = [];\r\n const warnings: ModuleConfigValidationWarning[] = [];\r\n\r\n if (config === null || config === undefined || typeof config !== 'object') {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: '', message: 'Config must be a non-null object' });\r\n return { passed: false, layer: 'schema', errors, warnings };\r\n }\r\n\r\n const cfg = config as Record<string, unknown>;\r\n\r\n // Required top-level fields\r\n const required: Array<{ name: string; type: string }> = [\r\n { name: 'moduleName', type: 'string' },\r\n { name: 'version', type: 'string' },\r\n { name: 'generatedAt', type: 'string' },\r\n { name: 'bodyTemplates', type: 'object' },\r\n { name: 'paramRewrites', type: 'object' },\r\n { name: 'idAliases', type: 'array' },\r\n { name: 'seed', type: 'array' },\r\n ];\r\n\r\n for (const f of required) {\r\n if (cfg[f.name] === undefined || cfg[f.name] === null) {\r\n errors.push({\r\n layer: 'schema', type: 'missing-field', path: f.name,\r\n message: `Required field '${f.name}' is missing`,\r\n suggestion: `Add '${f.name}' field of type ${f.type}`,\r\n });\r\n }\r\n }\r\n\r\n // Field type checks\r\n validateFieldTypes(cfg, errors);\r\n\r\n // bodyTemplates structure\r\n if (cfg.bodyTemplates && typeof cfg.bodyTemplates === 'object' && !Array.isArray(cfg.bodyTemplates)) {\r\n validateBodyTemplates(cfg.bodyTemplates as Record<string, unknown>, errors, warnings);\r\n }\r\n\r\n // paramRewrites structure\r\n if (cfg.paramRewrites && typeof cfg.paramRewrites === 'object' && !Array.isArray(cfg.paramRewrites)) {\r\n validateParamRewrites(cfg.paramRewrites as Record<string, unknown>, errors, warnings);\r\n }\r\n\r\n // idAliases structure\r\n if (Array.isArray(cfg.idAliases)) {\r\n validateIdAliases(cfg.idAliases, errors);\r\n }\r\n\r\n // seed array\r\n if (Array.isArray(cfg.seed)) {\r\n validateSeedArray(cfg.seed, errors, warnings);\r\n }\r\n\r\n // version format\r\n if (typeof cfg.version === 'string' && !/^\\d+\\.\\d+(\\.\\d+)?$/.test(cfg.version)) {\r\n warnings.push({ layer: 'schema', path: 'version', message: `Version '${cfg.version}' does not follow semver format` });\r\n }\r\n\r\n return { passed: errors.length === 0, layer: 'schema', errors, warnings };\r\n}\r\n\r\nfunction validateFieldTypes(cfg: Record<string, unknown>, errors: ModuleConfigValidationError[]): void {\r\n if (cfg.moduleName !== undefined && typeof cfg.moduleName !== 'string') {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'moduleName', message: `'moduleName' must be a string` });\r\n } else if (typeof cfg.moduleName === 'string' && cfg.moduleName.trim() === '') {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: 'moduleName', message: \"'moduleName' must not be empty\" });\r\n }\r\n\r\n if (cfg.bodyTemplates !== undefined && (typeof cfg.bodyTemplates !== 'object' || Array.isArray(cfg.bodyTemplates))) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'bodyTemplates', message: \"'bodyTemplates' must be a plain object\" });\r\n }\r\n if (cfg.paramRewrites !== undefined && (typeof cfg.paramRewrites !== 'object' || Array.isArray(cfg.paramRewrites))) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'paramRewrites', message: \"'paramRewrites' must be a plain object\" });\r\n }\r\n if (cfg.idAliases !== undefined && !Array.isArray(cfg.idAliases)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'idAliases', message: \"'idAliases' must be an array\" });\r\n }\r\n if (cfg.seed !== undefined && !Array.isArray(cfg.seed)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'seed', message: \"'seed' must be an array\" });\r\n }\r\n}\r\n\r\nfunction validateBodyTemplates(\r\n templates: Record<string, unknown>,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, value] of Object.entries(templates)) {\r\n const p = `bodyTemplates.${key}`;\r\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `Body template '${key}' must be a plain object` });\r\n continue;\r\n }\r\n if (!/^(GET|POST|PUT|PATCH|DELETE)\\s+\\//.test(key)) {\r\n warnings.push({ layer: 'schema', path: p, message: `Body template key '${key}' should follow format 'METHOD /path'` });\r\n }\r\n if (Object.keys(value).length === 0) {\r\n warnings.push({ layer: 'schema', path: p, message: `Body template '${key}' is empty` });\r\n }\r\n }\r\n}\r\n\r\nfunction validateParamRewrites(\r\n rewrites: Record<string, unknown>,\r\n errors: ModuleConfigValidationError[],\r\n _warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, value] of Object.entries(rewrites)) {\r\n const p = `paramRewrites.${key}`;\r\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `Param rewrite '${key}' must be a plain object` });\r\n continue;\r\n }\r\n for (const [paramName, paramValue] of Object.entries(value as Record<string, unknown>)) {\r\n if (typeof paramValue !== 'string') {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: `${p}.${paramName}`, message: `Param rewrite value for '${paramName}' must be a string` });\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction validateIdAliases(aliases: unknown[], errors: ModuleConfigValidationError[]): void {\r\n for (let i = 0; i < aliases.length; i++) {\r\n const alias = aliases[i] as Record<string, unknown> | null;\r\n const p = `idAliases[${i}]`;\r\n if (typeof alias !== 'object' || alias === null) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `idAlias at index ${i} must be an object` });\r\n continue;\r\n }\r\n if (typeof alias.pathPattern !== 'string') {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.pathPattern`, message: `idAlias at index ${i} must have a string 'pathPattern'` });\r\n }\r\n if (typeof alias.alias !== 'string') {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.alias`, message: `idAlias at index ${i} must have a string 'alias'` });\r\n }\r\n }\r\n}\r\n\r\nfunction validateSeedArray(\r\n seed: unknown[],\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n if (seed.length === 0) {\r\n warnings.push({ layer: 'schema', path: 'seed', message: 'Seed array is empty, no setup steps will be executed' });\r\n return;\r\n }\r\n\r\n const capturedVars = new Set<string>();\r\n const stepNumbers = new Set<number>();\r\n\r\n for (let i = 0; i < seed.length; i++) {\r\n const step = seed[i] as Record<string, unknown> | null;\r\n const p = `seed[${i}]`;\r\n if (typeof step !== 'object' || step === null) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `Seed step at index ${i} must be an object` });\r\n continue;\r\n }\r\n\r\n // step number\r\n if (step.step === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.step`, message: `Seed step at index ${i} is missing 'step' number` });\r\n } else if (typeof step.step !== 'number' || !Number.isInteger(step.step)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: `${p}.step`, message: `'step' must be an integer` });\r\n } else {\r\n if (stepNumbers.has(step.step)) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.step`, message: `Duplicate step number ${step.step}` });\r\n }\r\n stepNumbers.add(step.step);\r\n }\r\n\r\n // method\r\n if (step.method === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.method`, message: `Seed step at index ${i} is missing 'method'` });\r\n } else if (typeof step.method !== 'string' || !(VALID_HTTP_METHODS as readonly string[]).includes(step.method)) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.method`, message: `'method' must be one of ${VALID_HTTP_METHODS.join(', ')}` });\r\n }\r\n\r\n // path\r\n if (step.path === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.path`, message: `Seed step at index ${i} is missing 'path'` });\r\n } else if (typeof step.path !== 'string' || !(step.path as string).startsWith('/')) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.path`, message: `'path' must start with '/'` });\r\n }\r\n\r\n // required\r\n if (step.required === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.required`, message: `Seed step at index ${i} is missing 'required'` });\r\n }\r\n\r\n // body\r\n if (step.body !== undefined && (typeof step.body !== 'object' || step.body === null || Array.isArray(step.body))) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: `${p}.body`, message: `'body' must be a plain object` });\r\n } else if (step.body === undefined && typeof step.method === 'string' && ['POST', 'PUT', 'PATCH'].includes(step.method)) {\r\n warnings.push({ layer: 'schema', path: `${p}.body`, message: `${step.method} step at index ${i} has no body template` });\r\n }\r\n\r\n // captureAs uniqueness\r\n if (typeof step.captureAs === 'string') {\r\n if (capturedVars.has(step.captureAs)) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.captureAs`, message: `Duplicate captureAs variable '${step.captureAs}'` });\r\n }\r\n capturedVars.add(step.captureAs);\r\n }\r\n\r\n // dependsOn reference check\r\n if (Array.isArray(step.dependsOn)) {\r\n for (const dep of step.dependsOn) {\r\n if (typeof dep === 'string' && !capturedVars.has(dep)) {\r\n errors.push({\r\n layer: 'schema', type: 'dependency-missing', path: `${p}.dependsOn`,\r\n message: `Seed step ${step.step ?? i} depends on '${dep}' not captured by any preceding step`,\r\n suggestion: `Ensure a preceding step has captureAs: '${dep}'`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Semantic Validator — Layer 2 of three-layer module config validation.\r\n * Checks that config matches actual source code (routes, DTO fields, dependencies).\r\n */\r\n\r\nimport type {\r\n ModuleTestConfig,\r\n SeedStep,\r\n ApiEndpoint,\r\n DTOInfo,\r\n ModuleConfigValidationContext,\r\n LayerValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\n\r\nexport function validateSemantic(\r\n config: ModuleTestConfig,\r\n context: ModuleConfigValidationContext,\r\n): LayerValidationResult {\r\n const errors: ModuleConfigValidationError[] = [];\r\n const warnings: ModuleConfigValidationWarning[] = [];\r\n\r\n validateBodyTemplatesSemantic(config, context, errors, warnings);\r\n validateParamRewritesSemantic(config, context, errors, warnings);\r\n validateIdAliasesSemantic(config, context, warnings);\r\n validateSeedRoutesSemantic(config, context, errors);\r\n validateSeedDependenciesSemantic(config, errors, warnings);\r\n\r\n return { passed: errors.length === 0, layer: 'semantic', errors, warnings };\r\n}\r\n\r\n// ============================================================\r\n// Helpers\r\n// ============================================================\r\n\r\nfunction normalizePath(p: string): string {\r\n return p.replace(/\\/+$/, '').replace(/\\/+/g, '/');\r\n}\r\n\r\nfunction extractPathParams(routePath: string): string[] {\r\n const params: string[] = [];\r\n const regex = /:(\\w+)/g;\r\n let match;\r\n while ((match = regex.exec(routePath)) !== null) params.push(match[1]);\r\n return params;\r\n}\r\n\r\nfunction findMatchingEndpoint(\r\n method: string, configPath: string, endpoints: ApiEndpoint[],\r\n): ApiEndpoint | undefined {\r\n const normalized = normalizePath(configPath);\r\n return endpoints.find((ep) => {\r\n if (ep.method !== method.toUpperCase()) return false;\r\n const epNorm = normalizePath(ep.path);\r\n if (epNorm === normalized) return true;\r\n const cSegs = normalized.split('/');\r\n const eSegs = epNorm.split('/');\r\n if (cSegs.length !== eSegs.length) return false;\r\n return cSegs.every((seg, idx) => seg === eSegs[idx] || seg.startsWith(':') || eSegs[idx].startsWith(':'));\r\n });\r\n}\r\n\r\nfunction findMatchingDTO(method: string, dtos: DTOInfo[]): DTOInfo | undefined {\r\n if (dtos.length === 0) return undefined;\r\n if (['POST'].includes(method)) return dtos.find((d) => /Create|Input|Request/.test(d.name));\r\n if (['PUT', 'PATCH'].includes(method)) return dtos.find((d) => /Update/.test(d.name)) || dtos.find((d) => /Create|Input/.test(d.name));\r\n if (['GET'].includes(method)) return dtos.find((d) => /Query|List|Params/.test(d.name));\r\n return undefined;\r\n}\r\n\r\n// ============================================================\r\n// Validators\r\n// ============================================================\r\n\r\nfunction validateBodyTemplatesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, body] of Object.entries(config.bodyTemplates)) {\r\n const p = `bodyTemplates.${key}`;\r\n const spaceIdx = key.indexOf(' ');\r\n if (spaceIdx === -1) continue;\r\n\r\n const method = key.substring(0, spaceIdx).toUpperCase();\r\n const routePath = key.substring(spaceIdx + 1);\r\n\r\n const endpoint = findMatchingEndpoint(method, routePath, ctx.endpoints);\r\n if (!endpoint) {\r\n errors.push({\r\n layer: 'semantic', type: 'interface-not-found', path: p,\r\n message: `Route '${method} ${routePath}' in bodyTemplates does not match any parsed API endpoint`,\r\n suggestion: `Check that the controller defines ${method} ${routePath}`,\r\n });\r\n continue;\r\n }\r\n\r\n const bodyFields = Object.keys(body);\r\n if (bodyFields.length === 0) continue;\r\n\r\n const dto = findMatchingDTO(method, ctx.dtos);\r\n if (!dto) {\r\n warnings.push({ layer: 'semantic', path: p, message: `No matching DTO found for '${method} ${routePath}', cannot verify field completeness` });\r\n continue;\r\n }\r\n\r\n const dtoFieldNames = new Set(dto.fields.map((f) => f.name));\r\n for (const fieldName of bodyFields) {\r\n if (!dtoFieldNames.has(fieldName)) {\r\n warnings.push({ layer: 'semantic', path: `${p}.${fieldName}`, message: `Field '${fieldName}' in body template not found in DTO '${dto.name}'` });\r\n }\r\n }\r\n\r\n const bodyFieldSet = new Set(bodyFields);\r\n for (const field of dto.fields) {\r\n if (field.required && !field.isSystemField && !bodyFieldSet.has(field.name)) {\r\n warnings.push({ layer: 'semantic', path: p, message: `Required DTO field '${field.name}' (from ${dto.name}) not present in body template` });\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction validateParamRewritesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, mapping] of Object.entries(config.paramRewrites)) {\r\n const p = `paramRewrites.${key}`;\r\n const spaceIdx = key.indexOf(' ');\r\n if (spaceIdx === -1) continue;\r\n\r\n const method = key.substring(0, spaceIdx).toUpperCase();\r\n const routePath = key.substring(spaceIdx + 1);\r\n\r\n const endpoint = findMatchingEndpoint(method, routePath, ctx.endpoints);\r\n if (!endpoint) {\r\n errors.push({ layer: 'semantic', type: 'param-mapping-invalid', path: p, message: `Route '${method} ${routePath}' in paramRewrites does not match any parsed API endpoint` });\r\n continue;\r\n }\r\n\r\n const actualParams = new Set(extractPathParams(endpoint.path));\r\n for (const paramName of Object.keys(mapping)) {\r\n if (!actualParams.has(paramName)) {\r\n warnings.push({ layer: 'semantic', path: `${p}.${paramName}`, message: `Param '${paramName}' in paramRewrites not found in route path params [${[...actualParams].join(', ')}]` });\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction validateIdAliasesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (let i = 0; i < config.idAliases.length; i++) {\r\n const alias = config.idAliases[i];\r\n const matched = ctx.endpoints.some((ep) => {\r\n try { return new RegExp(alias.pathPattern).test(ep.path); } catch { return ep.path.includes(alias.pathPattern); }\r\n });\r\n if (!matched) {\r\n warnings.push({ layer: 'semantic', path: `idAliases[${i}].pathPattern`, message: `idAlias pathPattern '${alias.pathPattern}' does not match any known API route` });\r\n }\r\n }\r\n}\r\n\r\nfunction validateSeedRoutesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n errors: ModuleConfigValidationError[],\r\n): void {\r\n for (let i = 0; i < config.seed.length; i++) {\r\n const step = config.seed[i];\r\n const endpoint = findMatchingEndpoint(step.method, step.path, ctx.endpoints);\r\n if (!endpoint) {\r\n errors.push({\r\n layer: 'semantic', type: 'interface-not-found', path: `seed[${i}].path`,\r\n message: `Seed step ${step.step}: route '${step.method} ${step.path}' does not match any parsed API endpoint`,\r\n suggestion: `Verify that '${step.method} ${step.path}' exists in the module controllers`,\r\n });\r\n }\r\n }\r\n}\r\n\r\nfunction validateSeedDependenciesSemantic(\r\n config: ModuleTestConfig,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n if (config.seed.length === 0) return;\r\n\r\n const capturedSet = new Set<string>();\r\n const variablePattern = /\\{\\{(\\w+)\\}\\}|\\$\\{(\\w+)\\}/g;\r\n\r\n for (let i = 0; i < config.seed.length; i++) {\r\n const step = config.seed[i];\r\n\r\n // Check body variable references\r\n if (step.body) {\r\n const bodyStr = JSON.stringify(step.body);\r\n variablePattern.lastIndex = 0;\r\n let match;\r\n while ((match = variablePattern.exec(bodyStr)) !== null) {\r\n const varName = match[1] || match[2];\r\n if (!capturedSet.has(varName)) {\r\n warnings.push({ layer: 'semantic', path: `seed[${i}].body`, message: `Body references variable '${varName}' which may not be captured by a preceding seed step` });\r\n }\r\n }\r\n }\r\n\r\n // Check path variable references\r\n if (step.path) {\r\n variablePattern.lastIndex = 0;\r\n let match;\r\n while ((match = variablePattern.exec(step.path)) !== null) {\r\n const varName = match[1] || match[2];\r\n if (!capturedSet.has(varName)) {\r\n warnings.push({ layer: 'semantic', path: `seed[${i}].path`, message: `Path references variable '${varName}' which may not be captured by a preceding seed step` });\r\n }\r\n }\r\n }\r\n\r\n if (step.captureAs) capturedSet.add(step.captureAs);\r\n }\r\n\r\n // Cycle detection via DFS\r\n detectDependencyCycle(config.seed, errors);\r\n}\r\n\r\nfunction detectDependencyCycle(seed: SeedStep[], errors: ModuleConfigValidationError[]): void {\r\n const graph = new Map<string, string[]>();\r\n for (const step of seed) {\r\n if (step.captureAs) {\r\n graph.set(step.captureAs, step.dependsOn || []);\r\n }\r\n }\r\n\r\n const visited = new Set<string>();\r\n const inStack = new Set<string>();\r\n\r\n function dfs(node: string): boolean {\r\n if (inStack.has(node)) return true;\r\n if (visited.has(node)) return false;\r\n visited.add(node);\r\n inStack.add(node);\r\n for (const neighbor of graph.get(node) || []) {\r\n if (dfs(neighbor)) {\r\n errors.push({ layer: 'semantic', type: 'dependency-cycle', path: 'seed', message: `Circular dependency detected involving '${node}' → '${neighbor}'` });\r\n return true;\r\n }\r\n }\r\n inStack.delete(node);\r\n return false;\r\n }\r\n\r\n for (const node of graph.keys()) {\r\n if (!visited.has(node)) dfs(node);\r\n }\r\n}\r\n","/**\r\n * Dry-run Validator — Layer 3 of three-layer module config validation.\r\n * Generates temporary TypeScript code from config and runs ts-morph compile check.\r\n */\r\n\r\nimport { Project, DiagnosticCategory } from 'ts-morph';\r\nimport type {\r\n ModuleTestConfig,\r\n ModuleConfigValidationContext,\r\n LayerValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\n\r\nexport function validateDryrun(\r\n config: ModuleTestConfig,\r\n _context: ModuleConfigValidationContext,\r\n): LayerValidationResult {\r\n const errors: ModuleConfigValidationError[] = [];\r\n const warnings: ModuleConfigValidationWarning[] = [];\r\n\r\n const project = new Project({\r\n compilerOptions: {\r\n strict: false,\r\n noEmit: true,\r\n target: 2, // ES2015\r\n module: 1, // CommonJS\r\n esModuleInterop: true,\r\n skipLibCheck: true,\r\n },\r\n useInMemoryFileSystem: true,\r\n });\r\n\r\n // Add helper type declarations\r\n project.createSourceFile(\r\n '__helpers.d.ts',\r\n `\r\ndeclare function apiRequest(\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',\r\n path: string,\r\n body?: Record<string, any>,\r\n params?: Record<string, string>\r\n): Promise<{ status: number; data: any }>;\r\ndeclare function captureId(response: { data: any }, field?: string): string;\r\ndeclare const tenantId: string;\r\ndeclare const captured: Record<string, string>;\r\n`,\r\n );\r\n\r\n // Generate and check bodyTemplates test code\r\n const bodyCode = generateBodyTemplateTestCode(config);\r\n if (bodyCode) {\r\n const bodyFile = project.createSourceFile('__dryrun_body_test.ts', bodyCode);\r\n for (const diag of bodyFile.getPreEmitDiagnostics()) {\r\n const msg = diag.getMessageText();\r\n const msgStr = typeof msg === 'string' ? msg : msg.getMessageText();\r\n const line = diag.getLineNumber();\r\n const location = line ? `bodyTemplates (line ${line})` : 'bodyTemplates';\r\n\r\n if (diag.getCategory() === DiagnosticCategory.Error) {\r\n errors.push({\r\n layer: 'dryrun', type: 'compile-error', path: location,\r\n message: `TypeScript compile error: ${msgStr}`,\r\n suggestion: 'Fix the body template that causes this type error',\r\n });\r\n } else if (diag.getCategory() === DiagnosticCategory.Warning) {\r\n warnings.push({ layer: 'dryrun', path: location, message: `TypeScript warning: ${msgStr}` });\r\n }\r\n }\r\n }\r\n\r\n // Generate and check seed test code\r\n const seedCode = generateSeedTestCode(config);\r\n if (seedCode) {\r\n const seedFile = project.createSourceFile('__dryrun_seed_test.ts', seedCode);\r\n for (const diag of seedFile.getPreEmitDiagnostics()) {\r\n const msg = diag.getMessageText();\r\n const msgStr = typeof msg === 'string' ? msg : msg.getMessageText();\r\n const line = diag.getLineNumber();\r\n const location = line ? `seed (line ${line})` : 'seed';\r\n\r\n if (diag.getCategory() === DiagnosticCategory.Error) {\r\n errors.push({\r\n layer: 'dryrun', type: 'compile-error', path: location,\r\n message: `TypeScript compile error: ${msgStr}`,\r\n suggestion: 'Fix the seed step that causes this type error',\r\n });\r\n } else if (diag.getCategory() === DiagnosticCategory.Warning) {\r\n warnings.push({ layer: 'dryrun', path: location, message: `TypeScript warning: ${msgStr}` });\r\n }\r\n }\r\n }\r\n\r\n return { passed: errors.length === 0, layer: 'dryrun', errors, warnings };\r\n}\r\n\r\nfunction generateBodyTemplateTestCode(config: ModuleTestConfig): string | null {\r\n if (!config.bodyTemplates || Object.keys(config.bodyTemplates).length === 0) return null;\r\n\r\n const lines = ['async function testBodyTemplates() {'];\r\n let idx = 0;\r\n for (const [key, body] of Object.entries(config.bodyTemplates)) {\r\n const spaceIdx = key.indexOf(' ');\r\n if (spaceIdx === -1) continue;\r\n const method = key.substring(0, spaceIdx);\r\n const routePath = key.substring(spaceIdx + 1);\r\n\r\n lines.push(` const body_${idx} = ${JSON.stringify(body, null, 2)};`);\r\n lines.push(` const result_${idx} = await apiRequest('${method}', '${routePath}', body_${idx});`);\r\n lines.push(` if (result_${idx}.status !== 200 && result_${idx}.status !== 201) {`);\r\n lines.push(` throw new Error('Unexpected status: ' + result_${idx}.status);`);\r\n lines.push(' }');\r\n idx++;\r\n }\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n\r\nfunction generateSeedTestCode(config: ModuleTestConfig): string | null {\r\n if (!config.seed || config.seed.length === 0) return null;\r\n\r\n const lines = [\r\n 'async function testSeedSteps() {',\r\n ' const captured: Record<string, string> = {};',\r\n '',\r\n ];\r\n\r\n for (const step of config.seed) {\r\n if (step.body) {\r\n lines.push(` const body_step${step.step} = ${JSON.stringify(step.body, null, 2)};`);\r\n lines.push(` const result_step${step.step} = await apiRequest('${step.method}', '${step.path}', body_step${step.step});`);\r\n } else {\r\n lines.push(` const result_step${step.step} = await apiRequest('${step.method}', '${step.path}');`);\r\n }\r\n\r\n if (step.captureAs) {\r\n lines.push(` captured['${step.captureAs}'] = captureId(result_step${step.step});`);\r\n }\r\n\r\n if (step.required) {\r\n lines.push(` if (result_step${step.step}.status >= 400) {`);\r\n lines.push(` throw new Error('${step.failureMessage || `Required step ${step.step} failed`}');`);\r\n lines.push(' }');\r\n }\r\n lines.push('');\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n","import type {\r\n ValidationError,\r\n ModuleTestConfig,\r\n ModuleConfigValidationContext,\r\n ModuleConfigValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\nimport { validateSchema } from './schema-validator.js';\r\nimport { validateSemantic } from './semantic-validator.js';\r\nimport { validateDryrun } from './dryrun-validator.js';\r\n\r\nconst REQUIRED_FIELDS = ['backendRoot'];\r\n\r\nconst VALID_ADAPTERS = ['sequelize', 'typeorm', 'prisma', 'drizzle'];\r\nconst VALID_STEPS = ['scan', 'er-diagram', 'api-chain', 'plan', 'codegen', 'validate'];\r\nconst VALID_LLM_PROVIDERS = ['openai', 'zhipu', 'ollama', 'custom'];\r\nconst VALID_REPORT_FORMATS = ['html', 'json', 'markdown'];\r\nconst VALID_HEAL_MODES = ['config-only', 'config-and-source'];\r\n\r\n/**\r\n * Validate an OpenCroc configuration object.\r\n * Returns an array of ValidationErrors (empty = valid).\r\n */\r\nexport function validateConfig(config: Record<string, unknown>): ValidationError[] {\r\n const errors: ValidationError[] = [];\r\n\r\n // Required fields\r\n for (const field of REQUIRED_FIELDS) {\r\n if (!config[field]) {\r\n errors.push({\r\n module: 'config',\r\n field,\r\n message: `Missing required field: ${field}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n\r\n // backendRoot must be a string\r\n if (config.backendRoot && typeof config.backendRoot !== 'string') {\r\n errors.push({\r\n module: 'config',\r\n field: 'backendRoot',\r\n message: 'backendRoot must be a string path',\r\n severity: 'error',\r\n });\r\n }\r\n\r\n // adapter validation\r\n if (config.adapter && typeof config.adapter === 'string') {\r\n if (!VALID_ADAPTERS.includes(config.adapter)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'adapter',\r\n message: `Invalid adapter: ${config.adapter}. Must be one of: ${VALID_ADAPTERS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n\r\n // steps validation\r\n if (config.steps && Array.isArray(config.steps)) {\r\n for (const step of config.steps) {\r\n if (!VALID_STEPS.includes(step as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'steps',\r\n message: `Invalid pipeline step: ${step}. Must be one of: ${VALID_STEPS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n }\r\n\r\n // LLM config validation\r\n if (config.llm && typeof config.llm === 'object') {\r\n const llm = config.llm as Record<string, unknown>;\r\n if (llm.provider && !VALID_LLM_PROVIDERS.includes(llm.provider as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'llm.provider',\r\n message: `Invalid LLM provider: ${llm.provider}. Must be one of: ${VALID_LLM_PROVIDERS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n if (llm.provider && llm.provider !== 'ollama' && !llm.apiKey) {\r\n errors.push({\r\n module: 'config',\r\n field: 'llm.apiKey',\r\n message: 'LLM apiKey is required for cloud providers',\r\n severity: 'warning',\r\n });\r\n }\r\n }\r\n\r\n // Report config validation\r\n if (config.report && typeof config.report === 'object') {\r\n const report = config.report as Record<string, unknown>;\r\n if (report.format && Array.isArray(report.format)) {\r\n for (const fmt of report.format) {\r\n if (!VALID_REPORT_FORMATS.includes(fmt as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'report.format',\r\n message: `Invalid report format: ${fmt}. Must be one of: ${VALID_REPORT_FORMATS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Self-healing config validation\r\n if (config.selfHealing && typeof config.selfHealing === 'object') {\r\n const sh = config.selfHealing as Record<string, unknown>;\r\n if (sh.mode && !VALID_HEAL_MODES.includes(sh.mode as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'selfHealing.mode',\r\n message: `Invalid self-healing mode: ${sh.mode}. Must be one of: ${VALID_HEAL_MODES.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n if (sh.maxIterations && (typeof sh.maxIterations !== 'number' || sh.maxIterations < 1)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'selfHealing.maxIterations',\r\n message: 'maxIterations must be a positive number',\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n\r\n // Execution hooks validation\r\n if (config.execution && typeof config.execution === 'object') {\r\n const execution = config.execution as Record<string, unknown>;\r\n const hookFields = ['setupHook', 'authHook', 'teardownHook'];\r\n\r\n for (const hookField of hookFields) {\r\n const hook = execution[hookField];\r\n if (hook === undefined) continue;\r\n\r\n if (typeof hook === 'string') continue;\r\n\r\n if (typeof hook !== 'object' || hook === null) {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}`,\r\n message: `${hookField} must be a string command or an object { command, args?, cwd? }`,\r\n severity: 'error',\r\n });\r\n continue;\r\n }\r\n\r\n const hookObj = hook as Record<string, unknown>;\r\n if (typeof hookObj.command !== 'string' || hookObj.command.trim() === '') {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}.command`,\r\n message: 'command is required and must be a non-empty string',\r\n severity: 'error',\r\n });\r\n }\r\n\r\n if (hookObj.args !== undefined && (!Array.isArray(hookObj.args) || hookObj.args.some((a) => typeof a !== 'string'))) {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}.args`,\r\n message: 'args must be an array of strings',\r\n severity: 'error',\r\n });\r\n }\r\n\r\n if (hookObj.cwd !== undefined && typeof hookObj.cwd !== 'string') {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}.cwd`,\r\n message: 'cwd must be a string path',\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n// ============================================================\r\n// Three-Layer Module Config Validator\r\n// ============================================================\r\n\r\nexport interface ValidateModuleConfigOptions {\r\n stopOnFailure?: boolean;\r\n skipLayers?: Array<'schema' | 'semantic' | 'dryrun'>;\r\n}\r\n\r\nexport function validateModuleConfig(\r\n config: unknown,\r\n context?: ModuleConfigValidationContext,\r\n options?: ValidateModuleConfigOptions,\r\n): ModuleConfigValidationResult {\r\n const stopOnFailure = options?.stopOnFailure ?? true;\r\n const skipLayers = new Set(options?.skipLayers ?? []);\r\n\r\n const allErrors: ModuleConfigValidationError[] = [];\r\n const allWarnings: ModuleConfigValidationWarning[] = [];\r\n const result: ModuleConfigValidationResult = {\r\n passed: false,\r\n errors: allErrors,\r\n warnings: allWarnings,\r\n };\r\n\r\n if (!skipLayers.has('schema')) {\r\n const schemaResult = validateSchema(config);\r\n result.schemaResult = schemaResult;\r\n allErrors.push(...schemaResult.errors);\r\n allWarnings.push(...schemaResult.warnings);\r\n\r\n if (!schemaResult.passed) {\r\n result.failedAtLayer = 'schema';\r\n if (stopOnFailure) return result;\r\n } else {\r\n result.lastPassedLayer = 'schema';\r\n }\r\n }\r\n\r\n const validConfig = config as ModuleTestConfig;\r\n\r\n if (!skipLayers.has('semantic')) {\r\n if (!context) {\r\n allWarnings.push({ layer: 'semantic', path: '', message: 'ValidationContext not provided, skipping semantic validation' });\r\n } else {\r\n const semanticResult = validateSemantic(validConfig, context);\r\n result.semanticResult = semanticResult;\r\n allErrors.push(...semanticResult.errors);\r\n allWarnings.push(...semanticResult.warnings);\r\n\r\n if (!semanticResult.passed) {\r\n result.failedAtLayer = result.failedAtLayer || 'semantic';\r\n if (stopOnFailure) return result;\r\n } else {\r\n result.lastPassedLayer = 'semantic';\r\n }\r\n }\r\n }\r\n\r\n if (!skipLayers.has('dryrun')) {\r\n if (!context) {\r\n allWarnings.push({ layer: 'dryrun', path: '', message: 'ValidationContext not provided, skipping dry-run validation' });\r\n } else {\r\n const dryrunResult = validateDryrun(validConfig, context);\r\n result.dryrunResult = dryrunResult;\r\n allErrors.push(...dryrunResult.errors);\r\n allWarnings.push(...dryrunResult.warnings);\r\n\r\n if (!dryrunResult.passed) {\r\n result.failedAtLayer = result.failedAtLayer || 'dryrun';\r\n } else {\r\n result.lastPassedLayer = 'dryrun';\r\n }\r\n }\r\n }\r\n\r\n result.passed = allErrors.length === 0;\r\n return result;\r\n}\r\n\r\nexport function formatValidationResult(result: ModuleConfigValidationResult): string {\r\n const lines: string[] = [];\r\n lines.push(result.passed ? '\\u2705 Validation PASSED' : '\\u274c Validation FAILED');\r\n if (result.failedAtLayer) lines.push(` Failed at layer: ${result.failedAtLayer}`);\r\n if (result.lastPassedLayer) lines.push(` Last passed layer: ${result.lastPassedLayer}`);\r\n\r\n if (result.errors.length > 0) {\r\n lines.push('', `Errors (${result.errors.length}):`);\r\n for (const err of result.errors) {\r\n lines.push(` [${err.layer}] ${err.path}: ${err.message}`);\r\n if (err.suggestion) lines.push(` \\ud83d\\udca1 ${err.suggestion}`);\r\n }\r\n }\r\n\r\n if (result.warnings.length > 0) {\r\n lines.push('', `Warnings (${result.warnings.length}):`);\r\n for (const warn of result.warnings) {\r\n lines.push(` [${warn.layer}] ${warn.path}: ${warn.message}`);\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport type {\r\n OpenCrocConfig,\r\n PipelineRunResult,\r\n PipelineStep,\r\n ERDiagramResult,\r\n ChainPlanResult,\r\n} from '../types.js';\r\nimport { parseModuleModels } from '../parsers/model-parser.js';\r\nimport { parseControllerDirectory } from '../parsers/controller-parser.js';\r\nimport { parseAssociationFile } from '../parsers/association-parser.js';\r\nimport { createApiChainAnalyzer, topologicalSort } from '../analyzers/api-chain-analyzer.js';\r\nimport { createERDiagramGenerator } from '../generators/er-diagram-generator.js';\r\nimport { createTestCodeGenerator } from '../generators/test-code-generator.js';\r\nimport { validateConfig } from '../validators/config-validator.js';\r\n\r\nexport interface Pipeline {\r\n run(steps?: PipelineStep[]): Promise<PipelineRunResult>;\r\n}\r\n\r\nconst ALL_STEPS: PipelineStep[] = ['scan', 'er-diagram', 'api-chain', 'plan', 'codegen', 'validate'];\r\n\r\nexport function createPipeline(config: OpenCrocConfig): Pipeline {\r\n return {\r\n async run(steps) {\r\n const startTime = Date.now();\r\n const activeSteps = steps || config.steps || ALL_STEPS;\r\n\r\n const result: PipelineRunResult = {\r\n modules: [],\r\n erDiagrams: new Map(),\r\n chainPlans: new Map(),\r\n generatedFiles: [],\r\n validationErrors: [],\r\n duration: 0,\r\n };\r\n\r\n // Step 1: Scan — discover modules\r\n if (activeSteps.includes('scan')) {\r\n const backendRoot = path.resolve(config.backendRoot);\r\n const modelsDir = path.join(backendRoot, 'models');\r\n\r\n if (fs.existsSync(modelsDir)) {\r\n // Discover modules from subdirectories\r\n const dirs = fs.readdirSync(modelsDir, { withFileTypes: true })\r\n .filter((d) => d.isDirectory())\r\n .map((d) => d.name);\r\n\r\n const moduleFilter = config.modules;\r\n for (const dir of dirs) {\r\n if (moduleFilter && !moduleFilter.includes(dir)) continue;\r\n result.modules.push(dir);\r\n }\r\n\r\n // If no subdirectories, treat root as single \"default\" module\r\n if (result.modules.length === 0) {\r\n result.modules.push('default');\r\n } else {\r\n // Also include root-level model files as \"default\" module\r\n const rootFiles = fs.readdirSync(modelsDir)\r\n .filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts') && f !== 'index.ts');\r\n if (rootFiles.length > 0) {\r\n result.modules.unshift('default');\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Helper: resolve model dir for a module\r\n const resolveModelDir = (backendRoot: string, mod: string): string =>\r\n mod === 'default'\r\n ? path.join(backendRoot, 'models')\r\n : path.join(backendRoot, 'models', mod);\r\n\r\n // Helper: resolve controller dir for a module\r\n const resolveControllerDir = (backendRoot: string, mod: string): string =>\r\n mod === 'default'\r\n ? path.join(backendRoot, 'controllers')\r\n : path.join(backendRoot, 'controllers', mod);\r\n\r\n // Step 2: ER Diagram — parse models and generate relationship graphs\r\n if (activeSteps.includes('er-diagram')) {\r\n const erGen = createERDiagramGenerator();\r\n const backendRoot = path.resolve(config.backendRoot);\r\n\r\n for (const mod of result.modules) {\r\n const modelDir = resolveModelDir(backendRoot, mod);\r\n\r\n // For flat layouts, scan all model files for embedded associations\r\n const tables = fs.existsSync(modelDir) ? parseModuleModels(modelDir) : [];\r\n const relations: import('../types.js').ForeignKeyRelation[] = [];\r\n\r\n // Check for dedicated associations.ts first\r\n const assocFile = path.join(modelDir, 'associations.ts');\r\n if (fs.existsSync(assocFile)) {\r\n relations.push(...parseAssociationFile(assocFile));\r\n }\r\n\r\n // Also scan model files for embedded associations (belongsTo/hasMany at end of file)\r\n if (fs.existsSync(modelDir)) {\r\n const modelFiles = fs.readdirSync(modelDir)\r\n .filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts') && f !== 'index.ts' && f !== 'associations.ts');\r\n for (const file of modelFiles) {\r\n try {\r\n const embedded = parseAssociationFile(path.join(modelDir, file));\r\n relations.push(...embedded);\r\n } catch {\r\n // skip files that fail to parse\r\n }\r\n }\r\n }\r\n\r\n const erResult: ERDiagramResult = erGen.generate(tables, relations);\r\n result.erDiagrams.set(mod, erResult);\r\n }\r\n }\r\n\r\n // Step 3: API Chain — analyze controller routes and build dependency DAG\r\n if (activeSteps.includes('api-chain')) {\r\n const chainAnalyzer = createApiChainAnalyzer();\r\n const backendRoot = path.resolve(config.backendRoot);\r\n\r\n for (const mod of result.modules) {\r\n const controllerDir = resolveControllerDir(backendRoot, mod);\r\n const endpoints = fs.existsSync(controllerDir)\r\n ? parseControllerDirectory(controllerDir)\r\n : [];\r\n\r\n const analysis = chainAnalyzer.analyze(endpoints);\r\n analysis.moduleName = mod;\r\n\r\n if (analysis.hasCycles) {\r\n for (const warning of analysis.cycleWarnings) {\r\n result.validationErrors.push({\r\n module: mod,\r\n field: 'api-chain',\r\n message: warning,\r\n severity: 'warning',\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Step 4: Plan — generate test chains from dependency analysis\r\n if (activeSteps.includes('plan')) {\r\n const backendRoot = path.resolve(config.backendRoot);\r\n const chainAnalyzer = createApiChainAnalyzer();\r\n\r\n for (const mod of result.modules) {\r\n const controllerDir = resolveControllerDir(backendRoot, mod);\r\n const endpoints = fs.existsSync(controllerDir)\r\n ? parseControllerDirectory(controllerDir)\r\n : [];\r\n\r\n const analysis = chainAnalyzer.analyze(endpoints);\r\n const topoOrder = topologicalSort(analysis.dag);\r\n\r\n // Group by resource to create chains\r\n const chains = generateChainPlan(mod, endpoints, topoOrder);\r\n result.chainPlans.set(mod, chains);\r\n }\r\n }\r\n\r\n // Step 5: Codegen — emit Playwright test files from chain plans\r\n if (activeSteps.includes('codegen')) {\r\n const testGen = createTestCodeGenerator();\r\n const outDir = config.outDir || './opencroc-output';\r\n\r\n for (const [_mod, plan] of result.chainPlans) {\r\n const files = testGen.generate(plan.chains);\r\n for (const file of files) {\r\n file.filePath = path.join(outDir, file.filePath);\r\n }\r\n result.generatedFiles.push(...files);\r\n }\r\n }\r\n\r\n // Step 6: Validate — run validation on generated configs\r\n if (activeSteps.includes('validate')) {\r\n const configErrors = validateConfig(config as unknown as Record<string, unknown>);\r\n result.validationErrors.push(...configErrors);\r\n }\r\n\r\n result.duration = Date.now() - startTime;\r\n return result;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Generate a basic chain plan from endpoints and topological order.\r\n */\r\nfunction generateChainPlan(\r\n moduleName: string,\r\n endpoints: import('../types.js').ApiEndpoint[],\r\n _topoOrder: string[],\r\n): ChainPlanResult {\r\n // Group endpoints by resource (first non-param path segment)\r\n const groups = new Map<string, import('../types.js').ApiEndpoint[]>();\r\n\r\n for (const ep of endpoints) {\r\n const segments = ep.path.split('/').filter((s) => s && !s.startsWith(':'));\r\n const resource = segments[segments.length - 1] || 'default';\r\n if (!groups.has(resource)) groups.set(resource, []);\r\n groups.get(resource)!.push(ep);\r\n }\r\n\r\n const chains: import('../types.js').TestChain[] = [];\r\n let totalSteps = 0;\r\n\r\n for (const [resource, eps] of groups) {\r\n const steps: import('../types.js').TestStep[] = eps.map((ep, i) => ({\r\n order: i + 1,\r\n action: ep.method,\r\n endpoint: ep,\r\n description: ep.description || `${ep.method} ${ep.path}`,\r\n assertions: [],\r\n }));\r\n\r\n chains.push({ name: `${resource} CRUD chain`, module: moduleName, steps });\r\n totalSteps += steps.length;\r\n }\r\n\r\n return { chains, totalSteps };\r\n}\r\n","import chalk from 'chalk';\r\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\r\nimport { dirname } from 'node:path';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport type { PipelineStep, PipelineRunResult } from '../../types.js';\r\n\r\nconst VALID_STEPS: PipelineStep[] = ['scan', 'er-diagram', 'api-chain', 'plan', 'codegen', 'validate'];\r\n\r\nexport interface GenerateOptions {\r\n module?: string;\r\n all?: boolean;\r\n steps?: string;\r\n dryRun?: boolean;\r\n}\r\n\r\nfunction parseSteps(raw?: string): PipelineStep[] | undefined {\r\n if (!raw) return undefined;\r\n const names = raw.split(',').map((s) => s.trim());\r\n for (const name of names) {\r\n if (!VALID_STEPS.includes(name as PipelineStep)) {\r\n throw new Error(`Unknown pipeline step \"${name}\". Valid steps: ${VALID_STEPS.join(', ')}`);\r\n }\r\n }\r\n return names as PipelineStep[];\r\n}\r\n\r\nfunction writeGeneratedFiles(result: PipelineRunResult): number {\r\n let written = 0;\r\n for (const file of result.generatedFiles) {\r\n const dir = dirname(file.filePath);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n writeFileSync(file.filePath, file.content, 'utf-8');\r\n written++;\r\n console.log(chalk.green(` ✓ ${file.filePath}`));\r\n }\r\n return written;\r\n}\r\n\r\nfunction printSummary(result: PipelineRunResult, dryRun: boolean): void {\r\n console.log('');\r\n console.log(chalk.cyan.bold(' Summary'));\r\n console.log(` Modules discovered : ${result.modules.length}`);\r\n console.log(` ER diagrams : ${result.erDiagrams.size}`);\r\n console.log(` Chain plans : ${result.chainPlans.size}`);\r\n console.log(` Generated files : ${result.generatedFiles.length}${dryRun ? ' (dry-run, not written)' : ''}`);\r\n\r\n if (result.validationErrors.length > 0) {\r\n const errors = result.validationErrors.filter((e) => e.severity === 'error');\r\n const warnings = result.validationErrors.filter((e) => e.severity === 'warning');\r\n if (errors.length > 0) console.log(chalk.red(` Errors : ${errors.length}`));\r\n if (warnings.length > 0) console.log(chalk.yellow(` Warnings : ${warnings.length}`));\r\n\r\n for (const err of result.validationErrors) {\r\n const icon = err.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');\r\n console.log(` ${icon} [${err.module}] ${err.message}`);\r\n }\r\n }\r\n\r\n console.log(chalk.gray(` Duration : ${result.duration}ms`));\r\n console.log('');\r\n}\r\n\r\nexport async function generate(opts: GenerateOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Generate E2E Tests\\n'));\r\n\r\n // Load config\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n // Apply --module filter\r\n if (opts.module) {\r\n config.modules = [opts.module];\r\n }\r\n\r\n // Parse --steps\r\n const steps = parseSteps(opts.steps);\r\n\r\n // Create and run pipeline\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run(steps);\r\n\r\n // Write files (unless dry-run)\r\n if (!opts.dryRun && result.generatedFiles.length > 0) {\r\n console.log('');\r\n console.log(chalk.cyan(' Generated files:'));\r\n writeGeneratedFiles(result);\r\n } else if (opts.dryRun && result.generatedFiles.length > 0) {\r\n console.log('');\r\n console.log(chalk.yellow(' Dry-run — files that would be generated:'));\r\n for (const file of result.generatedFiles) {\r\n console.log(chalk.gray(` ${file.filePath}`));\r\n }\r\n }\r\n\r\n printSummary(result, !!opts.dryRun);\r\n}\r\n","import chalk from 'chalk';\r\nimport { readdirSync, existsSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { execFileSync } from 'node:child_process';\r\nimport { loadConfig } from '../load-config.js';\r\nimport type { ExecutionConfig, HookConfig } from '../../types.js';\r\n\r\nexport interface TestOptions {\r\n module?: string;\r\n headed?: boolean;\r\n setupHook?: string;\r\n authHook?: string;\r\n teardownHook?: string;\r\n}\r\n\r\nfunction normalizeHook(hook: HookConfig | undefined): HookConfig | undefined {\r\n if (!hook) return undefined;\r\n if (typeof hook === 'string') return hook.trim() ? hook : undefined;\r\n return hook.command.trim() ? hook : undefined;\r\n}\r\n\r\nfunction runShellCommand(command: string): void {\r\n if (process.platform === 'win32') {\r\n execFileSync('cmd.exe', ['/d', '/s', '/c', command], {\r\n stdio: 'inherit',\r\n cwd: process.cwd(),\r\n });\r\n return;\r\n }\r\n\r\n execFileSync('sh', ['-lc', command], {\r\n stdio: 'inherit',\r\n cwd: process.cwd(),\r\n });\r\n}\r\n\r\nfunction runHook(name: string, hook: HookConfig | undefined): void {\r\n const normalized = normalizeHook(hook);\r\n if (!normalized) return;\r\n\r\n console.log(chalk.cyan(` Running ${name} hook...`));\r\n\r\n if (typeof normalized === 'string') {\r\n runShellCommand(normalized);\r\n console.log(chalk.green(` ✓ ${name} hook passed`));\r\n return;\r\n }\r\n\r\n execFileSync(normalized.command, normalized.args ?? [], {\r\n stdio: 'inherit',\r\n cwd: normalized.cwd ? resolve(normalized.cwd) : process.cwd(),\r\n });\r\n console.log(chalk.green(` ✓ ${name} hook passed`));\r\n}\r\n\r\nfunction discoverTestFiles(outDir: string, moduleFilter?: string): string[] {\r\n const absDir = resolve(outDir);\r\n if (!existsSync(absDir)) return [];\r\n\r\n const files: string[] = [];\r\n const entries = readdirSync(absDir, { withFileTypes: true, recursive: true });\r\n for (const entry of entries) {\r\n if (!entry.isFile()) continue;\r\n if (!entry.name.endsWith('.spec.ts') && !entry.name.endsWith('.test.ts')) continue;\r\n const fullPath = join(entry.parentPath || (entry as unknown as { path: string }).path || absDir, entry.name);\r\n if (moduleFilter && !fullPath.includes(moduleFilter)) continue;\r\n files.push(fullPath);\r\n }\r\n return files;\r\n}\r\n\r\nexport async function runTests(opts: TestOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Run E2E Tests\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const execution: ExecutionConfig = {\r\n ...(config.execution || {}),\r\n ...(opts.setupHook ? { setupHook: opts.setupHook } : {}),\r\n ...(opts.authHook ? { authHook: opts.authHook } : {}),\r\n ...(opts.teardownHook ? { teardownHook: opts.teardownHook } : {}),\r\n };\r\n\r\n try {\r\n runHook('setup', execution.setupHook);\r\n runHook('auth', execution.authHook);\r\n } catch {\r\n console.log(chalk.red(' ✗ setup/auth hook failed. Abort test run.\\n'));\r\n process.exitCode = 1;\r\n try {\r\n runHook('teardown', execution.teardownHook);\r\n } catch {\r\n console.log(chalk.red(' ✗ teardown hook also failed.\\n'));\r\n }\r\n return;\r\n }\r\n\r\n const outDir = config.outDir || './opencroc-output';\r\n const testFiles = discoverTestFiles(outDir, opts.module);\r\n\r\n if (testFiles.length === 0) {\r\n console.log(chalk.yellow(' No test files found. Run `opencroc generate` first.\\n'));\r\n return;\r\n }\r\n\r\n console.log(` Found ${testFiles.length} test file(s)`);\r\n for (const f of testFiles) {\r\n console.log(chalk.gray(` ${f}`));\r\n }\r\n console.log('');\r\n\r\n // Build Playwright args\r\n const args = ['test', ...testFiles];\r\n if (!opts.headed) {\r\n args.push('--reporter=list');\r\n } else {\r\n args.push('--headed');\r\n }\r\n\r\n const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n try {\r\n console.log(chalk.cyan(' Running Playwright...\\n'));\r\n execFileSync(npxCmd, ['playwright', ...args], {\r\n stdio: 'inherit',\r\n cwd: process.cwd(),\r\n });\r\n console.log(chalk.green('\\n ✓ All tests passed.\\n'));\r\n } catch {\r\n console.log(chalk.red('\\n ✗ Some tests failed.\\n'));\r\n process.exitCode = 1;\r\n } finally {\r\n try {\r\n runHook('teardown', execution.teardownHook);\r\n } catch {\r\n console.log(chalk.red(' ✗ teardown hook failed.\\n'));\r\n process.exitCode = 1;\r\n }\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { validateConfig } from '../../validators/config-validator.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport type { ValidationError } from '../../types.js';\r\n\r\nexport interface ValidateOptions {\r\n module?: string;\r\n}\r\n\r\nfunction printErrors(errors: ValidationError[]): void {\r\n for (const err of errors) {\r\n const icon = err.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');\r\n const scope = err.module === 'config' ? '' : ` [${err.module}]`;\r\n console.log(` ${icon}${scope} ${err.field}: ${err.message}`);\r\n }\r\n}\r\n\r\nexport async function validate(opts: ValidateOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Validate\\n'));\r\n\r\n // Load and validate config\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const configErrors = validateConfig(config as unknown as Record<string, unknown>);\r\n\r\n // Apply module filter\r\n if (opts.module) {\r\n config.modules = [opts.module];\r\n }\r\n\r\n // Run pipeline in scan + validate mode to discover module-level issues\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run(['scan', 'validate']);\r\n\r\n const allErrors = [...configErrors, ...result.validationErrors];\r\n const errors = allErrors.filter((e) => e.severity === 'error');\r\n const warnings = allErrors.filter((e) => e.severity === 'warning');\r\n\r\n if (allErrors.length === 0) {\r\n console.log(chalk.green(' ✓ Configuration is valid.'));\r\n console.log(chalk.gray(` Modules: ${result.modules.join(', ') || '(none)'}\\n`));\r\n return;\r\n }\r\n\r\n if (errors.length > 0) {\r\n console.log(chalk.red(` ${errors.length} error(s):`));\r\n printErrors(errors);\r\n }\r\n if (warnings.length > 0) {\r\n console.log(chalk.yellow(` ${warnings.length} warning(s):`));\r\n printErrors(warnings);\r\n }\r\n\r\n console.log('');\r\n\r\n if (errors.length > 0) {\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import type { LlmProvider, LlmConfig } from '../types.js';\r\n\r\nexport interface ChatMessage {\r\n role: 'system' | 'user' | 'assistant';\r\n content: string;\r\n}\r\n\r\ninterface OpenAIResponse {\r\n choices: Array<{ message: { content: string } }>;\r\n usage?: { total_tokens: number };\r\n}\r\n\r\nconst DEFAULT_MODELS: Record<string, string> = {\r\n openai: 'gpt-4o-mini',\r\n zhipu: 'glm-4',\r\n};\r\n\r\nconst DEFAULT_BASE_URLS: Record<string, string> = {\r\n openai: 'https://api.openai.com/v1',\r\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\r\n};\r\n\r\n/**\r\n * Create an OpenAI-compatible LLM provider.\r\n * Works with OpenAI, Zhipu (GLM), and any OpenAI-compatible API.\r\n */\r\nexport function createOpenAIProvider(config: LlmConfig): LlmProvider {\r\n const provider = config.provider === 'zhipu' ? 'zhipu' : 'openai';\r\n const baseUrl = config.baseUrl || DEFAULT_BASE_URLS[provider];\r\n const model = config.model || DEFAULT_MODELS[provider];\r\n const maxTokens = config.maxTokens || 2048;\r\n const temperature = config.temperature ?? 0.3;\r\n\r\n if (!config.apiKey) {\r\n throw new Error(\r\n `API key is required for ${provider}. Set it in config or via OPENCROC_LLM_API_KEY env variable.`,\r\n );\r\n }\r\n\r\n return {\r\n name: provider,\r\n\r\n async chat(messages: Array<{ role: string; content: string }>): Promise<string> {\r\n const url = `${baseUrl}/chat/completions`;\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${config.apiKey}`,\r\n },\r\n body: JSON.stringify({\r\n model,\r\n messages,\r\n max_tokens: maxTokens,\r\n temperature,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'unknown error');\r\n throw new Error(`LLM API error (${response.status}): ${errorText}`);\r\n }\r\n\r\n const data = (await response.json()) as OpenAIResponse;\r\n const content = data.choices?.[0]?.message?.content;\r\n if (!content) {\r\n throw new Error('LLM returned empty response');\r\n }\r\n return content;\r\n },\r\n\r\n estimateTokens(text: string): number {\r\n // Rough estimate: ~4 chars per token for English, ~2 for CJK\r\n const cjkChars = (text.match(/[\\u4e00-\\u9fff\\u3000-\\u303f]/g) || []).length;\r\n const otherChars = text.length - cjkChars;\r\n return Math.ceil(otherChars / 4 + cjkChars / 2);\r\n },\r\n };\r\n}\r\n","import type { LlmProvider, LlmConfig } from '../types.js';\r\n\r\ninterface OllamaResponse {\r\n message: { content: string };\r\n}\r\n\r\n/**\r\n * Create an Ollama LLM provider for local model inference.\r\n */\r\nexport function createOllamaProvider(config: LlmConfig): LlmProvider {\r\n const baseUrl = config.baseUrl || 'http://localhost:11434';\r\n const model = config.model || 'llama3';\r\n\r\n return {\r\n name: 'ollama',\r\n\r\n async chat(messages: Array<{ role: string; content: string }>): Promise<string> {\r\n const url = `${baseUrl}/api/chat`;\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n model,\r\n messages,\r\n stream: false,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'unknown error');\r\n throw new Error(`Ollama API error (${response.status}): ${errorText}`);\r\n }\r\n\r\n const data = (await response.json()) as OllamaResponse;\r\n const content = data.message?.content;\r\n if (!content) {\r\n throw new Error('Ollama returned empty response');\r\n }\r\n return content;\r\n },\r\n\r\n estimateTokens(text: string): number {\r\n // Same rough estimate as OpenAI provider\r\n const cjkChars = (text.match(/[\\u4e00-\\u9fff\\u3000-\\u303f]/g) || []).length;\r\n const otherChars = text.length - cjkChars;\r\n return Math.ceil(otherChars / 4 + cjkChars / 2);\r\n },\r\n };\r\n}\r\n","import type { LlmProvider, LlmConfig } from '../types.js';\r\nimport { createOpenAIProvider } from './openai.js';\r\nimport { createOllamaProvider } from './ollama.js';\r\n\r\nexport { createOpenAIProvider } from './openai.js';\r\nexport { createOllamaProvider } from './ollama.js';\r\n\r\n/**\r\n * Create an LLM provider from config.\r\n * Resolves apiKey from config or OPENCROC_LLM_API_KEY env variable.\r\n */\r\nexport function createLlmProvider(config: LlmConfig): LlmProvider {\r\n const resolved: LlmConfig = {\r\n ...config,\r\n apiKey: config.apiKey || process.env.OPENCROC_LLM_API_KEY,\r\n };\r\n\r\n switch (config.provider) {\r\n case 'openai':\r\n case 'zhipu':\r\n return createOpenAIProvider(resolved);\r\n case 'ollama':\r\n return createOllamaProvider(resolved);\r\n default:\r\n throw new Error(\r\n `Unknown LLM provider: \"${config.provider}\". Available: openai, zhipu, ollama`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Token usage tracker — accumulates tokens across multiple LLM calls.\r\n */\r\nexport interface TokenTracker {\r\n track(text: string): void;\r\n trackChat(messages: Array<{ role: string; content: string }>, response: string): void;\r\n total: number;\r\n reset(): void;\r\n}\r\n\r\nexport function createTokenTracker(provider: LlmProvider): TokenTracker {\r\n let total = 0;\r\n\r\n return {\r\n track(text: string) {\r\n total += provider.estimateTokens(text);\r\n },\r\n\r\n trackChat(messages: Array<{ role: string; content: string }>, response: string) {\r\n for (const msg of messages) {\r\n total += provider.estimateTokens(msg.content);\r\n }\r\n total += provider.estimateTokens(response);\r\n },\r\n\r\n get total() {\r\n return total;\r\n },\r\n\r\n reset() {\r\n total = 0;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * System prompts for different LLM use cases in OpenCroc.\r\n */\r\nexport const SYSTEM_PROMPTS = {\r\n failureAnalysis: `You are an expert test failure analyst for an E2E testing framework.\r\nGiven a test failure error message and its context, analyze the root cause and suggest a fix.\r\nRespond in JSON format: { \"rootCause\": string, \"category\": string, \"suggestedFix\": string, \"confidence\": number }\r\nCategories: backend-5xx, timeout, endpoint-not-found, data-constraint, network, frontend-render, test-script, unknown.`,\r\n\r\n chainPlanning: `You are an API test chain planner.\r\nGiven a list of API endpoints and their dependencies, generate an optimal test execution order.\r\nConsider data dependencies, authentication requirements, and cleanup steps.\r\nRespond in JSON format: { \"chains\": [{ \"name\": string, \"steps\": [{ \"endpoint\": string, \"method\": string, \"description\": string }] }] }`,\r\n} as const;\r\n","/**\r\n * Dialog Loop Runner — multi-iteration self-healing loop.\r\n *\r\n * Runs tests, parses results, applies controlled fixes, and reruns\r\n * until all tests pass or the maximum iteration count is reached.\r\n * Tracks error history to avoid infinite loops on recurring failures.\r\n */\r\n\r\nimport type {\r\n DialogLoopConfig,\r\n TestFailureInfo,\r\n IterationResult,\r\n DialogLoopSummary,\r\n ControlledFixOutcome,\r\n} from '../types.js';\r\n\r\n// ===== Abstractions for testability =====\r\n\r\nexport interface TestRunner {\r\n run(): Promise<{ stdout: string; exitCode: number }>;\r\n}\r\n\r\nexport interface ResultParser {\r\n parse(stdout: string): TestFailureInfo[];\r\n countTotal(stdout: string): number;\r\n}\r\n\r\nexport interface FixApplier {\r\n apply(failure: TestFailureInfo): Promise<ControlledFixOutcome>;\r\n}\r\n\r\n// ===== Defaults =====\r\n\r\nconst DEFAULTS: Required<DialogLoopConfig> = {\r\n maxIterations: 3,\r\n pollIntervalMs: 10_000,\r\n sameErrorThreshold: 2,\r\n autoRerunOnFix: true,\r\n};\r\n\r\n// ===== JSON result parser (reads Playwright JSON output) =====\r\n\r\nexport function createJsonResultParser(): ResultParser {\r\n return {\r\n parse(stdout: string): TestFailureInfo[] {\r\n const failures: TestFailureInfo[] = [];\r\n // Match pass/fail summary from Playwright output\r\n const lines = stdout.split('\\n');\r\n for (const line of lines) {\r\n // Playwright format: \" ✘ [chromium] › test.spec.ts:10:5 › suite › title\"\r\n // or stderr lines with \"Error:\" prefix\r\n const failMatch = line.match(/[✘✗×]\\s+.*?›\\s+(.+)/);\r\n if (failMatch) {\r\n failures.push({\r\n title: failMatch[1].trim(),\r\n error: failMatch[1].trim(),\r\n });\r\n }\r\n }\r\n return failures;\r\n },\r\n countTotal(stdout: string): number {\r\n // Match \"X passed\" or \"X failed\" from Playwright summary\r\n let total = 0;\r\n const passMatch = stdout.match(/(\\d+)\\s+passed/);\r\n const failMatch = stdout.match(/(\\d+)\\s+failed/);\r\n if (passMatch) total += parseInt(passMatch[1], 10);\r\n if (failMatch) total += parseInt(failMatch[1], 10);\r\n return total || 1; // at least 1 to avoid division by zero\r\n },\r\n };\r\n}\r\n\r\n// ===== Dialog Loop =====\r\n\r\nexport interface DialogLoopOptions {\r\n runner: TestRunner;\r\n parser: ResultParser;\r\n fixer: FixApplier;\r\n config?: DialogLoopConfig;\r\n onIteration?: (result: IterationResult) => void;\r\n}\r\n\r\nexport async function runDialogLoop(options: DialogLoopOptions): Promise<DialogLoopSummary> {\r\n const cfg = { ...DEFAULTS, ...options.config };\r\n const { runner, parser, fixer } = options;\r\n\r\n const history: IterationResult[] = [];\r\n const errorTracker = new Map<string, number>();\r\n\r\n for (let iteration = 1; iteration <= cfg.maxIterations + 1; iteration++) {\r\n const iterStart = Date.now();\r\n\r\n // Step 1: Run tests\r\n const { stdout } = await runner.run();\r\n\r\n // Step 2: Parse results\r\n const failures = parser.parse(stdout);\r\n const totalTests = parser.countTotal(stdout);\r\n const passed = totalTests - failures.length;\r\n\r\n const iterResult: IterationResult = {\r\n iteration,\r\n totalTests,\r\n passed,\r\n failed: failures.length,\r\n failedTests: failures.map(f => f.title),\r\n fixesApplied: [],\r\n durationMs: 0,\r\n };\r\n\r\n // Step 3: All passed → success\r\n if (failures.length === 0) {\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n break;\r\n }\r\n\r\n // Step 4: Max iterations exceeded\r\n if (iteration > cfg.maxIterations) {\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n break;\r\n }\r\n\r\n // Step 5: Filter out repeated errors beyond threshold\r\n const newFailures = failures.filter(f => {\r\n const key = `${f.title}::${f.error}`;\r\n const count = (errorTracker.get(key) ?? 0) + 1;\r\n errorTracker.set(key, count);\r\n return count <= cfg.sameErrorThreshold;\r\n });\r\n\r\n if (newFailures.length === 0) {\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n break;\r\n }\r\n\r\n // Step 6: Apply controlled fixes\r\n for (const failure of newFailures) {\r\n const outcome = await fixer.apply(failure);\r\n if (outcome.success) {\r\n iterResult.fixesApplied.push(failure.title);\r\n }\r\n }\r\n\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n\r\n // Step 7: If any fixes applied and autoRerun, continue loop\r\n if (iterResult.fixesApplied.length === 0 || !cfg.autoRerunOnFix) {\r\n break;\r\n }\r\n }\r\n\r\n const final = history[history.length - 1];\r\n const totalFixesApplied = history.reduce((sum, h) => sum + h.fixesApplied.length, 0);\r\n\r\n return {\r\n iterations: history,\r\n finalPassed: final?.passed ?? 0,\r\n finalFailed: final?.failed ?? 0,\r\n totalFixesApplied,\r\n success: (final?.failed ?? 1) === 0,\r\n };\r\n}\r\n","/**\r\n * Controlled Fixer — two-phase fix engine with safety guarantees.\r\n *\r\n * Phase A (config-only): backup → validate → fix → dry-run → write → verify → cleanup.\r\n * Phase B (config-and-source): generates a draft PR with the AI-suggested code patch.\r\n *\r\n * All mutations are reversible — failures trigger automatic rollback.\r\n */\r\n\r\nimport { existsSync, copyFileSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';\r\nimport { dirname } from 'node:path';\r\nimport type {\r\n FixScope,\r\n ControlledFixOptions,\r\n ControlledFixOutcome,\r\n AIAttributionResult,\r\n} from '../types.js';\r\n\r\n// ===== Abstractions for testability =====\r\n\r\nexport interface ConfigValidator {\r\n validate(configContent: string): { passed: boolean; errors: string[] };\r\n}\r\n\r\nexport interface ConfigFixer {\r\n fix(configContent: string, errors: string[]): { success: boolean; fixedContent: string; fixedItems: string[]; remainingErrors: string[] };\r\n}\r\n\r\nexport interface PRGenerator {\r\n generate(attribution: AIAttributionResult): Promise<string>;\r\n}\r\n\r\n// ===== FS abstraction (injectable for tests) =====\r\n\r\nexport interface FsOps {\r\n exists(path: string): boolean;\r\n read(path: string): string;\r\n write(path: string, content: string): void;\r\n copy(src: string, dest: string): void;\r\n remove(path: string): void;\r\n mkdirp(dir: string): void;\r\n}\r\n\r\nconst defaultFs: FsOps = {\r\n exists: existsSync,\r\n read: (p) => readFileSync(p, 'utf-8'),\r\n write: (p, c) => { mkdirSync(dirname(p), { recursive: true }); writeFileSync(p, c, 'utf-8'); },\r\n copy: copyFileSync,\r\n remove: unlinkSync,\r\n mkdirp: (d) => mkdirSync(d, { recursive: true }),\r\n};\r\n\r\n// ===== Core =====\r\n\r\nexport interface ControlledFixerOptions {\r\n configPath: string;\r\n validator: ConfigValidator;\r\n fixer: ConfigFixer;\r\n prGenerator?: PRGenerator;\r\n attribution?: AIAttributionResult;\r\n fs?: FsOps;\r\n options?: ControlledFixOptions;\r\n}\r\n\r\nexport async function applyControlledFix(opts: ControlledFixerOptions): Promise<ControlledFixOutcome> {\r\n const fs = opts.fs ?? defaultFs;\r\n const scope: FixScope = opts.options?.scope ?? 'config-only';\r\n const dryRun = opts.options?.dryRun ?? true;\r\n const verify = opts.options?.verify ?? true;\r\n const configPath = opts.configPath;\r\n const backupPath = configPath + '.backup';\r\n\r\n // --- Phase A: Config-only fix ---\r\n\r\n // Load config\r\n if (!fs.exists(configPath)) {\r\n return { success: false, scope, fixedItems: [], rolledBack: false, error: `Config file not found: ${configPath}` };\r\n }\r\n\r\n const originalContent = fs.read(configPath);\r\n\r\n // Backup before mutation\r\n fs.write(backupPath, originalContent);\r\n\r\n // Validate current config\r\n const validation = opts.validator.validate(originalContent);\r\n\r\n if (validation.passed) {\r\n // No errors to fix\r\n cleanup(fs, backupPath);\r\n return { success: true, scope, fixedItems: [], rolledBack: false };\r\n }\r\n\r\n // Attempt fix\r\n let fixResult: ReturnType<ConfigFixer['fix']>;\r\n try {\r\n fixResult = opts.fixer.fix(originalContent, validation.errors);\r\n } catch (err) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: [], rolledBack: true, error: `Fix threw: ${err instanceof Error ? err.message : String(err)}` };\r\n }\r\n\r\n if (!fixResult.success) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: fixResult.fixedItems, rolledBack: true, error: `Remaining errors: ${fixResult.remainingErrors.join('; ')}` };\r\n }\r\n\r\n // Dry-run: validate fixed content before writing\r\n if (dryRun) {\r\n const dryValidation = opts.validator.validate(fixResult.fixedContent);\r\n if (!dryValidation.passed) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: fixResult.fixedItems, rolledBack: true, error: `Dry-run validation failed: ${dryValidation.errors.join('; ')}` };\r\n }\r\n }\r\n\r\n // Write fixed content\r\n fs.write(configPath, fixResult.fixedContent);\r\n\r\n // Verify after write\r\n if (verify) {\r\n const reloaded = fs.read(configPath);\r\n const postValidation = opts.validator.validate(reloaded);\r\n if (!postValidation.passed) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: fixResult.fixedItems, rolledBack: true, error: `Post-write verification failed: ${postValidation.errors.join('; ')}` };\r\n }\r\n }\r\n\r\n // Phase A success — clean up backup\r\n cleanup(fs, backupPath);\r\n\r\n // --- Phase B: Config-and-source (optional) ---\r\n let prUrl: string | undefined;\r\n if (scope === 'config-and-source' && opts.attribution && opts.prGenerator) {\r\n try {\r\n prUrl = await opts.prGenerator.generate(opts.attribution);\r\n } catch {\r\n // PR generation failure is non-fatal; config fix already succeeded\r\n }\r\n }\r\n\r\n return { success: true, scope, fixedItems: fixResult.fixedItems, rolledBack: false, prUrl };\r\n}\r\n\r\nfunction rollback(fs: FsOps, backupPath: string, configPath: string): void {\r\n if (fs.exists(backupPath)) {\r\n const backup = fs.read(backupPath);\r\n fs.write(configPath, backup);\r\n fs.remove(backupPath);\r\n }\r\n}\r\n\r\nfunction cleanup(fs: FsOps, backupPath: string): void {\r\n if (fs.exists(backupPath)) {\r\n fs.remove(backupPath);\r\n }\r\n}\r\n","/**\r\n * Auto-Fix PR Generator — creates draft PRs from AI attribution results.\r\n *\r\n * Flow: create branch → write patch → git apply → commit → push → gh pr create --draft.\r\n * All PRs are draft-only as a safety invariant.\r\n */\r\n\r\nimport type { AIAttributionResult, AutoFixPROptions, AutoFixPRResult } from '../types.js';\r\n\r\n// ===== Executor abstraction (injectable for tests) =====\r\n\r\nexport interface GitExecutor {\r\n exec(command: string, args: string[]): Promise<{ stdout: string; exitCode: number }>;\r\n}\r\n\r\nexport interface PatchWriter {\r\n write(path: string, content: string): Promise<void>;\r\n mkdir(dir: string): Promise<void>;\r\n}\r\n\r\n// ===== Defaults =====\r\n\r\nconst DEFAULT_OPTIONS: Required<AutoFixPROptions> = {\r\n branchPrefix: 'autofix/',\r\n baseBranch: 'main',\r\n draftOnly: true,\r\n};\r\n\r\n// ===== Core =====\r\n\r\nexport async function generateFixPR(\r\n attribution: AIAttributionResult,\r\n git: GitExecutor,\r\n patchWriter: PatchWriter,\r\n options?: AutoFixPROptions,\r\n): Promise<AutoFixPRResult> {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\r\n const branch = `${opts.branchPrefix}${ts}`;\r\n const patchFile = `report/patch-${ts}.patch`;\r\n\r\n // Create branch\r\n await git.exec('git', ['checkout', '-b', branch]);\r\n\r\n // Write patch\r\n await patchWriter.mkdir('report');\r\n await patchWriter.write(patchFile, attribution.fixSuggestion.codePatch);\r\n\r\n // Apply patch (non-fatal if fails)\r\n try {\r\n await git.exec('git', ['apply', patchFile]);\r\n } catch {\r\n // Patch may not apply cleanly — continue anyway\r\n }\r\n\r\n // Stage, commit, push\r\n await git.exec('git', ['add', '.']);\r\n await git.exec('git', ['commit', '-m', `fix: AI auto-patch for \"${attribution.testName}\"`]);\r\n await git.exec('git', ['push', 'origin', branch]);\r\n\r\n // Create draft PR (always draft for safety)\r\n const prArgs = [\r\n 'pr', 'create', '--draft',\r\n '--title', `[AI Fix] ${attribution.testName}`,\r\n '--body', buildPRBody(attribution),\r\n ];\r\n const { stdout: prUrl } = await git.exec('gh', prArgs);\r\n\r\n // Return to base branch\r\n await git.exec('git', ['checkout', opts.baseBranch]);\r\n\r\n return { prUrl: prUrl.trim(), branch, patchFile };\r\n}\r\n\r\nfunction buildPRBody(a: AIAttributionResult): string {\r\n return [\r\n '## AI Auto-Fix PR',\r\n '',\r\n `**Test:** ${a.testName}`,\r\n `**Category:** ${a.category} | **Severity:** ${a.severity} | **Confidence:** ${(a.confidence * 100).toFixed(0)}%`,\r\n '',\r\n '### Root Cause',\r\n a.rootCause,\r\n '',\r\n '### Fix',\r\n a.fixSuggestion.description,\r\n '',\r\n '---',\r\n '> This PR was auto-generated by AI and **must be reviewed before merging**.',\r\n ].join('\\n');\r\n}\r\n","import type { SelfHealingConfig, SelfHealingResult, FixOutcome, LlmProvider } from '../types.js';\r\nimport { SYSTEM_PROMPTS } from '../llm/index.js';\r\n\r\nexport type { SelfHealingResult, FixOutcome };\r\n\r\n// Advanced Self-Healing (v1.1)\r\nexport { runDialogLoop, createJsonResultParser } from './dialog-loop-runner.js';\r\nexport type { TestRunner, ResultParser, FixApplier, DialogLoopOptions } from './dialog-loop-runner.js';\r\nexport { applyControlledFix } from './controlled-fixer.js';\r\nexport type { ConfigValidator, ConfigFixer, PRGenerator, FsOps, ControlledFixerOptions } from './controlled-fixer.js';\r\nexport { generateFixPR } from './auto-fix-generator.js';\r\nexport type { GitExecutor, PatchWriter } from './auto-fix-generator.js';\r\n\r\nexport interface SelfHealingLoop {\r\n run(testResultsDir: string): Promise<SelfHealingResult>;\r\n}\r\n\r\nexport interface SelfHealingOptions {\r\n config: SelfHealingConfig;\r\n llm?: LlmProvider;\r\n}\r\n\r\n/**\r\n * Categorize a test failure by heuristic rules.\r\n */\r\nexport function categorizeFailure(errorMessage: string): {\r\n category: string;\r\n confidence: number;\r\n} {\r\n const msg = errorMessage.toLowerCase();\r\n\r\n if (/5\\d{2}|internal server error/.test(msg))\r\n return { category: 'backend-5xx', confidence: 0.9 };\r\n if (/timeout|timed?\\s*out/.test(msg))\r\n return { category: 'timeout', confidence: 0.8 };\r\n if (/404|not found/.test(msg))\r\n return { category: 'endpoint-not-found', confidence: 0.85 };\r\n if (/4[0-2]\\d|validation|constraint/.test(msg))\r\n return { category: 'data-constraint', confidence: 0.75 };\r\n if (/econnrefused|enotfound|network/.test(msg))\r\n return { category: 'network', confidence: 0.9 };\r\n if (/selector|locator|element/.test(msg))\r\n return { category: 'frontend-render', confidence: 0.7 };\r\n if (/storage\\s*state|auth|login/.test(msg))\r\n return { category: 'test-script', confidence: 0.8 };\r\n\r\n return { category: 'unknown', confidence: 0.5 };\r\n}\r\n\r\n/**\r\n * LLM-enhanced failure analysis with heuristic fallback.\r\n */\r\nexport async function analyzeFailureWithLLM(\r\n errorMessage: string,\r\n llm?: LlmProvider,\r\n): Promise<{ rootCause: string; category: string; suggestedFix: string; confidence: number }> {\r\n // Always get heuristic result as fallback\r\n const heuristic = categorizeFailure(errorMessage);\r\n\r\n if (!llm) {\r\n return {\r\n rootCause: errorMessage,\r\n category: heuristic.category,\r\n suggestedFix: '',\r\n confidence: heuristic.confidence,\r\n };\r\n }\r\n\r\n try {\r\n const response = await llm.chat([\r\n { role: 'system', content: SYSTEM_PROMPTS.failureAnalysis },\r\n { role: 'user', content: `Analyze this test failure:\\n\\n${errorMessage}` },\r\n ]);\r\n\r\n const parsed = JSON.parse(response) as {\r\n rootCause?: string;\r\n category?: string;\r\n suggestedFix?: string;\r\n confidence?: number;\r\n };\r\n\r\n return {\r\n rootCause: parsed.rootCause || errorMessage,\r\n category: parsed.category || heuristic.category,\r\n suggestedFix: parsed.suggestedFix || '',\r\n confidence: parsed.confidence || heuristic.confidence,\r\n };\r\n } catch {\r\n // LLM failed — fall back to heuristic\r\n return {\r\n rootCause: errorMessage,\r\n category: heuristic.category,\r\n suggestedFix: '',\r\n confidence: heuristic.confidence,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Attempt a config-only fix: validate and write corrected config JSON.\r\n */\r\nasync function attemptConfigFix(\r\n _testResultsDir: string,\r\n _mode: SelfHealingConfig['mode'],\r\n _llm?: LlmProvider,\r\n): Promise<FixOutcome> {\r\n // TODO: Load module config → run autoFix validation → write corrected JSON\r\n // For now, return a no-op outcome\r\n return {\r\n success: false,\r\n scope: 'config-only',\r\n fixedItems: [],\r\n rolledBack: false,\r\n };\r\n}\r\n\r\n/**\r\n * Create a self-healing loop. Accepts an optional LLM provider for AI-enhanced analysis.\r\n */\r\nexport function createSelfHealingLoop(config: SelfHealingConfig, llm?: LlmProvider): SelfHealingLoop {\r\n return {\r\n async run(testResultsDir: string): Promise<SelfHealingResult> {\r\n const maxIterations = config.maxIterations || 3;\r\n const mode = config.mode || 'config-only';\r\n const fixed: string[] = [];\r\n const remaining: string[] = [];\r\n let iterations = 0;\r\n let totalTokensUsed = 0;\r\n\r\n for (let i = 0; i < maxIterations; i++) {\r\n iterations = i + 1;\r\n\r\n const outcome = await attemptConfigFix(testResultsDir, mode, llm);\r\n if (outcome.success) {\r\n fixed.push(...outcome.fixedItems);\r\n } else {\r\n remaining.push(`iteration-${i + 1}: no fix applied`);\r\n }\r\n\r\n // Track token usage if LLM is available\r\n if (llm) {\r\n totalTokensUsed += llm.estimateTokens(`iteration-${i + 1}`);\r\n }\r\n\r\n // If all fixed, stop early\r\n if (outcome.success && outcome.fixedItems.length > 0) break;\r\n }\r\n\r\n return {\r\n iterations,\r\n fixed,\r\n remaining,\r\n totalTokensUsed,\r\n };\r\n },\r\n };\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createSelfHealingLoop } from '../../self-healing/index.js';\r\nimport type { SelfHealingConfig } from '../../types.js';\r\n\r\nexport interface HealOptions {\r\n module?: string;\r\n maxIterations?: string;\r\n}\r\n\r\nexport async function heal(opts: HealOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Self-Healing\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const outDir = config.outDir || './opencroc-output';\r\n const maxIterations = opts.maxIterations ? parseInt(opts.maxIterations, 10) : 3;\r\n\r\n const healingConfig: SelfHealingConfig = {\r\n enabled: true,\r\n maxIterations,\r\n mode: config.selfHealing?.mode || 'config-only',\r\n };\r\n\r\n console.log(chalk.gray(` Mode: ${healingConfig.mode}`));\r\n console.log(chalk.gray(` Max iterations: ${maxIterations}`));\r\n\r\n if (opts.module) {\r\n console.log(chalk.gray(` Module: ${opts.module}`));\r\n }\r\n console.log('');\r\n\r\n const loop = createSelfHealingLoop(healingConfig);\r\n const result = await loop.run(outDir);\r\n\r\n // Report results\r\n console.log(chalk.cyan(' Results:'));\r\n console.log(` Iterations : ${result.iterations}`);\r\n console.log(` Fixed : ${result.fixed.length > 0 ? chalk.green(result.fixed.join(', ')) : chalk.gray('(none)')}`);\r\n console.log(` Remaining : ${result.remaining.length > 0 ? chalk.yellow(result.remaining.join(', ')) : chalk.gray('(none)')}`);\r\n if (result.totalTokensUsed > 0) {\r\n console.log(` Tokens used : ${result.totalTokensUsed}`);\r\n }\r\n\r\n console.log('');\r\n\r\n if (result.remaining.length > 0) {\r\n console.log(chalk.yellow(' Some issues could not be auto-fixed. Manual review needed.\\n'));\r\n } else if (result.fixed.length > 0) {\r\n console.log(chalk.green(' ✓ All issues resolved.\\n'));\r\n } else {\r\n console.log(chalk.gray(' No issues detected.\\n'));\r\n }\r\n}\r\n","/**\r\n * CI template generators for popular CI/CD platforms.\r\n *\r\n * Usage:\r\n * npx opencroc ci --platform=github\r\n * npx opencroc ci --platform=gitlab\r\n */\r\n\r\nexport interface CiTemplateOptions {\r\n /** Node.js version(s). Default: ['20.x'] */\r\n nodeVersions?: string[];\r\n /** Install command. Default: 'npm ci' */\r\n installCommand?: string;\r\n /** Whether to run self-healing. Default: false */\r\n selfHeal?: boolean;\r\n /** Custom opencroc generate args */\r\n generateArgs?: string;\r\n /** Custom opencroc test args */\r\n testArgs?: string;\r\n}\r\n\r\nexport function generateGitHubActionsTemplate(opts: CiTemplateOptions = {}): string {\r\n const nodeVersions = opts.nodeVersions ?? ['20.x'];\r\n const install = opts.installCommand ?? 'npm ci';\r\n const genArgs = opts.generateArgs ?? '--all';\r\n const testArgs = opts.testArgs ?? '';\r\n const healStep = opts.selfHeal\r\n ? `\r\n - name: Self-heal failures\r\n if: failure()\r\n run: npx opencroc heal --max-iterations 3`\r\n : '';\r\n\r\n const matrix =\r\n nodeVersions.length > 1\r\n ? `\r\n strategy:\r\n matrix:\r\n node-version: [${nodeVersions.join(', ')}]`\r\n : '';\r\n\r\n const nodeSetup =\r\n nodeVersions.length > 1\r\n ? '${{ matrix.node-version }}'\r\n : nodeVersions[0];\r\n\r\n return `# Generated by OpenCroc — AI-native E2E testing\r\n# https://github.com/opencroc/opencroc\r\n\r\nname: OpenCroc E2E\r\n\r\non:\r\n push:\r\n branches: [main]\r\n pull_request:\r\n branches: [main]\r\n\r\njobs:\r\n e2e:\r\n runs-on: ubuntu-latest${matrix}\r\n steps:\r\n - uses: actions/checkout@v4\r\n\r\n - uses: actions/setup-node@v4\r\n with:\r\n node-version: '${nodeSetup}'\r\n\r\n - name: Install dependencies\r\n run: ${install}\r\n\r\n - name: Install Playwright browsers\r\n run: npx playwright install --with-deps chromium\r\n\r\n - name: Generate E2E tests\r\n run: npx opencroc generate ${genArgs}\r\n\r\n - name: Run E2E tests\r\n run: npx opencroc test ${testArgs}\r\n${healStep}\r\n - name: Upload test report\r\n if: always()\r\n uses: actions/upload-artifact@v4\r\n with:\r\n name: opencroc-report\r\n path: opencroc-output/\r\n retention-days: 14\r\n`;\r\n}\r\n\r\nexport function generateGitLabCITemplate(opts: CiTemplateOptions = {}): string {\r\n const install = opts.installCommand ?? 'npm ci';\r\n const genArgs = opts.generateArgs ?? '--all';\r\n const testArgs = opts.testArgs ?? '';\r\n const nodeVersion = opts.nodeVersions?.[0] ?? '20';\r\n\r\n return `# Generated by OpenCroc — AI-native E2E testing\r\n# https://github.com/opencroc/opencroc\r\n\r\nimage: node:${nodeVersion}\r\n\r\nstages:\r\n - generate\r\n - test\r\n\r\nvariables:\r\n PLAYWRIGHT_BROWSERS_PATH: \\${CI_PROJECT_DIR}/.cache/ms-playwright\r\n\r\ncache:\r\n key: \\${CI_COMMIT_REF_SLUG}\r\n paths:\r\n - node_modules/\r\n - .cache/ms-playwright/\r\n\r\ngenerate:\r\n stage: generate\r\n script:\r\n - ${install}\r\n - npx opencroc generate ${genArgs}\r\n artifacts:\r\n paths:\r\n - opencroc-output/\r\n expire_in: 1 day\r\n\r\ne2e:\r\n stage: test\r\n needs: [generate]\r\n before_script:\r\n - ${install}\r\n - npx playwright install --with-deps chromium\r\n script:\r\n - npx opencroc test ${testArgs}\r\n artifacts:\r\n when: always\r\n paths:\r\n - opencroc-output/\r\n expire_in: 14 days\r\n`;\r\n}\r\n\r\nconst TEMPLATES: Record<string, (opts: CiTemplateOptions) => string> = {\r\n github: generateGitHubActionsTemplate,\r\n gitlab: generateGitLabCITemplate,\r\n};\r\n\r\n/**\r\n * Get available CI platform names.\r\n */\r\nexport function listCiPlatforms(): string[] {\r\n return Object.keys(TEMPLATES);\r\n}\r\n\r\n/**\r\n * Generate a CI template for the given platform.\r\n */\r\nexport function generateCiTemplate(\r\n platform: string,\r\n opts: CiTemplateOptions = {},\r\n): string {\r\n const generator = TEMPLATES[platform];\r\n if (!generator) {\r\n throw new Error(\r\n `Unknown CI platform: \"${platform}\". Available: ${Object.keys(TEMPLATES).join(', ')}`,\r\n );\r\n }\r\n return generator(opts);\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\nimport { generateCiTemplate, listCiPlatforms } from '../../ci/index.js';\r\nimport type { CiTemplateOptions } from '../../ci/index.js';\r\n\r\nexport interface CiCommandOptions {\r\n platform?: string;\r\n selfHeal?: boolean;\r\n node?: string;\r\n}\r\n\r\nexport async function ci(opts: CiCommandOptions): Promise<void> {\r\n const platform = opts.platform ?? 'github';\r\n const available = listCiPlatforms();\r\n\r\n if (!available.includes(platform)) {\r\n console.error(chalk.red(`Unknown platform: \"${platform}\". Available: ${available.join(', ')}`));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const templateOpts: CiTemplateOptions = {\r\n selfHeal: opts.selfHeal ?? false,\r\n };\r\n if (opts.node) {\r\n templateOpts.nodeVersions = opts.node.split(',').map((s) => s.trim());\r\n }\r\n\r\n const content = generateCiTemplate(platform, templateOpts);\r\n\r\n let outputPath: string;\r\n if (platform === 'github') {\r\n outputPath = path.join('.github', 'workflows', 'opencroc.yml');\r\n } else if (platform === 'gitlab') {\r\n outputPath = '.gitlab-ci.yml';\r\n } else {\r\n outputPath = `opencroc-ci-${platform}.yml`;\r\n }\r\n\r\n const dir = path.dirname(outputPath);\r\n if (dir !== '.' && !fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(outputPath, content, 'utf-8');\r\n console.log(chalk.green(`✔ CI template written to ${outputPath}`));\r\n console.log(chalk.dim(` Platform: ${platform}`));\r\n}\r\n","/**\r\n * Checklist Reporter — generates backend fix checklists from test results.\r\n *\r\n * Groups failures by API domain, lists affected tests and endpoints,\r\n * and produces both structured data and Markdown output.\r\n */\r\n\r\nimport type {\r\n TestResultRecord,\r\n FailureCategory,\r\n FailureSummary,\r\n BackendDomainItem,\r\n LogCompletionSummary,\r\n} from '../types.js';\r\n\r\n// ===== Failure classification =====\r\n\r\nexport function classifyFailure(error?: string): FailureCategory {\r\n if (!error) return 'other';\r\n if (error.includes('[BACKEND_5XX]')) return 'backend-5xx';\r\n if (error.includes('[MIXED_5XX]')) return 'mixed-5xx';\r\n if (error.includes('[SLOW_API_FATAL]')) return 'slow-api';\r\n if (error.includes('[LOG_COMPLETION_FAIL]')) return 'log-fail';\r\n if (error.includes('[LOG_COMPLETION_TIMEOUT]')) return 'log-timeout';\r\n if (/waitForSelector|toHaveURL|Timeout/i.test(error)) return 'frontend-load';\r\n return 'other';\r\n}\r\n\r\n// ===== Failure summary =====\r\n\r\nexport function buildFailureSummary(records: TestResultRecord[]): FailureSummary {\r\n const failed = records.filter(r => r.status === 'failed');\r\n const cats = failed.map(r => classifyFailure(r.error));\r\n return {\r\n totalFailed: failed.length,\r\n backend5xx: cats.filter(c => c === 'backend-5xx').length,\r\n mixed5xx: cats.filter(c => c === 'mixed-5xx').length,\r\n slowApi: cats.filter(c => c === 'slow-api').length,\r\n logFail: cats.filter(c => c === 'log-fail').length,\r\n logTimeout: cats.filter(c => c === 'log-timeout').length,\r\n frontendLoad: cats.filter(c => c === 'frontend-load').length,\r\n other: cats.filter(c => c === 'other').length,\r\n };\r\n}\r\n\r\n// ===== Log completion aggregation =====\r\n\r\nexport function aggregateLogCompletion(records: TestResultRecord[]): LogCompletionSummary {\r\n let totalCandidates = 0;\r\n let succeeded = 0;\r\n let failed = 0;\r\n let timedOut = 0;\r\n const timedOutFreq = new Map<string, { method: string; path: string; count: number }>();\r\n\r\n for (const r of records) {\r\n const lc = r.logCompletion;\r\n if (!lc || lc.candidateCount === 0) continue;\r\n\r\n totalCandidates += lc.candidateCount;\r\n succeeded += lc.succeeded.length;\r\n failed += lc.failed.length;\r\n timedOut += lc.timedOut.length;\r\n\r\n for (const item of lc.timedOut) {\r\n const key = `${item.method}:${item.path}`;\r\n const existing = timedOutFreq.get(key);\r\n if (existing) existing.count += 1;\r\n else timedOutFreq.set(key, { method: item.method, path: item.path, count: 1 });\r\n }\r\n }\r\n\r\n const timedOutTop5 = Array.from(timedOutFreq.values())\r\n .sort((a, b) => b.count - a.count)\r\n .slice(0, 5)\r\n .map(t => ({ method: t.method, path: t.path, occurrences: t.count }));\r\n\r\n const matchRate = totalCandidates > 0 ? (succeeded + failed) / totalCandidates * 100 : 0;\r\n const effectiveRate = totalCandidates > 0 ? succeeded / totalCandidates * 100 : 0;\r\n\r\n return { totalCandidates, succeeded, failed, timedOut, matchRate, effectiveRate, timedOutTop5 };\r\n}\r\n\r\n// ===== URL extraction helpers =====\r\n\r\nfunction extractUrls(error?: string): string[] {\r\n if (!error) return [];\r\n const matched = error.match(/https?:\\/\\/[^\\s\\n]+/g);\r\n return matched ? Array.from(new Set(matched)) : [];\r\n}\r\n\r\nexport function parseApiDomain(urlStr: string): string | null {\r\n try {\r\n const u = new URL(urlStr);\r\n const segments = u.pathname.split('/').filter(Boolean);\r\n const v1Index = segments.findIndex(s => s === 'v1');\r\n if (v1Index === -1 || v1Index + 1 >= segments.length) return segments[0] || null;\r\n const afterV1 = segments.slice(v1Index + 1);\r\n if (afterV1.length === 0) return null;\r\n const first = afterV1[0];\r\n if (/^\\d+$/.test(first) && afterV1.length > 1) return afterV1[1];\r\n return first;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ===== Backend checklist =====\r\n\r\nexport function buildBackendChecklist(records: TestResultRecord[]): BackendDomainItem[] {\r\n const domainMap = new Map<string, { tests: Set<string>; endpoints: Set<string> }>();\r\n const failed = records.filter(r => r.status === 'failed');\r\n\r\n for (const item of failed) {\r\n const cat = classifyFailure(item.error);\r\n if (cat !== 'backend-5xx' && cat !== 'mixed-5xx') continue;\r\n const urls = extractUrls(item.error);\r\n for (const url of urls) {\r\n const domain = parseApiDomain(url);\r\n if (!domain) continue;\r\n const current = domainMap.get(domain) ?? { tests: new Set<string>(), endpoints: new Set<string>() };\r\n current.tests.add(item.title);\r\n current.endpoints.add(url);\r\n domainMap.set(domain, current);\r\n }\r\n }\r\n\r\n return Array.from(domainMap.entries())\r\n .map(([domain, v]) => ({\r\n domain,\r\n tests: Array.from(v.tests).sort(),\r\n endpoints: Array.from(v.endpoints).sort(),\r\n }))\r\n .sort((a, b) => b.tests.length - a.tests.length || a.domain.localeCompare(b.domain));\r\n}\r\n\r\n// ===== Markdown renderer =====\r\n\r\nexport function renderChecklistMarkdown(\r\n items: BackendDomainItem[],\r\n summary: FailureSummary,\r\n logSummary?: LogCompletionSummary,\r\n): string {\r\n const lines: string[] = [\r\n '# Backend Fix Checklist',\r\n '',\r\n ];\r\n\r\n if (logSummary) {\r\n lines.push(\r\n `- Log match rate: ${logSummary.matchRate.toFixed(2)}% (candidates=${logSummary.totalCandidates}, succeeded=${logSummary.succeeded}, failed=${logSummary.failed}, timedOut=${logSummary.timedOut})`,\r\n `- Effective success rate: ${logSummary.effectiveRate.toFixed(2)}%`,\r\n );\r\n }\r\n lines.push(\r\n `- Backend 5xx: ${summary.backend5xx}`,\r\n `- Mixed 5xx: ${summary.mixed5xx}`,\r\n `- Slow API: ${summary.slowApi}`,\r\n `- Log fail: ${summary.logFail}`,\r\n `- Log timeout: ${summary.logTimeout}`,\r\n `- Frontend load: ${summary.frontendLoad}`,\r\n `- Other: ${summary.other}`,\r\n '',\r\n );\r\n\r\n if (logSummary && logSummary.timedOutTop5.length > 0) {\r\n lines.push('### Timed-out APIs Top 5', '', '| # | Method | Path | Occurrences |', '|---|--------|------|-------------|');\r\n logSummary.timedOutTop5.forEach((t, i) => {\r\n lines.push(`| ${i + 1} | ${t.method} | ${t.path} | ${t.occurrences} |`);\r\n });\r\n lines.push('');\r\n }\r\n\r\n if (items.length === 0) {\r\n lines.push('No backend 5xx failures this run.');\r\n return lines.join('\\n') + '\\n';\r\n }\r\n\r\n for (const item of items) {\r\n lines.push(\r\n `## ${item.domain}`,\r\n '',\r\n `- Failed tests: ${item.tests.length}`,\r\n '- Affected tests:',\r\n );\r\n for (const t of item.tests) lines.push(` - ${t}`);\r\n lines.push('- Failed endpoints:');\r\n for (const e of item.endpoints) lines.push(` - ${e}`);\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n}\r\n","/**\r\n * Workorder Reporter — auto-generates prioritized backend work orders.\r\n *\r\n * Priority rules:\r\n * - P0: ≥3 affected tests OR log completion rate < 90%\r\n * - P1: 2 affected tests\r\n * - P2: 1 affected test\r\n *\r\n * Each workorder includes objective, affected scope, and acceptance criteria.\r\n */\r\n\r\nimport type {\r\n BackendDomainItem,\r\n FailureSummary,\r\n LogCompletionSummary,\r\n WorkorderItem,\r\n} from '../types.js';\r\n\r\n// ===== Priority assignment =====\r\n\r\nfunction assignPriority(item: BackendDomainItem, isLogRate: boolean): 'P0' | 'P1' | 'P2' {\r\n if (isLogRate) return 'P0';\r\n if (item.tests.length >= 3) return 'P0';\r\n if (item.tests.length === 2) return 'P1';\r\n return 'P2';\r\n}\r\n\r\n// ===== Workorder builder =====\r\n\r\nexport interface BuildWorkordersOptions {\r\n checklist: BackendDomainItem[];\r\n summary: FailureSummary;\r\n logSummary?: LogCompletionSummary;\r\n logRateThreshold?: number;\r\n}\r\n\r\nexport function buildWorkorders(opts: BuildWorkordersOptions): WorkorderItem[] {\r\n const { checklist, logSummary, logRateThreshold = 90 } = opts;\r\n const result: WorkorderItem[] = [];\r\n let idx = 1;\r\n\r\n // Auto P0 workorder if log completion rate is below threshold\r\n if (logSummary && logSummary.totalCandidates > 0 && logSummary.matchRate < logRateThreshold) {\r\n const logItem: BackendDomainItem = {\r\n domain: 'Log Completion Standards',\r\n tests: logSummary.timedOutTop5.map(t => `${t.method} ${t.path} (×${t.occurrences})`),\r\n endpoints: logSummary.timedOutTop5.map(t => t.path),\r\n };\r\n result.push({\r\n index: idx++,\r\n domain: logItem.domain,\r\n priority: 'P0',\r\n tests: logItem.tests,\r\n endpoints: logItem.endpoints,\r\n objective: 'Add missing end-phase structured logs for timed-out APIs',\r\n acceptanceCriteria: [\r\n `Log completion match rate ≥ ${logRateThreshold}%`,\r\n 'TimedOut API count drops to 0 or only SSE/long-polling endpoints remain',\r\n ],\r\n });\r\n }\r\n\r\n // Standard workorders from checklist\r\n for (const item of checklist) {\r\n const priority = assignPriority(item, false);\r\n result.push({\r\n index: idx++,\r\n domain: item.domain,\r\n priority,\r\n tests: item.tests,\r\n endpoints: item.endpoints,\r\n objective: 'Fix 500 errors and return a valid business response',\r\n acceptanceCriteria: [\r\n 'HTTP status returns 2xx for affected endpoints',\r\n 'No [BACKEND_5XX] errors in corresponding page traversal',\r\n 'All covered tests pass',\r\n ],\r\n });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ===== Markdown renderer =====\r\n\r\nexport function renderWorkordersMarkdown(\r\n workorders: WorkorderItem[],\r\n summary: FailureSummary,\r\n logSummary?: LogCompletionSummary,\r\n): string {\r\n const lines: string[] = [\r\n '# Backend Work Orders',\r\n '',\r\n ];\r\n\r\n if (logSummary) {\r\n lines.push(\r\n `- Log match rate: ${logSummary.matchRate.toFixed(2)}%`,\r\n `- Effective success rate: ${logSummary.effectiveRate.toFixed(2)}%`,\r\n );\r\n }\r\n lines.push(\r\n `- Total failed: ${summary.totalFailed}`,\r\n `- Backend 5xx: ${summary.backend5xx}`,\r\n `- Mixed 5xx: ${summary.mixed5xx}`,\r\n `- Slow API: ${summary.slowApi}`,\r\n `- Log fail: ${summary.logFail}`,\r\n `- Log timeout: ${summary.logTimeout}`,\r\n `- Frontend load: ${summary.frontendLoad}`,\r\n `- Other: ${summary.other}`,\r\n '',\r\n );\r\n\r\n if (workorders.length === 0) {\r\n lines.push('No backend work orders this run.');\r\n return lines.join('\\n') + '\\n';\r\n }\r\n\r\n for (const wo of workorders) {\r\n lines.push(\r\n `## Workorder ${wo.index} - ${wo.domain}`,\r\n '',\r\n `- Priority: ${wo.priority}`,\r\n `- Affected tests: ${wo.tests.length}`,\r\n '- Scope:',\r\n );\r\n for (const t of wo.tests) lines.push(` - ${t}`);\r\n if (wo.endpoints.length > 0) {\r\n lines.push('- Endpoints:');\r\n for (const e of wo.endpoints) lines.push(` - ${e}`);\r\n }\r\n lines.push(`- Objective: ${wo.objective}`);\r\n lines.push('- Acceptance criteria:');\r\n for (const c of wo.acceptanceCriteria) lines.push(` - ${c}`);\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n}\r\n","/**\r\n * Token Usage Reporter — tracks and reports LLM token consumption.\r\n *\r\n * Aggregates usage entries by category and model, computes costs,\r\n * tracks budget utilization, and renders both structured data and Markdown.\r\n */\r\n\r\nimport type { TokenUsageEntry, TokenUsageSummary } from '../types.js';\r\n\r\n// ===== Token Tracker =====\r\n\r\nexport class TokenTracker {\r\n private entries: TokenUsageEntry[] = [];\r\n private budget: number | null = null;\r\n\r\n setBudget(maxTokens: number): void {\r\n this.budget = maxTokens;\r\n }\r\n\r\n record(entry: TokenUsageEntry): void {\r\n this.entries.push(entry);\r\n }\r\n\r\n reset(): void {\r\n this.entries = [];\r\n }\r\n\r\n getSummary(): TokenUsageSummary {\r\n const totalRequests = this.entries.length;\r\n let totalPromptTokens = 0;\r\n let totalCompletionTokens = 0;\r\n let totalEstimatedCost = 0;\r\n let totalLatency = 0;\r\n\r\n const byCategory: TokenUsageSummary['byCategory'] = {};\r\n const byModel: TokenUsageSummary['byModel'] = {};\r\n\r\n for (const e of this.entries) {\r\n totalPromptTokens += e.promptTokens;\r\n totalCompletionTokens += e.completionTokens;\r\n totalEstimatedCost += e.estimatedCost;\r\n totalLatency += e.latencyMs;\r\n\r\n // By category\r\n if (!byCategory[e.category]) {\r\n byCategory[e.category] = { requests: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0, estimatedCost: 0 };\r\n }\r\n const cat = byCategory[e.category];\r\n cat.requests++;\r\n cat.promptTokens += e.promptTokens;\r\n cat.completionTokens += e.completionTokens;\r\n cat.totalTokens += e.promptTokens + e.completionTokens;\r\n cat.estimatedCost += e.estimatedCost;\r\n\r\n // By model\r\n if (!byModel[e.model]) {\r\n byModel[e.model] = { requests: 0, totalTokens: 0, estimatedCost: 0 };\r\n }\r\n const mod = byModel[e.model];\r\n mod.requests++;\r\n mod.totalTokens += e.promptTokens + e.completionTokens;\r\n mod.estimatedCost += e.estimatedCost;\r\n }\r\n\r\n const totalTokens = totalPromptTokens + totalCompletionTokens;\r\n const budgetUsedPercent = this.budget && this.budget > 0\r\n ? Math.round(totalTokens / this.budget * 10000) / 100\r\n : null;\r\n\r\n return {\r\n totalRequests,\r\n totalTokens,\r\n totalPromptTokens,\r\n totalCompletionTokens,\r\n totalEstimatedCost,\r\n avgLatencyMs: totalRequests > 0 ? Math.round(totalLatency / totalRequests) : 0,\r\n byCategory,\r\n byModel,\r\n budgetUsedPercent,\r\n budgetExceeded: budgetUsedPercent !== null && budgetUsedPercent > 100,\r\n };\r\n }\r\n}\r\n\r\n// ===== Markdown renderer =====\r\n\r\nexport function renderTokenReportMarkdown(summary: TokenUsageSummary): string {\r\n const lines: string[] = [\r\n '# AI Token Usage Report',\r\n '',\r\n `- Total requests: ${summary.totalRequests}`,\r\n `- Total tokens: ${summary.totalTokens.toLocaleString()}`,\r\n `- Prompt tokens: ${summary.totalPromptTokens.toLocaleString()}`,\r\n `- Completion tokens: ${summary.totalCompletionTokens.toLocaleString()}`,\r\n `- Estimated cost: ¥${summary.totalEstimatedCost.toFixed(4)}`,\r\n `- Average latency: ${summary.avgLatencyMs}ms`,\r\n ];\r\n\r\n if (summary.budgetUsedPercent !== null) {\r\n lines.push(`- Budget used: ${summary.budgetUsedPercent}%${summary.budgetExceeded ? ' **EXCEEDED**' : ''}`);\r\n }\r\n lines.push('');\r\n\r\n // By category\r\n const cats = Object.entries(summary.byCategory).sort((a, b) => b[1].totalTokens - a[1].totalTokens);\r\n if (cats.length > 0) {\r\n lines.push(\r\n '## By Category',\r\n '',\r\n '| Category | Requests | Prompt | Completion | Total | Cost |',\r\n '|----------|----------|--------|------------|-------|------|',\r\n );\r\n for (const [cat, d] of cats) {\r\n lines.push(`| ${cat} | ${d.requests} | ${d.promptTokens.toLocaleString()} | ${d.completionTokens.toLocaleString()} | ${d.totalTokens.toLocaleString()} | ¥${d.estimatedCost.toFixed(4)} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n // By model\r\n const models = Object.entries(summary.byModel);\r\n if (models.length > 0) {\r\n lines.push(\r\n '## By Model',\r\n '',\r\n '| Model | Requests | Total Tokens | Cost |',\r\n '|-------|----------|-------------|------|',\r\n );\r\n for (const [model, d] of models) {\r\n lines.push(`| ${model} | ${d.requests} | ${d.totalTokens.toLocaleString()} | ¥${d.estimatedCost.toFixed(4)} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n}\r\n","import type {\r\n PipelineRunResult,\r\n ERDiagramResult,\r\n ChainPlanResult,\r\n GeneratedTestFile,\r\n ValidationError,\r\n} from '../types.js';\r\n\r\n// ===== Reporter Types =====\r\n\r\nexport interface ReportOutput {\r\n format: 'html' | 'json' | 'markdown';\r\n content: string;\r\n filename: string;\r\n}\r\n\r\n// ===== JSON Reporter =====\r\n\r\nexport function generateJsonReport(result: PipelineRunResult): ReportOutput {\r\n const serializable = {\r\n modules: result.modules,\r\n erDiagrams: Object.fromEntries(\r\n Array.from(result.erDiagrams.entries()).map(([k, v]) => [\r\n k,\r\n { tables: v.tables.length, relations: v.relations.length, mermaidText: v.mermaidText },\r\n ]),\r\n ),\r\n chainPlans: Object.fromEntries(\r\n Array.from(result.chainPlans.entries()).map(([k, v]) => [\r\n k,\r\n { chains: v.chains.length, totalSteps: v.totalSteps },\r\n ]),\r\n ),\r\n generatedFiles: result.generatedFiles.map((f) => ({\r\n filePath: f.filePath,\r\n module: f.module,\r\n chain: f.chain,\r\n })),\r\n validationErrors: result.validationErrors,\r\n duration: result.duration,\r\n };\r\n\r\n return {\r\n format: 'json',\r\n content: JSON.stringify(serializable, null, 2),\r\n filename: 'opencroc-report.json',\r\n };\r\n}\r\n\r\n// ===== Markdown Reporter =====\r\n\r\nexport function generateMarkdownReport(result: PipelineRunResult): ReportOutput {\r\n const lines: string[] = [\r\n '# OpenCroc Report',\r\n '',\r\n `**Duration**: ${result.duration}ms`,\r\n `**Modules**: ${result.modules.length} (${result.modules.join(', ')})`,\r\n '',\r\n '## ER Diagrams',\r\n '',\r\n ];\r\n\r\n for (const [mod, er] of result.erDiagrams) {\r\n lines.push(`### ${mod}`);\r\n lines.push(`- Tables: ${er.tables.length}`);\r\n lines.push(`- Relations: ${er.relations.length}`);\r\n lines.push('');\r\n }\r\n\r\n lines.push('## Chain Plans', '');\r\n for (const [mod, plan] of result.chainPlans) {\r\n lines.push(`### ${mod}`);\r\n lines.push(`- Chains: ${plan.chains.length}`);\r\n lines.push(`- Total Steps: ${plan.totalSteps}`);\r\n lines.push('');\r\n }\r\n\r\n lines.push(`## Generated Files (${result.generatedFiles.length})`, '');\r\n for (const f of result.generatedFiles) {\r\n lines.push(`- \\`${f.filePath}\\` (${f.module} / ${f.chain})`);\r\n }\r\n\r\n if (result.validationErrors.length > 0) {\r\n lines.push('', '## Validation Issues', '');\r\n const errors = result.validationErrors.filter((e) => e.severity === 'error');\r\n const warnings = result.validationErrors.filter((e) => e.severity === 'warning');\r\n if (errors.length > 0) {\r\n lines.push(`### Errors (${errors.length})`, '');\r\n for (const e of errors) {\r\n lines.push(`- **[${e.module}]** ${e.field}: ${e.message}`);\r\n }\r\n }\r\n if (warnings.length > 0) {\r\n lines.push(`### Warnings (${warnings.length})`, '');\r\n for (const w of warnings) {\r\n lines.push(`- **[${w.module}]** ${w.field}: ${w.message}`);\r\n }\r\n }\r\n }\r\n\r\n lines.push('', '---', '*Generated by [OpenCroc](https://github.com/opencroc/opencroc)*');\r\n\r\n return {\r\n format: 'markdown',\r\n content: lines.join('\\n'),\r\n filename: 'opencroc-report.md',\r\n };\r\n}\r\n\r\n// ===== HTML Reporter =====\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\r\n}\r\n\r\nfunction erSummaryRows(erDiagrams: Map<string, ERDiagramResult>): string {\r\n const rows: string[] = [];\r\n for (const [mod, er] of erDiagrams) {\r\n rows.push(`<tr><td>${escapeHtml(mod)}</td><td>${er.tables.length}</td><td>${er.relations.length}</td></tr>`);\r\n }\r\n return rows.join('\\n');\r\n}\r\n\r\nfunction chainSummaryRows(chainPlans: Map<string, ChainPlanResult>): string {\r\n const rows: string[] = [];\r\n for (const [mod, plan] of chainPlans) {\r\n rows.push(`<tr><td>${escapeHtml(mod)}</td><td>${plan.chains.length}</td><td>${plan.totalSteps}</td></tr>`);\r\n }\r\n return rows.join('\\n');\r\n}\r\n\r\nfunction fileListRows(files: GeneratedTestFile[]): string {\r\n return files\r\n .map((f) => `<tr><td><code>${escapeHtml(f.filePath)}</code></td><td>${escapeHtml(f.module)}</td><td>${escapeHtml(f.chain)}</td></tr>`)\r\n .join('\\n');\r\n}\r\n\r\nfunction validationRows(errors: ValidationError[]): string {\r\n return errors\r\n .map(\r\n (e) =>\r\n `<tr class=\"${e.severity}\"><td><span class=\"badge ${e.severity}\">${e.severity}</span></td><td>${escapeHtml(e.module)}</td><td>${escapeHtml(e.field)}</td><td>${escapeHtml(e.message)}</td></tr>`,\r\n )\r\n .join('\\n');\r\n}\r\n\r\nexport function generateHtmlReport(result: PipelineRunResult): ReportOutput {\r\n const totalTables = Array.from(result.erDiagrams.values()).reduce((s, e) => s + e.tables.length, 0);\r\n const totalRelations = Array.from(result.erDiagrams.values()).reduce((s, e) => s + e.relations.length, 0);\r\n const totalChains = Array.from(result.chainPlans.values()).reduce((s, p) => s + p.chains.length, 0);\r\n const totalSteps = Array.from(result.chainPlans.values()).reduce((s, p) => s + p.totalSteps, 0);\r\n const errorCount = result.validationErrors.filter((e) => e.severity === 'error').length;\r\n const warnCount = result.validationErrors.filter((e) => e.severity === 'warning').length;\r\n\r\n const html = `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"utf-8\" />\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n<title>OpenCroc Report</title>\r\n<style>\r\n :root { --bg: #0d1117; --fg: #c9d1d9; --card: #161b22; --border: #30363d; --accent: #58a6ff; --green: #3fb950; --yellow: #d29922; --red: #f85149; }\r\n * { box-sizing: border-box; margin: 0; padding: 0; }\r\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; background: var(--bg); color: var(--fg); padding: 2rem; }\r\n h1 { color: var(--accent); margin-bottom: 0.25rem; }\r\n .subtitle { color: #8b949e; margin-bottom: 2rem; }\r\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }\r\n .card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; }\r\n .card .label { font-size: 0.85rem; color: #8b949e; }\r\n .card .value { font-size: 2rem; font-weight: 700; color: var(--accent); }\r\n .card .value.green { color: var(--green); }\r\n .card .value.yellow { color: var(--yellow); }\r\n .card .value.red { color: var(--red); }\r\n section { margin-bottom: 2rem; }\r\n h2 { color: var(--fg); border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; margin-bottom: 1rem; }\r\n table { width: 100%; border-collapse: collapse; background: var(--card); border-radius: 8px; overflow: hidden; }\r\n th, td { text-align: left; padding: 0.6rem 1rem; border-bottom: 1px solid var(--border); }\r\n th { background: #21262d; color: #8b949e; font-weight: 600; font-size: 0.85rem; text-transform: uppercase; }\r\n tr:last-child td { border-bottom: none; }\r\n code { background: #21262d; padding: 0.15rem 0.4rem; border-radius: 4px; font-size: 0.85rem; }\r\n .badge { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 12px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; }\r\n .badge.error { background: rgba(248,81,73,0.15); color: var(--red); }\r\n .badge.warning { background: rgba(210,153,34,0.15); color: var(--yellow); }\r\n footer { margin-top: 3rem; text-align: center; color: #484f58; font-size: 0.85rem; }\r\n footer a { color: var(--accent); text-decoration: none; }\r\n</style>\r\n</head>\r\n<body>\r\n<h1>OpenCroc Report</h1>\r\n<p class=\"subtitle\">Generated in ${result.duration}ms &middot; ${result.modules.length} module(s)</p>\r\n\r\n<div class=\"grid\">\r\n <div class=\"card\"><div class=\"label\">Modules</div><div class=\"value\">${result.modules.length}</div></div>\r\n <div class=\"card\"><div class=\"label\">Tables</div><div class=\"value\">${totalTables}</div></div>\r\n <div class=\"card\"><div class=\"label\">Relations</div><div class=\"value\">${totalRelations}</div></div>\r\n <div class=\"card\"><div class=\"label\">Chains</div><div class=\"value\">${totalChains}</div></div>\r\n <div class=\"card\"><div class=\"label\">Steps</div><div class=\"value\">${totalSteps}</div></div>\r\n <div class=\"card\"><div class=\"label\">Files</div><div class=\"value green\">${result.generatedFiles.length}</div></div>\r\n <div class=\"card\"><div class=\"label\">Errors</div><div class=\"value${errorCount > 0 ? ' red' : ''}\">${errorCount}</div></div>\r\n <div class=\"card\"><div class=\"label\">Warnings</div><div class=\"value${warnCount > 0 ? ' yellow' : ''}\">${warnCount}</div></div>\r\n</div>\r\n\r\n<section>\r\n<h2>ER Diagrams</h2>\r\n<table>\r\n<thead><tr><th>Module</th><th>Tables</th><th>Relations</th></tr></thead>\r\n<tbody>${erSummaryRows(result.erDiagrams)}</tbody>\r\n</table>\r\n</section>\r\n\r\n<section>\r\n<h2>Chain Plans</h2>\r\n<table>\r\n<thead><tr><th>Module</th><th>Chains</th><th>Steps</th></tr></thead>\r\n<tbody>${chainSummaryRows(result.chainPlans)}</tbody>\r\n</table>\r\n</section>\r\n\r\n<section>\r\n<h2>Generated Files (${result.generatedFiles.length})</h2>\r\n<table>\r\n<thead><tr><th>File</th><th>Module</th><th>Chain</th></tr></thead>\r\n<tbody>${fileListRows(result.generatedFiles)}</tbody>\r\n</table>\r\n</section>\r\n\r\n${\r\n result.validationErrors.length > 0\r\n ? `<section>\r\n<h2>Validation Issues (${result.validationErrors.length})</h2>\r\n<table>\r\n<thead><tr><th>Severity</th><th>Module</th><th>Field</th><th>Message</th></tr></thead>\r\n<tbody>${validationRows(result.validationErrors)}</tbody>\r\n</table>\r\n</section>`\r\n : ''\r\n}\r\n\r\n<footer>\r\n Generated by <a href=\"https://github.com/opencroc/opencroc\">OpenCroc</a>\r\n</footer>\r\n</body>\r\n</html>`;\r\n\r\n return {\r\n format: 'html',\r\n content: html,\r\n filename: 'opencroc-report.html',\r\n };\r\n}\r\n\r\n// ===== Report Orchestrator =====\r\n\r\nconst REPORTERS: Record<string, (result: PipelineRunResult) => ReportOutput> = {\r\n html: generateHtmlReport,\r\n json: generateJsonReport,\r\n markdown: generateMarkdownReport,\r\n};\r\n\r\n/**\r\n * Generate reports in the specified formats.\r\n */\r\nexport function generateReports(\r\n result: PipelineRunResult,\r\n formats: ('html' | 'json' | 'markdown')[] = ['html'],\r\n): ReportOutput[] {\r\n return formats.map((fmt) => {\r\n const gen = REPORTERS[fmt];\r\n if (!gen) throw new Error(`Unknown report format: \"${fmt}\". Available: ${Object.keys(REPORTERS).join(', ')}`);\r\n return gen(result);\r\n });\r\n}\r\n\r\n// ===== Advanced Reporters (v1.2) =====\r\nexport {\r\n classifyFailure,\r\n buildFailureSummary,\r\n aggregateLogCompletion,\r\n parseApiDomain,\r\n buildBackendChecklist,\r\n renderChecklistMarkdown,\r\n} from './checklist-reporter.js';\r\n\r\nexport {\r\n buildWorkorders,\r\n renderWorkordersMarkdown,\r\n} from './workorder-reporter.js';\r\nexport type { BuildWorkordersOptions } from './workorder-reporter.js';\r\n\r\nexport {\r\n TokenTracker,\r\n renderTokenReportMarkdown,\r\n} from './token-reporter.js';\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport { generateReports } from '../../reporters/index.js';\r\n\r\nexport interface ReportCommandOptions {\r\n format?: string;\r\n output?: string;\r\n}\r\n\r\nexport async function report(opts: ReportCommandOptions): Promise<void> {\r\n let loaded;\r\n try {\r\n loaded = await loadConfig();\r\n } catch {\r\n console.error(chalk.red('No opencroc config found. Run `opencroc init` first.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const { config } = loaded;\r\n\r\n console.log(chalk.cyan('Running pipeline to generate report...'));\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run();\r\n\r\n const formats = (opts.format ?? 'html').split(',').map((s) => s.trim()) as ('html' | 'json' | 'markdown')[];\r\n const reports = generateReports(result, formats);\r\n\r\n const outDir = opts.output ?? config.outDir ?? './opencroc-output';\r\n if (!fs.existsSync(outDir)) {\r\n fs.mkdirSync(outDir, { recursive: true });\r\n }\r\n\r\n for (const r of reports) {\r\n const filePath = path.join(outDir, r.filename);\r\n fs.writeFileSync(filePath, r.content, 'utf-8');\r\n console.log(chalk.green(`✔ ${r.format} report → ${filePath}`));\r\n }\r\n\r\n console.log(chalk.dim(` ${result.modules.length} modules, ${result.generatedFiles.length} files, ${result.duration}ms`));\r\n}\r\n","import type { PipelineRunResult } from '../types.js';\r\n\r\nexport interface DashboardData {\r\n generatedAt: string;\r\n durationMs: number;\r\n modules: string[];\r\n totals: {\r\n modules: number;\r\n tables: number;\r\n relations: number;\r\n chains: number;\r\n steps: number;\r\n files: number;\r\n errors: number;\r\n warnings: number;\r\n };\r\n moduleCards: Array<{\r\n module: string;\r\n tables: number;\r\n relations: number;\r\n chains: number;\r\n steps: number;\r\n }>;\r\n files: Array<{ filePath: string; module: string; chain: string }>;\r\n issues: Array<{ severity: string; module: string; field: string; message: string }>;\r\n}\r\n\r\nexport interface DashboardOutput {\r\n filename: string;\r\n content: string;\r\n}\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#39;');\r\n}\r\n\r\nfunction number(v: unknown): number {\r\n return typeof v === 'number' && Number.isFinite(v) ? v : 0;\r\n}\r\n\r\nexport function buildDashboardDataFromPipeline(result: PipelineRunResult): DashboardData {\r\n const moduleCards = result.modules.map((mod) => {\r\n const er = result.erDiagrams.get(mod);\r\n const plan = result.chainPlans.get(mod);\r\n return {\r\n module: mod,\r\n tables: er?.tables.length ?? 0,\r\n relations: er?.relations.length ?? 0,\r\n chains: plan?.chains.length ?? 0,\r\n steps: plan?.totalSteps ?? 0,\r\n };\r\n });\r\n\r\n const totals = {\r\n modules: result.modules.length,\r\n tables: moduleCards.reduce((s, m) => s + m.tables, 0),\r\n relations: moduleCards.reduce((s, m) => s + m.relations, 0),\r\n chains: moduleCards.reduce((s, m) => s + m.chains, 0),\r\n steps: moduleCards.reduce((s, m) => s + m.steps, 0),\r\n files: result.generatedFiles.length,\r\n errors: result.validationErrors.filter((e) => e.severity === 'error').length,\r\n warnings: result.validationErrors.filter((e) => e.severity === 'warning').length,\r\n };\r\n\r\n return {\r\n generatedAt: new Date().toISOString(),\r\n durationMs: result.duration,\r\n modules: [...result.modules],\r\n totals,\r\n moduleCards,\r\n files: result.generatedFiles.map((f) => ({ filePath: f.filePath, module: f.module, chain: f.chain })),\r\n issues: result.validationErrors.map((e) => ({\r\n severity: e.severity,\r\n module: e.module,\r\n field: e.field,\r\n message: e.message,\r\n })),\r\n };\r\n}\r\n\r\nexport function buildDashboardDataFromReportJson(input: unknown): DashboardData {\r\n const src = (input ?? {}) as Record<string, unknown>;\r\n const modules = Array.isArray(src.modules) ? src.modules.map((m) => String(m)) : [];\r\n const er = (src.erDiagrams ?? {}) as Record<string, { tables?: unknown; relations?: unknown }>;\r\n const plans = (src.chainPlans ?? {}) as Record<string, { chains?: unknown; totalSteps?: unknown }>;\r\n const files = Array.isArray(src.generatedFiles)\r\n ? src.generatedFiles.map((f) => {\r\n const row = f as Record<string, unknown>;\r\n return {\r\n filePath: String(row.filePath ?? ''),\r\n module: String(row.module ?? ''),\r\n chain: String(row.chain ?? ''),\r\n };\r\n })\r\n : [];\r\n\r\n const rawIssues = Array.isArray(src.validationErrors) ? src.validationErrors : [];\r\n const issues = rawIssues.map((item) => {\r\n const row = item as Record<string, unknown>;\r\n return {\r\n severity: String(row.severity ?? 'warning'),\r\n module: String(row.module ?? 'unknown'),\r\n field: String(row.field ?? 'unknown'),\r\n message: String(row.message ?? ''),\r\n };\r\n });\r\n\r\n const moduleCards = modules.map((mod) => ({\r\n module: mod,\r\n tables: number(er[mod]?.tables),\r\n relations: number(er[mod]?.relations),\r\n chains: number(plans[mod]?.chains),\r\n steps: number(plans[mod]?.totalSteps),\r\n }));\r\n\r\n return {\r\n generatedAt: new Date().toISOString(),\r\n durationMs: number(src.duration),\r\n modules,\r\n totals: {\r\n modules: modules.length,\r\n tables: moduleCards.reduce((s, m) => s + m.tables, 0),\r\n relations: moduleCards.reduce((s, m) => s + m.relations, 0),\r\n chains: moduleCards.reduce((s, m) => s + m.chains, 0),\r\n steps: moduleCards.reduce((s, m) => s + m.steps, 0),\r\n files: files.length,\r\n errors: issues.filter((i) => i.severity === 'error').length,\r\n warnings: issues.filter((i) => i.severity === 'warning').length,\r\n },\r\n moduleCards,\r\n files,\r\n issues,\r\n };\r\n}\r\n\r\nexport function generateVisualDashboardHtml(data: DashboardData): string {\r\n const moduleCardHtml = data.moduleCards\r\n .map(\r\n (m) => `<article class=\"module-card reveal\">\r\n <h3>${escapeHtml(m.module)}</h3>\r\n <div class=\"meta\">${m.tables} tables · ${m.relations} relations</div>\r\n <div class=\"bars\">\r\n <div class=\"bar\"><span>Chains</span><strong>${m.chains}</strong></div>\r\n <div class=\"bar\"><span>Steps</span><strong>${m.steps}</strong></div>\r\n </div>\r\n</article>`,\r\n )\r\n .join('\\n');\r\n\r\n const fileRows = data.files\r\n .slice(0, 20)\r\n .map(\r\n (f) => `<tr><td><code>${escapeHtml(f.filePath)}</code></td><td>${escapeHtml(f.module)}</td><td>${escapeHtml(f.chain)}</td></tr>`,\r\n )\r\n .join('\\n');\r\n\r\n const issueRows = data.issues\r\n .map(\r\n (i) => `<tr class=\"${escapeHtml(i.severity)}\"><td>${escapeHtml(i.severity)}</td><td>${escapeHtml(i.module)}</td><td>${escapeHtml(i.field)}</td><td>${escapeHtml(i.message)}</td></tr>`,\r\n )\r\n .join('\\n');\r\n\r\n return `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"utf-8\" />\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n <title>OpenCroc Visual Dashboard</title>\r\n <style>\r\n @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap');\r\n :root {\r\n --bg: #f4efe6;\r\n --ink: #18222c;\r\n --card: #fff8ef;\r\n --line: #d8c7b4;\r\n --accent: #0b7a75;\r\n --accent-2: #f26a2e;\r\n --ok: #2f7d32;\r\n --warn: #b7791f;\r\n --err: #c0392b;\r\n }\r\n * { box-sizing: border-box; }\r\n body {\r\n margin: 0;\r\n font-family: 'Space Grotesk', 'Segoe UI', sans-serif;\r\n color: var(--ink);\r\n background:\r\n radial-gradient(circle at 15% 10%, #f9e2c5 0%, transparent 32%),\r\n radial-gradient(circle at 85% 0%, #d3efe4 0%, transparent 28%),\r\n var(--bg);\r\n min-height: 100vh;\r\n }\r\n .wrap { max-width: 1160px; margin: 0 auto; padding: 24px 18px 40px; }\r\n .hero {\r\n border: 2px solid var(--line);\r\n border-radius: 18px;\r\n background: linear-gradient(130deg, #fff8ef 0%, #f8f2ea 45%, #f3ece3 100%);\r\n padding: 22px;\r\n box-shadow: 0 12px 24px rgba(24, 34, 44, 0.08);\r\n position: relative;\r\n overflow: hidden;\r\n }\r\n .hero::after {\r\n content: '';\r\n position: absolute;\r\n right: -42px;\r\n top: -38px;\r\n width: 160px;\r\n height: 160px;\r\n border-radius: 50%;\r\n background: conic-gradient(from 50deg, #f26a2e 0deg, #f7a35a 90deg, #0b7a75 220deg, #f26a2e 360deg);\r\n opacity: 0.14;\r\n }\r\n h1 { margin: 0; font-size: clamp(1.6rem, 3vw, 2.5rem); letter-spacing: -0.03em; }\r\n .subtitle { margin-top: 8px; font-size: 0.95rem; opacity: 0.82; }\r\n .kpi-grid {\r\n margin-top: 16px;\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\r\n gap: 10px;\r\n }\r\n .kpi {\r\n border: 1px solid var(--line);\r\n border-radius: 12px;\r\n padding: 10px 12px;\r\n background: #fffdf9;\r\n }\r\n .kpi .label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.7; }\r\n .kpi .value { font-size: 1.45rem; font-weight: 700; margin-top: 4px; }\r\n .kpi.error .value { color: var(--err); }\r\n .kpi.warning .value { color: var(--warn); }\r\n .kpi.files .value { color: var(--accent); }\r\n\r\n .section-title {\r\n margin: 24px 0 10px;\r\n font-size: 1rem;\r\n text-transform: uppercase;\r\n letter-spacing: 0.08em;\r\n color: #344250;\r\n }\r\n\r\n .module-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\r\n gap: 12px;\r\n }\r\n .module-card {\r\n border: 1px solid var(--line);\r\n border-radius: 14px;\r\n padding: 14px;\r\n background: var(--card);\r\n box-shadow: 0 8px 16px rgba(24, 34, 44, 0.06);\r\n }\r\n .module-card h3 { margin: 0 0 6px; font-size: 1.05rem; }\r\n .module-card .meta { font-size: 0.85rem; opacity: 0.78; }\r\n .bars { margin-top: 10px; display: grid; gap: 8px; }\r\n .bar { display: flex; justify-content: space-between; border-top: 1px dashed var(--line); padding-top: 7px; }\r\n\r\n table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n border: 1px solid var(--line);\r\n border-radius: 12px;\r\n overflow: hidden;\r\n background: #fffdf9;\r\n font-family: 'IBM Plex Mono', 'Consolas', monospace;\r\n font-size: 0.82rem;\r\n }\r\n thead { background: #efe5d8; }\r\n th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eadfce; }\r\n tbody tr:last-child td { border-bottom: none; }\r\n tr.error td:first-child { color: var(--err); font-weight: 700; }\r\n tr.warning td:first-child { color: var(--warn); font-weight: 700; }\r\n\r\n code {\r\n font-family: 'IBM Plex Mono', 'Consolas', monospace;\r\n background: #f5ece0;\r\n border: 1px solid #eadfce;\r\n border-radius: 6px;\r\n padding: 1px 5px;\r\n }\r\n\r\n .reveal { opacity: 0; transform: translateY(12px); animation: rise .55s ease forwards; }\r\n .module-card.reveal:nth-child(2) { animation-delay: .06s; }\r\n .module-card.reveal:nth-child(3) { animation-delay: .12s; }\r\n .module-card.reveal:nth-child(4) { animation-delay: .18s; }\r\n .module-card.reveal:nth-child(5) { animation-delay: .24s; }\r\n @keyframes rise {\r\n to { opacity: 1; transform: translateY(0); }\r\n }\r\n\r\n @media (max-width: 700px) {\r\n .wrap { padding: 14px 12px 28px; }\r\n .hero { padding: 16px; }\r\n table { font-size: 0.76rem; }\r\n th, td { padding: 7px 8px; }\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <main class=\"wrap\">\r\n <section class=\"hero reveal\">\r\n <h1>OpenCroc Visual Dashboard</h1>\r\n <p class=\"subtitle\">Pipeline finished in ${data.durationMs}ms · Generated ${escapeHtml(data.generatedAt)}</p>\r\n <div class=\"kpi-grid\">\r\n <div class=\"kpi\"><div class=\"label\">Modules</div><div class=\"value\">${data.totals.modules}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Tables</div><div class=\"value\">${data.totals.tables}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Relations</div><div class=\"value\">${data.totals.relations}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Chains</div><div class=\"value\">${data.totals.chains}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Steps</div><div class=\"value\">${data.totals.steps}</div></div>\r\n <div class=\"kpi files\"><div class=\"label\">Files</div><div class=\"value\">${data.totals.files}</div></div>\r\n <div class=\"kpi error\"><div class=\"label\">Errors</div><div class=\"value\">${data.totals.errors}</div></div>\r\n <div class=\"kpi warning\"><div class=\"label\">Warnings</div><div class=\"value\">${data.totals.warnings}</div></div>\r\n </div>\r\n </section>\r\n\r\n <h2 class=\"section-title\">Module Health</h2>\r\n <section class=\"module-grid\">\r\n ${moduleCardHtml || '<article class=\"module-card\">No module data</article>'}\r\n </section>\r\n\r\n <h2 class=\"section-title\">Generated Files (Top 20)</h2>\r\n <section>\r\n <table>\r\n <thead><tr><th>File</th><th>Module</th><th>Chain</th></tr></thead>\r\n <tbody>${fileRows || '<tr><td colspan=\"3\">No files generated</td></tr>'}</tbody>\r\n </table>\r\n </section>\r\n\r\n <h2 class=\"section-title\">Validation Issues</h2>\r\n <section>\r\n <table>\r\n <thead><tr><th>Severity</th><th>Module</th><th>Field</th><th>Message</th></tr></thead>\r\n <tbody>${issueRows || '<tr><td colspan=\"4\">No validation issues</td></tr>'}</tbody>\r\n </table>\r\n </section>\r\n </main>\r\n</body>\r\n</html>`;\r\n}\r\n\r\nexport function generateVisualDashboard(result: PipelineRunResult): DashboardOutput {\r\n const data = buildDashboardDataFromPipeline(result);\r\n return {\r\n filename: 'opencroc-dashboard.html',\r\n content: generateVisualDashboardHtml(data),\r\n };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport {\r\n buildDashboardDataFromReportJson,\r\n buildDashboardDataFromPipeline,\r\n generateVisualDashboardHtml,\r\n} from '../../dashboard/index.js';\r\n\r\nexport interface DashboardCommandOptions {\r\n output?: string;\r\n input?: string;\r\n}\r\n\r\nexport async function dashboard(opts: DashboardCommandOptions): Promise<void> {\r\n let dashboardHtml: string;\r\n\r\n if (opts.input) {\r\n const inputPath = path.resolve(opts.input);\r\n if (!fs.existsSync(inputPath)) {\r\n console.error(chalk.red(`Input report not found: ${inputPath}`));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const raw = fs.readFileSync(inputPath, 'utf-8');\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(raw);\r\n } catch {\r\n console.error(chalk.red('Invalid JSON input. Please provide opencroc-report.json output.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const data = buildDashboardDataFromReportJson(parsed);\r\n dashboardHtml = generateVisualDashboardHtml(data);\r\n console.log(chalk.cyan(`Building visual dashboard from ${inputPath}...`));\r\n } else {\r\n let loaded;\r\n try {\r\n loaded = await loadConfig();\r\n } catch {\r\n console.error(chalk.red('No opencroc config found. Run `opencroc init` first.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const { config } = loaded;\r\n console.log(chalk.cyan('Running pipeline to build visual dashboard...'));\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run();\r\n\r\n const data = buildDashboardDataFromPipeline(result);\r\n dashboardHtml = generateVisualDashboardHtml(data);\r\n }\r\n\r\n const outDir = opts.output ? path.resolve(opts.output) : path.resolve('./opencroc-output');\r\n if (!fs.existsSync(outDir)) {\r\n fs.mkdirSync(outDir, { recursive: true });\r\n }\r\n\r\n const outPath = path.join(outDir, 'opencroc-dashboard.html');\r\n fs.writeFileSync(outPath, dashboardHtml, 'utf-8');\r\n console.log(chalk.green(`✔ visual dashboard → ${outPath}`));\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate playwright.config.ts content based on OpenCroc config.\r\n */\r\nexport function generatePlaywrightConfig(config: OpenCrocConfig): string {\r\n const pw = config.playwright ?? {};\r\n const rt = config.runtime ?? {};\r\n const outDir = config.outDir || './opencroc-output';\r\n\r\n const baseURL = pw.baseURL ? `'${pw.baseURL}'` : \"process.env.BASE_URL || 'http://localhost:3000'\";\r\n const timeout = pw.timeout ?? 30_000;\r\n const workers = pw.workers ?? (null as number | null); // null → use expression\r\n const retries = pw.retries ?? (null as number | null);\r\n const actionTimeout = pw.actionTimeout ?? 10_000;\r\n const navigationTimeout = pw.navigationTimeout ?? timeout;\r\n const storageState = rt.auth?.storageStatePath || 'playwright/.auth/user.json';\r\n\r\n const hasAuth = !!(rt.auth?.loginUrl);\r\n\r\n const lines: string[] = [];\r\n lines.push(`import { defineConfig, devices } from '@playwright/test';`);\r\n lines.push('');\r\n lines.push('export default defineConfig({');\r\n lines.push(` testDir: '${outDir}',`);\r\n lines.push(` testMatch: ['**/*.spec.ts', '**/*.test.ts'],`);\r\n lines.push(` fullyParallel: false,`);\r\n lines.push(` forbidOnly: !!process.env.CI,`);\r\n lines.push(` retries: ${retries !== null ? retries : 'process.env.CI ? 1 : 0'},`);\r\n lines.push(` workers: ${workers !== null ? workers : 'process.env.CI ? 4 : 2'},`);\r\n lines.push(` timeout: ${timeout},`);\r\n lines.push(` globalSetup: './global-setup.ts',`);\r\n lines.push(` globalTeardown: './global-teardown.ts',`);\r\n lines.push(` reporter: [['list'], ['html', { open: 'never' }]],`);\r\n lines.push(` use: {`);\r\n lines.push(` baseURL: ${baseURL},`);\r\n lines.push(` trace: 'retain-on-failure',`);\r\n lines.push(` screenshot: 'only-on-failure',`);\r\n lines.push(` video: 'retain-on-failure',`);\r\n lines.push(` actionTimeout: ${actionTimeout},`);\r\n lines.push(` navigationTimeout: ${navigationTimeout},`);\r\n lines.push(` },`);\r\n\r\n // Projects\r\n lines.push(` projects: [`);\r\n\r\n if (hasAuth) {\r\n lines.push(` {`);\r\n lines.push(` name: 'setup',`);\r\n lines.push(` testMatch: '**/auth.setup.ts',`);\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n lines.push(` },`);\r\n lines.push(` {`);\r\n lines.push(` name: 'chromium',`);\r\n lines.push(` testIgnore: ['**/*.setup.ts'],`);\r\n lines.push(` dependencies: ['setup'],`);\r\n lines.push(` use: {`);\r\n lines.push(` ...devices['Desktop Chrome'],`);\r\n lines.push(` storageState: '${storageState}',`);\r\n lines.push(` },`);\r\n lines.push(` },`);\r\n lines.push(` {`);\r\n lines.push(` name: 'chromium-no-auth',`);\r\n lines.push(` testMatch: '**/login-flow.test.ts',`);\r\n lines.push(` testIgnore: ['**/*.setup.ts'],`);\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n lines.push(` },`);\r\n } else {\r\n lines.push(` {`);\r\n lines.push(` name: 'chromium',`);\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n lines.push(` },`);\r\n }\r\n\r\n // Extra projects from config\r\n if (rt.extraProjects) {\r\n for (const proj of rt.extraProjects) {\r\n lines.push(` {`);\r\n lines.push(` name: '${proj.name}',`);\r\n lines.push(` testMatch: '${proj.testMatch}',`);\r\n if (proj.dependencies?.length) {\r\n lines.push(` dependencies: [${proj.dependencies.map((d) => `'${d}'`).join(', ')}],`);\r\n }\r\n if (proj.useAuth !== false && hasAuth) {\r\n lines.push(` use: {`);\r\n lines.push(` ...devices['Desktop Chrome'],`);\r\n lines.push(` storageState: '${storageState}',`);\r\n lines.push(` },`);\r\n } else {\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n }\r\n lines.push(` },`);\r\n }\r\n }\r\n\r\n lines.push(` ],`);\r\n lines.push('});');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate global-setup.ts content for Playwright.\r\n * Handles backend readiness probe, seed endpoint call, and log cleanup.\r\n */\r\nexport function generateGlobalSetup(config: OpenCrocConfig): string {\r\n const pw = config.playwright ?? {};\r\n const rt = config.runtime ?? {};\r\n const baseURL = pw.baseURL || '';\r\n\r\n const hasSeedEndpoint = !!rt.db?.seedEndpoint;\r\n const hasLogEndpoint = !!rt.logEndpoint;\r\n const hasSeedFile = !!rt.db?.seedFile;\r\n\r\n const lines: string[] = [];\r\n\r\n lines.push(`/**`);\r\n lines.push(` * Global setup — generated by OpenCroc.`);\r\n lines.push(` * Runs once before all test projects.`);\r\n lines.push(` */`);\r\n lines.push('');\r\n lines.push(`const BASE_URL = process.env.BASE_URL || '${baseURL || 'http://localhost:3000'}';`);\r\n lines.push('');\r\n\r\n // fetchWithRetry helper\r\n lines.push(`async function fetchWithRetry(`);\r\n lines.push(` url: string,`);\r\n lines.push(` options: RequestInit,`);\r\n lines.push(` retries = 3,`);\r\n lines.push(`): Promise<Response | null> {`);\r\n lines.push(` for (let i = 0; i < retries; i++) {`);\r\n lines.push(` try {`);\r\n lines.push(` const resp = await fetch(url, { ...options, signal: AbortSignal.timeout(10_000) });`);\r\n lines.push(` if (resp.ok) return resp;`);\r\n lines.push(` console.warn(\\`[global-setup] \\${options.method} \\${url} → \\${resp.status} (\\${i + 1}/\\${retries})\\`);`);\r\n lines.push(` } catch (err: unknown) {`);\r\n lines.push(` const msg = err instanceof Error ? err.message : String(err);`);\r\n lines.push(` console.warn(\\`[global-setup] \\${options.method} \\${url} error (\\${i + 1}/\\${retries}): \\${msg}\\`);`);\r\n lines.push(` }`);\r\n lines.push(` if (i < retries - 1) await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, i)));`);\r\n lines.push(` }`);\r\n lines.push(` return null;`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n // waitForBackend helper\r\n lines.push(`async function waitForBackend(origin: string, timeoutMs = 30_000): Promise<void> {`);\r\n lines.push(` const start = Date.now();`);\r\n lines.push(` while (Date.now() - start < timeoutMs) {`);\r\n lines.push(` try {`);\r\n lines.push(` const res = await fetch(\\`\\${origin}/health\\`, { signal: AbortSignal.timeout(3_000) });`);\r\n lines.push(` if (res.ok) return;`);\r\n lines.push(` } catch {`);\r\n lines.push(` // not ready yet`);\r\n lines.push(` }`);\r\n lines.push(` await new Promise((r) => setTimeout(r, 1_000));`);\r\n lines.push(` }`);\r\n lines.push(` throw new Error(\\`Backend not ready after \\${timeoutMs}ms\\`);`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n // Main function\r\n lines.push(`export default async function globalSetup(): Promise<void> {`);\r\n lines.push(` console.log('[global-setup] Starting...');`);\r\n lines.push('');\r\n lines.push(` // Backend readiness probe`);\r\n lines.push(` try {`);\r\n lines.push(` await waitForBackend(BASE_URL, 30_000);`);\r\n lines.push(` console.log('[global-setup] Backend is ready.');`);\r\n lines.push(` } catch (e: unknown) {`);\r\n lines.push(` console.warn(\\`[global-setup] \\${e instanceof Error ? e.message : String(e)}\\`);`);\r\n lines.push(` }`);\r\n lines.push('');\r\n\r\n if (hasSeedEndpoint) {\r\n lines.push(` // Seed data via backend endpoint`);\r\n lines.push(` try {`);\r\n lines.push(` await fetchWithRetry(`);\r\n lines.push(` \\`\\${BASE_URL}${rt.db!.seedEndpoint}\\`,`);\r\n lines.push(` { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' },`);\r\n lines.push(` );`);\r\n lines.push(` console.log('[global-setup] Seed endpoint called.');`);\r\n lines.push(` } catch {`);\r\n lines.push(` console.warn('[global-setup] Seed endpoint failed.');`);\r\n lines.push(` }`);\r\n lines.push('');\r\n }\r\n\r\n if (hasLogEndpoint) {\r\n lines.push(` // Clear test logs`);\r\n lines.push(` try {`);\r\n lines.push(` await fetchWithRetry(\\`\\${BASE_URL}${rt.logEndpoint}\\`, { method: 'DELETE' });`);\r\n lines.push(` console.log('[global-setup] Test logs cleared.');`);\r\n lines.push(` } catch {`);\r\n lines.push(` console.warn('[global-setup] Failed to clear test logs.');`);\r\n lines.push(` }`);\r\n lines.push('');\r\n }\r\n\r\n if (hasSeedFile) {\r\n lines.push(` // SQL seed file execution (placeholder — wire to your DB helper)`);\r\n lines.push(` // import { executeSeedSQL } from './utils/db-helper';`);\r\n lines.push(` // await executeSeedSQL('${rt.db!.seedFile}');`);\r\n lines.push('');\r\n }\r\n\r\n lines.push(` console.log('[global-setup] Done.');`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate global-teardown.ts content for Playwright.\r\n * Handles cleanup endpoint call and optional SQL cleanup.\r\n */\r\nexport function generateGlobalTeardown(config: OpenCrocConfig): string {\r\n const pw = config.playwright ?? {};\r\n const rt = config.runtime ?? {};\r\n const baseURL = pw.baseURL || '';\r\n\r\n const hasCleanupEndpoint = !!rt.db?.cleanupEndpoint;\r\n const hasCleanupFile = !!rt.db?.cleanupFile;\r\n\r\n const lines: string[] = [];\r\n\r\n lines.push(`/**`);\r\n lines.push(` * Global teardown — generated by OpenCroc.`);\r\n lines.push(` * Runs once after all test projects complete.`);\r\n lines.push(` */`);\r\n lines.push('');\r\n lines.push(`const BASE_URL = process.env.BASE_URL || '${baseURL || 'http://localhost:3000'}';`);\r\n lines.push('');\r\n\r\n lines.push(`export default async function globalTeardown(): Promise<void> {`);\r\n lines.push(` console.log('[global-teardown] Starting...');`);\r\n lines.push('');\r\n\r\n if (hasCleanupEndpoint) {\r\n lines.push(` // Cleanup via backend endpoint`);\r\n lines.push(` try {`);\r\n lines.push(` await fetch(\\`\\${BASE_URL}${rt.db!.cleanupEndpoint}\\`, { method: 'POST' });`);\r\n lines.push(` console.log('[global-teardown] Cleanup endpoint called.');`);\r\n lines.push(` } catch {`);\r\n lines.push(` console.warn('[global-teardown] Cleanup endpoint failed.');`);\r\n lines.push(` }`);\r\n lines.push('');\r\n }\r\n\r\n if (hasCleanupFile) {\r\n lines.push(` // SQL cleanup file execution (placeholder — wire to your DB helper)`);\r\n lines.push(` // import { executeSeedSQL } from './utils/db-helper';`);\r\n lines.push(` // await executeSeedSQL('${rt.db!.cleanupFile}');`);\r\n lines.push('');\r\n }\r\n\r\n lines.push(` console.log('[global-teardown] Done.');`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate auth.setup.ts content for Playwright.\r\n * Creates an authentication setup project that stores state for reuse.\r\n */\r\nexport function generateAuthSetup(config: OpenCrocConfig): string {\r\n const rt = config.runtime ?? {};\r\n const pw = config.playwright ?? {};\r\n const auth = rt.auth ?? {};\r\n const baseURL = pw.baseURL || '';\r\n const storageStatePath = auth.storageStatePath || 'playwright/.auth/user.json';\r\n\r\n const lines: string[] = [];\r\n\r\n lines.push(`import { test as setup, expect } from '@playwright/test';`);\r\n lines.push(`import * as fs from 'fs/promises';`);\r\n lines.push(`import * as path from 'path';`);\r\n lines.push('');\r\n\r\n lines.push(`const authFile = '${storageStatePath}';`);\r\n lines.push('');\r\n\r\n if (auth.loginUrl) {\r\n // API-based authentication\r\n lines.push(`setup('authenticate', async ({ request }) => {`);\r\n lines.push(` const loginUrl = process.env.AUTH_LOGIN_URL || '${auth.loginUrl}';`);\r\n lines.push(` const username = process.env.AUTH_USERNAME || '${auth.username || 'admin'}';`);\r\n lines.push(` const password = process.env.AUTH_PASSWORD || '${auth.password || ''}';`);\r\n lines.push('');\r\n lines.push(` // API login`);\r\n lines.push(` const response = await request.post(loginUrl, {`);\r\n lines.push(` data: { username, password },`);\r\n lines.push(` });`);\r\n lines.push(` expect(response.ok()).toBeTruthy();`);\r\n lines.push('');\r\n lines.push(` const body = await response.json();`);\r\n lines.push(` const token = body.data?.token || body.token || '';`);\r\n lines.push(` console.log('[auth.setup] Login successful, token obtained:', !!token);`);\r\n lines.push('');\r\n lines.push(` // Save storage state with auth cookie/localStorage`);\r\n lines.push(` await fs.mkdir(path.dirname(authFile), { recursive: true });`);\r\n lines.push('');\r\n lines.push(` // Build storage state JSON`);\r\n lines.push(` const baseURL = process.env.BASE_URL || '${baseURL || 'http://localhost:3000'}';`);\r\n lines.push(` const origin = new URL(baseURL).origin;`);\r\n lines.push(` const storageState = {`);\r\n lines.push(` cookies: [],`);\r\n lines.push(` origins: [`);\r\n lines.push(` {`);\r\n lines.push(` origin,`);\r\n lines.push(` localStorage: [`);\r\n lines.push(` { name: 'token', value: token },`);\r\n lines.push(` ],`);\r\n lines.push(` },`);\r\n lines.push(` ],`);\r\n lines.push(` };`);\r\n lines.push('');\r\n lines.push(` await fs.writeFile(authFile, JSON.stringify(storageState, null, 2));`);\r\n lines.push(` console.log(\\`[auth.setup] Storage state saved → \\${authFile}\\`);`);\r\n lines.push(`});`);\r\n } else {\r\n // Browser-based authentication (placeholder)\r\n lines.push(`setup('authenticate', async ({ browser }) => {`);\r\n lines.push(` const context = await browser.newContext();`);\r\n lines.push(` const page = await context.newPage();`);\r\n lines.push('');\r\n lines.push(` // TODO: Implement your login flow here`);\r\n lines.push(` // await page.goto('/login');`);\r\n lines.push(` // await page.fill('[name=username]', 'admin');`);\r\n lines.push(` // await page.fill('[name=password]', 'password');`);\r\n lines.push(` // await page.click('button[type=submit]');`);\r\n lines.push(` // await page.waitForURL('/dashboard');`);\r\n lines.push('');\r\n lines.push(` await fs.mkdir(path.dirname(authFile), { recursive: true });`);\r\n lines.push(` await context.storageState({ path: authFile });`);\r\n lines.push(` console.log(\\`[auth.setup] Storage state saved → \\${authFile}\\`);`);\r\n lines.push('');\r\n lines.push(` await context.close();`);\r\n lines.push(`});`);\r\n }\r\n\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import chalk from 'chalk';\r\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { generatePlaywrightConfig } from '../../runtime/playwright-config-generator.js';\r\nimport { generateGlobalSetup } from '../../runtime/global-setup-generator.js';\r\nimport { generateGlobalTeardown } from '../../runtime/global-teardown-generator.js';\r\nimport { generateAuthSetup } from '../../runtime/auth-setup-generator.js';\r\n\r\nexport interface InitRuntimeOptions {\r\n output?: string;\r\n force?: boolean;\r\n}\r\n\r\nfunction writeIfNotExists(filePath: string, content: string, force: boolean): boolean {\r\n if (existsSync(filePath) && !force) {\r\n console.log(chalk.yellow(` ⊘ ${filePath} already exists (use --force to overwrite)`));\r\n return false;\r\n }\r\n const dir = resolve(filePath, '..');\r\n mkdirSync(dir, { recursive: true });\r\n writeFileSync(filePath, content, 'utf-8');\r\n console.log(chalk.green(` ✓ ${filePath}`));\r\n return true;\r\n}\r\n\r\nexport async function initRuntime(opts: InitRuntimeOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Initialize Playwright Runtime\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const outDir = resolve(opts.output || '.');\r\n const force = opts.force ?? false;\r\n const hasAuth = !!config.runtime?.auth?.loginUrl;\r\n\r\n let written = 0;\r\n\r\n console.log(chalk.cyan('\\n Generating runtime files...\\n'));\r\n\r\n if (writeIfNotExists(join(outDir, 'playwright.config.ts'), generatePlaywrightConfig(config), force)) written++;\r\n if (writeIfNotExists(join(outDir, 'global-setup.ts'), generateGlobalSetup(config), force)) written++;\r\n if (writeIfNotExists(join(outDir, 'global-teardown.ts'), generateGlobalTeardown(config), force)) written++;\r\n\r\n if (hasAuth) {\r\n if (writeIfNotExists(join(outDir, 'auth.setup.ts'), generateAuthSetup(config), force)) written++;\r\n }\r\n\r\n console.log('');\r\n if (written > 0) {\r\n console.log(chalk.green(` ✓ Generated ${written} runtime file(s) in ${outDir}\\n`));\r\n } else {\r\n console.log(chalk.yellow(` No files written. Use --force to overwrite existing files.\\n`));\r\n }\r\n}\r\n","/**\r\n * Full orchestration engine: generate → execute → analyze → heal → report.\r\n *\r\n * Runs a multi-phase pipeline with per-phase tracking, failure detection,\r\n * token budget management, and structured result reporting.\r\n */\r\n\r\nimport { writeFileSync, mkdirSync, existsSync, readdirSync } from 'node:fs';\r\nimport { dirname, join } from 'node:path';\r\nimport { execFileSync } from 'node:child_process';\r\nimport type {\r\n OpenCrocConfig,\r\n PipelineRunResult,\r\n SelfHealingConfig,\r\n SelfHealingResult,\r\n} from '../types.js';\r\nimport { createPipeline } from '../pipeline/index.js';\r\nimport { createSelfHealingLoop } from '../self-healing/index.js';\r\nimport { generateReports } from '../reporters/index.js';\r\nimport type { ReportOutput } from '../reporters/index.js';\r\n\r\n// ===== Types =====\r\n\r\nexport type PhaseStatus = 'success' | 'warn' | 'error' | 'skipped';\r\n\r\nexport interface PhaseResult<T = unknown> {\r\n phase: string;\r\n status: PhaseStatus;\r\n output?: T;\r\n error?: string;\r\n durationMs: number;\r\n}\r\n\r\nexport interface OrchestrationOptions {\r\n /** Which phases to run (default: all) */\r\n phases?: OrchestrationPhase[];\r\n /** Enable self-healing phase (default: false) */\r\n selfHeal?: boolean;\r\n /** Max self-healing iterations */\r\n maxHealIterations?: number;\r\n /** Report formats to generate */\r\n reportFormats?: ('html' | 'json' | 'markdown')[];\r\n /** Run Playwright in headed mode */\r\n headed?: boolean;\r\n /** Module filter */\r\n module?: string;\r\n /** LLM token budget (0 = unlimited) */\r\n tokenBudget?: number;\r\n /** Abort on phase error (default: false — continue where possible) */\r\n abortOnError?: boolean;\r\n}\r\n\r\nexport type OrchestrationPhase = 'generate' | 'execute' | 'analyze' | 'heal' | 'report';\r\n\r\nexport interface ExecutionMetrics {\r\n passed: number;\r\n failed: number;\r\n skipped: number;\r\n timedOut: number;\r\n}\r\n\r\nexport interface OrchestrationSummary {\r\n overallStatus: 'success' | 'partial-fail' | 'fatal-fail';\r\n phases: PhaseResult[];\r\n totalDurationMs: number;\r\n modules: string[];\r\n executionMetrics?: ExecutionMetrics;\r\n reports?: ReportOutput[];\r\n healingResult?: SelfHealingResult;\r\n recommendation?: string;\r\n}\r\n\r\nconst ALL_PHASES: OrchestrationPhase[] = ['generate', 'execute', 'analyze', 'heal', 'report'];\r\n\r\n// ===== Helpers =====\r\n\r\nfunction discoverTestFiles(outDir: string, moduleFilter?: string): string[] {\r\n const absDir = join(process.cwd(), outDir);\r\n if (!existsSync(absDir)) return [];\r\n\r\n const files: string[] = [];\r\n const entries = readdirSync(absDir, { withFileTypes: true, recursive: true });\r\n for (const entry of entries) {\r\n if (!entry.isFile()) continue;\r\n if (!entry.name.endsWith('.spec.ts') && !entry.name.endsWith('.test.ts')) continue;\r\n const fullPath = join(\r\n entry.parentPath || (entry as unknown as { path: string }).path || absDir,\r\n entry.name,\r\n );\r\n if (moduleFilter && !fullPath.includes(moduleFilter)) continue;\r\n files.push(fullPath);\r\n }\r\n return files;\r\n}\r\n\r\nfunction parsePlaywrightOutput(stderr: string): ExecutionMetrics {\r\n const metrics: ExecutionMetrics = { passed: 0, failed: 0, skipped: 0, timedOut: 0 };\r\n\r\n // Playwright summary line: \"X passed\", \"Y failed\", \"Z skipped\"\r\n const passedMatch = stderr.match(/(\\d+)\\s+passed/);\r\n const failedMatch = stderr.match(/(\\d+)\\s+failed/);\r\n const skippedMatch = stderr.match(/(\\d+)\\s+skipped/);\r\n const timedOutMatch = stderr.match(/(\\d+)\\s+timed?\\s*out/i);\r\n\r\n if (passedMatch) metrics.passed = parseInt(passedMatch[1], 10);\r\n if (failedMatch) metrics.failed = parseInt(failedMatch[1], 10);\r\n if (skippedMatch) metrics.skipped = parseInt(skippedMatch[1], 10);\r\n if (timedOutMatch) metrics.timedOut = parseInt(timedOutMatch[1], 10);\r\n\r\n return metrics;\r\n}\r\n\r\n// ===== Orchestrator =====\r\n\r\nexport function createOrchestrator(config: OpenCrocConfig, options: OrchestrationOptions = {}) {\r\n const {\r\n phases = ALL_PHASES,\r\n selfHeal = false,\r\n maxHealIterations = 3,\r\n reportFormats = ['html', 'json'],\r\n headed = false,\r\n module: moduleFilter,\r\n tokenBudget = 0,\r\n abortOnError = false,\r\n } = options;\r\n\r\n const outDir = config.outDir || './opencroc-output';\r\n const phaseResults: PhaseResult[] = [];\r\n let pipelineResult: PipelineRunResult | undefined;\r\n let executionMetrics: ExecutionMetrics | undefined;\r\n let healingResult: SelfHealingResult | undefined;\r\n let reports: ReportOutput[] | undefined;\r\n let tokensUsed = 0;\r\n\r\n function isBudgetExceeded(): boolean {\r\n return tokenBudget > 0 && tokensUsed >= tokenBudget;\r\n }\r\n\r\n function shouldRun(phase: OrchestrationPhase): boolean {\r\n if (phase === 'heal' && !selfHeal) return false;\r\n return phases.includes(phase);\r\n }\r\n\r\n async function runPhase<T>(\r\n name: string,\r\n fn: () => Promise<T>,\r\n ): Promise<PhaseResult<T>> {\r\n const start = Date.now();\r\n try {\r\n const output = await fn();\r\n const result: PhaseResult<T> = {\r\n phase: name,\r\n status: 'success',\r\n output,\r\n durationMs: Date.now() - start,\r\n };\r\n phaseResults.push(result);\r\n return result;\r\n } catch (err) {\r\n const result: PhaseResult<T> = {\r\n phase: name,\r\n status: 'error',\r\n error: err instanceof Error ? err.message : String(err),\r\n durationMs: Date.now() - start,\r\n };\r\n phaseResults.push(result);\r\n return result;\r\n }\r\n }\r\n\r\n function skipPhase(name: string, reason: string): PhaseResult {\r\n const result: PhaseResult = {\r\n phase: name,\r\n status: 'skipped',\r\n error: reason,\r\n durationMs: 0,\r\n };\r\n phaseResults.push(result);\r\n return result;\r\n }\r\n\r\n return {\r\n async run(): Promise<OrchestrationSummary> {\r\n const orchestrationStart = Date.now();\r\n\r\n // ── Phase 1: Generate ──\r\n if (shouldRun('generate')) {\r\n const genResult = await runPhase('generate', async () => {\r\n if (moduleFilter) config.modules = [moduleFilter];\r\n const pipeline = createPipeline(config);\r\n pipelineResult = await pipeline.run();\r\n\r\n // Write generated files\r\n for (const file of pipelineResult.generatedFiles) {\r\n const dir = dirname(file.filePath);\r\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\r\n writeFileSync(file.filePath, file.content, 'utf-8');\r\n }\r\n\r\n return pipelineResult;\r\n });\r\n\r\n if (genResult.status === 'error' && abortOnError) {\r\n return buildSummary(orchestrationStart);\r\n }\r\n }\r\n\r\n // ── Phase 2: Execute ──\r\n if (shouldRun('execute')) {\r\n const testFiles = discoverTestFiles(outDir, moduleFilter);\r\n\r\n if (testFiles.length === 0) {\r\n skipPhase('execute', 'No test files found');\r\n } else {\r\n const execResult = await runPhase('execute', async () => {\r\n const args = ['test', ...testFiles];\r\n if (!headed) args.push('--reporter=list');\r\n else args.push('--headed');\r\n\r\n const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n try {\r\n execFileSync(npxCmd, ['playwright', ...args], {\r\n cwd: process.cwd(),\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n timeout: 300_000,\r\n });\r\n return { passed: testFiles.length, failed: 0, skipped: 0, timedOut: 0 } as ExecutionMetrics;\r\n } catch (err) {\r\n const stderr = (err as { stderr?: Buffer })?.stderr?.toString() ?? '';\r\n const metrics = parsePlaywrightOutput(stderr);\r\n if (metrics.passed === 0 && metrics.failed === 0) {\r\n metrics.failed = testFiles.length;\r\n }\r\n executionMetrics = metrics;\r\n if (metrics.failed > 0) {\r\n throw new Error(`${metrics.failed} test(s) failed, ${metrics.passed} passed`, { cause: err });\r\n }\r\n return metrics;\r\n }\r\n });\r\n\r\n if (!executionMetrics && execResult.output) {\r\n executionMetrics = execResult.output as ExecutionMetrics;\r\n }\r\n\r\n if (execResult.status === 'error' && abortOnError) {\r\n return buildSummary(orchestrationStart);\r\n }\r\n }\r\n }\r\n\r\n // ── Phase 3: Analyze ──\r\n if (shouldRun('analyze')) {\r\n if (!pipelineResult || !executionMetrics || executionMetrics.failed === 0) {\r\n skipPhase('analyze', 'No failures to analyze');\r\n } else {\r\n await runPhase('analyze', async () => {\r\n // Re-run pipeline validation for failure context\r\n const pipeline = createPipeline(config);\r\n const validationResult = await pipeline.run(['validate']);\r\n return {\r\n validationErrors: validationResult.validationErrors,\r\n failedTestCount: executionMetrics!.failed,\r\n };\r\n });\r\n }\r\n }\r\n\r\n // ── Phase 4: Heal ──\r\n if (shouldRun('heal')) {\r\n if (isBudgetExceeded()) {\r\n skipPhase('heal', 'Token budget exceeded');\r\n } else if (!executionMetrics || executionMetrics.failed === 0) {\r\n skipPhase('heal', 'No failures to heal');\r\n } else {\r\n const healResult = await runPhase('heal', async () => {\r\n const healConfig: SelfHealingConfig = {\r\n enabled: true,\r\n maxIterations: maxHealIterations,\r\n mode: config.selfHealing?.mode || 'config-only',\r\n };\r\n\r\n const loop = createSelfHealingLoop(healConfig);\r\n const result = await loop.run(outDir);\r\n tokensUsed += result.totalTokensUsed;\r\n healingResult = result;\r\n return result;\r\n });\r\n\r\n if (healResult.status === 'success' && healingResult && healingResult.remaining.length > 0) {\r\n healResult.status = 'warn';\r\n phaseResults[phaseResults.length - 1] = healResult;\r\n }\r\n }\r\n }\r\n\r\n // ── Phase 5: Report ──\r\n if (shouldRun('report')) {\r\n if (!pipelineResult) {\r\n skipPhase('report', 'No pipeline results to report');\r\n } else {\r\n await runPhase('report', async () => {\r\n reports = generateReports(pipelineResult!, reportFormats);\r\n\r\n if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });\r\n for (const r of reports) {\r\n writeFileSync(join(outDir, r.filename), r.content, 'utf-8');\r\n }\r\n\r\n return reports;\r\n });\r\n }\r\n }\r\n\r\n return buildSummary(orchestrationStart);\r\n },\r\n };\r\n\r\n function buildSummary(startTime: number): OrchestrationSummary {\r\n const errorCount = phaseResults.filter((p) => p.status === 'error').length;\r\n const allSuccess = phaseResults.every((p) => p.status === 'success' || p.status === 'skipped');\r\n\r\n let overallStatus: OrchestrationSummary['overallStatus'];\r\n if (allSuccess) overallStatus = 'success';\r\n else if (errorCount > 1) overallStatus = 'fatal-fail';\r\n else overallStatus = 'partial-fail';\r\n\r\n let recommendation: string | undefined;\r\n if (executionMetrics && executionMetrics.failed > 0 && !selfHeal) {\r\n recommendation = 'Test failures detected. Consider running with --self-heal to attempt automated fixes.';\r\n } else if (healingResult && healingResult.remaining.length > 0) {\r\n recommendation = `${healingResult.remaining.length} issue(s) could not be auto-fixed. Manual review needed.`;\r\n }\r\n\r\n return {\r\n overallStatus,\r\n phases: phaseResults,\r\n totalDurationMs: Date.now() - startTime,\r\n modules: pipelineResult?.modules ?? [],\r\n executionMetrics,\r\n reports,\r\n healingResult,\r\n recommendation,\r\n };\r\n }\r\n}\r\n","/**\r\n * Orchestration summary reporter.\r\n * Writes phase-by-phase JSON and a human-readable console summary.\r\n */\r\n\r\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\r\nimport { join } from 'node:path';\r\nimport type { OrchestrationSummary, PhaseResult } from './index.js';\r\n\r\nexport interface OrchestrationReportOptions {\r\n outputDir: string;\r\n module?: string;\r\n}\r\n\r\n/**\r\n * Write the orchestration summary to a JSON file.\r\n * Returns the written file path.\r\n */\r\nexport function writeOrchestrationSummary(\r\n summary: OrchestrationSummary,\r\n options: OrchestrationReportOptions,\r\n): string {\r\n const { outputDir, module: mod } = options;\r\n if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });\r\n\r\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\r\n const moduleName = mod ?? 'all';\r\n const filename = `orchestration-${moduleName}-${timestamp}.json`;\r\n const filePath = join(outputDir, filename);\r\n\r\n const serializable = {\r\n ...summary,\r\n phases: summary.phases.map((p) => ({\r\n phase: p.phase,\r\n status: p.status,\r\n error: p.error,\r\n durationMs: p.durationMs,\r\n })),\r\n // Strip report content to avoid massive JSON\r\n reports: summary.reports?.map((r) => ({\r\n format: r.format,\r\n filename: r.filename,\r\n })),\r\n };\r\n\r\n writeFileSync(filePath, JSON.stringify(serializable, null, 2), 'utf-8');\r\n return filePath;\r\n}\r\n\r\n/**\r\n * Format a phase result as a single console line.\r\n */\r\nfunction formatPhase(p: PhaseResult): string {\r\n const icons: Record<string, string> = {\r\n success: '✓',\r\n warn: '⚠',\r\n error: '✗',\r\n skipped: '○',\r\n };\r\n const icon = icons[p.status] ?? '?';\r\n const dur = p.durationMs > 0 ? ` (${p.durationMs}ms)` : '';\r\n const err = p.error ? ` — ${p.error}` : '';\r\n return ` ${icon} ${p.phase}${dur}${err}`;\r\n}\r\n\r\n/**\r\n * Print a human-readable console summary.\r\n */\r\nexport function printOrchestrationSummary(summary: OrchestrationSummary): string[] {\r\n const lines: string[] = [];\r\n\r\n lines.push('');\r\n lines.push(' ═══════════════════════════════════════');\r\n lines.push(' Orchestration Summary');\r\n lines.push(' ═══════════════════════════════════════');\r\n lines.push('');\r\n\r\n for (const p of summary.phases) {\r\n lines.push(formatPhase(p));\r\n }\r\n\r\n lines.push('');\r\n lines.push(` Overall : ${summary.overallStatus}`);\r\n lines.push(` Modules : ${summary.modules.join(', ') || '(none)'}`);\r\n lines.push(` Duration : ${summary.totalDurationMs}ms`);\r\n\r\n if (summary.executionMetrics) {\r\n const m = summary.executionMetrics;\r\n lines.push(` Tests : ${m.passed} passed, ${m.failed} failed, ${m.skipped} skipped`);\r\n }\r\n\r\n if (summary.healingResult) {\r\n const h = summary.healingResult;\r\n lines.push(` Healing : ${h.fixed.length} fixed, ${h.remaining.length} remaining (${h.iterations} iterations)`);\r\n }\r\n\r\n if (summary.reports) {\r\n lines.push(` Reports : ${summary.reports.map((r) => r.format).join(', ')}`);\r\n }\r\n\r\n if (summary.recommendation) {\r\n lines.push('');\r\n lines.push(` → ${summary.recommendation}`);\r\n }\r\n\r\n lines.push('');\r\n\r\n return lines;\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createOrchestrator } from '../../orchestrator/index.js';\r\nimport { writeOrchestrationSummary, printOrchestrationSummary } from '../../orchestrator/reporter.js';\r\nimport type { OrchestrationPhase } from '../../orchestrator/index.js';\r\n\r\nexport interface RunOptions {\r\n module?: string;\r\n phases?: string;\r\n selfHeal?: boolean;\r\n headed?: boolean;\r\n report?: string;\r\n tokenBudget?: string;\r\n abortOnError?: boolean;\r\n}\r\n\r\nconst VALID_PHASES: OrchestrationPhase[] = ['generate', 'execute', 'analyze', 'heal', 'report'];\r\n\r\nfunction parsePhases(raw?: string): OrchestrationPhase[] | undefined {\r\n if (!raw) return undefined;\r\n const names = raw.split(',').map((s) => s.trim());\r\n for (const name of names) {\r\n if (!VALID_PHASES.includes(name as OrchestrationPhase)) {\r\n console.log(chalk.red(` Unknown phase \"${name}\". Valid: ${VALID_PHASES.join(', ')}`));\r\n process.exitCode = 1;\r\n return undefined;\r\n }\r\n }\r\n return names as OrchestrationPhase[];\r\n}\r\n\r\nexport async function run(opts: RunOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Full Orchestration\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const phases = parsePhases(opts.phases);\r\n if (phases === undefined && opts.phases) return;\r\n\r\n const reportFormats = (opts.report ?? 'html,json').split(',').map((s) => s.trim()) as ('html' | 'json' | 'markdown')[];\r\n\r\n const orchestrator = createOrchestrator(config, {\r\n phases,\r\n selfHeal: opts.selfHeal ?? false,\r\n headed: opts.headed ?? false,\r\n module: opts.module,\r\n reportFormats,\r\n tokenBudget: opts.tokenBudget ? parseInt(opts.tokenBudget, 10) : 0,\r\n abortOnError: opts.abortOnError ?? false,\r\n });\r\n\r\n console.log(chalk.gray(` Phases: ${(phases ?? VALID_PHASES).join(' → ')}`));\r\n if (opts.selfHeal) console.log(chalk.gray(' Self-heal: enabled'));\r\n console.log('');\r\n\r\n const summary = await orchestrator.run();\r\n\r\n // Print console summary\r\n const lines = printOrchestrationSummary(summary);\r\n for (const line of lines) {\r\n const color =\r\n summary.overallStatus === 'success' ? chalk.green :\r\n summary.overallStatus === 'partial-fail' ? chalk.yellow :\r\n chalk.red;\r\n console.log(color(line));\r\n }\r\n\r\n // Write JSON summary\r\n const outDir = config.outDir || './opencroc-output';\r\n const summaryPath = writeOrchestrationSummary(summary, { outputDir: outDir, module: opts.module });\r\n console.log(chalk.gray(` Summary: ${summaryPath}\\n`));\r\n\r\n if (summary.overallStatus !== 'success') {\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import type { FastifyInstance } from 'fastify';\r\nimport type { CrocOffice } from '../croc-office.js';\r\n\r\nexport function registerProjectRoutes(app: FastifyInstance, office: CrocOffice): void {\r\n // GET /api/project — full project info + knowledge graph\r\n app.get('/api/project', async () => {\r\n return office.getProjectInfo();\r\n });\r\n\r\n // GET /api/project/graph — knowledge graph only\r\n app.get('/api/project/graph', async () => {\r\n return office.buildKnowledgeGraph();\r\n });\r\n\r\n // POST /api/project/refresh — invalidate cache and re-scan\r\n app.post('/api/project/refresh', async () => {\r\n office.invalidateCache();\r\n const graph = await office.buildKnowledgeGraph();\r\n office.broadcast('graph:update', graph);\r\n return { ok: true, nodes: graph.nodes.length, edges: graph.edges.length };\r\n });\r\n}\r\n","import type { FastifyInstance } from 'fastify';\r\nimport type { CrocOffice } from '../croc-office.js';\r\n\r\nexport function registerAgentRoutes(app: FastifyInstance, office: CrocOffice): void {\r\n // GET /api/agents — list all croc agents\r\n app.get('/api/agents', async () => {\r\n return office.getAgents();\r\n });\r\n\r\n // GET /api/agents/:id — get specific agent\r\n app.get<{ Params: { id: string } }>('/api/agents/:id', async (req, reply) => {\r\n const agent = office.getAgent(req.params.id);\r\n if (!agent) {\r\n reply.code(404).send({ error: 'Agent not found' });\r\n return;\r\n }\r\n return agent;\r\n });\r\n\r\n // POST /api/agents/:id/task — assign a task to an agent (M2: full implementation)\r\n app.post<{ Params: { id: string }; Body: { task: string } }>('/api/agents/:id/task', async (req, reply) => {\r\n const agent = office.getAgent(req.params.id);\r\n if (!agent) {\r\n reply.code(404).send({ error: 'Agent not found' });\r\n return;\r\n }\r\n\r\n office.updateAgent(req.params.id, {\r\n status: 'working',\r\n currentTask: req.body?.task || 'Processing...',\r\n });\r\n\r\n // M2 will implement actual task dispatch to LLM\r\n // For now, simulate work\r\n setTimeout(() => {\r\n office.updateAgent(req.params.id, {\r\n status: 'done',\r\n currentTask: 'Task completed',\r\n });\r\n }, 2000);\r\n\r\n return { ok: true, agent: req.params.id, task: req.body?.task };\r\n });\r\n}\r\n","import type { WebSocket } from 'ws';\r\nimport type { OpenCrocConfig } from '../types.js';\r\n\r\nexport interface CrocAgent {\r\n id: string;\r\n name: string;\r\n role: 'parser' | 'analyzer' | 'tester' | 'healer' | 'planner' | 'reporter';\r\n sprite: string;\r\n status: 'idle' | 'working' | 'thinking' | 'done' | 'error';\r\n currentTask?: string;\r\n tokensUsed: number;\r\n}\r\n\r\nexport interface KnowledgeGraphNode {\r\n id: string;\r\n label: string;\r\n type: 'model' | 'controller' | 'api' | 'dto' | 'module';\r\n status: 'idle' | 'testing' | 'passed' | 'failed';\r\n fields?: string[];\r\n module?: string;\r\n}\r\n\r\nexport interface KnowledgeGraphEdge {\r\n source: string;\r\n target: string;\r\n relation: string;\r\n}\r\n\r\nexport interface KnowledgeGraph {\r\n nodes: KnowledgeGraphNode[];\r\n edges: KnowledgeGraphEdge[];\r\n}\r\n\r\nexport interface ProjectInfo {\r\n name: string;\r\n backendRoot: string;\r\n adapter: string;\r\n stats: {\r\n modules: number;\r\n models: number;\r\n endpoints: number;\r\n relations: number;\r\n };\r\n graph: KnowledgeGraph;\r\n agents: CrocAgent[];\r\n}\r\n\r\nconst DEFAULT_AGENTS: CrocAgent[] = [\r\n { id: 'parser-croc', name: '解析鳄', role: 'parser', sprite: 'parser', status: 'idle', tokensUsed: 0 },\r\n { id: 'analyzer-croc', name: '分析鳄', role: 'analyzer', sprite: 'analyzer', status: 'idle', tokensUsed: 0 },\r\n { id: 'tester-croc', name: '测试鳄', role: 'tester', sprite: 'tester', status: 'idle', tokensUsed: 0 },\r\n { id: 'healer-croc', name: '修复鳄', role: 'healer', sprite: 'healer', status: 'idle', tokensUsed: 0 },\r\n { id: 'planner-croc', name: '规划鳄', role: 'planner', sprite: 'planner', status: 'idle', tokensUsed: 0 },\r\n { id: 'reporter-croc', name: '汇报鳄', role: 'reporter', sprite: 'reporter', status: 'idle', tokensUsed: 0 },\r\n];\r\n\r\nexport class CrocOffice {\r\n private config: OpenCrocConfig;\r\n private cwd: string;\r\n private clients: Set<WebSocket> = new Set();\r\n private agents: CrocAgent[];\r\n private cachedGraph: KnowledgeGraph | null = null;\r\n\r\n constructor(config: OpenCrocConfig, cwd: string) {\r\n this.config = config;\r\n this.cwd = cwd;\r\n this.agents = DEFAULT_AGENTS.map((a) => ({ ...a }));\r\n }\r\n\r\n addClient(ws: WebSocket): void {\r\n this.clients.add(ws);\r\n }\r\n\r\n removeClient(ws: WebSocket): void {\r\n this.clients.delete(ws);\r\n }\r\n\r\n broadcast(type: string, payload: unknown): void {\r\n const msg = JSON.stringify({ type, payload });\r\n for (const client of this.clients) {\r\n try {\r\n client.send(msg);\r\n } catch {\r\n this.clients.delete(client);\r\n }\r\n }\r\n }\r\n\r\n getAgents(): CrocAgent[] {\r\n return this.agents;\r\n }\r\n\r\n getAgent(id: string): CrocAgent | undefined {\r\n return this.agents.find((a) => a.id === id);\r\n }\r\n\r\n updateAgent(id: string, update: Partial<CrocAgent>): void {\r\n const agent = this.agents.find((a) => a.id === id);\r\n if (agent) {\r\n Object.assign(agent, update);\r\n this.broadcast('agent:update', this.agents);\r\n }\r\n }\r\n\r\n getConfig(): OpenCrocConfig {\r\n return this.config;\r\n }\r\n\r\n getCwd(): string {\r\n return this.cwd;\r\n }\r\n\r\n /** Build knowledge graph from project source code */\r\n async buildKnowledgeGraph(): Promise<KnowledgeGraph> {\r\n if (this.cachedGraph) return this.cachedGraph;\r\n\r\n this.updateAgent('parser-croc', { status: 'working', currentTask: 'Scanning project structure...' });\r\n\r\n try {\r\n const { resolve: resolvePath } = await import('node:path');\r\n const { glob } = await import('glob');\r\n\r\n const backendRoot = resolvePath(this.cwd, this.config.backendRoot);\r\n const nodes: KnowledgeGraphNode[] = [];\r\n const edges: KnowledgeGraphEdge[] = [];\r\n const moduleSet = new Set<string>();\r\n\r\n // Scan for models\r\n const modelFiles = await glob('**/models/**/*.{ts,js}', {\r\n cwd: backendRoot,\r\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*', '**/index.*'],\r\n });\r\n\r\n for (const file of modelFiles) {\r\n const parts = file.split('/');\r\n const moduleName = parts.length >= 3 ? parts[parts.length - 3] : 'default';\r\n const fileName = parts[parts.length - 1].replace(/\\.(ts|js)$/, '');\r\n const nodeId = `model:${fileName}`;\r\n\r\n moduleSet.add(moduleName);\r\n nodes.push({\r\n id: nodeId,\r\n label: fileName,\r\n type: 'model',\r\n status: 'idle',\r\n module: moduleName,\r\n });\r\n }\r\n\r\n // Scan for controllers\r\n const controllerFiles = await glob('**/controllers/**/*.{ts,js}', {\r\n cwd: backendRoot,\r\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*', '**/index.*'],\r\n });\r\n\r\n for (const file of controllerFiles) {\r\n const parts = file.split('/');\r\n const moduleName = parts.length >= 3 ? parts[parts.length - 3] : 'default';\r\n const fileName = parts[parts.length - 1].replace(/\\.(ts|js)$/, '').replace('.controller', '');\r\n const nodeId = `controller:${fileName}`;\r\n\r\n moduleSet.add(moduleName);\r\n nodes.push({\r\n id: nodeId,\r\n label: `${fileName} (ctrl)`,\r\n type: 'controller',\r\n status: 'idle',\r\n module: moduleName,\r\n });\r\n\r\n // Link controller to its model\r\n const modelNode = nodes.find((n) => n.type === 'model' && n.label.toLowerCase() === fileName.toLowerCase());\r\n if (modelNode) {\r\n edges.push({ source: nodeId, target: modelNode.id, relation: 'uses' });\r\n }\r\n }\r\n\r\n // Add module nodes\r\n for (const mod of moduleSet) {\r\n const moduleNodeId = `module:${mod}`;\r\n nodes.push({\r\n id: moduleNodeId,\r\n label: mod,\r\n type: 'module',\r\n status: 'idle',\r\n });\r\n\r\n // Link models/controllers to their module\r\n for (const n of nodes) {\r\n if (n.module === mod && n.type !== 'module') {\r\n edges.push({ source: moduleNodeId, target: n.id, relation: 'contains' });\r\n }\r\n }\r\n }\r\n\r\n this.cachedGraph = { nodes, edges };\r\n this.updateAgent('parser-croc', { status: 'done', currentTask: `Found ${nodes.length} nodes` });\r\n return this.cachedGraph;\r\n\r\n } catch (err) {\r\n this.updateAgent('parser-croc', { status: 'error', currentTask: String(err) });\r\n return { nodes: [], edges: [] };\r\n }\r\n }\r\n\r\n invalidateCache(): void {\r\n this.cachedGraph = null;\r\n }\r\n\r\n async getProjectInfo(): Promise<ProjectInfo> {\r\n const graph = await this.buildKnowledgeGraph();\r\n const stats = {\r\n modules: graph.nodes.filter((n) => n.type === 'module').length,\r\n models: graph.nodes.filter((n) => n.type === 'model').length,\r\n endpoints: graph.nodes.filter((n) => n.type === 'api' || n.type === 'controller').length,\r\n relations: graph.edges.length,\r\n };\r\n\r\n return {\r\n name: this.config.backendRoot.split('/').pop() || 'project',\r\n backendRoot: this.config.backendRoot,\r\n adapter: typeof this.config.adapter === 'string' ? this.config.adapter : 'custom',\r\n stats,\r\n graph,\r\n agents: this.agents,\r\n };\r\n }\r\n}\r\n","import Fastify from 'fastify';\r\nimport fastifyStatic from '@fastify/static';\r\nimport fastifyWebsocket from '@fastify/websocket';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { dirname, join, resolve } from 'node:path';\r\nimport { existsSync } from 'node:fs';\r\nimport { registerProjectRoutes } from './routes/project.js';\r\nimport { registerAgentRoutes } from './routes/agents.js';\r\nimport { CrocOffice } from './croc-office.js';\r\nimport type { OpenCrocConfig } from '../types.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = dirname(__filename);\r\n\r\nexport interface ServeOptions {\r\n port: number;\r\n host: string;\r\n open: boolean;\r\n config: OpenCrocConfig;\r\n cwd: string;\r\n}\r\n\r\nexport async function startServer(opts: ServeOptions): Promise<void> {\r\n const app = Fastify({ logger: false });\r\n\r\n // --- WebSocket ---\r\n await app.register(fastifyWebsocket);\r\n\r\n // --- Static frontend assets ---\r\n const webDir = resolve(__dirname, '../web');\r\n if (existsSync(webDir)) {\r\n await app.register(fastifyStatic, {\r\n root: webDir,\r\n prefix: '/',\r\n decorateReply: false,\r\n });\r\n }\r\n\r\n // --- Croc Office (Agent orchestrator) ---\r\n const office = new CrocOffice(opts.config, opts.cwd);\r\n\r\n // --- REST API routes ---\r\n registerProjectRoutes(app, office);\r\n registerAgentRoutes(app, office);\r\n\r\n // --- WebSocket endpoint for real-time updates ---\r\n app.register(async (fastify) => {\r\n fastify.get('/ws', { websocket: true }, (socket) => {\r\n office.addClient(socket);\r\n socket.on('close', () => office.removeClient(socket));\r\n });\r\n });\r\n\r\n // --- SPA fallback: serve index.html for non-API, non-asset routes ---\r\n app.setNotFoundHandler((req, reply) => {\r\n if (req.url.startsWith('/api/')) {\r\n reply.code(404).send({ error: 'Not found' });\r\n return;\r\n }\r\n const indexPath = join(webDir, 'index.html');\r\n if (existsSync(indexPath)) {\r\n reply.sendFile('index.html');\r\n } else {\r\n reply.code(200).header('content-type', 'text/html').send(getEmbeddedHtml());\r\n }\r\n });\r\n\r\n try {\r\n await app.listen({ port: opts.port, host: opts.host });\r\n const url = `http://${opts.host === '0.0.0.0' ? 'localhost' : opts.host}:${opts.port}`;\r\n console.log(`\\n 🐊 OpenCroc Studio is running at ${url}\\n`);\r\n\r\n if (opts.open) {\r\n const { exec } = await import('node:child_process');\r\n const cmd = process.platform === 'win32' ? 'start' :\r\n process.platform === 'darwin' ? 'open' : 'xdg-open';\r\n exec(`${cmd} ${url}`);\r\n }\r\n } catch (err) {\r\n console.error('Failed to start server:', err);\r\n process.exit(1);\r\n }\r\n}\r\n\r\n/** Minimal embedded HTML when no web build is present */\r\nfunction getEmbeddedHtml(): string {\r\n return `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n<title>OpenCroc Studio 🐊</title>\r\n<style>\r\n * { margin:0; padding:0; box-sizing:border-box; }\r\n body { background:#1a1a2e; color:#e0e0e0; font-family:'Courier New',monospace; display:flex; justify-content:center; align-items:center; min-height:100vh; }\r\n .container { text-align:center; }\r\n h1 { font-size:3rem; color:#4ecca3; margin-bottom:1rem; }\r\n .croc { font-size:6rem; animation: bounce 1s infinite alternate; }\r\n @keyframes bounce { from{transform:translateY(0)} to{transform:translateY(-20px)} }\r\n p { margin-top:1rem; color:#888; }\r\n .status { margin-top:2rem; padding:1rem; background:#16213e; border-radius:8px; }\r\n #graph-container { margin-top:2rem; min-height:400px; background:#0f3460; border-radius:8px; position:relative; }\r\n .loading { color:#4ecca3; padding:2rem; }\r\n</style>\r\n</head>\r\n<body>\r\n<div class=\"container\">\r\n <div class=\"croc\">🐊</div>\r\n <h1>OpenCroc Studio</h1>\r\n <p>AI-native E2E testing — Pixel Croc Office</p>\r\n <div class=\"status\" id=\"status\">Connecting...</div>\r\n <div id=\"graph-container\"><div class=\"loading\">Loading project graph...</div></div>\r\n</div>\r\n<script>\r\n(async () => {\r\n // Fetch project graph data\r\n try {\r\n const res = await fetch('/api/project');\r\n const data = await res.json();\r\n document.getElementById('status').innerHTML =\r\n '<b>Project:</b> ' + (data.name || 'unknown') +\r\n ' | <b>Modules:</b> ' + (data.stats?.modules || 0) +\r\n ' | <b>Models:</b> ' + (data.stats?.models || 0) +\r\n ' | <b>APIs:</b> ' + (data.stats?.endpoints || 0);\r\n\r\n renderGraph(data.graph);\r\n } catch(e) {\r\n document.getElementById('status').textContent = 'Error loading project: ' + e.message;\r\n }\r\n\r\n // WebSocket for live updates\r\n const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\r\n const ws = new WebSocket(protocol + '//' + location.host + '/ws');\r\n ws.onmessage = (e) => {\r\n try {\r\n const msg = JSON.parse(e.data);\r\n if (msg.type === 'agent:update') {\r\n updateAgentStatus(msg.payload);\r\n } else if (msg.type === 'graph:update') {\r\n renderGraph(msg.payload);\r\n }\r\n } catch {}\r\n };\r\n ws.onclose = () => {\r\n document.getElementById('status').textContent += ' [disconnected]';\r\n };\r\n})();\r\n\r\nfunction renderGraph(graph) {\r\n if (!graph || (!graph.nodes?.length)) {\r\n document.getElementById('graph-container').innerHTML = '<div class=\"loading\">No modules found. Run opencroc init first.</div>';\r\n return;\r\n }\r\n\r\n const container = document.getElementById('graph-container');\r\n const w = container.clientWidth || 800;\r\n const h = 500;\r\n\r\n // Simple force-directed placement\r\n const nodes = graph.nodes.map((n, i) => ({\r\n ...n,\r\n x: w/2 + Math.cos(i * 2 * Math.PI / graph.nodes.length) * Math.min(w,h) * 0.35,\r\n y: h/2 + Math.sin(i * 2 * Math.PI / graph.nodes.length) * Math.min(w,h) * 0.35,\r\n vx: 0, vy: 0,\r\n }));\r\n\r\n const nodeMap = new Map(nodes.map(n => [n.id, n]));\r\n\r\n // Render SVG\r\n const colors = { model:'#4ecca3', controller:'#e94560', api:'#f39c12', dto:'#3498db', default:'#888' };\r\n\r\n let svg = '<svg width=\"'+w+'\" height=\"'+h+'\" xmlns=\"http://www.w3.org/2000/svg\">';\r\n\r\n // Edges\r\n for (const edge of (graph.edges || [])) {\r\n const s = nodeMap.get(edge.source);\r\n const t = nodeMap.get(edge.target);\r\n if (s && t) {\r\n svg += '<line x1=\"'+s.x+'\" y1=\"'+s.y+'\" x2=\"'+t.x+'\" y2=\"'+t.y+'\" stroke=\"#555\" stroke-width=\"1.5\" opacity=\"0.6\"/>';\r\n }\r\n }\r\n\r\n // Nodes\r\n for (const n of nodes) {\r\n const color = colors[n.type] || colors.default;\r\n const statusColor = n.status === 'passed' ? '#4ecca3' : n.status === 'failed' ? '#e94560' : n.status === 'testing' ? '#f39c12' : '#555';\r\n // Pixel-art style square nodes\r\n svg += '<rect x=\"'+(n.x-16)+'\" y=\"'+(n.y-16)+'\" width=\"32\" height=\"32\" fill=\"'+color+'\" rx=\"4\" stroke=\"'+statusColor+'\" stroke-width=\"2\"/>';\r\n svg += '<text x=\"'+n.x+'\" y=\"'+(n.y+32)+'\" text-anchor=\"middle\" fill=\"#ccc\" font-size=\"10\" font-family=\"Courier New\">'+escapeHtml(n.label || n.id)+'</text>';\r\n }\r\n\r\n svg += '</svg>';\r\n container.innerHTML = svg;\r\n}\r\n\r\nfunction escapeHtml(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }\r\n\r\nfunction updateAgentStatus(agents) {\r\n // Will be enhanced with pixel croc animations in M2\r\n console.log('Agent update:', agents);\r\n}\r\n</script>\r\n</body>\r\n</html>`;\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\n\r\nexport interface ServeCommandOptions {\r\n port?: string;\r\n host?: string;\r\n open?: boolean;\r\n}\r\n\r\nexport async function serve(opts: ServeCommandOptions): Promise<void> {\r\n let loaded;\r\n try {\r\n loaded = await loadConfig();\r\n } catch {\r\n console.error(chalk.red('No opencroc config found. Run `opencroc init` first.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const port = parseInt(opts.port || '8765', 10);\r\n const host = opts.host || 'localhost';\r\n\r\n console.log(chalk.cyan('🐊 Starting OpenCroc Studio...'));\r\n console.log(chalk.gray(` Config: ${loaded.filepath}`));\r\n console.log(chalk.gray(` Backend: ${loaded.config.backendRoot}`));\r\n\r\n const { startServer } = await import('../../server/index.js');\r\n await startServer({\r\n port,\r\n host,\r\n open: opts.open ?? true,\r\n config: loaded.config,\r\n cwd: process.cwd(),\r\n });\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('opencroc')\r\n .description('AI-native E2E testing framework')\r\n .version('1.0.0');\r\n\r\nprogram\r\n .command('init')\r\n .description('Initialize OpenCroc in the current project')\r\n .option('-y, --yes', 'Skip prompts and use defaults')\r\n .action(async (opts) => {\r\n const { initProject } = await import('./commands/init.js');\r\n await initProject(opts);\r\n });\r\n\r\nprogram\r\n .command('generate')\r\n .description('Generate E2E test cases from source code')\r\n .option('-m, --module <name>', 'Generate for a specific module')\r\n .option('-a, --all', 'Generate for all discovered modules')\r\n .option('--steps <steps>', 'Run specific pipeline steps (comma-separated)')\r\n .option('--dry-run', 'Preview without writing files')\r\n .action(async (opts) => {\r\n const { generate } = await import('./commands/generate.js');\r\n await generate(opts);\r\n });\r\n\r\nprogram\r\n .command('test')\r\n .description('Run generated E2E tests')\r\n .option('-m, --module <name>', 'Run tests for a specific module')\r\n .option('--headed', 'Run in headed browser mode')\r\n .option('--setup-hook <cmd>', 'Run setup hook command before test execution')\r\n .option('--auth-hook <cmd>', 'Run auth hook command before test execution')\r\n .option('--teardown-hook <cmd>', 'Run teardown hook command after test execution')\r\n .action(async (opts) => {\r\n const { runTests } = await import('./commands/test.js');\r\n await runTests(opts);\r\n });\r\n\r\nprogram\r\n .command('validate')\r\n .description('Validate module configurations and generated tests')\r\n .option('-m, --module <name>', 'Validate a specific module')\r\n .action(async (opts) => {\r\n const { validate } = await import('./commands/validate.js');\r\n await validate(opts);\r\n });\r\n\r\nprogram\r\n .command('heal')\r\n .description('Run self-healing loop on failed tests')\r\n .option('-m, --module <name>', 'Heal a specific module')\r\n .option('--max-iterations <n>', 'Maximum healing iterations', '3')\r\n .action(async (opts) => {\r\n const { heal } = await import('./commands/heal.js');\r\n await heal(opts);\r\n });\r\n\r\nprogram\r\n .command('ci')\r\n .description('Generate CI/CD pipeline template')\r\n .option('-p, --platform <name>', 'CI platform (github, gitlab)', 'github')\r\n .option('--self-heal', 'Include self-healing step')\r\n .option('--node <versions>', 'Node.js versions (comma-separated)', '20.x')\r\n .action(async (opts) => {\r\n const { ci } = await import('./commands/ci.js');\r\n await ci(opts);\r\n });\r\n\r\nprogram\r\n .command('report')\r\n .description('Generate pipeline report (HTML/JSON/Markdown)')\r\n .option('-f, --format <formats>', 'Report formats (comma-separated)', 'html')\r\n .option('-o, --output <dir>', 'Output directory')\r\n .action(async (opts) => {\r\n const { report } = await import('./commands/report.js');\r\n await report(opts);\r\n });\r\n\r\nprogram\r\n .command('dashboard')\r\n .description('Generate visual dashboard (opencroc-dashboard.html)')\r\n .option('-i, --input <file>', 'Build from existing opencroc-report.json file')\r\n .option('-o, --output <dir>', 'Output directory', './opencroc-output')\r\n .action(async (opts) => {\r\n const { dashboard } = await import('./commands/dashboard.js');\r\n await dashboard(opts);\r\n });\r\n\r\nprogram\r\n .command('init-runtime')\r\n .description('Generate Playwright runtime infrastructure (config, setup, teardown, auth)')\r\n .option('-o, --output <dir>', 'Output directory for generated files', '.')\r\n .option('--force', 'Overwrite existing files')\r\n .action(async (opts) => {\r\n const { initRuntime } = await import('./commands/init-runtime.js');\r\n await initRuntime(opts);\r\n });\r\n\r\nprogram\r\n .command('run')\r\n .description('Full orchestration: generate → execute → analyze → heal → report')\r\n .option('-m, --module <name>', 'Run for a specific module')\r\n .option('--phases <phases>', 'Phases to run (comma-separated: generate,execute,analyze,heal,report)')\r\n .option('--self-heal', 'Enable self-healing on test failures')\r\n .option('--headed', 'Run Playwright in headed mode')\r\n .option('--report <formats>', 'Report formats (comma-separated)', 'html,json')\r\n .option('--token-budget <n>', 'LLM token budget (0 = unlimited)')\r\n .option('--abort-on-error', 'Abort pipeline on first phase error')\r\n .action(async (opts) => {\r\n const { run } = await import('./commands/run.js');\r\n await run(opts);\r\n });\r\n\r\nprogram\r\n .command('serve')\r\n .description('Start OpenCroc Studio — pixel croc office + knowledge graph UI')\r\n .option('-p, --port <port>', 'Server port', '8765')\r\n .option('-H, --host <host>', 'Server host', 'localhost')\r\n .option('--no-open', 'Do not auto-open browser')\r\n .action(async (opts) => {\r\n const { serve } = await import('./commands/serve.js');\r\n await serve(opts);\r\n });\r\n\r\nprogram.parse();\r\n"],"mappings":"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","names":["path","fs","path","Project","SyntaxKind","fullPath","fs","path","Project","SyntaxKind","producer","path","Project","report","fs","path","chalk","writeFileSync","mkdirSync","existsSync","VALID_STEPS","chalk","readdirSync","existsSync","join","resolve","chalk","existsSync","readFileSync","writeFileSync","mkdirSync","dirname","chalk","fs","path","chalk","init_ci","fs","path","chalk","escapeHtml","fs","path","chalk","init_dashboard","chalk","existsSync","mkdirSync","writeFileSync","join","resolve","writeFileSync","mkdirSync","existsSync","readdirSync","dirname","join","execFileSync","discoverTestFiles","writeFileSync","mkdirSync","existsSync","join","chalk","fileURLToPath","dirname","join","resolve","existsSync","__dirname","__filename","chalk","startServer","initProject","generate","runTests","validate","heal","ci","report","dashboard","initRuntime","run","serve"]}
1
+ {"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/cli/commands/init.ts","../../src/cli/load-config.ts","../../src/parsers/model-parser.ts","../../src/parsers/controller-parser.ts","../../src/parsers/association-parser.ts","../../src/analyzers/api-chain-analyzer.ts","../../src/generators/er-diagram-generator.ts","../../src/generators/test-code-generator.ts","../../src/validators/schema-validator.ts","../../src/validators/semantic-validator.ts","../../src/validators/dryrun-validator.ts","../../src/validators/config-validator.ts","../../src/pipeline/index.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/test.ts","../../src/cli/commands/validate.ts","../../src/llm/openai.ts","../../src/llm/ollama.ts","../../src/llm/index.ts","../../src/self-healing/dialog-loop-runner.ts","../../src/self-healing/controlled-fixer.ts","../../src/self-healing/auto-fix-generator.ts","../../src/self-healing/index.ts","../../src/cli/commands/heal.ts","../../src/ci/index.ts","../../src/cli/commands/ci.ts","../../src/reporters/checklist-reporter.ts","../../src/reporters/workorder-reporter.ts","../../src/reporters/token-reporter.ts","../../src/reporters/index.ts","../../src/cli/commands/report.ts","../../src/dashboard/index.ts","../../src/cli/commands/dashboard.ts","../../src/runtime/playwright-config-generator.ts","../../src/runtime/global-setup-generator.ts","../../src/runtime/global-teardown-generator.ts","../../src/runtime/auth-setup-generator.ts","../../src/cli/commands/init-runtime.ts","../../src/orchestrator/index.ts","../../src/orchestrator/reporter.ts","../../src/cli/commands/run.ts","../../src/server/routes/project.ts","../../src/server/routes/agents.ts","../../src/server/croc-office.ts","../../src/server/index.ts","../../src/cli/commands/serve.ts","../../src/cli/index.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import chalk from 'chalk';\r\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\r\nimport { join } from 'node:path';\r\nimport { createInterface } from 'node:readline/promises';\r\nimport { stdin, stdout } from 'node:process';\r\n\r\nconst ADAPTERS = ['sequelize', 'typeorm', 'prisma'] as const;\r\nconst LLM_PROVIDERS = ['openai', 'zhipu', 'ollama', 'none'] as const;\r\n\r\nexport interface InitAnswers {\r\n backendRoot: string;\r\n adapter: string;\r\n llmProvider: string;\r\n outDir: string;\r\n}\r\n\r\nconst DEFAULTS: InitAnswers = {\r\n backendRoot: './backend',\r\n adapter: 'sequelize',\r\n llmProvider: 'openai',\r\n outDir: './opencroc-output',\r\n};\r\n\r\nexport function buildConfigContent(answers: InitAnswers): string {\r\n const llmBlock =\r\n answers.llmProvider === 'none'\r\n ? ''\r\n : `\r\n llm: {\r\n provider: '${answers.llmProvider}',${answers.llmProvider === 'ollama' ? '' : \"\\n // apiKey: process.env.OPENCROC_LLM_API_KEY,\"}\r\n model: '${answers.llmProvider === 'zhipu' ? 'glm-4' : answers.llmProvider === 'ollama' ? 'llama3' : 'gpt-4o-mini'}',\r\n },`;\r\n\r\n return `import { defineConfig } from 'opencroc';\r\n\r\nexport default defineConfig({\r\n backendRoot: '${answers.backendRoot}',\r\n adapter: '${answers.adapter}',${llmBlock}\r\n outDir: '${answers.outDir}',\r\n selfHealing: {\r\n enabled: true,\r\n maxIterations: 3,\r\n },\r\n});\r\n`;\r\n}\r\n\r\nasync function prompt(\r\n rl: ReturnType<typeof createInterface>,\r\n question: string,\r\n defaultValue: string,\r\n): Promise<string> {\r\n const answer = await rl.question(` ${question} ${chalk.gray(`(${defaultValue})`)}: `);\r\n return answer.trim() || defaultValue;\r\n}\r\n\r\nasync function promptChoice(\r\n rl: ReturnType<typeof createInterface>,\r\n question: string,\r\n choices: readonly string[],\r\n defaultValue: string,\r\n): Promise<string> {\r\n const list = choices\r\n .map((c) => (c === defaultValue ? chalk.underline(c) : c))\r\n .join(' / ');\r\n const answer = await rl.question(` ${question} [${list}]: `);\r\n const trimmed = answer.trim().toLowerCase();\r\n if (!trimmed) return defaultValue;\r\n return choices.find((c) => c.toLowerCase() === trimmed) || defaultValue;\r\n}\r\n\r\nasync function collectAnswers(): Promise<InitAnswers> {\r\n const rl = createInterface({ input: stdin, output: stdout });\r\n try {\r\n const backendRoot = await prompt(rl, 'Backend source root', DEFAULTS.backendRoot);\r\n const adapter = await promptChoice(rl, 'ORM adapter', ADAPTERS, DEFAULTS.adapter);\r\n const llmProvider = await promptChoice(rl, 'LLM provider', LLM_PROVIDERS, DEFAULTS.llmProvider);\r\n const outDir = await prompt(rl, 'Test output directory', DEFAULTS.outDir);\r\n return { backendRoot, adapter, llmProvider, outDir };\r\n } finally {\r\n rl.close();\r\n }\r\n}\r\n\r\nfunction writeProject(cwd: string, answers: InitAnswers): void {\r\n const configPath = join(cwd, 'opencroc.config.ts');\r\n writeFileSync(configPath, buildConfigContent(answers), 'utf-8');\r\n console.log(chalk.green(' ✓ Created opencroc.config.ts'));\r\n\r\n const outputDir = join(cwd, answers.outDir);\r\n if (!existsSync(outputDir)) {\r\n mkdirSync(outputDir, { recursive: true });\r\n console.log(chalk.green(` ✓ Created ${answers.outDir}/`));\r\n }\r\n}\r\n\r\nfunction printNextSteps(answers: InitAnswers): void {\r\n const needsKey = answers.llmProvider !== 'none' && answers.llmProvider !== 'ollama';\r\n console.log('');\r\n console.log(chalk.cyan(' Next steps:'));\r\n let step = 1;\r\n console.log(` ${step++}. Review opencroc.config.ts`);\r\n if (needsKey) {\r\n console.log(` ${step++}. Set OPENCROC_LLM_API_KEY environment variable`);\r\n }\r\n console.log(` ${step++}. npx opencroc generate --all`);\r\n console.log(` ${step}. npx opencroc test`);\r\n}\r\n\r\nexport async function initProject(opts?: { yes?: boolean }): Promise<void> {\r\n const cwd = process.cwd();\r\n const configPath = join(cwd, 'opencroc.config.ts');\r\n\r\n if (existsSync(configPath)) {\r\n console.log(chalk.yellow('\\n ⚠ opencroc.config.ts already exists. Skipping.\\n'));\r\n return;\r\n }\r\n\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Project Setup\\n'));\r\n\r\n const answers = opts?.yes ? { ...DEFAULTS } : await collectAnswers();\r\n\r\n console.log('');\r\n writeProject(cwd, answers);\r\n printNextSteps(answers);\r\n console.log('');\r\n}\r\n","import { cosmiconfig } from 'cosmiconfig';\r\nimport type { OpenCrocConfig } from '../types.js';\r\n\r\nconst MODULE_NAME = 'opencroc';\r\n\r\nconst SEARCH_PLACES = [\r\n 'opencroc.config.ts',\r\n 'opencroc.config.js',\r\n 'opencroc.config.json',\r\n '.opencrocrc.json',\r\n 'package.json',\r\n];\r\n\r\nexport interface LoadConfigResult {\r\n config: OpenCrocConfig;\r\n filepath: string;\r\n}\r\n\r\nexport async function loadConfig(cwd?: string): Promise<LoadConfigResult> {\r\n const explorer = cosmiconfig(MODULE_NAME, {\r\n searchPlaces: SEARCH_PLACES,\r\n ...(cwd ? { stopDir: cwd } : {}),\r\n });\r\n\r\n const result = cwd ? await explorer.search(cwd) : await explorer.search();\r\n\r\n if (!result || result.isEmpty) {\r\n throw new Error(\r\n 'No opencroc config found. Run `opencroc init` to create one.',\r\n );\r\n }\r\n\r\n const config: OpenCrocConfig =\r\n result.config?.default ?? result.config;\r\n\r\n if (!config.backendRoot) {\r\n throw new Error(\r\n `Invalid config in ${result.filepath}: \"backendRoot\" is required.`,\r\n );\r\n }\r\n\r\n return { config, filepath: result.filepath };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport {\r\n Project,\r\n SyntaxKind,\r\n type CallExpression,\r\n type ObjectLiteralExpression,\r\n type PropertyAssignment,\r\n type Node,\r\n} from 'ts-morph';\r\nimport type { TableSchema, FieldSchema, IndexSchema } from '../types.js';\r\n\r\nexport interface ModelParser {\r\n parseFile(filePath: string): Promise<TableSchema | null>;\r\n parseDirectory(dirPath: string): Promise<TableSchema[]>;\r\n}\r\n\r\n/**\r\n * Parse a single Sequelize Model file and extract TableSchema.\r\n */\r\nexport function parseModelFile(filePath: string): TableSchema | null {\r\n const absolutePath = path.resolve(filePath);\r\n if (!fs.existsSync(absolutePath)) return null;\r\n\r\n const project = new Project({ compilerOptions: { strict: false } });\r\n const sourceFile = project.addSourceFileAtPath(absolutePath);\r\n\r\n const initCall = findInitCall(sourceFile);\r\n if (!initCall) return null;\r\n\r\n const args = initCall.getArguments();\r\n if (args.length < 2) return null;\r\n\r\n const fields = parseFieldDefinitions(args[0]);\r\n const { tableName, indexes } = parseOptions(args[1]);\r\n\r\n if (!tableName) return null;\r\n\r\n return { tableName, fields, indexes };\r\n}\r\n\r\n/**\r\n * Batch parse all Model files in a directory.\r\n */\r\nexport function parseModuleModels(modelDir: string): TableSchema[] {\r\n const absoluteDir = path.resolve(modelDir);\r\n if (!fs.existsSync(absoluteDir)) return [];\r\n\r\n const files = fs.readdirSync(absoluteDir).filter((f) =>\r\n f.endsWith('.ts') &&\r\n !f.endsWith('.test.ts') &&\r\n !f.endsWith('.spec.ts') &&\r\n f !== 'index.ts' &&\r\n f !== 'associations.ts',\r\n );\r\n\r\n const schemas: TableSchema[] = [];\r\n for (const file of files) {\r\n try {\r\n const schema = parseModelFile(path.join(absoluteDir, file));\r\n if (schema) schemas.push(schema);\r\n } catch {\r\n // skip unparseable files\r\n }\r\n }\r\n return schemas;\r\n}\r\n\r\nfunction findInitCall(sourceFile: Node): CallExpression | null {\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n for (const call of calls) {\r\n const expr = call.getExpression();\r\n if (expr.getKind() === SyntaxKind.PropertyAccessExpression) {\r\n const propAccess = expr.asKindOrThrow(SyntaxKind.PropertyAccessExpression);\r\n if (propAccess.getName() === 'init') return call;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction parseFieldDefinitions(fieldsNode: Node): FieldSchema[] {\r\n const fields: FieldSchema[] = [];\r\n if (fieldsNode.getKind() !== SyntaxKind.ObjectLiteralExpression) return fields;\r\n\r\n const objLiteral = fieldsNode as ObjectLiteralExpression;\r\n for (const prop of objLiteral.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const propAssign = prop as PropertyAssignment;\r\n const initializer = propAssign.getInitializer();\r\n if (!initializer || initializer.getKind() !== SyntaxKind.ObjectLiteralExpression) continue;\r\n fields.push(parseFieldObject(propAssign.getName(), initializer as ObjectLiteralExpression));\r\n }\r\n return fields;\r\n}\r\n\r\nfunction parseFieldObject(fieldName: string, fieldObj: ObjectLiteralExpression): FieldSchema {\r\n const field: FieldSchema = { name: fieldName, type: 'STRING', allowNull: true, primaryKey: false };\r\n\r\n for (const prop of fieldObj.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const propAssign = prop as PropertyAssignment;\r\n const key = propAssign.getName();\r\n const init = propAssign.getInitializer();\r\n if (!init) continue;\r\n\r\n switch (key) {\r\n case 'type': field.type = extractDataType(init); break;\r\n case 'allowNull': field.allowNull = init.getText().trim() === 'true'; break;\r\n case 'primaryKey': field.primaryKey = init.getText().trim() === 'true'; break;\r\n case 'defaultValue': field.defaultValue = extractDefaultValue(init); break;\r\n }\r\n }\r\n return field;\r\n}\r\n\r\nfunction extractDataType(node: Node): string {\r\n const text = node.getText().trim();\r\n const callMatch = text.match(/^DataTypes\\.(\\w+)\\((.+)\\)$/);\r\n if (callMatch) return `${callMatch[1]}(${callMatch[2]})`;\r\n const propMatch = text.match(/^DataTypes\\.(\\w+)$/);\r\n if (propMatch) return propMatch[1];\r\n return text;\r\n}\r\n\r\nfunction extractDefaultValue(node: Node): unknown {\r\n const text = node.getText().trim();\r\n if (text === 'DataTypes.NOW') return 'DataTypes.NOW';\r\n if ((text.startsWith(\"'\") && text.endsWith(\"'\")) || (text.startsWith('\"') && text.endsWith('\"')))\r\n return text.slice(1, -1);\r\n if (/^-?\\d+(\\.\\d+)?$/.test(text)) return Number(text);\r\n if (text === 'true') return true;\r\n if (text === 'false') return false;\r\n if (text === 'null') return null;\r\n return text;\r\n}\r\n\r\nfunction parseOptions(optionsNode: Node): { tableName: string | null; indexes: IndexSchema[] } {\r\n let tableName: string | null = null;\r\n let indexes: IndexSchema[] = [];\r\n\r\n if (optionsNode.getKind() !== SyntaxKind.ObjectLiteralExpression) return { tableName, indexes };\r\n\r\n const objLiteral = optionsNode as ObjectLiteralExpression;\r\n for (const prop of objLiteral.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const propAssign = prop as PropertyAssignment;\r\n const key = propAssign.getName();\r\n const init = propAssign.getInitializer();\r\n if (!init) continue;\r\n\r\n if (key === 'tableName') tableName = extractStringValue(init);\r\n if (key === 'indexes') indexes = parseIndexes(init);\r\n }\r\n return { tableName, indexes };\r\n}\r\n\r\nfunction extractStringValue(node: Node): string | null {\r\n const text = node.getText().trim();\r\n if ((text.startsWith(\"'\") && text.endsWith(\"'\")) || (text.startsWith('\"') && text.endsWith('\"')))\r\n return text.slice(1, -1);\r\n return null;\r\n}\r\n\r\nfunction parseIndexes(node: Node): IndexSchema[] {\r\n if (node.getKind() !== SyntaxKind.ArrayLiteralExpression) return [];\r\n const arr = node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);\r\n const indexes: IndexSchema[] = [];\r\n for (const el of arr.getElements()) {\r\n if (el.getKind() !== SyntaxKind.ObjectLiteralExpression) continue;\r\n const idx = parseIndexObject(el as ObjectLiteralExpression);\r\n if (idx) indexes.push(idx);\r\n }\r\n return indexes;\r\n}\r\n\r\nfunction parseIndexObject(obj: ObjectLiteralExpression): IndexSchema | null {\r\n let name = '';\r\n let fields: string[] = [];\r\n let unique = false;\r\n\r\n for (const prop of obj.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const pa = prop as PropertyAssignment;\r\n const init = pa.getInitializer();\r\n if (!init) continue;\r\n switch (pa.getName()) {\r\n case 'name': name = extractStringValue(init) || ''; break;\r\n case 'fields': fields = extractStringArray(init); break;\r\n case 'unique': unique = init.getText().trim() === 'true'; break;\r\n }\r\n }\r\n if (!name || fields.length === 0) return null;\r\n return { name, fields, unique };\r\n}\r\n\r\nfunction extractStringArray(node: Node): string[] {\r\n if (node.getKind() !== SyntaxKind.ArrayLiteralExpression) return [];\r\n const arr = node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);\r\n return arr.getElements()\r\n .map((el) => el.getText().trim())\r\n .filter((t) => (t.startsWith(\"'\") || t.startsWith('\"')))\r\n .map((t) => t.slice(1, -1));\r\n}\r\n\r\nexport function createModelParser(): ModelParser {\r\n return {\r\n async parseFile(filePath: string) {\r\n return parseModelFile(filePath);\r\n },\r\n async parseDirectory(dirPath: string) {\r\n return parseModuleModels(dirPath);\r\n },\r\n };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport {\r\n Project,\r\n SyntaxKind,\r\n type CallExpression,\r\n type SourceFile,\r\n Node,\r\n type PropertyAccessExpression,\r\n type Decorator,\r\n type MethodDeclaration,\r\n type ObjectLiteralExpression,\r\n} from 'ts-morph';\r\nimport type { ApiEndpoint } from '../types.js';\r\n\r\nexport interface ControllerParser {\r\n parseFile(filePath: string): Promise<ApiEndpoint[]>;\r\n parseDirectory(dirPath: string): Promise<ApiEndpoint[]>;\r\n}\r\n\r\nconst HTTP_METHODS = new Set(['get', 'post', 'put', 'delete', 'patch']);\r\n\r\nconst METHOD_MAP: Record<string, string> = {\r\n get: 'GET', post: 'POST', put: 'PUT', delete: 'DELETE', patch: 'PATCH',\r\n};\r\n\r\nconst NEST_HTTP_DECORATORS = new Set(['get', 'post', 'put', 'delete', 'patch']);\r\n\r\n/**\r\n * Parse a single Controller file and extract API endpoints.\r\n */\r\nexport function parseControllerFile(filePath: string): ApiEndpoint[] {\r\n const absolutePath = path.resolve(filePath);\r\n if (!fs.existsSync(absolutePath)) return [];\r\n\r\n try {\r\n const project = new Project({ compilerOptions: { strict: false } });\r\n const sourceFile = project.addSourceFileAtPath(absolutePath);\r\n\r\n const endpoints: ApiEndpoint[] = [];\r\n endpoints.push(...extractRouterCalls(sourceFile));\r\n endpoints.push(...extractBaseCrudRoutes(sourceFile));\r\n endpoints.push(...extractNestControllerRoutes(sourceFile));\r\n\r\n return deduplicateEndpoints(endpoints);\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Parse all Controller files in a directory.\r\n */\r\nexport function parseControllerDirectory(dirPath: string): ApiEndpoint[] {\r\n const absoluteDir = path.resolve(dirPath);\r\n if (!fs.existsSync(absoluteDir)) return [];\r\n\r\n const files = fs.readdirSync(absoluteDir).filter((f) =>\r\n f.endsWith('.ts') &&\r\n !f.endsWith('.test.ts') &&\r\n !f.endsWith('.spec.ts') &&\r\n f !== 'index.ts',\r\n );\r\n\r\n const endpoints: ApiEndpoint[] = [];\r\n for (const file of files) {\r\n endpoints.push(...parseControllerFile(path.join(absoluteDir, file)));\r\n }\r\n return deduplicateEndpoints(endpoints);\r\n}\r\n\r\nfunction extractRouterCalls(sourceFile: SourceFile): ApiEndpoint[] {\r\n const endpoints: ApiEndpoint[] = [];\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expr = call.getExpression();\r\n if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) continue;\r\n\r\n const propAccess = expr as PropertyAccessExpression;\r\n const methodName = propAccess.getName().toLowerCase();\r\n if (!HTTP_METHODS.has(methodName)) continue;\r\n\r\n const objectText = propAccess.getExpression().getText().trim();\r\n if (!isRouterLike(objectText)) continue;\r\n\r\n const args = call.getArguments();\r\n if (args.length === 0) continue;\r\n\r\n const routePath = resolveRoutePath(args[0], sourceFile);\r\n if (!routePath) continue;\r\n\r\n endpoints.push({\r\n method: METHOD_MAP[methodName],\r\n path: routePath,\r\n pathParams: extractPathParams(routePath),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: extractDescription(call),\r\n });\r\n }\r\n return endpoints;\r\n}\r\n\r\nfunction isRouterLike(text: string): boolean {\r\n return text === 'router' || text === 'this.router';\r\n}\r\n\r\nfunction extractBaseCrudRoutes(sourceFile: SourceFile): ApiEndpoint[] {\r\n const endpoints: ApiEndpoint[] = [];\r\n\r\n let isBaseCrud = false;\r\n for (const cls of sourceFile.getClasses()) {\r\n const heritage = cls.getExtends();\r\n if (heritage?.getText().includes('BaseCrudController')) {\r\n isBaseCrud = true;\r\n break;\r\n }\r\n }\r\n if (!isBaseCrud) return endpoints;\r\n\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n let resourcePath: string | null = null;\r\n\r\n for (const call of calls) {\r\n const exprText = call.getExpression().getText();\r\n if (\r\n (exprText === 'super.registerRoutes' || exprText.endsWith('.registerRoutes')) &&\r\n !exprText.includes('Custom')\r\n ) {\r\n const args = call.getArguments();\r\n if (args.length >= 2) resourcePath = extractStringLiteral(args[1]);\r\n }\r\n }\r\n if (!resourcePath) return endpoints;\r\n\r\n const basePath = `/v1/:tenantId/${resourcePath}`;\r\n const crudRoutes: Array<{ method: string; path: string; desc: string }> = [\r\n { method: 'GET', path: basePath, desc: `List ${resourcePath}` },\r\n { method: 'GET', path: `${basePath}/:id`, desc: `Get ${resourcePath} by ID` },\r\n { method: 'POST', path: basePath, desc: `Create ${resourcePath}` },\r\n { method: 'PUT', path: `${basePath}/:id`, desc: `Update ${resourcePath}` },\r\n { method: 'DELETE', path: `${basePath}/:id`, desc: `Delete ${resourcePath}` },\r\n { method: 'POST', path: `${basePath}/batch-delete`, desc: `Batch delete ${resourcePath}` },\r\n ];\r\n\r\n for (const route of crudRoutes) {\r\n endpoints.push({\r\n method: route.method,\r\n path: route.path,\r\n pathParams: extractPathParams(route.path),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: route.desc,\r\n });\r\n }\r\n return endpoints;\r\n}\r\n\r\nfunction extractNestControllerRoutes(sourceFile: SourceFile): ApiEndpoint[] {\r\n const endpoints: ApiEndpoint[] = [];\r\n\r\n for (const cls of sourceFile.getClasses()) {\r\n const controllerDecorator = cls.getDecorators().find((d) => d.getName().toLowerCase() === 'controller');\r\n if (!controllerDecorator) continue;\r\n\r\n const controllerBasePath = normalizeRoutePath(extractDecoratorPath(controllerDecorator, sourceFile) ?? '');\r\n\r\n for (const methodDecl of cls.getMethods()) {\r\n const requestMapping = extractRequestMapping(methodDecl, sourceFile);\r\n if (requestMapping) {\r\n const fullPath = joinRoutePath(controllerBasePath, normalizeRoutePath(requestMapping.path));\r\n endpoints.push({\r\n method: requestMapping.method,\r\n path: fullPath,\r\n pathParams: extractPathParams(fullPath),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: extractMethodDescription(methodDecl),\r\n });\r\n continue;\r\n }\r\n\r\n const httpDecorator = methodDecl.getDecorators().find((d) => NEST_HTTP_DECORATORS.has(d.getName().toLowerCase()));\r\n if (!httpDecorator) continue;\r\n\r\n const methodName = httpDecorator.getName().toLowerCase();\r\n const method = METHOD_MAP[methodName];\r\n if (!method) continue;\r\n\r\n const methodPath = normalizeRoutePath(extractDecoratorPath(httpDecorator, sourceFile) ?? '');\r\n const fullPath = joinRoutePath(controllerBasePath, methodPath);\r\n\r\n endpoints.push({\r\n method,\r\n path: fullPath,\r\n pathParams: extractPathParams(fullPath),\r\n queryParams: [],\r\n bodyFields: [],\r\n responseFields: [],\r\n relatedTables: [],\r\n description: extractMethodDescription(methodDecl),\r\n });\r\n }\r\n }\r\n\r\n return endpoints;\r\n}\r\n\r\n/**\r\n * Infer related database table names from Service file imports.\r\n */\r\nexport function inferRelatedTables(servicePaths: string[]): string[] {\r\n const tables = new Set<string>();\r\n for (const sp of servicePaths) {\r\n const absolutePath = path.resolve(sp);\r\n if (!fs.existsSync(absolutePath)) continue;\r\n try {\r\n const content = fs.readFileSync(absolutePath, 'utf-8');\r\n const importRegex = /import\\s*\\{([^}]+)\\}\\s*from\\s*['\"][^'\"]*models[^'\"]*['\"]/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = importRegex.exec(content)) !== null) {\r\n const names = match[1].split(',').map((s) => s.trim());\r\n for (const name of names) {\r\n const cleanName = name.replace(/\\s+as\\s+\\w+/, '').trim();\r\n if (cleanName) tables.add(pascalToSnake(cleanName));\r\n }\r\n }\r\n } catch {\r\n // skip\r\n }\r\n }\r\n return Array.from(tables);\r\n}\r\n\r\nfunction resolveRoutePath(node: Node, sourceFile: SourceFile): string | null {\r\n const kind = node.getKind();\r\n if (kind === SyntaxKind.StringLiteral) return node.getText().slice(1, -1);\r\n if (kind === SyntaxKind.TemplateExpression || kind === SyntaxKind.NoSubstitutionTemplateLiteral) {\r\n return resolveTemplateLiteral(node, sourceFile);\r\n }\r\n if (kind === SyntaxKind.Identifier) {\r\n return resolveVariableValue(sourceFile, node.getText().trim());\r\n }\r\n return null;\r\n}\r\n\r\nfunction extractDecoratorPath(decorator: Decorator, sourceFile: SourceFile): string | null {\r\n const args = decorator.getArguments();\r\n if (args.length === 0) return '';\r\n\r\n const firstArg = args[0];\r\n if (firstArg.getKind() === SyntaxKind.ObjectLiteralExpression) {\r\n return extractPathFromObjectLiteral(firstArg as ObjectLiteralExpression, sourceFile);\r\n }\r\n\r\n return resolveRoutePath(firstArg, sourceFile);\r\n}\r\n\r\nfunction extractPathFromObjectLiteral(node: ObjectLiteralExpression, sourceFile: SourceFile): string | null {\r\n const pathProp = node.getProperty('path');\r\n if (!pathProp || !Node.isPropertyAssignment(pathProp)) return null;\r\n const initializer = pathProp.getInitializer();\r\n if (!initializer) return null;\r\n return resolveRoutePath(initializer, sourceFile);\r\n}\r\n\r\nfunction extractRequestMapping(\r\n methodDecl: MethodDeclaration,\r\n sourceFile: SourceFile,\r\n): { method: string; path: string } | null {\r\n const decorator = methodDecl.getDecorators().find((d) => d.getName().toLowerCase() === 'requestmapping');\r\n if (!decorator) return null;\r\n\r\n const args = decorator.getArguments();\r\n if (args.length === 0) return null;\r\n const firstArg = args[0];\r\n if (firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) return null;\r\n\r\n const obj = firstArg as ObjectLiteralExpression;\r\n const methodProp = obj.getProperty('method');\r\n let method = 'GET';\r\n if (methodProp && Node.isPropertyAssignment(methodProp)) {\r\n const init = methodProp.getInitializer();\r\n const methodText = init?.getText() || '';\r\n const normalized = methodText\r\n .replace(/['\"`]/g, '')\r\n .split('.')\r\n .pop()\r\n ?.toUpperCase();\r\n if (normalized && ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(normalized)) {\r\n method = normalized;\r\n }\r\n }\r\n\r\n const pathValue = extractPathFromObjectLiteral(obj, sourceFile) ?? '';\r\n return { method, path: pathValue };\r\n}\r\n\r\nfunction normalizeRoutePath(routePath: string): string {\r\n const cleaned = routePath.trim();\r\n if (!cleaned || cleaned === '/') return '';\r\n return `/${cleaned.replace(/^\\/+|\\/+$/g, '')}`;\r\n}\r\n\r\nfunction joinRoutePath(basePath: string, childPath: string): string {\r\n const joined = `${basePath}${childPath}`.replace(/\\/+/g, '/');\r\n return joined || '/';\r\n}\r\n\r\nfunction extractMethodDescription(methodDecl: MethodDeclaration): string {\r\n const docs = methodDecl.getJsDocs();\r\n if (docs.length > 0) {\r\n const desc = docs[0].getDescription().trim();\r\n if (desc) return desc;\r\n }\r\n return '';\r\n}\r\n\r\nfunction resolveTemplateLiteral(node: Node, sourceFile: SourceFile): string {\r\n let result = node.getText().slice(1, -1);\r\n result = result.replace(/\\$\\{([^}]+)\\}/g, (_match, expr: string) => {\r\n const resolved = resolveVariableValue(sourceFile, expr.trim());\r\n return resolved || `{${expr.trim()}}`;\r\n });\r\n return result;\r\n}\r\n\r\nfunction resolveVariableValue(sourceFile: SourceFile, varName: string): string | null {\r\n for (const decl of sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {\r\n if (decl.getName() === varName) {\r\n const init = decl.getInitializer();\r\n if (!init) continue;\r\n const t = init.getText().trim();\r\n if ((t.startsWith(\"'\") && t.endsWith(\"'\")) || (t.startsWith('\"') && t.endsWith('\"')))\r\n return t.slice(1, -1);\r\n if (t.startsWith('`') && t.endsWith('`'))\r\n return resolveTemplateLiteral(init, sourceFile);\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction extractPathParams(routePath: string): string[] {\r\n const params: string[] = [];\r\n const regex = /:(\\w+)/g;\r\n let match: RegExpExecArray | null;\r\n while ((match = regex.exec(routePath)) !== null) params.push(match[1]);\r\n return params;\r\n}\r\n\r\nfunction extractDescription(call: CallExpression): string {\r\n let current: Node = call;\r\n while (\r\n current.getParent() &&\r\n current.getParent()!.getKind() !== SyntaxKind.SourceFile &&\r\n current.getParent()!.getKind() !== SyntaxKind.Block\r\n ) {\r\n current = current.getParent()!;\r\n }\r\n const fullText = current.getFullText();\r\n const leadingText = fullText.substring(0, fullText.indexOf(current.getText()));\r\n const jsdocMatch = leadingText.match(/\\/\\*\\*[\\s\\S]*?\\*\\s+(.+?)(?:\\n|\\*\\/)/);\r\n if (jsdocMatch) return jsdocMatch[1].replace(/^\\*\\s*/, '').trim();\r\n const lineMatch = leadingText.match(/\\/\\/\\s*(.+)/);\r\n if (lineMatch) return lineMatch[1].trim();\r\n return '';\r\n}\r\n\r\nfunction extractStringLiteral(node: Node): string | null {\r\n const t = node.getText().trim();\r\n if ((t.startsWith(\"'\") && t.endsWith(\"'\")) || (t.startsWith('\"') && t.endsWith('\"')))\r\n return t.slice(1, -1);\r\n return null;\r\n}\r\n\r\nfunction pascalToSnake(name: string): string {\r\n return name.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');\r\n}\r\n\r\nfunction deduplicateEndpoints(endpoints: ApiEndpoint[]): ApiEndpoint[] {\r\n const seen = new Map<string, ApiEndpoint>();\r\n for (const ep of endpoints) {\r\n const key = `${ep.method}:${ep.path}`;\r\n if (!seen.has(key)) {\r\n seen.set(key, ep);\r\n } else {\r\n const existing = seen.get(key)!;\r\n const merged = new Set([...existing.relatedTables, ...ep.relatedTables]);\r\n existing.relatedTables = Array.from(merged);\r\n if (!existing.description && ep.description) existing.description = ep.description;\r\n }\r\n }\r\n return Array.from(seen.values());\r\n}\r\n\r\nexport function createControllerParser(): ControllerParser {\r\n return {\r\n async parseFile(filePath: string) {\r\n return parseControllerFile(filePath);\r\n },\r\n async parseDirectory(dirPath: string) {\r\n return parseControllerDirectory(dirPath);\r\n },\r\n };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport {\r\n Project,\r\n SyntaxKind,\r\n type ObjectLiteralExpression,\r\n type PropertyAssignment,\r\n type SourceFile,\r\n} from 'ts-morph';\r\nimport type { ForeignKeyRelation } from '../types.js';\r\nimport { parseModelFile } from './model-parser.js';\r\n\r\nexport interface AssociationParser {\r\n parseFile(filePath: string): Promise<ForeignKeyRelation[]>;\r\n}\r\n\r\ninterface RawAssociation {\r\n sourceClass: string;\r\n targetClass: string;\r\n foreignKey: string;\r\n type: 'hasMany' | 'belongsTo' | 'hasOne';\r\n importPath?: string;\r\n}\r\n\r\n/**\r\n * Parse an associations.ts file to extract all foreign key relations.\r\n */\r\nexport function parseAssociationFile(\r\n filePath: string,\r\n classToTableMap?: Map<string, string>,\r\n moduleTablePrefix?: string,\r\n): ForeignKeyRelation[] {\r\n const absolutePath = path.resolve(filePath);\r\n if (!fs.existsSync(absolutePath)) return [];\r\n\r\n const project = new Project({ compilerOptions: { strict: false } });\r\n const sourceFile = project.addSourceFileAtPath(absolutePath);\r\n\r\n const importPathMap = collectImportPaths(sourceFile);\r\n const rawAssociations = extractAssociationCalls(sourceFile, importPathMap);\r\n if (rawAssociations.length === 0) return [];\r\n\r\n return deduplicateRelations(rawAssociations, classToTableMap, moduleTablePrefix);\r\n}\r\n\r\n/**\r\n * Build className → tableName map from Model files in a directory.\r\n */\r\nexport function buildClassToTableMap(modelDir: string): Map<string, string> {\r\n const map = new Map<string, string>();\r\n const absoluteDir = path.resolve(modelDir);\r\n if (!fs.existsSync(absoluteDir)) return map;\r\n\r\n const files = fs.readdirSync(absoluteDir).filter((f) =>\r\n f.endsWith('.ts') &&\r\n !f.endsWith('.test.ts') &&\r\n !f.endsWith('.spec.ts') &&\r\n f !== 'index.ts' &&\r\n f !== 'associations.ts',\r\n );\r\n\r\n for (const file of files) {\r\n try {\r\n const schema = parseModelFile(path.join(absoluteDir, file));\r\n if (schema) {\r\n const className = file.replace('.ts', '');\r\n map.set(className, schema.tableName);\r\n }\r\n } catch {\r\n // skip\r\n }\r\n }\r\n return map;\r\n}\r\n\r\nfunction collectImportPaths(sourceFile: SourceFile): Map<string, string> {\r\n const map = new Map<string, string>();\r\n for (const decl of sourceFile.getImportDeclarations()) {\r\n const moduleSpecifier = decl.getModuleSpecifierValue();\r\n for (const named of decl.getNamedImports()) {\r\n map.set(named.getName(), moduleSpecifier);\r\n }\r\n }\r\n return map;\r\n}\r\n\r\nfunction extractAssociationCalls(\r\n sourceFile: SourceFile,\r\n importPathMap: Map<string, string>,\r\n): RawAssociation[] {\r\n const associations: RawAssociation[] = [];\r\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expr = call.getExpression();\r\n if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) continue;\r\n\r\n const propAccess = expr.asKindOrThrow(SyntaxKind.PropertyAccessExpression);\r\n const methodName = propAccess.getName();\r\n if (methodName !== 'hasMany' && methodName !== 'belongsTo' && methodName !== 'hasOne') continue;\r\n\r\n const sourceClass = propAccess.getExpression().getText().trim();\r\n const args = call.getArguments();\r\n if (args.length < 1) continue;\r\n\r\n const targetClass = args[0].getText().trim();\r\n let foreignKey = '';\r\n\r\n if (args.length >= 2 && args[1].getKind() === SyntaxKind.ObjectLiteralExpression) {\r\n foreignKey = extractStringProperty(args[1] as ObjectLiteralExpression, 'foreignKey');\r\n }\r\n\r\n associations.push({\r\n sourceClass,\r\n targetClass,\r\n foreignKey,\r\n type: methodName as RawAssociation['type'],\r\n importPath: importPathMap.get(targetClass),\r\n });\r\n }\r\n return associations;\r\n}\r\n\r\nfunction extractStringProperty(obj: ObjectLiteralExpression, propertyName: string): string {\r\n for (const prop of obj.getProperties()) {\r\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;\r\n const pa = prop as PropertyAssignment;\r\n if (pa.getName() !== propertyName) continue;\r\n const init = pa.getInitializer();\r\n if (!init) continue;\r\n const text = init.getText().trim();\r\n if ((text.startsWith(\"'\") && text.endsWith(\"'\")) || (text.startsWith('\"') && text.endsWith('\"')))\r\n return text.slice(1, -1);\r\n return text;\r\n }\r\n return '';\r\n}\r\n\r\nexport function classNameToTableName(className: string): string {\r\n return className.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');\r\n}\r\n\r\nfunction resolveTableName(className: string, classToTableMap?: Map<string, string>): string {\r\n if (classToTableMap?.has(className)) return classToTableMap.get(className)!;\r\n return classNameToTableName(className);\r\n}\r\n\r\nfunction isCrossModuleRef(\r\n targetTableName: string,\r\n importPath: string | undefined,\r\n moduleTablePrefix?: string,\r\n): boolean {\r\n if (moduleTablePrefix) return !targetTableName.startsWith(moduleTablePrefix);\r\n if (importPath) {\r\n const upLevels = (importPath.match(/\\.\\.\\//g) || []).length;\r\n return upLevels >= 2;\r\n }\r\n return false;\r\n}\r\n\r\nfunction deduplicateRelations(\r\n rawAssociations: RawAssociation[],\r\n classToTableMap?: Map<string, string>,\r\n moduleTablePrefix?: string,\r\n): ForeignKeyRelation[] {\r\n const seen = new Map<string, ForeignKeyRelation>();\r\n\r\n for (const raw of rawAssociations) {\r\n const sourceTable = resolveTableName(raw.sourceClass, classToTableMap);\r\n const targetTable = resolveTableName(raw.targetClass, classToTableMap);\r\n const crossModule = isCrossModuleRef(targetTable, raw.importPath, moduleTablePrefix);\r\n\r\n let parentTable: string;\r\n let childTable: string;\r\n let cardinality: ForeignKeyRelation['cardinality'];\r\n\r\n switch (raw.type) {\r\n case 'hasMany':\r\n parentTable = sourceTable; childTable = targetTable; cardinality = '1:N'; break;\r\n case 'belongsTo':\r\n parentTable = targetTable; childTable = sourceTable; cardinality = 'N:1'; break;\r\n case 'hasOne':\r\n parentTable = sourceTable; childTable = targetTable; cardinality = '1:1'; break;\r\n }\r\n\r\n const dedupeKey = `${parentTable}|${childTable}|${raw.foreignKey}`;\r\n if (seen.has(dedupeKey)) {\r\n const existing = seen.get(dedupeKey)!;\r\n if (existing.cardinality === 'N:1' && (cardinality === '1:N' || cardinality === '1:1')) {\r\n seen.set(dedupeKey, {\r\n sourceTable: parentTable, sourceField: 'id',\r\n targetTable: childTable, targetField: raw.foreignKey,\r\n cardinality, isCrossModule: crossModule || existing.isCrossModule,\r\n });\r\n }\r\n } else {\r\n seen.set(dedupeKey, {\r\n sourceTable: parentTable, sourceField: 'id',\r\n targetTable: childTable, targetField: raw.foreignKey,\r\n cardinality, isCrossModule: crossModule,\r\n });\r\n }\r\n }\r\n return Array.from(seen.values());\r\n}\r\n\r\nexport function createAssociationParser(): AssociationParser {\r\n return {\r\n async parseFile(filePath: string) {\r\n return parseAssociationFile(filePath);\r\n },\r\n };\r\n}\r\n","import type {\r\n ApiEndpoint,\r\n ApiDependency,\r\n ApiChainAnalysisResult,\r\n DirectedAcyclicGraph,\r\n} from '../types.js';\r\n\r\nconst EXCLUDED_PARAMS = new Set(['tenantId']);\r\n\r\nconst enum Color { WHITE = 0, GRAY = 1, BLACK = 2 }\r\n\r\nfunction toNodeKey(endpoint: ApiEndpoint): string {\r\n return `${endpoint.method} ${endpoint.path}`;\r\n}\r\n\r\nfunction paramToResourceHint(param: string): string {\r\n const stripped = param.endsWith('Id') ? param.slice(0, -2) : param;\r\n return stripped.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');\r\n}\r\n\r\nfunction postProducesResource(postEndpoint: ApiEndpoint, resourceHint: string): boolean {\r\n const segments = postEndpoint.path.split('/').filter((s) => s && !s.startsWith(':'));\r\n if (segments.length === 0) return false;\r\n\r\n const lastSegment = segments[segments.length - 1].toLowerCase();\r\n if (lastSegment.includes(resourceHint)) return true;\r\n\r\n const parts = lastSegment.split('-');\r\n if (parts.some((p) => p === resourceHint || p.startsWith(resourceHint))) return true;\r\n\r\n if (resourceHint.length <= 4) {\r\n const abbreviation = parts.map((p) => p[0]).join('');\r\n if (abbreviation.startsWith(resourceHint)) return true;\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Infer API dependencies via path parameter matching.\r\n * POST endpoints produce IDs; GET/PUT/DELETE endpoints consume them.\r\n */\r\nexport function inferDependencies(endpoints: ApiEndpoint[]): ApiDependency[] {\r\n const dependencies: ApiDependency[] = [];\r\n const postEndpoints = endpoints.filter((ep) => ep.method === 'POST');\r\n\r\n for (const consumer of endpoints) {\r\n const consumedParams = consumer.pathParams.filter((p) => !EXCLUDED_PARAMS.has(p));\r\n if (consumedParams.length === 0) continue;\r\n\r\n for (const param of consumedParams) {\r\n if (param === 'id') {\r\n const basePath = consumer.path.replace(/\\/:id(\\/.*)?$/, '');\r\n const producer = postEndpoints.find((ep) => ep.path === basePath);\r\n if (producer && toNodeKey(producer) !== toNodeKey(consumer)) {\r\n dependencies.push({ from: consumer, to: producer, paramMapping: { [`:${param}`]: 'response.data.id' } });\r\n }\r\n continue;\r\n }\r\n\r\n const resourceHint = paramToResourceHint(param);\r\n if (!resourceHint) continue;\r\n\r\n const producer = postEndpoints.find((ep) => postProducesResource(ep, resourceHint));\r\n if (producer && toNodeKey(producer) !== toNodeKey(consumer)) {\r\n dependencies.push({ from: consumer, to: producer, paramMapping: { [`:${param}`]: 'response.data.id' } });\r\n }\r\n }\r\n }\r\n return deduplicateDependencies(dependencies);\r\n}\r\n\r\nfunction deduplicateDependencies(deps: ApiDependency[]): ApiDependency[] {\r\n const map = new Map<string, ApiDependency>();\r\n for (const dep of deps) {\r\n const key = `${toNodeKey(dep.from)}→${toNodeKey(dep.to)}`;\r\n if (map.has(key)) {\r\n Object.assign(map.get(key)!.paramMapping, dep.paramMapping);\r\n } else {\r\n map.set(key, { ...dep, paramMapping: { ...dep.paramMapping } });\r\n }\r\n }\r\n return Array.from(map.values());\r\n}\r\n\r\n/**\r\n * Build a directed graph from endpoints and their dependencies.\r\n */\r\nexport function buildGraph(\r\n endpoints: ApiEndpoint[],\r\n dependencies: ApiDependency[],\r\n): DirectedAcyclicGraph {\r\n const nodeSet = new Set<string>();\r\n for (const ep of endpoints) nodeSet.add(toNodeKey(ep));\r\n\r\n const edges: Array<{ from: string; to: string; label?: string }> = [];\r\n for (const dep of dependencies) {\r\n edges.push({\r\n from: toNodeKey(dep.from),\r\n to: toNodeKey(dep.to),\r\n label: Object.keys(dep.paramMapping).join(', ') || undefined,\r\n });\r\n }\r\n return { nodes: Array.from(nodeSet), edges };\r\n}\r\n\r\n/**\r\n * Detect cycles in a directed graph using DFS coloring.\r\n */\r\nexport function detectCycles(dag: DirectedAcyclicGraph): string[] {\r\n const adjacency = new Map<string, string[]>();\r\n for (const node of dag.nodes) adjacency.set(node, []);\r\n for (const edge of dag.edges) adjacency.get(edge.from)?.push(edge.to);\r\n\r\n const color = new Map<string, Color>();\r\n for (const node of dag.nodes) color.set(node, Color.WHITE);\r\n\r\n const warnings: string[] = [];\r\n const path: string[] = [];\r\n\r\n function dfs(node: string): void {\r\n color.set(node, Color.GRAY);\r\n path.push(node);\r\n for (const neighbor of adjacency.get(node) || []) {\r\n const nc = color.get(neighbor);\r\n if (nc === Color.GRAY) {\r\n const cycleStart = path.indexOf(neighbor);\r\n warnings.push(`Cycle detected: ${path.slice(cycleStart).concat(neighbor).join(' → ')}`);\r\n } else if (nc === Color.WHITE) {\r\n dfs(neighbor);\r\n }\r\n }\r\n path.pop();\r\n color.set(node, Color.BLACK);\r\n }\r\n\r\n for (const node of dag.nodes) {\r\n if (color.get(node) === Color.WHITE) dfs(node);\r\n }\r\n return warnings;\r\n}\r\n\r\n/**\r\n * Topological sort using Kahn's algorithm.\r\n */\r\nexport function topologicalSort(dag: DirectedAcyclicGraph): string[] {\r\n const inDegree = new Map<string, number>();\r\n const adjacency = new Map<string, string[]>();\r\n\r\n for (const node of dag.nodes) { inDegree.set(node, 0); adjacency.set(node, []); }\r\n for (const edge of dag.edges) {\r\n adjacency.get(edge.from)?.push(edge.to);\r\n inDegree.set(edge.to, (inDegree.get(edge.to) || 0) + 1);\r\n }\r\n\r\n const queue: string[] = [];\r\n for (const [node, degree] of inDegree) {\r\n if (degree === 0) queue.push(node);\r\n }\r\n\r\n const sorted: string[] = [];\r\n while (queue.length > 0) {\r\n const node = queue.shift()!;\r\n sorted.push(node);\r\n for (const neighbor of adjacency.get(node) || []) {\r\n const nd = (inDegree.get(neighbor) || 1) - 1;\r\n inDegree.set(neighbor, nd);\r\n if (nd === 0) queue.push(neighbor);\r\n }\r\n }\r\n return sorted;\r\n}\r\n\r\nexport interface ApiChainAnalyzer {\r\n analyze(endpoints: ApiEndpoint[]): ApiChainAnalysisResult;\r\n}\r\n\r\n/**\r\n * Analyze API endpoints: infer dependencies, build DAG, detect cycles, topological sort.\r\n */\r\nexport function createApiChainAnalyzer(): ApiChainAnalyzer {\r\n return {\r\n analyze(endpoints: ApiEndpoint[]): ApiChainAnalysisResult {\r\n const dependencies = inferDependencies(endpoints);\r\n const dag = buildGraph(endpoints, dependencies);\r\n const cycleWarnings = detectCycles(dag);\r\n\r\n return {\r\n moduleName: '',\r\n endpoints,\r\n dependencies,\r\n dag,\r\n hasCycles: cycleWarnings.length > 0,\r\n cycleWarnings,\r\n };\r\n },\r\n };\r\n}\r\n","import type { ERDiagramResult, TableSchema, ForeignKeyRelation } from '../types.js';\r\n\r\nexport interface ERDiagramGenerator {\r\n generate(tables: TableSchema[], relations: ForeignKeyRelation[]): ERDiagramResult;\r\n}\r\n\r\n/**\r\n * Map field type string to a short Mermaid ER type label.\r\n */\r\nfunction toMermaidType(fieldType: string): string {\r\n const upper = fieldType.toUpperCase();\r\n if (upper.startsWith('STRING')) return 'string';\r\n if (upper === 'BIGINT' || upper === 'INTEGER') return 'bigint';\r\n if (upper === 'BOOLEAN') return 'boolean';\r\n if (upper.startsWith('DATE') || upper === 'NOW') return 'datetime';\r\n if (upper === 'JSON' || upper === 'JSONB') return 'json';\r\n if (upper === 'TEXT') return 'text';\r\n if (upper === 'FLOAT' || upper === 'DOUBLE' || upper === 'DECIMAL') return 'float';\r\n if (upper === 'UUID') return 'uuid';\r\n if (upper.startsWith('ENUM')) return 'enum';\r\n return 'string';\r\n}\r\n\r\n/**\r\n * Mermaid requires entity names without special characters.\r\n */\r\nfunction sanitizeEntityName(name: string): string {\r\n return name.replace(/[^a-zA-Z0-9_]/g, '_');\r\n}\r\n\r\n/**\r\n * Generate Mermaid ER diagram syntax from parsed schemas and relations.\r\n */\r\nfunction generateMermaidER(tables: TableSchema[], relations: ForeignKeyRelation[]): string {\r\n const lines: string[] = ['erDiagram'];\r\n\r\n // Entity blocks\r\n for (const table of tables) {\r\n const entityName = sanitizeEntityName(table.tableName);\r\n lines.push(` ${entityName} {`);\r\n for (const field of table.fields) {\r\n const mType = toMermaidType(field.type);\r\n const pk = field.primaryKey ? 'PK' : '';\r\n const comment = field.comment ? ` \"${field.comment}\"` : '';\r\n lines.push(` ${mType} ${field.name}${pk ? ' ' + pk : ''}${comment}`);\r\n }\r\n lines.push(' }');\r\n }\r\n\r\n // Relationships\r\n const tableNames = new Set(tables.map((t) => t.tableName));\r\n for (const rel of relations) {\r\n if (!tableNames.has(rel.sourceTable) || !tableNames.has(rel.targetTable)) continue;\r\n\r\n const src = sanitizeEntityName(rel.sourceTable);\r\n const tgt = sanitizeEntityName(rel.targetTable);\r\n const linkStyle = rel.isCrossModule ? '..' : '--';\r\n\r\n let cardinality: string;\r\n switch (rel.cardinality) {\r\n case '1:N': cardinality = `||${linkStyle}o{`; break;\r\n case 'N:1': cardinality = `}o${linkStyle}||`; break;\r\n case '1:1': cardinality = `||${linkStyle}||`; break;\r\n default: cardinality = `||${linkStyle}o{`;\r\n }\r\n\r\n lines.push(` ${src} ${cardinality} ${tgt} : \"${rel.targetField}\"`);\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\nexport function createERDiagramGenerator(): ERDiagramGenerator {\r\n return {\r\n generate(tables: TableSchema[], relations: ForeignKeyRelation[]): ERDiagramResult {\r\n const mermaidText = generateMermaidER(tables, relations);\r\n return { tables, relations, mermaidText };\r\n },\r\n };\r\n}\r\n","import type { GeneratedTestFile, TestChain, TestStep } from '../types.js';\r\n\r\nexport interface TestCodeGenerator {\r\n generate(chains: TestChain[]): GeneratedTestFile[];\r\n}\r\n\r\n/**\r\n * Resolve a path parameter from the available createdIds.\r\n */\r\nfunction resolvePathParam(param: string, ids: string[]): string {\r\n // Try direct match (e.g., 'kbId' → look for 'kbId' in ids)\r\n if (ids.includes(param)) return `createdIds['${param}']`;\r\n // Try with 'Id' suffix stripped\r\n const stripped = param.endsWith('Id') ? param.slice(0, -2) : param;\r\n if (ids.includes(stripped)) return `createdIds['${stripped}']`;\r\n // Generic id\r\n if (param === 'id') return `createdIds['id']`;\r\n return `createdIds['${param}'] || '1'`;\r\n}\r\n\r\n/**\r\n * Generate URL building code for a test step.\r\n */\r\nfunction buildUrlCode(step: TestStep): string {\r\n const pathParams = step.endpoint.pathParams;\r\n if (pathParams.length === 0) return `const url = '${step.endpoint.path}';`;\r\n\r\n let urlTemplate = step.endpoint.path;\r\n const replacements: string[] = [];\r\n for (const param of pathParams) {\r\n urlTemplate = urlTemplate.replace(`:${param}`, `\\${${resolvePathParam(param, pathParams)}}`);\r\n replacements.push(param);\r\n }\r\n return `const url = \\`${urlTemplate}\\`;`;\r\n}\r\n\r\n/**\r\n * Generate assertion code for a test step.\r\n */\r\nfunction generateAssertions(step: TestStep): string[] {\r\n const lines: string[] = [];\r\n if (step.assertions.length > 0) {\r\n for (const assertion of step.assertions) {\r\n lines.push(` expect(${assertion}).toBeTruthy();`);\r\n }\r\n } else {\r\n // Default assertions\r\n if (step.endpoint.method === 'POST') {\r\n lines.push(' expect(response.status()).toBeLessThan(400);');\r\n lines.push(' const body = await response.json();');\r\n lines.push(\" if (body.data?.id) createdIds['id'] = body.data.id;\");\r\n } else if (step.endpoint.method === 'GET') {\r\n lines.push(' expect(response.ok()).toBeTruthy();');\r\n } else if (step.endpoint.method === 'DELETE') {\r\n lines.push(' expect(response.status()).toBeLessThan(400);');\r\n } else {\r\n lines.push(' expect(response.status()).toBeLessThan(400);');\r\n }\r\n }\r\n return lines;\r\n}\r\n\r\n/**\r\n * Generate a single Playwright test file from a test chain.\r\n */\r\nfunction generateTestFile(chain: TestChain): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`import { test, expect } from '@playwright/test';`);\r\n lines.push('');\r\n lines.push(`test.describe('${chain.name}', () => {`);\r\n lines.push(\" const createdIds: Record<string, string> = {};\");\r\n lines.push('');\r\n\r\n for (const step of chain.steps) {\r\n lines.push(` test('Step ${step.order}: ${step.description}', async ({ request }) => {`);\r\n lines.push(` // ${step.action}: ${step.endpoint.method} ${step.endpoint.path}`);\r\n lines.push(` ${buildUrlCode(step)}`);\r\n lines.push('');\r\n\r\n if (step.endpoint.method === 'GET') {\r\n lines.push(' const response = await request.get(url);');\r\n } else if (step.endpoint.method === 'POST') {\r\n lines.push(' const response = await request.post(url, { data: {} });');\r\n } else if (step.endpoint.method === 'PUT') {\r\n lines.push(' const response = await request.put(url, { data: {} });');\r\n } else if (step.endpoint.method === 'DELETE') {\r\n lines.push(' const response = await request.delete(url);');\r\n } else if (step.endpoint.method === 'PATCH') {\r\n lines.push(' const response = await request.patch(url, { data: {} });');\r\n }\r\n\r\n lines.push('');\r\n lines.push(...generateAssertions(step));\r\n lines.push(' });');\r\n lines.push('');\r\n }\r\n\r\n lines.push('});');\r\n return lines.join('\\n');\r\n}\r\n\r\nexport function createTestCodeGenerator(): TestCodeGenerator {\r\n return {\r\n generate(chains: TestChain[]): GeneratedTestFile[] {\r\n return chains.map((chain) => ({\r\n filePath: `${chain.module}/${chain.name.replace(/\\s+/g, '-').toLowerCase()}.spec.ts`,\r\n content: generateTestFile(chain),\r\n module: chain.module,\r\n chain: chain.name,\r\n }));\r\n },\r\n };\r\n}\r\n","/**\r\n * Schema Validator — Layer 1 of three-layer module config validation.\r\n * Checks structural integrity, field types, format conventions.\r\n */\r\n\r\nimport type {\r\n LayerValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\n\r\nconst VALID_HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as const;\r\n\r\nexport function validateSchema(config: unknown): LayerValidationResult {\r\n const errors: ModuleConfigValidationError[] = [];\r\n const warnings: ModuleConfigValidationWarning[] = [];\r\n\r\n if (config === null || config === undefined || typeof config !== 'object') {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: '', message: 'Config must be a non-null object' });\r\n return { passed: false, layer: 'schema', errors, warnings };\r\n }\r\n\r\n const cfg = config as Record<string, unknown>;\r\n\r\n // Required top-level fields\r\n const required: Array<{ name: string; type: string }> = [\r\n { name: 'moduleName', type: 'string' },\r\n { name: 'version', type: 'string' },\r\n { name: 'generatedAt', type: 'string' },\r\n { name: 'bodyTemplates', type: 'object' },\r\n { name: 'paramRewrites', type: 'object' },\r\n { name: 'idAliases', type: 'array' },\r\n { name: 'seed', type: 'array' },\r\n ];\r\n\r\n for (const f of required) {\r\n if (cfg[f.name] === undefined || cfg[f.name] === null) {\r\n errors.push({\r\n layer: 'schema', type: 'missing-field', path: f.name,\r\n message: `Required field '${f.name}' is missing`,\r\n suggestion: `Add '${f.name}' field of type ${f.type}`,\r\n });\r\n }\r\n }\r\n\r\n // Field type checks\r\n validateFieldTypes(cfg, errors);\r\n\r\n // bodyTemplates structure\r\n if (cfg.bodyTemplates && typeof cfg.bodyTemplates === 'object' && !Array.isArray(cfg.bodyTemplates)) {\r\n validateBodyTemplates(cfg.bodyTemplates as Record<string, unknown>, errors, warnings);\r\n }\r\n\r\n // paramRewrites structure\r\n if (cfg.paramRewrites && typeof cfg.paramRewrites === 'object' && !Array.isArray(cfg.paramRewrites)) {\r\n validateParamRewrites(cfg.paramRewrites as Record<string, unknown>, errors, warnings);\r\n }\r\n\r\n // idAliases structure\r\n if (Array.isArray(cfg.idAliases)) {\r\n validateIdAliases(cfg.idAliases, errors);\r\n }\r\n\r\n // seed array\r\n if (Array.isArray(cfg.seed)) {\r\n validateSeedArray(cfg.seed, errors, warnings);\r\n }\r\n\r\n // version format\r\n if (typeof cfg.version === 'string' && !/^\\d+\\.\\d+(\\.\\d+)?$/.test(cfg.version)) {\r\n warnings.push({ layer: 'schema', path: 'version', message: `Version '${cfg.version}' does not follow semver format` });\r\n }\r\n\r\n return { passed: errors.length === 0, layer: 'schema', errors, warnings };\r\n}\r\n\r\nfunction validateFieldTypes(cfg: Record<string, unknown>, errors: ModuleConfigValidationError[]): void {\r\n if (cfg.moduleName !== undefined && typeof cfg.moduleName !== 'string') {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'moduleName', message: `'moduleName' must be a string` });\r\n } else if (typeof cfg.moduleName === 'string' && cfg.moduleName.trim() === '') {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: 'moduleName', message: \"'moduleName' must not be empty\" });\r\n }\r\n\r\n if (cfg.bodyTemplates !== undefined && (typeof cfg.bodyTemplates !== 'object' || Array.isArray(cfg.bodyTemplates))) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'bodyTemplates', message: \"'bodyTemplates' must be a plain object\" });\r\n }\r\n if (cfg.paramRewrites !== undefined && (typeof cfg.paramRewrites !== 'object' || Array.isArray(cfg.paramRewrites))) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'paramRewrites', message: \"'paramRewrites' must be a plain object\" });\r\n }\r\n if (cfg.idAliases !== undefined && !Array.isArray(cfg.idAliases)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'idAliases', message: \"'idAliases' must be an array\" });\r\n }\r\n if (cfg.seed !== undefined && !Array.isArray(cfg.seed)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: 'seed', message: \"'seed' must be an array\" });\r\n }\r\n}\r\n\r\nfunction validateBodyTemplates(\r\n templates: Record<string, unknown>,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, value] of Object.entries(templates)) {\r\n const p = `bodyTemplates.${key}`;\r\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `Body template '${key}' must be a plain object` });\r\n continue;\r\n }\r\n if (!/^(GET|POST|PUT|PATCH|DELETE)\\s+\\//.test(key)) {\r\n warnings.push({ layer: 'schema', path: p, message: `Body template key '${key}' should follow format 'METHOD /path'` });\r\n }\r\n if (Object.keys(value).length === 0) {\r\n warnings.push({ layer: 'schema', path: p, message: `Body template '${key}' is empty` });\r\n }\r\n }\r\n}\r\n\r\nfunction validateParamRewrites(\r\n rewrites: Record<string, unknown>,\r\n errors: ModuleConfigValidationError[],\r\n _warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, value] of Object.entries(rewrites)) {\r\n const p = `paramRewrites.${key}`;\r\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `Param rewrite '${key}' must be a plain object` });\r\n continue;\r\n }\r\n for (const [paramName, paramValue] of Object.entries(value as Record<string, unknown>)) {\r\n if (typeof paramValue !== 'string') {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: `${p}.${paramName}`, message: `Param rewrite value for '${paramName}' must be a string` });\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction validateIdAliases(aliases: unknown[], errors: ModuleConfigValidationError[]): void {\r\n for (let i = 0; i < aliases.length; i++) {\r\n const alias = aliases[i] as Record<string, unknown> | null;\r\n const p = `idAliases[${i}]`;\r\n if (typeof alias !== 'object' || alias === null) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `idAlias at index ${i} must be an object` });\r\n continue;\r\n }\r\n if (typeof alias.pathPattern !== 'string') {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.pathPattern`, message: `idAlias at index ${i} must have a string 'pathPattern'` });\r\n }\r\n if (typeof alias.alias !== 'string') {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.alias`, message: `idAlias at index ${i} must have a string 'alias'` });\r\n }\r\n }\r\n}\r\n\r\nfunction validateSeedArray(\r\n seed: unknown[],\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n if (seed.length === 0) {\r\n warnings.push({ layer: 'schema', path: 'seed', message: 'Seed array is empty, no setup steps will be executed' });\r\n return;\r\n }\r\n\r\n const capturedVars = new Set<string>();\r\n const stepNumbers = new Set<number>();\r\n\r\n for (let i = 0; i < seed.length; i++) {\r\n const step = seed[i] as Record<string, unknown> | null;\r\n const p = `seed[${i}]`;\r\n if (typeof step !== 'object' || step === null) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: p, message: `Seed step at index ${i} must be an object` });\r\n continue;\r\n }\r\n\r\n // step number\r\n if (step.step === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.step`, message: `Seed step at index ${i} is missing 'step' number` });\r\n } else if (typeof step.step !== 'number' || !Number.isInteger(step.step)) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: `${p}.step`, message: `'step' must be an integer` });\r\n } else {\r\n if (stepNumbers.has(step.step)) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.step`, message: `Duplicate step number ${step.step}` });\r\n }\r\n stepNumbers.add(step.step);\r\n }\r\n\r\n // method\r\n if (step.method === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.method`, message: `Seed step at index ${i} is missing 'method'` });\r\n } else if (typeof step.method !== 'string' || !(VALID_HTTP_METHODS as readonly string[]).includes(step.method)) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.method`, message: `'method' must be one of ${VALID_HTTP_METHODS.join(', ')}` });\r\n }\r\n\r\n // path\r\n if (step.path === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.path`, message: `Seed step at index ${i} is missing 'path'` });\r\n } else if (typeof step.path !== 'string' || !(step.path as string).startsWith('/')) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.path`, message: `'path' must start with '/'` });\r\n }\r\n\r\n // required\r\n if (step.required === undefined) {\r\n errors.push({ layer: 'schema', type: 'missing-field', path: `${p}.required`, message: `Seed step at index ${i} is missing 'required'` });\r\n }\r\n\r\n // body\r\n if (step.body !== undefined && (typeof step.body !== 'object' || step.body === null || Array.isArray(step.body))) {\r\n errors.push({ layer: 'schema', type: 'invalid-type', path: `${p}.body`, message: `'body' must be a plain object` });\r\n } else if (step.body === undefined && typeof step.method === 'string' && ['POST', 'PUT', 'PATCH'].includes(step.method)) {\r\n warnings.push({ layer: 'schema', path: `${p}.body`, message: `${step.method} step at index ${i} has no body template` });\r\n }\r\n\r\n // captureAs uniqueness\r\n if (typeof step.captureAs === 'string') {\r\n if (capturedVars.has(step.captureAs)) {\r\n errors.push({ layer: 'schema', type: 'invalid-format', path: `${p}.captureAs`, message: `Duplicate captureAs variable '${step.captureAs}'` });\r\n }\r\n capturedVars.add(step.captureAs);\r\n }\r\n\r\n // dependsOn reference check\r\n if (Array.isArray(step.dependsOn)) {\r\n for (const dep of step.dependsOn) {\r\n if (typeof dep === 'string' && !capturedVars.has(dep)) {\r\n errors.push({\r\n layer: 'schema', type: 'dependency-missing', path: `${p}.dependsOn`,\r\n message: `Seed step ${step.step ?? i} depends on '${dep}' not captured by any preceding step`,\r\n suggestion: `Ensure a preceding step has captureAs: '${dep}'`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Semantic Validator — Layer 2 of three-layer module config validation.\r\n * Checks that config matches actual source code (routes, DTO fields, dependencies).\r\n */\r\n\r\nimport type {\r\n ModuleTestConfig,\r\n SeedStep,\r\n ApiEndpoint,\r\n DTOInfo,\r\n ModuleConfigValidationContext,\r\n LayerValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\n\r\nexport function validateSemantic(\r\n config: ModuleTestConfig,\r\n context: ModuleConfigValidationContext,\r\n): LayerValidationResult {\r\n const errors: ModuleConfigValidationError[] = [];\r\n const warnings: ModuleConfigValidationWarning[] = [];\r\n\r\n validateBodyTemplatesSemantic(config, context, errors, warnings);\r\n validateParamRewritesSemantic(config, context, errors, warnings);\r\n validateIdAliasesSemantic(config, context, warnings);\r\n validateSeedRoutesSemantic(config, context, errors);\r\n validateSeedDependenciesSemantic(config, errors, warnings);\r\n\r\n return { passed: errors.length === 0, layer: 'semantic', errors, warnings };\r\n}\r\n\r\n// ============================================================\r\n// Helpers\r\n// ============================================================\r\n\r\nfunction normalizePath(p: string): string {\r\n return p.replace(/\\/+$/, '').replace(/\\/+/g, '/');\r\n}\r\n\r\nfunction extractPathParams(routePath: string): string[] {\r\n const params: string[] = [];\r\n const regex = /:(\\w+)/g;\r\n let match;\r\n while ((match = regex.exec(routePath)) !== null) params.push(match[1]);\r\n return params;\r\n}\r\n\r\nfunction findMatchingEndpoint(\r\n method: string, configPath: string, endpoints: ApiEndpoint[],\r\n): ApiEndpoint | undefined {\r\n const normalized = normalizePath(configPath);\r\n return endpoints.find((ep) => {\r\n if (ep.method !== method.toUpperCase()) return false;\r\n const epNorm = normalizePath(ep.path);\r\n if (epNorm === normalized) return true;\r\n const cSegs = normalized.split('/');\r\n const eSegs = epNorm.split('/');\r\n if (cSegs.length !== eSegs.length) return false;\r\n return cSegs.every((seg, idx) => seg === eSegs[idx] || seg.startsWith(':') || eSegs[idx].startsWith(':'));\r\n });\r\n}\r\n\r\nfunction findMatchingDTO(method: string, dtos: DTOInfo[]): DTOInfo | undefined {\r\n if (dtos.length === 0) return undefined;\r\n if (['POST'].includes(method)) return dtos.find((d) => /Create|Input|Request/.test(d.name));\r\n if (['PUT', 'PATCH'].includes(method)) return dtos.find((d) => /Update/.test(d.name)) || dtos.find((d) => /Create|Input/.test(d.name));\r\n if (['GET'].includes(method)) return dtos.find((d) => /Query|List|Params/.test(d.name));\r\n return undefined;\r\n}\r\n\r\n// ============================================================\r\n// Validators\r\n// ============================================================\r\n\r\nfunction validateBodyTemplatesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, body] of Object.entries(config.bodyTemplates)) {\r\n const p = `bodyTemplates.${key}`;\r\n const spaceIdx = key.indexOf(' ');\r\n if (spaceIdx === -1) continue;\r\n\r\n const method = key.substring(0, spaceIdx).toUpperCase();\r\n const routePath = key.substring(spaceIdx + 1);\r\n\r\n const endpoint = findMatchingEndpoint(method, routePath, ctx.endpoints);\r\n if (!endpoint) {\r\n errors.push({\r\n layer: 'semantic', type: 'interface-not-found', path: p,\r\n message: `Route '${method} ${routePath}' in bodyTemplates does not match any parsed API endpoint`,\r\n suggestion: `Check that the controller defines ${method} ${routePath}`,\r\n });\r\n continue;\r\n }\r\n\r\n const bodyFields = Object.keys(body);\r\n if (bodyFields.length === 0) continue;\r\n\r\n const dto = findMatchingDTO(method, ctx.dtos);\r\n if (!dto) {\r\n warnings.push({ layer: 'semantic', path: p, message: `No matching DTO found for '${method} ${routePath}', cannot verify field completeness` });\r\n continue;\r\n }\r\n\r\n const dtoFieldNames = new Set(dto.fields.map((f) => f.name));\r\n for (const fieldName of bodyFields) {\r\n if (!dtoFieldNames.has(fieldName)) {\r\n warnings.push({ layer: 'semantic', path: `${p}.${fieldName}`, message: `Field '${fieldName}' in body template not found in DTO '${dto.name}'` });\r\n }\r\n }\r\n\r\n const bodyFieldSet = new Set(bodyFields);\r\n for (const field of dto.fields) {\r\n if (field.required && !field.isSystemField && !bodyFieldSet.has(field.name)) {\r\n warnings.push({ layer: 'semantic', path: p, message: `Required DTO field '${field.name}' (from ${dto.name}) not present in body template` });\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction validateParamRewritesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (const [key, mapping] of Object.entries(config.paramRewrites)) {\r\n const p = `paramRewrites.${key}`;\r\n const spaceIdx = key.indexOf(' ');\r\n if (spaceIdx === -1) continue;\r\n\r\n const method = key.substring(0, spaceIdx).toUpperCase();\r\n const routePath = key.substring(spaceIdx + 1);\r\n\r\n const endpoint = findMatchingEndpoint(method, routePath, ctx.endpoints);\r\n if (!endpoint) {\r\n errors.push({ layer: 'semantic', type: 'param-mapping-invalid', path: p, message: `Route '${method} ${routePath}' in paramRewrites does not match any parsed API endpoint` });\r\n continue;\r\n }\r\n\r\n const actualParams = new Set(extractPathParams(endpoint.path));\r\n for (const paramName of Object.keys(mapping)) {\r\n if (!actualParams.has(paramName)) {\r\n warnings.push({ layer: 'semantic', path: `${p}.${paramName}`, message: `Param '${paramName}' in paramRewrites not found in route path params [${[...actualParams].join(', ')}]` });\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction validateIdAliasesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n for (let i = 0; i < config.idAliases.length; i++) {\r\n const alias = config.idAliases[i];\r\n const matched = ctx.endpoints.some((ep) => {\r\n try { return new RegExp(alias.pathPattern).test(ep.path); } catch { return ep.path.includes(alias.pathPattern); }\r\n });\r\n if (!matched) {\r\n warnings.push({ layer: 'semantic', path: `idAliases[${i}].pathPattern`, message: `idAlias pathPattern '${alias.pathPattern}' does not match any known API route` });\r\n }\r\n }\r\n}\r\n\r\nfunction validateSeedRoutesSemantic(\r\n config: ModuleTestConfig,\r\n ctx: ModuleConfigValidationContext,\r\n errors: ModuleConfigValidationError[],\r\n): void {\r\n for (let i = 0; i < config.seed.length; i++) {\r\n const step = config.seed[i];\r\n const endpoint = findMatchingEndpoint(step.method, step.path, ctx.endpoints);\r\n if (!endpoint) {\r\n errors.push({\r\n layer: 'semantic', type: 'interface-not-found', path: `seed[${i}].path`,\r\n message: `Seed step ${step.step}: route '${step.method} ${step.path}' does not match any parsed API endpoint`,\r\n suggestion: `Verify that '${step.method} ${step.path}' exists in the module controllers`,\r\n });\r\n }\r\n }\r\n}\r\n\r\nfunction validateSeedDependenciesSemantic(\r\n config: ModuleTestConfig,\r\n errors: ModuleConfigValidationError[],\r\n warnings: ModuleConfigValidationWarning[],\r\n): void {\r\n if (config.seed.length === 0) return;\r\n\r\n const capturedSet = new Set<string>();\r\n const variablePattern = /\\{\\{(\\w+)\\}\\}|\\$\\{(\\w+)\\}/g;\r\n\r\n for (let i = 0; i < config.seed.length; i++) {\r\n const step = config.seed[i];\r\n\r\n // Check body variable references\r\n if (step.body) {\r\n const bodyStr = JSON.stringify(step.body);\r\n variablePattern.lastIndex = 0;\r\n let match;\r\n while ((match = variablePattern.exec(bodyStr)) !== null) {\r\n const varName = match[1] || match[2];\r\n if (!capturedSet.has(varName)) {\r\n warnings.push({ layer: 'semantic', path: `seed[${i}].body`, message: `Body references variable '${varName}' which may not be captured by a preceding seed step` });\r\n }\r\n }\r\n }\r\n\r\n // Check path variable references\r\n if (step.path) {\r\n variablePattern.lastIndex = 0;\r\n let match;\r\n while ((match = variablePattern.exec(step.path)) !== null) {\r\n const varName = match[1] || match[2];\r\n if (!capturedSet.has(varName)) {\r\n warnings.push({ layer: 'semantic', path: `seed[${i}].path`, message: `Path references variable '${varName}' which may not be captured by a preceding seed step` });\r\n }\r\n }\r\n }\r\n\r\n if (step.captureAs) capturedSet.add(step.captureAs);\r\n }\r\n\r\n // Cycle detection via DFS\r\n detectDependencyCycle(config.seed, errors);\r\n}\r\n\r\nfunction detectDependencyCycle(seed: SeedStep[], errors: ModuleConfigValidationError[]): void {\r\n const graph = new Map<string, string[]>();\r\n for (const step of seed) {\r\n if (step.captureAs) {\r\n graph.set(step.captureAs, step.dependsOn || []);\r\n }\r\n }\r\n\r\n const visited = new Set<string>();\r\n const inStack = new Set<string>();\r\n\r\n function dfs(node: string): boolean {\r\n if (inStack.has(node)) return true;\r\n if (visited.has(node)) return false;\r\n visited.add(node);\r\n inStack.add(node);\r\n for (const neighbor of graph.get(node) || []) {\r\n if (dfs(neighbor)) {\r\n errors.push({ layer: 'semantic', type: 'dependency-cycle', path: 'seed', message: `Circular dependency detected involving '${node}' → '${neighbor}'` });\r\n return true;\r\n }\r\n }\r\n inStack.delete(node);\r\n return false;\r\n }\r\n\r\n for (const node of graph.keys()) {\r\n if (!visited.has(node)) dfs(node);\r\n }\r\n}\r\n","/**\r\n * Dry-run Validator — Layer 3 of three-layer module config validation.\r\n * Generates temporary TypeScript code from config and runs ts-morph compile check.\r\n */\r\n\r\nimport { Project, DiagnosticCategory } from 'ts-morph';\r\nimport type {\r\n ModuleTestConfig,\r\n ModuleConfigValidationContext,\r\n LayerValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\n\r\nexport function validateDryrun(\r\n config: ModuleTestConfig,\r\n _context: ModuleConfigValidationContext,\r\n): LayerValidationResult {\r\n const errors: ModuleConfigValidationError[] = [];\r\n const warnings: ModuleConfigValidationWarning[] = [];\r\n\r\n const project = new Project({\r\n compilerOptions: {\r\n strict: false,\r\n noEmit: true,\r\n target: 2, // ES2015\r\n module: 1, // CommonJS\r\n esModuleInterop: true,\r\n skipLibCheck: true,\r\n },\r\n useInMemoryFileSystem: true,\r\n });\r\n\r\n // Add helper type declarations\r\n project.createSourceFile(\r\n '__helpers.d.ts',\r\n `\r\ndeclare function apiRequest(\r\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',\r\n path: string,\r\n body?: Record<string, any>,\r\n params?: Record<string, string>\r\n): Promise<{ status: number; data: any }>;\r\ndeclare function captureId(response: { data: any }, field?: string): string;\r\ndeclare const tenantId: string;\r\ndeclare const captured: Record<string, string>;\r\n`,\r\n );\r\n\r\n // Generate and check bodyTemplates test code\r\n const bodyCode = generateBodyTemplateTestCode(config);\r\n if (bodyCode) {\r\n const bodyFile = project.createSourceFile('__dryrun_body_test.ts', bodyCode);\r\n for (const diag of bodyFile.getPreEmitDiagnostics()) {\r\n const msg = diag.getMessageText();\r\n const msgStr = typeof msg === 'string' ? msg : msg.getMessageText();\r\n const line = diag.getLineNumber();\r\n const location = line ? `bodyTemplates (line ${line})` : 'bodyTemplates';\r\n\r\n if (diag.getCategory() === DiagnosticCategory.Error) {\r\n errors.push({\r\n layer: 'dryrun', type: 'compile-error', path: location,\r\n message: `TypeScript compile error: ${msgStr}`,\r\n suggestion: 'Fix the body template that causes this type error',\r\n });\r\n } else if (diag.getCategory() === DiagnosticCategory.Warning) {\r\n warnings.push({ layer: 'dryrun', path: location, message: `TypeScript warning: ${msgStr}` });\r\n }\r\n }\r\n }\r\n\r\n // Generate and check seed test code\r\n const seedCode = generateSeedTestCode(config);\r\n if (seedCode) {\r\n const seedFile = project.createSourceFile('__dryrun_seed_test.ts', seedCode);\r\n for (const diag of seedFile.getPreEmitDiagnostics()) {\r\n const msg = diag.getMessageText();\r\n const msgStr = typeof msg === 'string' ? msg : msg.getMessageText();\r\n const line = diag.getLineNumber();\r\n const location = line ? `seed (line ${line})` : 'seed';\r\n\r\n if (diag.getCategory() === DiagnosticCategory.Error) {\r\n errors.push({\r\n layer: 'dryrun', type: 'compile-error', path: location,\r\n message: `TypeScript compile error: ${msgStr}`,\r\n suggestion: 'Fix the seed step that causes this type error',\r\n });\r\n } else if (diag.getCategory() === DiagnosticCategory.Warning) {\r\n warnings.push({ layer: 'dryrun', path: location, message: `TypeScript warning: ${msgStr}` });\r\n }\r\n }\r\n }\r\n\r\n return { passed: errors.length === 0, layer: 'dryrun', errors, warnings };\r\n}\r\n\r\nfunction generateBodyTemplateTestCode(config: ModuleTestConfig): string | null {\r\n if (!config.bodyTemplates || Object.keys(config.bodyTemplates).length === 0) return null;\r\n\r\n const lines = ['async function testBodyTemplates() {'];\r\n let idx = 0;\r\n for (const [key, body] of Object.entries(config.bodyTemplates)) {\r\n const spaceIdx = key.indexOf(' ');\r\n if (spaceIdx === -1) continue;\r\n const method = key.substring(0, spaceIdx);\r\n const routePath = key.substring(spaceIdx + 1);\r\n\r\n lines.push(` const body_${idx} = ${JSON.stringify(body, null, 2)};`);\r\n lines.push(` const result_${idx} = await apiRequest('${method}', '${routePath}', body_${idx});`);\r\n lines.push(` if (result_${idx}.status !== 200 && result_${idx}.status !== 201) {`);\r\n lines.push(` throw new Error('Unexpected status: ' + result_${idx}.status);`);\r\n lines.push(' }');\r\n idx++;\r\n }\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n\r\nfunction generateSeedTestCode(config: ModuleTestConfig): string | null {\r\n if (!config.seed || config.seed.length === 0) return null;\r\n\r\n const lines = [\r\n 'async function testSeedSteps() {',\r\n ' const captured: Record<string, string> = {};',\r\n '',\r\n ];\r\n\r\n for (const step of config.seed) {\r\n if (step.body) {\r\n lines.push(` const body_step${step.step} = ${JSON.stringify(step.body, null, 2)};`);\r\n lines.push(` const result_step${step.step} = await apiRequest('${step.method}', '${step.path}', body_step${step.step});`);\r\n } else {\r\n lines.push(` const result_step${step.step} = await apiRequest('${step.method}', '${step.path}');`);\r\n }\r\n\r\n if (step.captureAs) {\r\n lines.push(` captured['${step.captureAs}'] = captureId(result_step${step.step});`);\r\n }\r\n\r\n if (step.required) {\r\n lines.push(` if (result_step${step.step}.status >= 400) {`);\r\n lines.push(` throw new Error('${step.failureMessage || `Required step ${step.step} failed`}');`);\r\n lines.push(' }');\r\n }\r\n lines.push('');\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n","import type {\r\n ValidationError,\r\n ModuleTestConfig,\r\n ModuleConfigValidationContext,\r\n ModuleConfigValidationResult,\r\n ModuleConfigValidationError,\r\n ModuleConfigValidationWarning,\r\n} from '../types.js';\r\nimport { validateSchema } from './schema-validator.js';\r\nimport { validateSemantic } from './semantic-validator.js';\r\nimport { validateDryrun } from './dryrun-validator.js';\r\n\r\nconst REQUIRED_FIELDS = ['backendRoot'];\r\n\r\nconst VALID_ADAPTERS = ['sequelize', 'typeorm', 'prisma', 'drizzle'];\r\nconst VALID_STEPS = ['scan', 'er-diagram', 'api-chain', 'plan', 'codegen', 'validate'];\r\nconst VALID_LLM_PROVIDERS = ['openai', 'zhipu', 'ollama', 'custom'];\r\nconst VALID_REPORT_FORMATS = ['html', 'json', 'markdown'];\r\nconst VALID_HEAL_MODES = ['config-only', 'config-and-source'];\r\n\r\n/**\r\n * Validate an OpenCroc configuration object.\r\n * Returns an array of ValidationErrors (empty = valid).\r\n */\r\nexport function validateConfig(config: Record<string, unknown>): ValidationError[] {\r\n const errors: ValidationError[] = [];\r\n\r\n // Required fields\r\n for (const field of REQUIRED_FIELDS) {\r\n if (!config[field]) {\r\n errors.push({\r\n module: 'config',\r\n field,\r\n message: `Missing required field: ${field}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n\r\n // backendRoot must be a string\r\n if (config.backendRoot && typeof config.backendRoot !== 'string') {\r\n errors.push({\r\n module: 'config',\r\n field: 'backendRoot',\r\n message: 'backendRoot must be a string path',\r\n severity: 'error',\r\n });\r\n }\r\n\r\n // adapter validation\r\n if (config.adapter && typeof config.adapter === 'string') {\r\n if (!VALID_ADAPTERS.includes(config.adapter)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'adapter',\r\n message: `Invalid adapter: ${config.adapter}. Must be one of: ${VALID_ADAPTERS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n\r\n // steps validation\r\n if (config.steps && Array.isArray(config.steps)) {\r\n for (const step of config.steps) {\r\n if (!VALID_STEPS.includes(step as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'steps',\r\n message: `Invalid pipeline step: ${step}. Must be one of: ${VALID_STEPS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n }\r\n\r\n // LLM config validation\r\n if (config.llm && typeof config.llm === 'object') {\r\n const llm = config.llm as Record<string, unknown>;\r\n if (llm.provider && !VALID_LLM_PROVIDERS.includes(llm.provider as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'llm.provider',\r\n message: `Invalid LLM provider: ${llm.provider}. Must be one of: ${VALID_LLM_PROVIDERS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n if (llm.provider && llm.provider !== 'ollama' && !llm.apiKey) {\r\n errors.push({\r\n module: 'config',\r\n field: 'llm.apiKey',\r\n message: 'LLM apiKey is required for cloud providers',\r\n severity: 'warning',\r\n });\r\n }\r\n }\r\n\r\n // Report config validation\r\n if (config.report && typeof config.report === 'object') {\r\n const report = config.report as Record<string, unknown>;\r\n if (report.format && Array.isArray(report.format)) {\r\n for (const fmt of report.format) {\r\n if (!VALID_REPORT_FORMATS.includes(fmt as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'report.format',\r\n message: `Invalid report format: ${fmt}. Must be one of: ${VALID_REPORT_FORMATS.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Self-healing config validation\r\n if (config.selfHealing && typeof config.selfHealing === 'object') {\r\n const sh = config.selfHealing as Record<string, unknown>;\r\n if (sh.mode && !VALID_HEAL_MODES.includes(sh.mode as string)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'selfHealing.mode',\r\n message: `Invalid self-healing mode: ${sh.mode}. Must be one of: ${VALID_HEAL_MODES.join(', ')}`,\r\n severity: 'error',\r\n });\r\n }\r\n if (sh.maxIterations && (typeof sh.maxIterations !== 'number' || sh.maxIterations < 1)) {\r\n errors.push({\r\n module: 'config',\r\n field: 'selfHealing.maxIterations',\r\n message: 'maxIterations must be a positive number',\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n\r\n // Execution hooks validation\r\n if (config.execution && typeof config.execution === 'object') {\r\n const execution = config.execution as Record<string, unknown>;\r\n const hookFields = ['setupHook', 'authHook', 'teardownHook'];\r\n\r\n for (const hookField of hookFields) {\r\n const hook = execution[hookField];\r\n if (hook === undefined) continue;\r\n\r\n if (typeof hook === 'string') continue;\r\n\r\n if (typeof hook !== 'object' || hook === null) {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}`,\r\n message: `${hookField} must be a string command or an object { command, args?, cwd? }`,\r\n severity: 'error',\r\n });\r\n continue;\r\n }\r\n\r\n const hookObj = hook as Record<string, unknown>;\r\n if (typeof hookObj.command !== 'string' || hookObj.command.trim() === '') {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}.command`,\r\n message: 'command is required and must be a non-empty string',\r\n severity: 'error',\r\n });\r\n }\r\n\r\n if (hookObj.args !== undefined && (!Array.isArray(hookObj.args) || hookObj.args.some((a) => typeof a !== 'string'))) {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}.args`,\r\n message: 'args must be an array of strings',\r\n severity: 'error',\r\n });\r\n }\r\n\r\n if (hookObj.cwd !== undefined && typeof hookObj.cwd !== 'string') {\r\n errors.push({\r\n module: 'config',\r\n field: `execution.${hookField}.cwd`,\r\n message: 'cwd must be a string path',\r\n severity: 'error',\r\n });\r\n }\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n// ============================================================\r\n// Three-Layer Module Config Validator\r\n// ============================================================\r\n\r\nexport interface ValidateModuleConfigOptions {\r\n stopOnFailure?: boolean;\r\n skipLayers?: Array<'schema' | 'semantic' | 'dryrun'>;\r\n}\r\n\r\nexport function validateModuleConfig(\r\n config: unknown,\r\n context?: ModuleConfigValidationContext,\r\n options?: ValidateModuleConfigOptions,\r\n): ModuleConfigValidationResult {\r\n const stopOnFailure = options?.stopOnFailure ?? true;\r\n const skipLayers = new Set(options?.skipLayers ?? []);\r\n\r\n const allErrors: ModuleConfigValidationError[] = [];\r\n const allWarnings: ModuleConfigValidationWarning[] = [];\r\n const result: ModuleConfigValidationResult = {\r\n passed: false,\r\n errors: allErrors,\r\n warnings: allWarnings,\r\n };\r\n\r\n if (!skipLayers.has('schema')) {\r\n const schemaResult = validateSchema(config);\r\n result.schemaResult = schemaResult;\r\n allErrors.push(...schemaResult.errors);\r\n allWarnings.push(...schemaResult.warnings);\r\n\r\n if (!schemaResult.passed) {\r\n result.failedAtLayer = 'schema';\r\n if (stopOnFailure) return result;\r\n } else {\r\n result.lastPassedLayer = 'schema';\r\n }\r\n }\r\n\r\n const validConfig = config as ModuleTestConfig;\r\n\r\n if (!skipLayers.has('semantic')) {\r\n if (!context) {\r\n allWarnings.push({ layer: 'semantic', path: '', message: 'ValidationContext not provided, skipping semantic validation' });\r\n } else {\r\n const semanticResult = validateSemantic(validConfig, context);\r\n result.semanticResult = semanticResult;\r\n allErrors.push(...semanticResult.errors);\r\n allWarnings.push(...semanticResult.warnings);\r\n\r\n if (!semanticResult.passed) {\r\n result.failedAtLayer = result.failedAtLayer || 'semantic';\r\n if (stopOnFailure) return result;\r\n } else {\r\n result.lastPassedLayer = 'semantic';\r\n }\r\n }\r\n }\r\n\r\n if (!skipLayers.has('dryrun')) {\r\n if (!context) {\r\n allWarnings.push({ layer: 'dryrun', path: '', message: 'ValidationContext not provided, skipping dry-run validation' });\r\n } else {\r\n const dryrunResult = validateDryrun(validConfig, context);\r\n result.dryrunResult = dryrunResult;\r\n allErrors.push(...dryrunResult.errors);\r\n allWarnings.push(...dryrunResult.warnings);\r\n\r\n if (!dryrunResult.passed) {\r\n result.failedAtLayer = result.failedAtLayer || 'dryrun';\r\n } else {\r\n result.lastPassedLayer = 'dryrun';\r\n }\r\n }\r\n }\r\n\r\n result.passed = allErrors.length === 0;\r\n return result;\r\n}\r\n\r\nexport function formatValidationResult(result: ModuleConfigValidationResult): string {\r\n const lines: string[] = [];\r\n lines.push(result.passed ? '\\u2705 Validation PASSED' : '\\u274c Validation FAILED');\r\n if (result.failedAtLayer) lines.push(` Failed at layer: ${result.failedAtLayer}`);\r\n if (result.lastPassedLayer) lines.push(` Last passed layer: ${result.lastPassedLayer}`);\r\n\r\n if (result.errors.length > 0) {\r\n lines.push('', `Errors (${result.errors.length}):`);\r\n for (const err of result.errors) {\r\n lines.push(` [${err.layer}] ${err.path}: ${err.message}`);\r\n if (err.suggestion) lines.push(` \\ud83d\\udca1 ${err.suggestion}`);\r\n }\r\n }\r\n\r\n if (result.warnings.length > 0) {\r\n lines.push('', `Warnings (${result.warnings.length}):`);\r\n for (const warn of result.warnings) {\r\n lines.push(` [${warn.layer}] ${warn.path}: ${warn.message}`);\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport type {\r\n OpenCrocConfig,\r\n PipelineRunResult,\r\n PipelineStep,\r\n ERDiagramResult,\r\n ChainPlanResult,\r\n} from '../types.js';\r\nimport { parseModuleModels } from '../parsers/model-parser.js';\r\nimport { parseControllerDirectory } from '../parsers/controller-parser.js';\r\nimport { parseAssociationFile } from '../parsers/association-parser.js';\r\nimport { createApiChainAnalyzer, topologicalSort } from '../analyzers/api-chain-analyzer.js';\r\nimport { createERDiagramGenerator } from '../generators/er-diagram-generator.js';\r\nimport { createTestCodeGenerator } from '../generators/test-code-generator.js';\r\nimport { validateConfig } from '../validators/config-validator.js';\r\n\r\nexport interface Pipeline {\r\n run(steps?: PipelineStep[]): Promise<PipelineRunResult>;\r\n}\r\n\r\nconst ALL_STEPS: PipelineStep[] = ['scan', 'er-diagram', 'api-chain', 'plan', 'codegen', 'validate'];\r\n\r\nexport function createPipeline(config: OpenCrocConfig): Pipeline {\r\n return {\r\n async run(steps) {\r\n const startTime = Date.now();\r\n const activeSteps = steps || config.steps || ALL_STEPS;\r\n\r\n const result: PipelineRunResult = {\r\n modules: [],\r\n erDiagrams: new Map(),\r\n chainPlans: new Map(),\r\n generatedFiles: [],\r\n validationErrors: [],\r\n duration: 0,\r\n };\r\n\r\n // Step 1: Scan — discover modules\r\n if (activeSteps.includes('scan')) {\r\n const backendRoot = path.resolve(config.backendRoot);\r\n const modelsDir = path.join(backendRoot, 'models');\r\n\r\n if (fs.existsSync(modelsDir)) {\r\n // Discover modules from subdirectories\r\n const dirs = fs.readdirSync(modelsDir, { withFileTypes: true })\r\n .filter((d) => d.isDirectory())\r\n .map((d) => d.name);\r\n\r\n const moduleFilter = config.modules;\r\n for (const dir of dirs) {\r\n if (moduleFilter && !moduleFilter.includes(dir)) continue;\r\n result.modules.push(dir);\r\n }\r\n\r\n // If no subdirectories, treat root as single \"default\" module\r\n if (result.modules.length === 0) {\r\n result.modules.push('default');\r\n } else {\r\n // Also include root-level model files as \"default\" module\r\n const rootFiles = fs.readdirSync(modelsDir)\r\n .filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts') && f !== 'index.ts');\r\n if (rootFiles.length > 0) {\r\n result.modules.unshift('default');\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Helper: resolve model dir for a module\r\n const resolveModelDir = (backendRoot: string, mod: string): string =>\r\n mod === 'default'\r\n ? path.join(backendRoot, 'models')\r\n : path.join(backendRoot, 'models', mod);\r\n\r\n // Helper: resolve controller dir for a module\r\n const resolveControllerDir = (backendRoot: string, mod: string): string =>\r\n mod === 'default'\r\n ? path.join(backendRoot, 'controllers')\r\n : path.join(backendRoot, 'controllers', mod);\r\n\r\n // Step 2: ER Diagram — parse models and generate relationship graphs\r\n if (activeSteps.includes('er-diagram')) {\r\n const erGen = createERDiagramGenerator();\r\n const backendRoot = path.resolve(config.backendRoot);\r\n\r\n for (const mod of result.modules) {\r\n const modelDir = resolveModelDir(backendRoot, mod);\r\n\r\n // For flat layouts, scan all model files for embedded associations\r\n const tables = fs.existsSync(modelDir) ? parseModuleModels(modelDir) : [];\r\n const relations: import('../types.js').ForeignKeyRelation[] = [];\r\n\r\n // Check for dedicated associations.ts first\r\n const assocFile = path.join(modelDir, 'associations.ts');\r\n if (fs.existsSync(assocFile)) {\r\n relations.push(...parseAssociationFile(assocFile));\r\n }\r\n\r\n // Also scan model files for embedded associations (belongsTo/hasMany at end of file)\r\n if (fs.existsSync(modelDir)) {\r\n const modelFiles = fs.readdirSync(modelDir)\r\n .filter((f) => f.endsWith('.ts') && !f.endsWith('.test.ts') && f !== 'index.ts' && f !== 'associations.ts');\r\n for (const file of modelFiles) {\r\n try {\r\n const embedded = parseAssociationFile(path.join(modelDir, file));\r\n relations.push(...embedded);\r\n } catch {\r\n // skip files that fail to parse\r\n }\r\n }\r\n }\r\n\r\n const erResult: ERDiagramResult = erGen.generate(tables, relations);\r\n result.erDiagrams.set(mod, erResult);\r\n }\r\n }\r\n\r\n // Step 3: API Chain — analyze controller routes and build dependency DAG\r\n if (activeSteps.includes('api-chain')) {\r\n const chainAnalyzer = createApiChainAnalyzer();\r\n const backendRoot = path.resolve(config.backendRoot);\r\n\r\n for (const mod of result.modules) {\r\n const controllerDir = resolveControllerDir(backendRoot, mod);\r\n const endpoints = fs.existsSync(controllerDir)\r\n ? parseControllerDirectory(controllerDir)\r\n : [];\r\n\r\n const analysis = chainAnalyzer.analyze(endpoints);\r\n analysis.moduleName = mod;\r\n\r\n if (analysis.hasCycles) {\r\n for (const warning of analysis.cycleWarnings) {\r\n result.validationErrors.push({\r\n module: mod,\r\n field: 'api-chain',\r\n message: warning,\r\n severity: 'warning',\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Step 4: Plan — generate test chains from dependency analysis\r\n if (activeSteps.includes('plan')) {\r\n const backendRoot = path.resolve(config.backendRoot);\r\n const chainAnalyzer = createApiChainAnalyzer();\r\n\r\n for (const mod of result.modules) {\r\n const controllerDir = resolveControllerDir(backendRoot, mod);\r\n const endpoints = fs.existsSync(controllerDir)\r\n ? parseControllerDirectory(controllerDir)\r\n : [];\r\n\r\n const analysis = chainAnalyzer.analyze(endpoints);\r\n const topoOrder = topologicalSort(analysis.dag);\r\n\r\n // Group by resource to create chains\r\n const chains = generateChainPlan(mod, endpoints, topoOrder);\r\n result.chainPlans.set(mod, chains);\r\n }\r\n }\r\n\r\n // Step 5: Codegen — emit Playwright test files from chain plans\r\n if (activeSteps.includes('codegen')) {\r\n const testGen = createTestCodeGenerator();\r\n const outDir = config.outDir || './opencroc-output';\r\n\r\n for (const [_mod, plan] of result.chainPlans) {\r\n const files = testGen.generate(plan.chains);\r\n for (const file of files) {\r\n file.filePath = path.join(outDir, file.filePath);\r\n }\r\n result.generatedFiles.push(...files);\r\n }\r\n }\r\n\r\n // Step 6: Validate — run validation on generated configs\r\n if (activeSteps.includes('validate')) {\r\n const configErrors = validateConfig(config as unknown as Record<string, unknown>);\r\n result.validationErrors.push(...configErrors);\r\n }\r\n\r\n result.duration = Date.now() - startTime;\r\n return result;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Generate a basic chain plan from endpoints and topological order.\r\n */\r\nfunction generateChainPlan(\r\n moduleName: string,\r\n endpoints: import('../types.js').ApiEndpoint[],\r\n _topoOrder: string[],\r\n): ChainPlanResult {\r\n // Group endpoints by resource (first non-param path segment)\r\n const groups = new Map<string, import('../types.js').ApiEndpoint[]>();\r\n\r\n for (const ep of endpoints) {\r\n const segments = ep.path.split('/').filter((s) => s && !s.startsWith(':'));\r\n const resource = segments[segments.length - 1] || 'default';\r\n if (!groups.has(resource)) groups.set(resource, []);\r\n groups.get(resource)!.push(ep);\r\n }\r\n\r\n const chains: import('../types.js').TestChain[] = [];\r\n let totalSteps = 0;\r\n\r\n for (const [resource, eps] of groups) {\r\n const steps: import('../types.js').TestStep[] = eps.map((ep, i) => ({\r\n order: i + 1,\r\n action: ep.method,\r\n endpoint: ep,\r\n description: ep.description || `${ep.method} ${ep.path}`,\r\n assertions: [],\r\n }));\r\n\r\n chains.push({ name: `${resource} CRUD chain`, module: moduleName, steps });\r\n totalSteps += steps.length;\r\n }\r\n\r\n return { chains, totalSteps };\r\n}\r\n","import chalk from 'chalk';\r\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\r\nimport { dirname } from 'node:path';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport type { PipelineStep, PipelineRunResult } from '../../types.js';\r\n\r\nconst VALID_STEPS: PipelineStep[] = ['scan', 'er-diagram', 'api-chain', 'plan', 'codegen', 'validate'];\r\n\r\nexport interface GenerateOptions {\r\n module?: string;\r\n all?: boolean;\r\n steps?: string;\r\n dryRun?: boolean;\r\n}\r\n\r\nfunction parseSteps(raw?: string): PipelineStep[] | undefined {\r\n if (!raw) return undefined;\r\n const names = raw.split(',').map((s) => s.trim());\r\n for (const name of names) {\r\n if (!VALID_STEPS.includes(name as PipelineStep)) {\r\n throw new Error(`Unknown pipeline step \"${name}\". Valid steps: ${VALID_STEPS.join(', ')}`);\r\n }\r\n }\r\n return names as PipelineStep[];\r\n}\r\n\r\nfunction writeGeneratedFiles(result: PipelineRunResult): number {\r\n let written = 0;\r\n for (const file of result.generatedFiles) {\r\n const dir = dirname(file.filePath);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n writeFileSync(file.filePath, file.content, 'utf-8');\r\n written++;\r\n console.log(chalk.green(` ✓ ${file.filePath}`));\r\n }\r\n return written;\r\n}\r\n\r\nfunction printSummary(result: PipelineRunResult, dryRun: boolean): void {\r\n console.log('');\r\n console.log(chalk.cyan.bold(' Summary'));\r\n console.log(` Modules discovered : ${result.modules.length}`);\r\n console.log(` ER diagrams : ${result.erDiagrams.size}`);\r\n console.log(` Chain plans : ${result.chainPlans.size}`);\r\n console.log(` Generated files : ${result.generatedFiles.length}${dryRun ? ' (dry-run, not written)' : ''}`);\r\n\r\n if (result.validationErrors.length > 0) {\r\n const errors = result.validationErrors.filter((e) => e.severity === 'error');\r\n const warnings = result.validationErrors.filter((e) => e.severity === 'warning');\r\n if (errors.length > 0) console.log(chalk.red(` Errors : ${errors.length}`));\r\n if (warnings.length > 0) console.log(chalk.yellow(` Warnings : ${warnings.length}`));\r\n\r\n for (const err of result.validationErrors) {\r\n const icon = err.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');\r\n console.log(` ${icon} [${err.module}] ${err.message}`);\r\n }\r\n }\r\n\r\n console.log(chalk.gray(` Duration : ${result.duration}ms`));\r\n console.log('');\r\n}\r\n\r\nexport async function generate(opts: GenerateOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Generate E2E Tests\\n'));\r\n\r\n // Load config\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n // Apply --module filter\r\n if (opts.module) {\r\n config.modules = [opts.module];\r\n }\r\n\r\n // Parse --steps\r\n const steps = parseSteps(opts.steps);\r\n\r\n // Create and run pipeline\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run(steps);\r\n\r\n // Write files (unless dry-run)\r\n if (!opts.dryRun && result.generatedFiles.length > 0) {\r\n console.log('');\r\n console.log(chalk.cyan(' Generated files:'));\r\n writeGeneratedFiles(result);\r\n } else if (opts.dryRun && result.generatedFiles.length > 0) {\r\n console.log('');\r\n console.log(chalk.yellow(' Dry-run — files that would be generated:'));\r\n for (const file of result.generatedFiles) {\r\n console.log(chalk.gray(` ${file.filePath}`));\r\n }\r\n }\r\n\r\n printSummary(result, !!opts.dryRun);\r\n}\r\n","import chalk from 'chalk';\r\nimport { readdirSync, existsSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { execFileSync } from 'node:child_process';\r\nimport { loadConfig } from '../load-config.js';\r\nimport type { ExecutionConfig, HookConfig } from '../../types.js';\r\n\r\nexport interface TestOptions {\r\n module?: string;\r\n headed?: boolean;\r\n setupHook?: string;\r\n authHook?: string;\r\n teardownHook?: string;\r\n}\r\n\r\nfunction normalizeHook(hook: HookConfig | undefined): HookConfig | undefined {\r\n if (!hook) return undefined;\r\n if (typeof hook === 'string') return hook.trim() ? hook : undefined;\r\n return hook.command.trim() ? hook : undefined;\r\n}\r\n\r\nfunction runShellCommand(command: string): void {\r\n if (process.platform === 'win32') {\r\n execFileSync('cmd.exe', ['/d', '/s', '/c', command], {\r\n stdio: 'inherit',\r\n cwd: process.cwd(),\r\n });\r\n return;\r\n }\r\n\r\n execFileSync('sh', ['-lc', command], {\r\n stdio: 'inherit',\r\n cwd: process.cwd(),\r\n });\r\n}\r\n\r\nfunction runHook(name: string, hook: HookConfig | undefined): void {\r\n const normalized = normalizeHook(hook);\r\n if (!normalized) return;\r\n\r\n console.log(chalk.cyan(` Running ${name} hook...`));\r\n\r\n if (typeof normalized === 'string') {\r\n runShellCommand(normalized);\r\n console.log(chalk.green(` ✓ ${name} hook passed`));\r\n return;\r\n }\r\n\r\n execFileSync(normalized.command, normalized.args ?? [], {\r\n stdio: 'inherit',\r\n cwd: normalized.cwd ? resolve(normalized.cwd) : process.cwd(),\r\n });\r\n console.log(chalk.green(` ✓ ${name} hook passed`));\r\n}\r\n\r\nfunction discoverTestFiles(outDir: string, moduleFilter?: string): string[] {\r\n const absDir = resolve(outDir);\r\n if (!existsSync(absDir)) return [];\r\n\r\n const files: string[] = [];\r\n const entries = readdirSync(absDir, { withFileTypes: true, recursive: true });\r\n for (const entry of entries) {\r\n if (!entry.isFile()) continue;\r\n if (!entry.name.endsWith('.spec.ts') && !entry.name.endsWith('.test.ts')) continue;\r\n const fullPath = join(entry.parentPath || (entry as unknown as { path: string }).path || absDir, entry.name);\r\n if (moduleFilter && !fullPath.includes(moduleFilter)) continue;\r\n files.push(fullPath);\r\n }\r\n return files;\r\n}\r\n\r\nexport async function runTests(opts: TestOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Run E2E Tests\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const execution: ExecutionConfig = {\r\n ...(config.execution || {}),\r\n ...(opts.setupHook ? { setupHook: opts.setupHook } : {}),\r\n ...(opts.authHook ? { authHook: opts.authHook } : {}),\r\n ...(opts.teardownHook ? { teardownHook: opts.teardownHook } : {}),\r\n };\r\n\r\n try {\r\n runHook('setup', execution.setupHook);\r\n runHook('auth', execution.authHook);\r\n } catch {\r\n console.log(chalk.red(' ✗ setup/auth hook failed. Abort test run.\\n'));\r\n process.exitCode = 1;\r\n try {\r\n runHook('teardown', execution.teardownHook);\r\n } catch {\r\n console.log(chalk.red(' ✗ teardown hook also failed.\\n'));\r\n }\r\n return;\r\n }\r\n\r\n const outDir = config.outDir || './opencroc-output';\r\n const testFiles = discoverTestFiles(outDir, opts.module);\r\n\r\n if (testFiles.length === 0) {\r\n console.log(chalk.yellow(' No test files found. Run `opencroc generate` first.\\n'));\r\n return;\r\n }\r\n\r\n console.log(` Found ${testFiles.length} test file(s)`);\r\n for (const f of testFiles) {\r\n console.log(chalk.gray(` ${f}`));\r\n }\r\n console.log('');\r\n\r\n // Build Playwright args\r\n const args = ['test', ...testFiles];\r\n if (!opts.headed) {\r\n args.push('--reporter=list');\r\n } else {\r\n args.push('--headed');\r\n }\r\n\r\n const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n try {\r\n console.log(chalk.cyan(' Running Playwright...\\n'));\r\n execFileSync(npxCmd, ['playwright', ...args], {\r\n stdio: 'inherit',\r\n cwd: process.cwd(),\r\n });\r\n console.log(chalk.green('\\n ✓ All tests passed.\\n'));\r\n } catch {\r\n console.log(chalk.red('\\n ✗ Some tests failed.\\n'));\r\n process.exitCode = 1;\r\n } finally {\r\n try {\r\n runHook('teardown', execution.teardownHook);\r\n } catch {\r\n console.log(chalk.red(' ✗ teardown hook failed.\\n'));\r\n process.exitCode = 1;\r\n }\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { validateConfig } from '../../validators/config-validator.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport type { ValidationError } from '../../types.js';\r\n\r\nexport interface ValidateOptions {\r\n module?: string;\r\n}\r\n\r\nfunction printErrors(errors: ValidationError[]): void {\r\n for (const err of errors) {\r\n const icon = err.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');\r\n const scope = err.module === 'config' ? '' : ` [${err.module}]`;\r\n console.log(` ${icon}${scope} ${err.field}: ${err.message}`);\r\n }\r\n}\r\n\r\nexport async function validate(opts: ValidateOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Validate\\n'));\r\n\r\n // Load and validate config\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const configErrors = validateConfig(config as unknown as Record<string, unknown>);\r\n\r\n // Apply module filter\r\n if (opts.module) {\r\n config.modules = [opts.module];\r\n }\r\n\r\n // Run pipeline in scan + validate mode to discover module-level issues\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run(['scan', 'validate']);\r\n\r\n const allErrors = [...configErrors, ...result.validationErrors];\r\n const errors = allErrors.filter((e) => e.severity === 'error');\r\n const warnings = allErrors.filter((e) => e.severity === 'warning');\r\n\r\n if (allErrors.length === 0) {\r\n console.log(chalk.green(' ✓ Configuration is valid.'));\r\n console.log(chalk.gray(` Modules: ${result.modules.join(', ') || '(none)'}\\n`));\r\n return;\r\n }\r\n\r\n if (errors.length > 0) {\r\n console.log(chalk.red(` ${errors.length} error(s):`));\r\n printErrors(errors);\r\n }\r\n if (warnings.length > 0) {\r\n console.log(chalk.yellow(` ${warnings.length} warning(s):`));\r\n printErrors(warnings);\r\n }\r\n\r\n console.log('');\r\n\r\n if (errors.length > 0) {\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import type { LlmProvider, LlmConfig } from '../types.js';\r\n\r\nexport interface ChatMessage {\r\n role: 'system' | 'user' | 'assistant';\r\n content: string;\r\n}\r\n\r\ninterface OpenAIResponse {\r\n choices: Array<{ message: { content: string } }>;\r\n usage?: { total_tokens: number };\r\n}\r\n\r\nconst DEFAULT_MODELS: Record<string, string> = {\r\n openai: 'gpt-4o-mini',\r\n zhipu: 'glm-4',\r\n};\r\n\r\nconst DEFAULT_BASE_URLS: Record<string, string> = {\r\n openai: 'https://api.openai.com/v1',\r\n zhipu: 'https://open.bigmodel.cn/api/paas/v4',\r\n};\r\n\r\n/**\r\n * Create an OpenAI-compatible LLM provider.\r\n * Works with OpenAI, Zhipu (GLM), and any OpenAI-compatible API.\r\n */\r\nexport function createOpenAIProvider(config: LlmConfig): LlmProvider {\r\n const provider = config.provider === 'zhipu' ? 'zhipu' : 'openai';\r\n const baseUrl = config.baseUrl || DEFAULT_BASE_URLS[provider];\r\n const model = config.model || DEFAULT_MODELS[provider];\r\n const maxTokens = config.maxTokens || 2048;\r\n const temperature = config.temperature ?? 0.3;\r\n\r\n if (!config.apiKey) {\r\n throw new Error(\r\n `API key is required for ${provider}. Set it in config or via OPENCROC_LLM_API_KEY env variable.`,\r\n );\r\n }\r\n\r\n return {\r\n name: provider,\r\n\r\n async chat(messages: Array<{ role: string; content: string }>): Promise<string> {\r\n const url = `${baseUrl}/chat/completions`;\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${config.apiKey}`,\r\n },\r\n body: JSON.stringify({\r\n model,\r\n messages,\r\n max_tokens: maxTokens,\r\n temperature,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'unknown error');\r\n throw new Error(`LLM API error (${response.status}): ${errorText}`);\r\n }\r\n\r\n const data = (await response.json()) as OpenAIResponse;\r\n const content = data.choices?.[0]?.message?.content;\r\n if (!content) {\r\n throw new Error('LLM returned empty response');\r\n }\r\n return content;\r\n },\r\n\r\n estimateTokens(text: string): number {\r\n // Rough estimate: ~4 chars per token for English, ~2 for CJK\r\n const cjkChars = (text.match(/[\\u4e00-\\u9fff\\u3000-\\u303f]/g) || []).length;\r\n const otherChars = text.length - cjkChars;\r\n return Math.ceil(otherChars / 4 + cjkChars / 2);\r\n },\r\n };\r\n}\r\n","import type { LlmProvider, LlmConfig } from '../types.js';\r\n\r\ninterface OllamaResponse {\r\n message: { content: string };\r\n}\r\n\r\n/**\r\n * Create an Ollama LLM provider for local model inference.\r\n */\r\nexport function createOllamaProvider(config: LlmConfig): LlmProvider {\r\n const baseUrl = config.baseUrl || 'http://localhost:11434';\r\n const model = config.model || 'llama3';\r\n\r\n return {\r\n name: 'ollama',\r\n\r\n async chat(messages: Array<{ role: string; content: string }>): Promise<string> {\r\n const url = `${baseUrl}/api/chat`;\r\n\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n model,\r\n messages,\r\n stream: false,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'unknown error');\r\n throw new Error(`Ollama API error (${response.status}): ${errorText}`);\r\n }\r\n\r\n const data = (await response.json()) as OllamaResponse;\r\n const content = data.message?.content;\r\n if (!content) {\r\n throw new Error('Ollama returned empty response');\r\n }\r\n return content;\r\n },\r\n\r\n estimateTokens(text: string): number {\r\n // Same rough estimate as OpenAI provider\r\n const cjkChars = (text.match(/[\\u4e00-\\u9fff\\u3000-\\u303f]/g) || []).length;\r\n const otherChars = text.length - cjkChars;\r\n return Math.ceil(otherChars / 4 + cjkChars / 2);\r\n },\r\n };\r\n}\r\n","import type { LlmProvider, LlmConfig } from '../types.js';\r\nimport { createOpenAIProvider } from './openai.js';\r\nimport { createOllamaProvider } from './ollama.js';\r\n\r\nexport { createOpenAIProvider } from './openai.js';\r\nexport { createOllamaProvider } from './ollama.js';\r\n\r\n/**\r\n * Create an LLM provider from config.\r\n * Resolves apiKey from config or OPENCROC_LLM_API_KEY env variable.\r\n */\r\nexport function createLlmProvider(config: LlmConfig): LlmProvider {\r\n const resolved: LlmConfig = {\r\n ...config,\r\n apiKey: config.apiKey || process.env.OPENCROC_LLM_API_KEY,\r\n };\r\n\r\n switch (config.provider) {\r\n case 'openai':\r\n case 'zhipu':\r\n return createOpenAIProvider(resolved);\r\n case 'ollama':\r\n return createOllamaProvider(resolved);\r\n default:\r\n throw new Error(\r\n `Unknown LLM provider: \"${config.provider}\". Available: openai, zhipu, ollama`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Token usage tracker — accumulates tokens across multiple LLM calls.\r\n */\r\nexport interface TokenTracker {\r\n track(text: string): void;\r\n trackChat(messages: Array<{ role: string; content: string }>, response: string): void;\r\n total: number;\r\n reset(): void;\r\n}\r\n\r\nexport function createTokenTracker(provider: LlmProvider): TokenTracker {\r\n let total = 0;\r\n\r\n return {\r\n track(text: string) {\r\n total += provider.estimateTokens(text);\r\n },\r\n\r\n trackChat(messages: Array<{ role: string; content: string }>, response: string) {\r\n for (const msg of messages) {\r\n total += provider.estimateTokens(msg.content);\r\n }\r\n total += provider.estimateTokens(response);\r\n },\r\n\r\n get total() {\r\n return total;\r\n },\r\n\r\n reset() {\r\n total = 0;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * System prompts for different LLM use cases in OpenCroc.\r\n */\r\nexport const SYSTEM_PROMPTS = {\r\n failureAnalysis: `You are an expert test failure analyst for an E2E testing framework.\r\nGiven a test failure error message and its context, analyze the root cause and suggest a fix.\r\nRespond in JSON format: { \"rootCause\": string, \"category\": string, \"suggestedFix\": string, \"confidence\": number }\r\nCategories: backend-5xx, timeout, endpoint-not-found, data-constraint, network, frontend-render, test-script, unknown.`,\r\n\r\n chainPlanning: `You are an API test chain planner.\r\nGiven a list of API endpoints and their dependencies, generate an optimal test execution order.\r\nConsider data dependencies, authentication requirements, and cleanup steps.\r\nRespond in JSON format: { \"chains\": [{ \"name\": string, \"steps\": [{ \"endpoint\": string, \"method\": string, \"description\": string }] }] }`,\r\n} as const;\r\n","/**\r\n * Dialog Loop Runner — multi-iteration self-healing loop.\r\n *\r\n * Runs tests, parses results, applies controlled fixes, and reruns\r\n * until all tests pass or the maximum iteration count is reached.\r\n * Tracks error history to avoid infinite loops on recurring failures.\r\n */\r\n\r\nimport type {\r\n DialogLoopConfig,\r\n TestFailureInfo,\r\n IterationResult,\r\n DialogLoopSummary,\r\n ControlledFixOutcome,\r\n} from '../types.js';\r\n\r\n// ===== Abstractions for testability =====\r\n\r\nexport interface TestRunner {\r\n run(): Promise<{ stdout: string; exitCode: number }>;\r\n}\r\n\r\nexport interface ResultParser {\r\n parse(stdout: string): TestFailureInfo[];\r\n countTotal(stdout: string): number;\r\n}\r\n\r\nexport interface FixApplier {\r\n apply(failure: TestFailureInfo): Promise<ControlledFixOutcome>;\r\n}\r\n\r\n// ===== Defaults =====\r\n\r\nconst DEFAULTS: Required<DialogLoopConfig> = {\r\n maxIterations: 3,\r\n pollIntervalMs: 10_000,\r\n sameErrorThreshold: 2,\r\n autoRerunOnFix: true,\r\n};\r\n\r\n// ===== JSON result parser (reads Playwright JSON output) =====\r\n\r\nexport function createJsonResultParser(): ResultParser {\r\n return {\r\n parse(stdout: string): TestFailureInfo[] {\r\n const failures: TestFailureInfo[] = [];\r\n // Match pass/fail summary from Playwright output\r\n const lines = stdout.split('\\n');\r\n for (const line of lines) {\r\n // Playwright format: \" ✘ [chromium] › test.spec.ts:10:5 › suite › title\"\r\n // or stderr lines with \"Error:\" prefix\r\n const failMatch = line.match(/[✘✗×]\\s+.*?›\\s+(.+)/);\r\n if (failMatch) {\r\n failures.push({\r\n title: failMatch[1].trim(),\r\n error: failMatch[1].trim(),\r\n });\r\n }\r\n }\r\n return failures;\r\n },\r\n countTotal(stdout: string): number {\r\n // Match \"X passed\" or \"X failed\" from Playwright summary\r\n let total = 0;\r\n const passMatch = stdout.match(/(\\d+)\\s+passed/);\r\n const failMatch = stdout.match(/(\\d+)\\s+failed/);\r\n if (passMatch) total += parseInt(passMatch[1], 10);\r\n if (failMatch) total += parseInt(failMatch[1], 10);\r\n return total || 1; // at least 1 to avoid division by zero\r\n },\r\n };\r\n}\r\n\r\n// ===== Dialog Loop =====\r\n\r\nexport interface DialogLoopOptions {\r\n runner: TestRunner;\r\n parser: ResultParser;\r\n fixer: FixApplier;\r\n config?: DialogLoopConfig;\r\n onIteration?: (result: IterationResult) => void;\r\n}\r\n\r\nexport async function runDialogLoop(options: DialogLoopOptions): Promise<DialogLoopSummary> {\r\n const cfg = { ...DEFAULTS, ...options.config };\r\n const { runner, parser, fixer } = options;\r\n\r\n const history: IterationResult[] = [];\r\n const errorTracker = new Map<string, number>();\r\n\r\n for (let iteration = 1; iteration <= cfg.maxIterations + 1; iteration++) {\r\n const iterStart = Date.now();\r\n\r\n // Step 1: Run tests\r\n const { stdout } = await runner.run();\r\n\r\n // Step 2: Parse results\r\n const failures = parser.parse(stdout);\r\n const totalTests = parser.countTotal(stdout);\r\n const passed = totalTests - failures.length;\r\n\r\n const iterResult: IterationResult = {\r\n iteration,\r\n totalTests,\r\n passed,\r\n failed: failures.length,\r\n failedTests: failures.map(f => f.title),\r\n fixesApplied: [],\r\n durationMs: 0,\r\n };\r\n\r\n // Step 3: All passed → success\r\n if (failures.length === 0) {\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n break;\r\n }\r\n\r\n // Step 4: Max iterations exceeded\r\n if (iteration > cfg.maxIterations) {\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n break;\r\n }\r\n\r\n // Step 5: Filter out repeated errors beyond threshold\r\n const newFailures = failures.filter(f => {\r\n const key = `${f.title}::${f.error}`;\r\n const count = (errorTracker.get(key) ?? 0) + 1;\r\n errorTracker.set(key, count);\r\n return count <= cfg.sameErrorThreshold;\r\n });\r\n\r\n if (newFailures.length === 0) {\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n break;\r\n }\r\n\r\n // Step 6: Apply controlled fixes\r\n for (const failure of newFailures) {\r\n const outcome = await fixer.apply(failure);\r\n if (outcome.success) {\r\n iterResult.fixesApplied.push(failure.title);\r\n }\r\n }\r\n\r\n iterResult.durationMs = Date.now() - iterStart;\r\n history.push(iterResult);\r\n options.onIteration?.(iterResult);\r\n\r\n // Step 7: If any fixes applied and autoRerun, continue loop\r\n if (iterResult.fixesApplied.length === 0 || !cfg.autoRerunOnFix) {\r\n break;\r\n }\r\n }\r\n\r\n const final = history[history.length - 1];\r\n const totalFixesApplied = history.reduce((sum, h) => sum + h.fixesApplied.length, 0);\r\n\r\n return {\r\n iterations: history,\r\n finalPassed: final?.passed ?? 0,\r\n finalFailed: final?.failed ?? 0,\r\n totalFixesApplied,\r\n success: (final?.failed ?? 1) === 0,\r\n };\r\n}\r\n","/**\r\n * Controlled Fixer — two-phase fix engine with safety guarantees.\r\n *\r\n * Phase A (config-only): backup → validate → fix → dry-run → write → verify → cleanup.\r\n * Phase B (config-and-source): generates a draft PR with the AI-suggested code patch.\r\n *\r\n * All mutations are reversible — failures trigger automatic rollback.\r\n */\r\n\r\nimport { existsSync, copyFileSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';\r\nimport { dirname } from 'node:path';\r\nimport type {\r\n FixScope,\r\n ControlledFixOptions,\r\n ControlledFixOutcome,\r\n AIAttributionResult,\r\n} from '../types.js';\r\n\r\n// ===== Abstractions for testability =====\r\n\r\nexport interface ConfigValidator {\r\n validate(configContent: string): { passed: boolean; errors: string[] };\r\n}\r\n\r\nexport interface ConfigFixer {\r\n fix(configContent: string, errors: string[]): { success: boolean; fixedContent: string; fixedItems: string[]; remainingErrors: string[] };\r\n}\r\n\r\nexport interface PRGenerator {\r\n generate(attribution: AIAttributionResult): Promise<string>;\r\n}\r\n\r\n// ===== FS abstraction (injectable for tests) =====\r\n\r\nexport interface FsOps {\r\n exists(path: string): boolean;\r\n read(path: string): string;\r\n write(path: string, content: string): void;\r\n copy(src: string, dest: string): void;\r\n remove(path: string): void;\r\n mkdirp(dir: string): void;\r\n}\r\n\r\nconst defaultFs: FsOps = {\r\n exists: existsSync,\r\n read: (p) => readFileSync(p, 'utf-8'),\r\n write: (p, c) => { mkdirSync(dirname(p), { recursive: true }); writeFileSync(p, c, 'utf-8'); },\r\n copy: copyFileSync,\r\n remove: unlinkSync,\r\n mkdirp: (d) => mkdirSync(d, { recursive: true }),\r\n};\r\n\r\n// ===== Core =====\r\n\r\nexport interface ControlledFixerOptions {\r\n configPath: string;\r\n validator: ConfigValidator;\r\n fixer: ConfigFixer;\r\n prGenerator?: PRGenerator;\r\n attribution?: AIAttributionResult;\r\n fs?: FsOps;\r\n options?: ControlledFixOptions;\r\n}\r\n\r\nexport async function applyControlledFix(opts: ControlledFixerOptions): Promise<ControlledFixOutcome> {\r\n const fs = opts.fs ?? defaultFs;\r\n const scope: FixScope = opts.options?.scope ?? 'config-only';\r\n const dryRun = opts.options?.dryRun ?? true;\r\n const verify = opts.options?.verify ?? true;\r\n const configPath = opts.configPath;\r\n const backupPath = configPath + '.backup';\r\n\r\n // --- Phase A: Config-only fix ---\r\n\r\n // Load config\r\n if (!fs.exists(configPath)) {\r\n return { success: false, scope, fixedItems: [], rolledBack: false, error: `Config file not found: ${configPath}` };\r\n }\r\n\r\n const originalContent = fs.read(configPath);\r\n\r\n // Backup before mutation\r\n fs.write(backupPath, originalContent);\r\n\r\n // Validate current config\r\n const validation = opts.validator.validate(originalContent);\r\n\r\n if (validation.passed) {\r\n // No errors to fix\r\n cleanup(fs, backupPath);\r\n return { success: true, scope, fixedItems: [], rolledBack: false };\r\n }\r\n\r\n // Attempt fix\r\n let fixResult: ReturnType<ConfigFixer['fix']>;\r\n try {\r\n fixResult = opts.fixer.fix(originalContent, validation.errors);\r\n } catch (err) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: [], rolledBack: true, error: `Fix threw: ${err instanceof Error ? err.message : String(err)}` };\r\n }\r\n\r\n if (!fixResult.success) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: fixResult.fixedItems, rolledBack: true, error: `Remaining errors: ${fixResult.remainingErrors.join('; ')}` };\r\n }\r\n\r\n // Dry-run: validate fixed content before writing\r\n if (dryRun) {\r\n const dryValidation = opts.validator.validate(fixResult.fixedContent);\r\n if (!dryValidation.passed) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: fixResult.fixedItems, rolledBack: true, error: `Dry-run validation failed: ${dryValidation.errors.join('; ')}` };\r\n }\r\n }\r\n\r\n // Write fixed content\r\n fs.write(configPath, fixResult.fixedContent);\r\n\r\n // Verify after write\r\n if (verify) {\r\n const reloaded = fs.read(configPath);\r\n const postValidation = opts.validator.validate(reloaded);\r\n if (!postValidation.passed) {\r\n rollback(fs, backupPath, configPath);\r\n return { success: false, scope, fixedItems: fixResult.fixedItems, rolledBack: true, error: `Post-write verification failed: ${postValidation.errors.join('; ')}` };\r\n }\r\n }\r\n\r\n // Phase A success — clean up backup\r\n cleanup(fs, backupPath);\r\n\r\n // --- Phase B: Config-and-source (optional) ---\r\n let prUrl: string | undefined;\r\n if (scope === 'config-and-source' && opts.attribution && opts.prGenerator) {\r\n try {\r\n prUrl = await opts.prGenerator.generate(opts.attribution);\r\n } catch {\r\n // PR generation failure is non-fatal; config fix already succeeded\r\n }\r\n }\r\n\r\n return { success: true, scope, fixedItems: fixResult.fixedItems, rolledBack: false, prUrl };\r\n}\r\n\r\nfunction rollback(fs: FsOps, backupPath: string, configPath: string): void {\r\n if (fs.exists(backupPath)) {\r\n const backup = fs.read(backupPath);\r\n fs.write(configPath, backup);\r\n fs.remove(backupPath);\r\n }\r\n}\r\n\r\nfunction cleanup(fs: FsOps, backupPath: string): void {\r\n if (fs.exists(backupPath)) {\r\n fs.remove(backupPath);\r\n }\r\n}\r\n","/**\r\n * Auto-Fix PR Generator — creates draft PRs from AI attribution results.\r\n *\r\n * Flow: create branch → write patch → git apply → commit → push → gh pr create --draft.\r\n * All PRs are draft-only as a safety invariant.\r\n */\r\n\r\nimport type { AIAttributionResult, AutoFixPROptions, AutoFixPRResult } from '../types.js';\r\n\r\n// ===== Executor abstraction (injectable for tests) =====\r\n\r\nexport interface GitExecutor {\r\n exec(command: string, args: string[]): Promise<{ stdout: string; exitCode: number }>;\r\n}\r\n\r\nexport interface PatchWriter {\r\n write(path: string, content: string): Promise<void>;\r\n mkdir(dir: string): Promise<void>;\r\n}\r\n\r\n// ===== Defaults =====\r\n\r\nconst DEFAULT_OPTIONS: Required<AutoFixPROptions> = {\r\n branchPrefix: 'autofix/',\r\n baseBranch: 'main',\r\n draftOnly: true,\r\n};\r\n\r\n// ===== Core =====\r\n\r\nexport async function generateFixPR(\r\n attribution: AIAttributionResult,\r\n git: GitExecutor,\r\n patchWriter: PatchWriter,\r\n options?: AutoFixPROptions,\r\n): Promise<AutoFixPRResult> {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\r\n const branch = `${opts.branchPrefix}${ts}`;\r\n const patchFile = `report/patch-${ts}.patch`;\r\n\r\n // Create branch\r\n await git.exec('git', ['checkout', '-b', branch]);\r\n\r\n // Write patch\r\n await patchWriter.mkdir('report');\r\n await patchWriter.write(patchFile, attribution.fixSuggestion.codePatch);\r\n\r\n // Apply patch (non-fatal if fails)\r\n try {\r\n await git.exec('git', ['apply', patchFile]);\r\n } catch {\r\n // Patch may not apply cleanly — continue anyway\r\n }\r\n\r\n // Stage, commit, push\r\n await git.exec('git', ['add', '.']);\r\n await git.exec('git', ['commit', '-m', `fix: AI auto-patch for \"${attribution.testName}\"`]);\r\n await git.exec('git', ['push', 'origin', branch]);\r\n\r\n // Create draft PR (always draft for safety)\r\n const prArgs = [\r\n 'pr', 'create', '--draft',\r\n '--title', `[AI Fix] ${attribution.testName}`,\r\n '--body', buildPRBody(attribution),\r\n ];\r\n const { stdout: prUrl } = await git.exec('gh', prArgs);\r\n\r\n // Return to base branch\r\n await git.exec('git', ['checkout', opts.baseBranch]);\r\n\r\n return { prUrl: prUrl.trim(), branch, patchFile };\r\n}\r\n\r\nfunction buildPRBody(a: AIAttributionResult): string {\r\n return [\r\n '## AI Auto-Fix PR',\r\n '',\r\n `**Test:** ${a.testName}`,\r\n `**Category:** ${a.category} | **Severity:** ${a.severity} | **Confidence:** ${(a.confidence * 100).toFixed(0)}%`,\r\n '',\r\n '### Root Cause',\r\n a.rootCause,\r\n '',\r\n '### Fix',\r\n a.fixSuggestion.description,\r\n '',\r\n '---',\r\n '> This PR was auto-generated by AI and **must be reviewed before merging**.',\r\n ].join('\\n');\r\n}\r\n","import type { SelfHealingConfig, SelfHealingResult, FixOutcome, LlmProvider } from '../types.js';\r\nimport { SYSTEM_PROMPTS } from '../llm/index.js';\r\n\r\nexport type { SelfHealingResult, FixOutcome };\r\n\r\n// Advanced Self-Healing (v1.1)\r\nexport { runDialogLoop, createJsonResultParser } from './dialog-loop-runner.js';\r\nexport type { TestRunner, ResultParser, FixApplier, DialogLoopOptions } from './dialog-loop-runner.js';\r\nexport { applyControlledFix } from './controlled-fixer.js';\r\nexport type { ConfigValidator, ConfigFixer, PRGenerator, FsOps, ControlledFixerOptions } from './controlled-fixer.js';\r\nexport { generateFixPR } from './auto-fix-generator.js';\r\nexport type { GitExecutor, PatchWriter } from './auto-fix-generator.js';\r\n\r\nexport interface SelfHealingLoop {\r\n run(testResultsDir: string): Promise<SelfHealingResult>;\r\n}\r\n\r\nexport interface SelfHealingOptions {\r\n config: SelfHealingConfig;\r\n llm?: LlmProvider;\r\n}\r\n\r\n/**\r\n * Categorize a test failure by heuristic rules.\r\n */\r\nexport function categorizeFailure(errorMessage: string): {\r\n category: string;\r\n confidence: number;\r\n} {\r\n const msg = errorMessage.toLowerCase();\r\n\r\n if (/5\\d{2}|internal server error/.test(msg))\r\n return { category: 'backend-5xx', confidence: 0.9 };\r\n if (/timeout|timed?\\s*out/.test(msg))\r\n return { category: 'timeout', confidence: 0.8 };\r\n if (/404|not found/.test(msg))\r\n return { category: 'endpoint-not-found', confidence: 0.85 };\r\n if (/4[0-2]\\d|validation|constraint/.test(msg))\r\n return { category: 'data-constraint', confidence: 0.75 };\r\n if (/econnrefused|enotfound|network/.test(msg))\r\n return { category: 'network', confidence: 0.9 };\r\n if (/selector|locator|element/.test(msg))\r\n return { category: 'frontend-render', confidence: 0.7 };\r\n if (/storage\\s*state|auth|login/.test(msg))\r\n return { category: 'test-script', confidence: 0.8 };\r\n\r\n return { category: 'unknown', confidence: 0.5 };\r\n}\r\n\r\n/**\r\n * LLM-enhanced failure analysis with heuristic fallback.\r\n */\r\nexport async function analyzeFailureWithLLM(\r\n errorMessage: string,\r\n llm?: LlmProvider,\r\n): Promise<{ rootCause: string; category: string; suggestedFix: string; confidence: number }> {\r\n // Always get heuristic result as fallback\r\n const heuristic = categorizeFailure(errorMessage);\r\n\r\n if (!llm) {\r\n return {\r\n rootCause: errorMessage,\r\n category: heuristic.category,\r\n suggestedFix: '',\r\n confidence: heuristic.confidence,\r\n };\r\n }\r\n\r\n try {\r\n const response = await llm.chat([\r\n { role: 'system', content: SYSTEM_PROMPTS.failureAnalysis },\r\n { role: 'user', content: `Analyze this test failure:\\n\\n${errorMessage}` },\r\n ]);\r\n\r\n const parsed = JSON.parse(response) as {\r\n rootCause?: string;\r\n category?: string;\r\n suggestedFix?: string;\r\n confidence?: number;\r\n };\r\n\r\n return {\r\n rootCause: parsed.rootCause || errorMessage,\r\n category: parsed.category || heuristic.category,\r\n suggestedFix: parsed.suggestedFix || '',\r\n confidence: parsed.confidence || heuristic.confidence,\r\n };\r\n } catch {\r\n // LLM failed — fall back to heuristic\r\n return {\r\n rootCause: errorMessage,\r\n category: heuristic.category,\r\n suggestedFix: '',\r\n confidence: heuristic.confidence,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Attempt a config-only fix: validate and write corrected config JSON.\r\n */\r\nasync function attemptConfigFix(\r\n _testResultsDir: string,\r\n _mode: SelfHealingConfig['mode'],\r\n _llm?: LlmProvider,\r\n): Promise<FixOutcome> {\r\n // TODO: Load module config → run autoFix validation → write corrected JSON\r\n // For now, return a no-op outcome\r\n return {\r\n success: false,\r\n scope: 'config-only',\r\n fixedItems: [],\r\n rolledBack: false,\r\n };\r\n}\r\n\r\n/**\r\n * Create a self-healing loop. Accepts an optional LLM provider for AI-enhanced analysis.\r\n */\r\nexport function createSelfHealingLoop(config: SelfHealingConfig, llm?: LlmProvider): SelfHealingLoop {\r\n return {\r\n async run(testResultsDir: string): Promise<SelfHealingResult> {\r\n const maxIterations = config.maxIterations || 3;\r\n const mode = config.mode || 'config-only';\r\n const fixed: string[] = [];\r\n const remaining: string[] = [];\r\n let iterations = 0;\r\n let totalTokensUsed = 0;\r\n\r\n for (let i = 0; i < maxIterations; i++) {\r\n iterations = i + 1;\r\n\r\n const outcome = await attemptConfigFix(testResultsDir, mode, llm);\r\n if (outcome.success) {\r\n fixed.push(...outcome.fixedItems);\r\n } else {\r\n remaining.push(`iteration-${i + 1}: no fix applied`);\r\n }\r\n\r\n // Track token usage if LLM is available\r\n if (llm) {\r\n totalTokensUsed += llm.estimateTokens(`iteration-${i + 1}`);\r\n }\r\n\r\n // If all fixed, stop early\r\n if (outcome.success && outcome.fixedItems.length > 0) break;\r\n }\r\n\r\n return {\r\n iterations,\r\n fixed,\r\n remaining,\r\n totalTokensUsed,\r\n };\r\n },\r\n };\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createSelfHealingLoop } from '../../self-healing/index.js';\r\nimport type { SelfHealingConfig } from '../../types.js';\r\n\r\nexport interface HealOptions {\r\n module?: string;\r\n maxIterations?: string;\r\n}\r\n\r\nexport async function heal(opts: HealOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Self-Healing\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const outDir = config.outDir || './opencroc-output';\r\n const maxIterations = opts.maxIterations ? parseInt(opts.maxIterations, 10) : 3;\r\n\r\n const healingConfig: SelfHealingConfig = {\r\n enabled: true,\r\n maxIterations,\r\n mode: config.selfHealing?.mode || 'config-only',\r\n };\r\n\r\n console.log(chalk.gray(` Mode: ${healingConfig.mode}`));\r\n console.log(chalk.gray(` Max iterations: ${maxIterations}`));\r\n\r\n if (opts.module) {\r\n console.log(chalk.gray(` Module: ${opts.module}`));\r\n }\r\n console.log('');\r\n\r\n const loop = createSelfHealingLoop(healingConfig);\r\n const result = await loop.run(outDir);\r\n\r\n // Report results\r\n console.log(chalk.cyan(' Results:'));\r\n console.log(` Iterations : ${result.iterations}`);\r\n console.log(` Fixed : ${result.fixed.length > 0 ? chalk.green(result.fixed.join(', ')) : chalk.gray('(none)')}`);\r\n console.log(` Remaining : ${result.remaining.length > 0 ? chalk.yellow(result.remaining.join(', ')) : chalk.gray('(none)')}`);\r\n if (result.totalTokensUsed > 0) {\r\n console.log(` Tokens used : ${result.totalTokensUsed}`);\r\n }\r\n\r\n console.log('');\r\n\r\n if (result.remaining.length > 0) {\r\n console.log(chalk.yellow(' Some issues could not be auto-fixed. Manual review needed.\\n'));\r\n } else if (result.fixed.length > 0) {\r\n console.log(chalk.green(' ✓ All issues resolved.\\n'));\r\n } else {\r\n console.log(chalk.gray(' No issues detected.\\n'));\r\n }\r\n}\r\n","/**\r\n * CI template generators for popular CI/CD platforms.\r\n *\r\n * Usage:\r\n * npx opencroc ci --platform=github\r\n * npx opencroc ci --platform=gitlab\r\n */\r\n\r\nexport interface CiTemplateOptions {\r\n /** Node.js version(s). Default: ['20.x'] */\r\n nodeVersions?: string[];\r\n /** Install command. Default: 'npm ci' */\r\n installCommand?: string;\r\n /** Whether to run self-healing. Default: false */\r\n selfHeal?: boolean;\r\n /** Custom opencroc generate args */\r\n generateArgs?: string;\r\n /** Custom opencroc test args */\r\n testArgs?: string;\r\n}\r\n\r\nexport function generateGitHubActionsTemplate(opts: CiTemplateOptions = {}): string {\r\n const nodeVersions = opts.nodeVersions ?? ['20.x'];\r\n const install = opts.installCommand ?? 'npm ci';\r\n const genArgs = opts.generateArgs ?? '--all';\r\n const testArgs = opts.testArgs ?? '';\r\n const healStep = opts.selfHeal\r\n ? `\r\n - name: Self-heal failures\r\n if: failure()\r\n run: npx opencroc heal --max-iterations 3`\r\n : '';\r\n\r\n const matrix =\r\n nodeVersions.length > 1\r\n ? `\r\n strategy:\r\n matrix:\r\n node-version: [${nodeVersions.join(', ')}]`\r\n : '';\r\n\r\n const nodeSetup =\r\n nodeVersions.length > 1\r\n ? '${{ matrix.node-version }}'\r\n : nodeVersions[0];\r\n\r\n return `# Generated by OpenCroc — AI-native E2E testing\r\n# https://github.com/opencroc/opencroc\r\n\r\nname: OpenCroc E2E\r\n\r\non:\r\n push:\r\n branches: [main]\r\n pull_request:\r\n branches: [main]\r\n\r\njobs:\r\n e2e:\r\n runs-on: ubuntu-latest${matrix}\r\n steps:\r\n - uses: actions/checkout@v4\r\n\r\n - uses: actions/setup-node@v4\r\n with:\r\n node-version: '${nodeSetup}'\r\n\r\n - name: Install dependencies\r\n run: ${install}\r\n\r\n - name: Install Playwright browsers\r\n run: npx playwright install --with-deps chromium\r\n\r\n - name: Generate E2E tests\r\n run: npx opencroc generate ${genArgs}\r\n\r\n - name: Run E2E tests\r\n run: npx opencroc test ${testArgs}\r\n${healStep}\r\n - name: Upload test report\r\n if: always()\r\n uses: actions/upload-artifact@v4\r\n with:\r\n name: opencroc-report\r\n path: opencroc-output/\r\n retention-days: 14\r\n`;\r\n}\r\n\r\nexport function generateGitLabCITemplate(opts: CiTemplateOptions = {}): string {\r\n const install = opts.installCommand ?? 'npm ci';\r\n const genArgs = opts.generateArgs ?? '--all';\r\n const testArgs = opts.testArgs ?? '';\r\n const nodeVersion = opts.nodeVersions?.[0] ?? '20';\r\n\r\n return `# Generated by OpenCroc — AI-native E2E testing\r\n# https://github.com/opencroc/opencroc\r\n\r\nimage: node:${nodeVersion}\r\n\r\nstages:\r\n - generate\r\n - test\r\n\r\nvariables:\r\n PLAYWRIGHT_BROWSERS_PATH: \\${CI_PROJECT_DIR}/.cache/ms-playwright\r\n\r\ncache:\r\n key: \\${CI_COMMIT_REF_SLUG}\r\n paths:\r\n - node_modules/\r\n - .cache/ms-playwright/\r\n\r\ngenerate:\r\n stage: generate\r\n script:\r\n - ${install}\r\n - npx opencroc generate ${genArgs}\r\n artifacts:\r\n paths:\r\n - opencroc-output/\r\n expire_in: 1 day\r\n\r\ne2e:\r\n stage: test\r\n needs: [generate]\r\n before_script:\r\n - ${install}\r\n - npx playwright install --with-deps chromium\r\n script:\r\n - npx opencroc test ${testArgs}\r\n artifacts:\r\n when: always\r\n paths:\r\n - opencroc-output/\r\n expire_in: 14 days\r\n`;\r\n}\r\n\r\nconst TEMPLATES: Record<string, (opts: CiTemplateOptions) => string> = {\r\n github: generateGitHubActionsTemplate,\r\n gitlab: generateGitLabCITemplate,\r\n};\r\n\r\n/**\r\n * Get available CI platform names.\r\n */\r\nexport function listCiPlatforms(): string[] {\r\n return Object.keys(TEMPLATES);\r\n}\r\n\r\n/**\r\n * Generate a CI template for the given platform.\r\n */\r\nexport function generateCiTemplate(\r\n platform: string,\r\n opts: CiTemplateOptions = {},\r\n): string {\r\n const generator = TEMPLATES[platform];\r\n if (!generator) {\r\n throw new Error(\r\n `Unknown CI platform: \"${platform}\". Available: ${Object.keys(TEMPLATES).join(', ')}`,\r\n );\r\n }\r\n return generator(opts);\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\nimport { generateCiTemplate, listCiPlatforms } from '../../ci/index.js';\r\nimport type { CiTemplateOptions } from '../../ci/index.js';\r\n\r\nexport interface CiCommandOptions {\r\n platform?: string;\r\n selfHeal?: boolean;\r\n node?: string;\r\n}\r\n\r\nexport async function ci(opts: CiCommandOptions): Promise<void> {\r\n const platform = opts.platform ?? 'github';\r\n const available = listCiPlatforms();\r\n\r\n if (!available.includes(platform)) {\r\n console.error(chalk.red(`Unknown platform: \"${platform}\". Available: ${available.join(', ')}`));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const templateOpts: CiTemplateOptions = {\r\n selfHeal: opts.selfHeal ?? false,\r\n };\r\n if (opts.node) {\r\n templateOpts.nodeVersions = opts.node.split(',').map((s) => s.trim());\r\n }\r\n\r\n const content = generateCiTemplate(platform, templateOpts);\r\n\r\n let outputPath: string;\r\n if (platform === 'github') {\r\n outputPath = path.join('.github', 'workflows', 'opencroc.yml');\r\n } else if (platform === 'gitlab') {\r\n outputPath = '.gitlab-ci.yml';\r\n } else {\r\n outputPath = `opencroc-ci-${platform}.yml`;\r\n }\r\n\r\n const dir = path.dirname(outputPath);\r\n if (dir !== '.' && !fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(outputPath, content, 'utf-8');\r\n console.log(chalk.green(`✔ CI template written to ${outputPath}`));\r\n console.log(chalk.dim(` Platform: ${platform}`));\r\n}\r\n","/**\r\n * Checklist Reporter — generates backend fix checklists from test results.\r\n *\r\n * Groups failures by API domain, lists affected tests and endpoints,\r\n * and produces both structured data and Markdown output.\r\n */\r\n\r\nimport type {\r\n TestResultRecord,\r\n FailureCategory,\r\n FailureSummary,\r\n BackendDomainItem,\r\n LogCompletionSummary,\r\n} from '../types.js';\r\n\r\n// ===== Failure classification =====\r\n\r\nexport function classifyFailure(error?: string): FailureCategory {\r\n if (!error) return 'other';\r\n if (error.includes('[BACKEND_5XX]')) return 'backend-5xx';\r\n if (error.includes('[MIXED_5XX]')) return 'mixed-5xx';\r\n if (error.includes('[SLOW_API_FATAL]')) return 'slow-api';\r\n if (error.includes('[LOG_COMPLETION_FAIL]')) return 'log-fail';\r\n if (error.includes('[LOG_COMPLETION_TIMEOUT]')) return 'log-timeout';\r\n if (/waitForSelector|toHaveURL|Timeout/i.test(error)) return 'frontend-load';\r\n return 'other';\r\n}\r\n\r\n// ===== Failure summary =====\r\n\r\nexport function buildFailureSummary(records: TestResultRecord[]): FailureSummary {\r\n const failed = records.filter(r => r.status === 'failed');\r\n const cats = failed.map(r => classifyFailure(r.error));\r\n return {\r\n totalFailed: failed.length,\r\n backend5xx: cats.filter(c => c === 'backend-5xx').length,\r\n mixed5xx: cats.filter(c => c === 'mixed-5xx').length,\r\n slowApi: cats.filter(c => c === 'slow-api').length,\r\n logFail: cats.filter(c => c === 'log-fail').length,\r\n logTimeout: cats.filter(c => c === 'log-timeout').length,\r\n frontendLoad: cats.filter(c => c === 'frontend-load').length,\r\n other: cats.filter(c => c === 'other').length,\r\n };\r\n}\r\n\r\n// ===== Log completion aggregation =====\r\n\r\nexport function aggregateLogCompletion(records: TestResultRecord[]): LogCompletionSummary {\r\n let totalCandidates = 0;\r\n let succeeded = 0;\r\n let failed = 0;\r\n let timedOut = 0;\r\n const timedOutFreq = new Map<string, { method: string; path: string; count: number }>();\r\n\r\n for (const r of records) {\r\n const lc = r.logCompletion;\r\n if (!lc || lc.candidateCount === 0) continue;\r\n\r\n totalCandidates += lc.candidateCount;\r\n succeeded += lc.succeeded.length;\r\n failed += lc.failed.length;\r\n timedOut += lc.timedOut.length;\r\n\r\n for (const item of lc.timedOut) {\r\n const key = `${item.method}:${item.path}`;\r\n const existing = timedOutFreq.get(key);\r\n if (existing) existing.count += 1;\r\n else timedOutFreq.set(key, { method: item.method, path: item.path, count: 1 });\r\n }\r\n }\r\n\r\n const timedOutTop5 = Array.from(timedOutFreq.values())\r\n .sort((a, b) => b.count - a.count)\r\n .slice(0, 5)\r\n .map(t => ({ method: t.method, path: t.path, occurrences: t.count }));\r\n\r\n const matchRate = totalCandidates > 0 ? (succeeded + failed) / totalCandidates * 100 : 0;\r\n const effectiveRate = totalCandidates > 0 ? succeeded / totalCandidates * 100 : 0;\r\n\r\n return { totalCandidates, succeeded, failed, timedOut, matchRate, effectiveRate, timedOutTop5 };\r\n}\r\n\r\n// ===== URL extraction helpers =====\r\n\r\nfunction extractUrls(error?: string): string[] {\r\n if (!error) return [];\r\n const matched = error.match(/https?:\\/\\/[^\\s\\n]+/g);\r\n return matched ? Array.from(new Set(matched)) : [];\r\n}\r\n\r\nexport function parseApiDomain(urlStr: string): string | null {\r\n try {\r\n const u = new URL(urlStr);\r\n const segments = u.pathname.split('/').filter(Boolean);\r\n const v1Index = segments.findIndex(s => s === 'v1');\r\n if (v1Index === -1 || v1Index + 1 >= segments.length) return segments[0] || null;\r\n const afterV1 = segments.slice(v1Index + 1);\r\n if (afterV1.length === 0) return null;\r\n const first = afterV1[0];\r\n if (/^\\d+$/.test(first) && afterV1.length > 1) return afterV1[1];\r\n return first;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ===== Backend checklist =====\r\n\r\nexport function buildBackendChecklist(records: TestResultRecord[]): BackendDomainItem[] {\r\n const domainMap = new Map<string, { tests: Set<string>; endpoints: Set<string> }>();\r\n const failed = records.filter(r => r.status === 'failed');\r\n\r\n for (const item of failed) {\r\n const cat = classifyFailure(item.error);\r\n if (cat !== 'backend-5xx' && cat !== 'mixed-5xx') continue;\r\n const urls = extractUrls(item.error);\r\n for (const url of urls) {\r\n const domain = parseApiDomain(url);\r\n if (!domain) continue;\r\n const current = domainMap.get(domain) ?? { tests: new Set<string>(), endpoints: new Set<string>() };\r\n current.tests.add(item.title);\r\n current.endpoints.add(url);\r\n domainMap.set(domain, current);\r\n }\r\n }\r\n\r\n return Array.from(domainMap.entries())\r\n .map(([domain, v]) => ({\r\n domain,\r\n tests: Array.from(v.tests).sort(),\r\n endpoints: Array.from(v.endpoints).sort(),\r\n }))\r\n .sort((a, b) => b.tests.length - a.tests.length || a.domain.localeCompare(b.domain));\r\n}\r\n\r\n// ===== Markdown renderer =====\r\n\r\nexport function renderChecklistMarkdown(\r\n items: BackendDomainItem[],\r\n summary: FailureSummary,\r\n logSummary?: LogCompletionSummary,\r\n): string {\r\n const lines: string[] = [\r\n '# Backend Fix Checklist',\r\n '',\r\n ];\r\n\r\n if (logSummary) {\r\n lines.push(\r\n `- Log match rate: ${logSummary.matchRate.toFixed(2)}% (candidates=${logSummary.totalCandidates}, succeeded=${logSummary.succeeded}, failed=${logSummary.failed}, timedOut=${logSummary.timedOut})`,\r\n `- Effective success rate: ${logSummary.effectiveRate.toFixed(2)}%`,\r\n );\r\n }\r\n lines.push(\r\n `- Backend 5xx: ${summary.backend5xx}`,\r\n `- Mixed 5xx: ${summary.mixed5xx}`,\r\n `- Slow API: ${summary.slowApi}`,\r\n `- Log fail: ${summary.logFail}`,\r\n `- Log timeout: ${summary.logTimeout}`,\r\n `- Frontend load: ${summary.frontendLoad}`,\r\n `- Other: ${summary.other}`,\r\n '',\r\n );\r\n\r\n if (logSummary && logSummary.timedOutTop5.length > 0) {\r\n lines.push('### Timed-out APIs Top 5', '', '| # | Method | Path | Occurrences |', '|---|--------|------|-------------|');\r\n logSummary.timedOutTop5.forEach((t, i) => {\r\n lines.push(`| ${i + 1} | ${t.method} | ${t.path} | ${t.occurrences} |`);\r\n });\r\n lines.push('');\r\n }\r\n\r\n if (items.length === 0) {\r\n lines.push('No backend 5xx failures this run.');\r\n return lines.join('\\n') + '\\n';\r\n }\r\n\r\n for (const item of items) {\r\n lines.push(\r\n `## ${item.domain}`,\r\n '',\r\n `- Failed tests: ${item.tests.length}`,\r\n '- Affected tests:',\r\n );\r\n for (const t of item.tests) lines.push(` - ${t}`);\r\n lines.push('- Failed endpoints:');\r\n for (const e of item.endpoints) lines.push(` - ${e}`);\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n}\r\n","/**\r\n * Workorder Reporter — auto-generates prioritized backend work orders.\r\n *\r\n * Priority rules:\r\n * - P0: ≥3 affected tests OR log completion rate < 90%\r\n * - P1: 2 affected tests\r\n * - P2: 1 affected test\r\n *\r\n * Each workorder includes objective, affected scope, and acceptance criteria.\r\n */\r\n\r\nimport type {\r\n BackendDomainItem,\r\n FailureSummary,\r\n LogCompletionSummary,\r\n WorkorderItem,\r\n} from '../types.js';\r\n\r\n// ===== Priority assignment =====\r\n\r\nfunction assignPriority(item: BackendDomainItem, isLogRate: boolean): 'P0' | 'P1' | 'P2' {\r\n if (isLogRate) return 'P0';\r\n if (item.tests.length >= 3) return 'P0';\r\n if (item.tests.length === 2) return 'P1';\r\n return 'P2';\r\n}\r\n\r\n// ===== Workorder builder =====\r\n\r\nexport interface BuildWorkordersOptions {\r\n checklist: BackendDomainItem[];\r\n summary: FailureSummary;\r\n logSummary?: LogCompletionSummary;\r\n logRateThreshold?: number;\r\n}\r\n\r\nexport function buildWorkorders(opts: BuildWorkordersOptions): WorkorderItem[] {\r\n const { checklist, logSummary, logRateThreshold = 90 } = opts;\r\n const result: WorkorderItem[] = [];\r\n let idx = 1;\r\n\r\n // Auto P0 workorder if log completion rate is below threshold\r\n if (logSummary && logSummary.totalCandidates > 0 && logSummary.matchRate < logRateThreshold) {\r\n const logItem: BackendDomainItem = {\r\n domain: 'Log Completion Standards',\r\n tests: logSummary.timedOutTop5.map(t => `${t.method} ${t.path} (×${t.occurrences})`),\r\n endpoints: logSummary.timedOutTop5.map(t => t.path),\r\n };\r\n result.push({\r\n index: idx++,\r\n domain: logItem.domain,\r\n priority: 'P0',\r\n tests: logItem.tests,\r\n endpoints: logItem.endpoints,\r\n objective: 'Add missing end-phase structured logs for timed-out APIs',\r\n acceptanceCriteria: [\r\n `Log completion match rate ≥ ${logRateThreshold}%`,\r\n 'TimedOut API count drops to 0 or only SSE/long-polling endpoints remain',\r\n ],\r\n });\r\n }\r\n\r\n // Standard workorders from checklist\r\n for (const item of checklist) {\r\n const priority = assignPriority(item, false);\r\n result.push({\r\n index: idx++,\r\n domain: item.domain,\r\n priority,\r\n tests: item.tests,\r\n endpoints: item.endpoints,\r\n objective: 'Fix 500 errors and return a valid business response',\r\n acceptanceCriteria: [\r\n 'HTTP status returns 2xx for affected endpoints',\r\n 'No [BACKEND_5XX] errors in corresponding page traversal',\r\n 'All covered tests pass',\r\n ],\r\n });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ===== Markdown renderer =====\r\n\r\nexport function renderWorkordersMarkdown(\r\n workorders: WorkorderItem[],\r\n summary: FailureSummary,\r\n logSummary?: LogCompletionSummary,\r\n): string {\r\n const lines: string[] = [\r\n '# Backend Work Orders',\r\n '',\r\n ];\r\n\r\n if (logSummary) {\r\n lines.push(\r\n `- Log match rate: ${logSummary.matchRate.toFixed(2)}%`,\r\n `- Effective success rate: ${logSummary.effectiveRate.toFixed(2)}%`,\r\n );\r\n }\r\n lines.push(\r\n `- Total failed: ${summary.totalFailed}`,\r\n `- Backend 5xx: ${summary.backend5xx}`,\r\n `- Mixed 5xx: ${summary.mixed5xx}`,\r\n `- Slow API: ${summary.slowApi}`,\r\n `- Log fail: ${summary.logFail}`,\r\n `- Log timeout: ${summary.logTimeout}`,\r\n `- Frontend load: ${summary.frontendLoad}`,\r\n `- Other: ${summary.other}`,\r\n '',\r\n );\r\n\r\n if (workorders.length === 0) {\r\n lines.push('No backend work orders this run.');\r\n return lines.join('\\n') + '\\n';\r\n }\r\n\r\n for (const wo of workorders) {\r\n lines.push(\r\n `## Workorder ${wo.index} - ${wo.domain}`,\r\n '',\r\n `- Priority: ${wo.priority}`,\r\n `- Affected tests: ${wo.tests.length}`,\r\n '- Scope:',\r\n );\r\n for (const t of wo.tests) lines.push(` - ${t}`);\r\n if (wo.endpoints.length > 0) {\r\n lines.push('- Endpoints:');\r\n for (const e of wo.endpoints) lines.push(` - ${e}`);\r\n }\r\n lines.push(`- Objective: ${wo.objective}`);\r\n lines.push('- Acceptance criteria:');\r\n for (const c of wo.acceptanceCriteria) lines.push(` - ${c}`);\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n}\r\n","/**\r\n * Token Usage Reporter — tracks and reports LLM token consumption.\r\n *\r\n * Aggregates usage entries by category and model, computes costs,\r\n * tracks budget utilization, and renders both structured data and Markdown.\r\n */\r\n\r\nimport type { TokenUsageEntry, TokenUsageSummary } from '../types.js';\r\n\r\n// ===== Token Tracker =====\r\n\r\nexport class TokenTracker {\r\n private entries: TokenUsageEntry[] = [];\r\n private budget: number | null = null;\r\n\r\n setBudget(maxTokens: number): void {\r\n this.budget = maxTokens;\r\n }\r\n\r\n record(entry: TokenUsageEntry): void {\r\n this.entries.push(entry);\r\n }\r\n\r\n reset(): void {\r\n this.entries = [];\r\n }\r\n\r\n getSummary(): TokenUsageSummary {\r\n const totalRequests = this.entries.length;\r\n let totalPromptTokens = 0;\r\n let totalCompletionTokens = 0;\r\n let totalEstimatedCost = 0;\r\n let totalLatency = 0;\r\n\r\n const byCategory: TokenUsageSummary['byCategory'] = {};\r\n const byModel: TokenUsageSummary['byModel'] = {};\r\n\r\n for (const e of this.entries) {\r\n totalPromptTokens += e.promptTokens;\r\n totalCompletionTokens += e.completionTokens;\r\n totalEstimatedCost += e.estimatedCost;\r\n totalLatency += e.latencyMs;\r\n\r\n // By category\r\n if (!byCategory[e.category]) {\r\n byCategory[e.category] = { requests: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0, estimatedCost: 0 };\r\n }\r\n const cat = byCategory[e.category];\r\n cat.requests++;\r\n cat.promptTokens += e.promptTokens;\r\n cat.completionTokens += e.completionTokens;\r\n cat.totalTokens += e.promptTokens + e.completionTokens;\r\n cat.estimatedCost += e.estimatedCost;\r\n\r\n // By model\r\n if (!byModel[e.model]) {\r\n byModel[e.model] = { requests: 0, totalTokens: 0, estimatedCost: 0 };\r\n }\r\n const mod = byModel[e.model];\r\n mod.requests++;\r\n mod.totalTokens += e.promptTokens + e.completionTokens;\r\n mod.estimatedCost += e.estimatedCost;\r\n }\r\n\r\n const totalTokens = totalPromptTokens + totalCompletionTokens;\r\n const budgetUsedPercent = this.budget && this.budget > 0\r\n ? Math.round(totalTokens / this.budget * 10000) / 100\r\n : null;\r\n\r\n return {\r\n totalRequests,\r\n totalTokens,\r\n totalPromptTokens,\r\n totalCompletionTokens,\r\n totalEstimatedCost,\r\n avgLatencyMs: totalRequests > 0 ? Math.round(totalLatency / totalRequests) : 0,\r\n byCategory,\r\n byModel,\r\n budgetUsedPercent,\r\n budgetExceeded: budgetUsedPercent !== null && budgetUsedPercent > 100,\r\n };\r\n }\r\n}\r\n\r\n// ===== Markdown renderer =====\r\n\r\nexport function renderTokenReportMarkdown(summary: TokenUsageSummary): string {\r\n const lines: string[] = [\r\n '# AI Token Usage Report',\r\n '',\r\n `- Total requests: ${summary.totalRequests}`,\r\n `- Total tokens: ${summary.totalTokens.toLocaleString()}`,\r\n `- Prompt tokens: ${summary.totalPromptTokens.toLocaleString()}`,\r\n `- Completion tokens: ${summary.totalCompletionTokens.toLocaleString()}`,\r\n `- Estimated cost: ¥${summary.totalEstimatedCost.toFixed(4)}`,\r\n `- Average latency: ${summary.avgLatencyMs}ms`,\r\n ];\r\n\r\n if (summary.budgetUsedPercent !== null) {\r\n lines.push(`- Budget used: ${summary.budgetUsedPercent}%${summary.budgetExceeded ? ' **EXCEEDED**' : ''}`);\r\n }\r\n lines.push('');\r\n\r\n // By category\r\n const cats = Object.entries(summary.byCategory).sort((a, b) => b[1].totalTokens - a[1].totalTokens);\r\n if (cats.length > 0) {\r\n lines.push(\r\n '## By Category',\r\n '',\r\n '| Category | Requests | Prompt | Completion | Total | Cost |',\r\n '|----------|----------|--------|------------|-------|------|',\r\n );\r\n for (const [cat, d] of cats) {\r\n lines.push(`| ${cat} | ${d.requests} | ${d.promptTokens.toLocaleString()} | ${d.completionTokens.toLocaleString()} | ${d.totalTokens.toLocaleString()} | ¥${d.estimatedCost.toFixed(4)} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n // By model\r\n const models = Object.entries(summary.byModel);\r\n if (models.length > 0) {\r\n lines.push(\r\n '## By Model',\r\n '',\r\n '| Model | Requests | Total Tokens | Cost |',\r\n '|-------|----------|-------------|------|',\r\n );\r\n for (const [model, d] of models) {\r\n lines.push(`| ${model} | ${d.requests} | ${d.totalTokens.toLocaleString()} | ¥${d.estimatedCost.toFixed(4)} |`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n}\r\n","import type {\r\n PipelineRunResult,\r\n ERDiagramResult,\r\n ChainPlanResult,\r\n GeneratedTestFile,\r\n ValidationError,\r\n} from '../types.js';\r\n\r\n// ===== Reporter Types =====\r\n\r\nexport interface ReportOutput {\r\n format: 'html' | 'json' | 'markdown';\r\n content: string;\r\n filename: string;\r\n}\r\n\r\n// ===== JSON Reporter =====\r\n\r\nexport function generateJsonReport(result: PipelineRunResult): ReportOutput {\r\n const serializable = {\r\n modules: result.modules,\r\n erDiagrams: Object.fromEntries(\r\n Array.from(result.erDiagrams.entries()).map(([k, v]) => [\r\n k,\r\n { tables: v.tables.length, relations: v.relations.length, mermaidText: v.mermaidText },\r\n ]),\r\n ),\r\n chainPlans: Object.fromEntries(\r\n Array.from(result.chainPlans.entries()).map(([k, v]) => [\r\n k,\r\n { chains: v.chains.length, totalSteps: v.totalSteps },\r\n ]),\r\n ),\r\n generatedFiles: result.generatedFiles.map((f) => ({\r\n filePath: f.filePath,\r\n module: f.module,\r\n chain: f.chain,\r\n })),\r\n validationErrors: result.validationErrors,\r\n duration: result.duration,\r\n };\r\n\r\n return {\r\n format: 'json',\r\n content: JSON.stringify(serializable, null, 2),\r\n filename: 'opencroc-report.json',\r\n };\r\n}\r\n\r\n// ===== Markdown Reporter =====\r\n\r\nexport function generateMarkdownReport(result: PipelineRunResult): ReportOutput {\r\n const lines: string[] = [\r\n '# OpenCroc Report',\r\n '',\r\n `**Duration**: ${result.duration}ms`,\r\n `**Modules**: ${result.modules.length} (${result.modules.join(', ')})`,\r\n '',\r\n '## ER Diagrams',\r\n '',\r\n ];\r\n\r\n for (const [mod, er] of result.erDiagrams) {\r\n lines.push(`### ${mod}`);\r\n lines.push(`- Tables: ${er.tables.length}`);\r\n lines.push(`- Relations: ${er.relations.length}`);\r\n lines.push('');\r\n }\r\n\r\n lines.push('## Chain Plans', '');\r\n for (const [mod, plan] of result.chainPlans) {\r\n lines.push(`### ${mod}`);\r\n lines.push(`- Chains: ${plan.chains.length}`);\r\n lines.push(`- Total Steps: ${plan.totalSteps}`);\r\n lines.push('');\r\n }\r\n\r\n lines.push(`## Generated Files (${result.generatedFiles.length})`, '');\r\n for (const f of result.generatedFiles) {\r\n lines.push(`- \\`${f.filePath}\\` (${f.module} / ${f.chain})`);\r\n }\r\n\r\n if (result.validationErrors.length > 0) {\r\n lines.push('', '## Validation Issues', '');\r\n const errors = result.validationErrors.filter((e) => e.severity === 'error');\r\n const warnings = result.validationErrors.filter((e) => e.severity === 'warning');\r\n if (errors.length > 0) {\r\n lines.push(`### Errors (${errors.length})`, '');\r\n for (const e of errors) {\r\n lines.push(`- **[${e.module}]** ${e.field}: ${e.message}`);\r\n }\r\n }\r\n if (warnings.length > 0) {\r\n lines.push(`### Warnings (${warnings.length})`, '');\r\n for (const w of warnings) {\r\n lines.push(`- **[${w.module}]** ${w.field}: ${w.message}`);\r\n }\r\n }\r\n }\r\n\r\n lines.push('', '---', '*Generated by [OpenCroc](https://github.com/opencroc/opencroc)*');\r\n\r\n return {\r\n format: 'markdown',\r\n content: lines.join('\\n'),\r\n filename: 'opencroc-report.md',\r\n };\r\n}\r\n\r\n// ===== HTML Reporter =====\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\r\n}\r\n\r\nfunction erSummaryRows(erDiagrams: Map<string, ERDiagramResult>): string {\r\n const rows: string[] = [];\r\n for (const [mod, er] of erDiagrams) {\r\n rows.push(`<tr><td>${escapeHtml(mod)}</td><td>${er.tables.length}</td><td>${er.relations.length}</td></tr>`);\r\n }\r\n return rows.join('\\n');\r\n}\r\n\r\nfunction chainSummaryRows(chainPlans: Map<string, ChainPlanResult>): string {\r\n const rows: string[] = [];\r\n for (const [mod, plan] of chainPlans) {\r\n rows.push(`<tr><td>${escapeHtml(mod)}</td><td>${plan.chains.length}</td><td>${plan.totalSteps}</td></tr>`);\r\n }\r\n return rows.join('\\n');\r\n}\r\n\r\nfunction fileListRows(files: GeneratedTestFile[]): string {\r\n return files\r\n .map((f) => `<tr><td><code>${escapeHtml(f.filePath)}</code></td><td>${escapeHtml(f.module)}</td><td>${escapeHtml(f.chain)}</td></tr>`)\r\n .join('\\n');\r\n}\r\n\r\nfunction validationRows(errors: ValidationError[]): string {\r\n return errors\r\n .map(\r\n (e) =>\r\n `<tr class=\"${e.severity}\"><td><span class=\"badge ${e.severity}\">${e.severity}</span></td><td>${escapeHtml(e.module)}</td><td>${escapeHtml(e.field)}</td><td>${escapeHtml(e.message)}</td></tr>`,\r\n )\r\n .join('\\n');\r\n}\r\n\r\nexport function generateHtmlReport(result: PipelineRunResult): ReportOutput {\r\n const totalTables = Array.from(result.erDiagrams.values()).reduce((s, e) => s + e.tables.length, 0);\r\n const totalRelations = Array.from(result.erDiagrams.values()).reduce((s, e) => s + e.relations.length, 0);\r\n const totalChains = Array.from(result.chainPlans.values()).reduce((s, p) => s + p.chains.length, 0);\r\n const totalSteps = Array.from(result.chainPlans.values()).reduce((s, p) => s + p.totalSteps, 0);\r\n const errorCount = result.validationErrors.filter((e) => e.severity === 'error').length;\r\n const warnCount = result.validationErrors.filter((e) => e.severity === 'warning').length;\r\n\r\n const html = `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"utf-8\" />\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n<title>OpenCroc Report</title>\r\n<style>\r\n :root { --bg: #0d1117; --fg: #c9d1d9; --card: #161b22; --border: #30363d; --accent: #58a6ff; --green: #3fb950; --yellow: #d29922; --red: #f85149; }\r\n * { box-sizing: border-box; margin: 0; padding: 0; }\r\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; background: var(--bg); color: var(--fg); padding: 2rem; }\r\n h1 { color: var(--accent); margin-bottom: 0.25rem; }\r\n .subtitle { color: #8b949e; margin-bottom: 2rem; }\r\n .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }\r\n .card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; }\r\n .card .label { font-size: 0.85rem; color: #8b949e; }\r\n .card .value { font-size: 2rem; font-weight: 700; color: var(--accent); }\r\n .card .value.green { color: var(--green); }\r\n .card .value.yellow { color: var(--yellow); }\r\n .card .value.red { color: var(--red); }\r\n section { margin-bottom: 2rem; }\r\n h2 { color: var(--fg); border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; margin-bottom: 1rem; }\r\n table { width: 100%; border-collapse: collapse; background: var(--card); border-radius: 8px; overflow: hidden; }\r\n th, td { text-align: left; padding: 0.6rem 1rem; border-bottom: 1px solid var(--border); }\r\n th { background: #21262d; color: #8b949e; font-weight: 600; font-size: 0.85rem; text-transform: uppercase; }\r\n tr:last-child td { border-bottom: none; }\r\n code { background: #21262d; padding: 0.15rem 0.4rem; border-radius: 4px; font-size: 0.85rem; }\r\n .badge { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 12px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; }\r\n .badge.error { background: rgba(248,81,73,0.15); color: var(--red); }\r\n .badge.warning { background: rgba(210,153,34,0.15); color: var(--yellow); }\r\n footer { margin-top: 3rem; text-align: center; color: #484f58; font-size: 0.85rem; }\r\n footer a { color: var(--accent); text-decoration: none; }\r\n</style>\r\n</head>\r\n<body>\r\n<h1>OpenCroc Report</h1>\r\n<p class=\"subtitle\">Generated in ${result.duration}ms &middot; ${result.modules.length} module(s)</p>\r\n\r\n<div class=\"grid\">\r\n <div class=\"card\"><div class=\"label\">Modules</div><div class=\"value\">${result.modules.length}</div></div>\r\n <div class=\"card\"><div class=\"label\">Tables</div><div class=\"value\">${totalTables}</div></div>\r\n <div class=\"card\"><div class=\"label\">Relations</div><div class=\"value\">${totalRelations}</div></div>\r\n <div class=\"card\"><div class=\"label\">Chains</div><div class=\"value\">${totalChains}</div></div>\r\n <div class=\"card\"><div class=\"label\">Steps</div><div class=\"value\">${totalSteps}</div></div>\r\n <div class=\"card\"><div class=\"label\">Files</div><div class=\"value green\">${result.generatedFiles.length}</div></div>\r\n <div class=\"card\"><div class=\"label\">Errors</div><div class=\"value${errorCount > 0 ? ' red' : ''}\">${errorCount}</div></div>\r\n <div class=\"card\"><div class=\"label\">Warnings</div><div class=\"value${warnCount > 0 ? ' yellow' : ''}\">${warnCount}</div></div>\r\n</div>\r\n\r\n<section>\r\n<h2>ER Diagrams</h2>\r\n<table>\r\n<thead><tr><th>Module</th><th>Tables</th><th>Relations</th></tr></thead>\r\n<tbody>${erSummaryRows(result.erDiagrams)}</tbody>\r\n</table>\r\n</section>\r\n\r\n<section>\r\n<h2>Chain Plans</h2>\r\n<table>\r\n<thead><tr><th>Module</th><th>Chains</th><th>Steps</th></tr></thead>\r\n<tbody>${chainSummaryRows(result.chainPlans)}</tbody>\r\n</table>\r\n</section>\r\n\r\n<section>\r\n<h2>Generated Files (${result.generatedFiles.length})</h2>\r\n<table>\r\n<thead><tr><th>File</th><th>Module</th><th>Chain</th></tr></thead>\r\n<tbody>${fileListRows(result.generatedFiles)}</tbody>\r\n</table>\r\n</section>\r\n\r\n${\r\n result.validationErrors.length > 0\r\n ? `<section>\r\n<h2>Validation Issues (${result.validationErrors.length})</h2>\r\n<table>\r\n<thead><tr><th>Severity</th><th>Module</th><th>Field</th><th>Message</th></tr></thead>\r\n<tbody>${validationRows(result.validationErrors)}</tbody>\r\n</table>\r\n</section>`\r\n : ''\r\n}\r\n\r\n<footer>\r\n Generated by <a href=\"https://github.com/opencroc/opencroc\">OpenCroc</a>\r\n</footer>\r\n</body>\r\n</html>`;\r\n\r\n return {\r\n format: 'html',\r\n content: html,\r\n filename: 'opencroc-report.html',\r\n };\r\n}\r\n\r\n// ===== Report Orchestrator =====\r\n\r\nconst REPORTERS: Record<string, (result: PipelineRunResult) => ReportOutput> = {\r\n html: generateHtmlReport,\r\n json: generateJsonReport,\r\n markdown: generateMarkdownReport,\r\n};\r\n\r\n/**\r\n * Generate reports in the specified formats.\r\n */\r\nexport function generateReports(\r\n result: PipelineRunResult,\r\n formats: ('html' | 'json' | 'markdown')[] = ['html'],\r\n): ReportOutput[] {\r\n return formats.map((fmt) => {\r\n const gen = REPORTERS[fmt];\r\n if (!gen) throw new Error(`Unknown report format: \"${fmt}\". Available: ${Object.keys(REPORTERS).join(', ')}`);\r\n return gen(result);\r\n });\r\n}\r\n\r\n// ===== Advanced Reporters (v1.2) =====\r\nexport {\r\n classifyFailure,\r\n buildFailureSummary,\r\n aggregateLogCompletion,\r\n parseApiDomain,\r\n buildBackendChecklist,\r\n renderChecklistMarkdown,\r\n} from './checklist-reporter.js';\r\n\r\nexport {\r\n buildWorkorders,\r\n renderWorkordersMarkdown,\r\n} from './workorder-reporter.js';\r\nexport type { BuildWorkordersOptions } from './workorder-reporter.js';\r\n\r\nexport {\r\n TokenTracker,\r\n renderTokenReportMarkdown,\r\n} from './token-reporter.js';\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport { generateReports } from '../../reporters/index.js';\r\n\r\nexport interface ReportCommandOptions {\r\n format?: string;\r\n output?: string;\r\n}\r\n\r\nexport async function report(opts: ReportCommandOptions): Promise<void> {\r\n let loaded;\r\n try {\r\n loaded = await loadConfig();\r\n } catch {\r\n console.error(chalk.red('No opencroc config found. Run `opencroc init` first.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const { config } = loaded;\r\n\r\n console.log(chalk.cyan('Running pipeline to generate report...'));\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run();\r\n\r\n const formats = (opts.format ?? 'html').split(',').map((s) => s.trim()) as ('html' | 'json' | 'markdown')[];\r\n const reports = generateReports(result, formats);\r\n\r\n const outDir = opts.output ?? config.outDir ?? './opencroc-output';\r\n if (!fs.existsSync(outDir)) {\r\n fs.mkdirSync(outDir, { recursive: true });\r\n }\r\n\r\n for (const r of reports) {\r\n const filePath = path.join(outDir, r.filename);\r\n fs.writeFileSync(filePath, r.content, 'utf-8');\r\n console.log(chalk.green(`✔ ${r.format} report → ${filePath}`));\r\n }\r\n\r\n console.log(chalk.dim(` ${result.modules.length} modules, ${result.generatedFiles.length} files, ${result.duration}ms`));\r\n}\r\n","import type { PipelineRunResult } from '../types.js';\r\n\r\nexport interface DashboardData {\r\n generatedAt: string;\r\n durationMs: number;\r\n modules: string[];\r\n totals: {\r\n modules: number;\r\n tables: number;\r\n relations: number;\r\n chains: number;\r\n steps: number;\r\n files: number;\r\n errors: number;\r\n warnings: number;\r\n };\r\n moduleCards: Array<{\r\n module: string;\r\n tables: number;\r\n relations: number;\r\n chains: number;\r\n steps: number;\r\n }>;\r\n files: Array<{ filePath: string; module: string; chain: string }>;\r\n issues: Array<{ severity: string; module: string; field: string; message: string }>;\r\n}\r\n\r\nexport interface DashboardOutput {\r\n filename: string;\r\n content: string;\r\n}\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#39;');\r\n}\r\n\r\nfunction number(v: unknown): number {\r\n return typeof v === 'number' && Number.isFinite(v) ? v : 0;\r\n}\r\n\r\nexport function buildDashboardDataFromPipeline(result: PipelineRunResult): DashboardData {\r\n const moduleCards = result.modules.map((mod) => {\r\n const er = result.erDiagrams.get(mod);\r\n const plan = result.chainPlans.get(mod);\r\n return {\r\n module: mod,\r\n tables: er?.tables.length ?? 0,\r\n relations: er?.relations.length ?? 0,\r\n chains: plan?.chains.length ?? 0,\r\n steps: plan?.totalSteps ?? 0,\r\n };\r\n });\r\n\r\n const totals = {\r\n modules: result.modules.length,\r\n tables: moduleCards.reduce((s, m) => s + m.tables, 0),\r\n relations: moduleCards.reduce((s, m) => s + m.relations, 0),\r\n chains: moduleCards.reduce((s, m) => s + m.chains, 0),\r\n steps: moduleCards.reduce((s, m) => s + m.steps, 0),\r\n files: result.generatedFiles.length,\r\n errors: result.validationErrors.filter((e) => e.severity === 'error').length,\r\n warnings: result.validationErrors.filter((e) => e.severity === 'warning').length,\r\n };\r\n\r\n return {\r\n generatedAt: new Date().toISOString(),\r\n durationMs: result.duration,\r\n modules: [...result.modules],\r\n totals,\r\n moduleCards,\r\n files: result.generatedFiles.map((f) => ({ filePath: f.filePath, module: f.module, chain: f.chain })),\r\n issues: result.validationErrors.map((e) => ({\r\n severity: e.severity,\r\n module: e.module,\r\n field: e.field,\r\n message: e.message,\r\n })),\r\n };\r\n}\r\n\r\nexport function buildDashboardDataFromReportJson(input: unknown): DashboardData {\r\n const src = (input ?? {}) as Record<string, unknown>;\r\n const modules = Array.isArray(src.modules) ? src.modules.map((m) => String(m)) : [];\r\n const er = (src.erDiagrams ?? {}) as Record<string, { tables?: unknown; relations?: unknown }>;\r\n const plans = (src.chainPlans ?? {}) as Record<string, { chains?: unknown; totalSteps?: unknown }>;\r\n const files = Array.isArray(src.generatedFiles)\r\n ? src.generatedFiles.map((f) => {\r\n const row = f as Record<string, unknown>;\r\n return {\r\n filePath: String(row.filePath ?? ''),\r\n module: String(row.module ?? ''),\r\n chain: String(row.chain ?? ''),\r\n };\r\n })\r\n : [];\r\n\r\n const rawIssues = Array.isArray(src.validationErrors) ? src.validationErrors : [];\r\n const issues = rawIssues.map((item) => {\r\n const row = item as Record<string, unknown>;\r\n return {\r\n severity: String(row.severity ?? 'warning'),\r\n module: String(row.module ?? 'unknown'),\r\n field: String(row.field ?? 'unknown'),\r\n message: String(row.message ?? ''),\r\n };\r\n });\r\n\r\n const moduleCards = modules.map((mod) => ({\r\n module: mod,\r\n tables: number(er[mod]?.tables),\r\n relations: number(er[mod]?.relations),\r\n chains: number(plans[mod]?.chains),\r\n steps: number(plans[mod]?.totalSteps),\r\n }));\r\n\r\n return {\r\n generatedAt: new Date().toISOString(),\r\n durationMs: number(src.duration),\r\n modules,\r\n totals: {\r\n modules: modules.length,\r\n tables: moduleCards.reduce((s, m) => s + m.tables, 0),\r\n relations: moduleCards.reduce((s, m) => s + m.relations, 0),\r\n chains: moduleCards.reduce((s, m) => s + m.chains, 0),\r\n steps: moduleCards.reduce((s, m) => s + m.steps, 0),\r\n files: files.length,\r\n errors: issues.filter((i) => i.severity === 'error').length,\r\n warnings: issues.filter((i) => i.severity === 'warning').length,\r\n },\r\n moduleCards,\r\n files,\r\n issues,\r\n };\r\n}\r\n\r\nexport function generateVisualDashboardHtml(data: DashboardData): string {\r\n const moduleCardHtml = data.moduleCards\r\n .map(\r\n (m) => `<article class=\"module-card reveal\">\r\n <h3>${escapeHtml(m.module)}</h3>\r\n <div class=\"meta\">${m.tables} tables · ${m.relations} relations</div>\r\n <div class=\"bars\">\r\n <div class=\"bar\"><span>Chains</span><strong>${m.chains}</strong></div>\r\n <div class=\"bar\"><span>Steps</span><strong>${m.steps}</strong></div>\r\n </div>\r\n</article>`,\r\n )\r\n .join('\\n');\r\n\r\n const fileRows = data.files\r\n .slice(0, 20)\r\n .map(\r\n (f) => `<tr><td><code>${escapeHtml(f.filePath)}</code></td><td>${escapeHtml(f.module)}</td><td>${escapeHtml(f.chain)}</td></tr>`,\r\n )\r\n .join('\\n');\r\n\r\n const issueRows = data.issues\r\n .map(\r\n (i) => `<tr class=\"${escapeHtml(i.severity)}\"><td>${escapeHtml(i.severity)}</td><td>${escapeHtml(i.module)}</td><td>${escapeHtml(i.field)}</td><td>${escapeHtml(i.message)}</td></tr>`,\r\n )\r\n .join('\\n');\r\n\r\n return `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"utf-8\" />\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n <title>OpenCroc Visual Dashboard</title>\r\n <style>\r\n @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap');\r\n :root {\r\n --bg: #f4efe6;\r\n --ink: #18222c;\r\n --card: #fff8ef;\r\n --line: #d8c7b4;\r\n --accent: #0b7a75;\r\n --accent-2: #f26a2e;\r\n --ok: #2f7d32;\r\n --warn: #b7791f;\r\n --err: #c0392b;\r\n }\r\n * { box-sizing: border-box; }\r\n body {\r\n margin: 0;\r\n font-family: 'Space Grotesk', 'Segoe UI', sans-serif;\r\n color: var(--ink);\r\n background:\r\n radial-gradient(circle at 15% 10%, #f9e2c5 0%, transparent 32%),\r\n radial-gradient(circle at 85% 0%, #d3efe4 0%, transparent 28%),\r\n var(--bg);\r\n min-height: 100vh;\r\n }\r\n .wrap { max-width: 1160px; margin: 0 auto; padding: 24px 18px 40px; }\r\n .hero {\r\n border: 2px solid var(--line);\r\n border-radius: 18px;\r\n background: linear-gradient(130deg, #fff8ef 0%, #f8f2ea 45%, #f3ece3 100%);\r\n padding: 22px;\r\n box-shadow: 0 12px 24px rgba(24, 34, 44, 0.08);\r\n position: relative;\r\n overflow: hidden;\r\n }\r\n .hero::after {\r\n content: '';\r\n position: absolute;\r\n right: -42px;\r\n top: -38px;\r\n width: 160px;\r\n height: 160px;\r\n border-radius: 50%;\r\n background: conic-gradient(from 50deg, #f26a2e 0deg, #f7a35a 90deg, #0b7a75 220deg, #f26a2e 360deg);\r\n opacity: 0.14;\r\n }\r\n h1 { margin: 0; font-size: clamp(1.6rem, 3vw, 2.5rem); letter-spacing: -0.03em; }\r\n .subtitle { margin-top: 8px; font-size: 0.95rem; opacity: 0.82; }\r\n .kpi-grid {\r\n margin-top: 16px;\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\r\n gap: 10px;\r\n }\r\n .kpi {\r\n border: 1px solid var(--line);\r\n border-radius: 12px;\r\n padding: 10px 12px;\r\n background: #fffdf9;\r\n }\r\n .kpi .label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.7; }\r\n .kpi .value { font-size: 1.45rem; font-weight: 700; margin-top: 4px; }\r\n .kpi.error .value { color: var(--err); }\r\n .kpi.warning .value { color: var(--warn); }\r\n .kpi.files .value { color: var(--accent); }\r\n\r\n .section-title {\r\n margin: 24px 0 10px;\r\n font-size: 1rem;\r\n text-transform: uppercase;\r\n letter-spacing: 0.08em;\r\n color: #344250;\r\n }\r\n\r\n .module-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\r\n gap: 12px;\r\n }\r\n .module-card {\r\n border: 1px solid var(--line);\r\n border-radius: 14px;\r\n padding: 14px;\r\n background: var(--card);\r\n box-shadow: 0 8px 16px rgba(24, 34, 44, 0.06);\r\n }\r\n .module-card h3 { margin: 0 0 6px; font-size: 1.05rem; }\r\n .module-card .meta { font-size: 0.85rem; opacity: 0.78; }\r\n .bars { margin-top: 10px; display: grid; gap: 8px; }\r\n .bar { display: flex; justify-content: space-between; border-top: 1px dashed var(--line); padding-top: 7px; }\r\n\r\n table {\r\n width: 100%;\r\n border-collapse: collapse;\r\n border: 1px solid var(--line);\r\n border-radius: 12px;\r\n overflow: hidden;\r\n background: #fffdf9;\r\n font-family: 'IBM Plex Mono', 'Consolas', monospace;\r\n font-size: 0.82rem;\r\n }\r\n thead { background: #efe5d8; }\r\n th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eadfce; }\r\n tbody tr:last-child td { border-bottom: none; }\r\n tr.error td:first-child { color: var(--err); font-weight: 700; }\r\n tr.warning td:first-child { color: var(--warn); font-weight: 700; }\r\n\r\n code {\r\n font-family: 'IBM Plex Mono', 'Consolas', monospace;\r\n background: #f5ece0;\r\n border: 1px solid #eadfce;\r\n border-radius: 6px;\r\n padding: 1px 5px;\r\n }\r\n\r\n .reveal { opacity: 0; transform: translateY(12px); animation: rise .55s ease forwards; }\r\n .module-card.reveal:nth-child(2) { animation-delay: .06s; }\r\n .module-card.reveal:nth-child(3) { animation-delay: .12s; }\r\n .module-card.reveal:nth-child(4) { animation-delay: .18s; }\r\n .module-card.reveal:nth-child(5) { animation-delay: .24s; }\r\n @keyframes rise {\r\n to { opacity: 1; transform: translateY(0); }\r\n }\r\n\r\n @media (max-width: 700px) {\r\n .wrap { padding: 14px 12px 28px; }\r\n .hero { padding: 16px; }\r\n table { font-size: 0.76rem; }\r\n th, td { padding: 7px 8px; }\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <main class=\"wrap\">\r\n <section class=\"hero reveal\">\r\n <h1>OpenCroc Visual Dashboard</h1>\r\n <p class=\"subtitle\">Pipeline finished in ${data.durationMs}ms · Generated ${escapeHtml(data.generatedAt)}</p>\r\n <div class=\"kpi-grid\">\r\n <div class=\"kpi\"><div class=\"label\">Modules</div><div class=\"value\">${data.totals.modules}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Tables</div><div class=\"value\">${data.totals.tables}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Relations</div><div class=\"value\">${data.totals.relations}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Chains</div><div class=\"value\">${data.totals.chains}</div></div>\r\n <div class=\"kpi\"><div class=\"label\">Steps</div><div class=\"value\">${data.totals.steps}</div></div>\r\n <div class=\"kpi files\"><div class=\"label\">Files</div><div class=\"value\">${data.totals.files}</div></div>\r\n <div class=\"kpi error\"><div class=\"label\">Errors</div><div class=\"value\">${data.totals.errors}</div></div>\r\n <div class=\"kpi warning\"><div class=\"label\">Warnings</div><div class=\"value\">${data.totals.warnings}</div></div>\r\n </div>\r\n </section>\r\n\r\n <h2 class=\"section-title\">Module Health</h2>\r\n <section class=\"module-grid\">\r\n ${moduleCardHtml || '<article class=\"module-card\">No module data</article>'}\r\n </section>\r\n\r\n <h2 class=\"section-title\">Generated Files (Top 20)</h2>\r\n <section>\r\n <table>\r\n <thead><tr><th>File</th><th>Module</th><th>Chain</th></tr></thead>\r\n <tbody>${fileRows || '<tr><td colspan=\"3\">No files generated</td></tr>'}</tbody>\r\n </table>\r\n </section>\r\n\r\n <h2 class=\"section-title\">Validation Issues</h2>\r\n <section>\r\n <table>\r\n <thead><tr><th>Severity</th><th>Module</th><th>Field</th><th>Message</th></tr></thead>\r\n <tbody>${issueRows || '<tr><td colspan=\"4\">No validation issues</td></tr>'}</tbody>\r\n </table>\r\n </section>\r\n </main>\r\n</body>\r\n</html>`;\r\n}\r\n\r\nexport function generateVisualDashboard(result: PipelineRunResult): DashboardOutput {\r\n const data = buildDashboardDataFromPipeline(result);\r\n return {\r\n filename: 'opencroc-dashboard.html',\r\n content: generateVisualDashboardHtml(data),\r\n };\r\n}\r\n","import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createPipeline } from '../../pipeline/index.js';\r\nimport {\r\n buildDashboardDataFromReportJson,\r\n buildDashboardDataFromPipeline,\r\n generateVisualDashboardHtml,\r\n} from '../../dashboard/index.js';\r\n\r\nexport interface DashboardCommandOptions {\r\n output?: string;\r\n input?: string;\r\n}\r\n\r\nexport async function dashboard(opts: DashboardCommandOptions): Promise<void> {\r\n let dashboardHtml: string;\r\n\r\n if (opts.input) {\r\n const inputPath = path.resolve(opts.input);\r\n if (!fs.existsSync(inputPath)) {\r\n console.error(chalk.red(`Input report not found: ${inputPath}`));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const raw = fs.readFileSync(inputPath, 'utf-8');\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(raw);\r\n } catch {\r\n console.error(chalk.red('Invalid JSON input. Please provide opencroc-report.json output.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const data = buildDashboardDataFromReportJson(parsed);\r\n dashboardHtml = generateVisualDashboardHtml(data);\r\n console.log(chalk.cyan(`Building visual dashboard from ${inputPath}...`));\r\n } else {\r\n let loaded;\r\n try {\r\n loaded = await loadConfig();\r\n } catch {\r\n console.error(chalk.red('No opencroc config found. Run `opencroc init` first.'));\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n const { config } = loaded;\r\n console.log(chalk.cyan('Running pipeline to build visual dashboard...'));\r\n const pipeline = createPipeline(config);\r\n const result = await pipeline.run();\r\n\r\n const data = buildDashboardDataFromPipeline(result);\r\n dashboardHtml = generateVisualDashboardHtml(data);\r\n }\r\n\r\n const outDir = opts.output ? path.resolve(opts.output) : path.resolve('./opencroc-output');\r\n if (!fs.existsSync(outDir)) {\r\n fs.mkdirSync(outDir, { recursive: true });\r\n }\r\n\r\n const outPath = path.join(outDir, 'opencroc-dashboard.html');\r\n fs.writeFileSync(outPath, dashboardHtml, 'utf-8');\r\n console.log(chalk.green(`✔ visual dashboard → ${outPath}`));\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate playwright.config.ts content based on OpenCroc config.\r\n */\r\nexport function generatePlaywrightConfig(config: OpenCrocConfig): string {\r\n const pw = config.playwright ?? {};\r\n const rt = config.runtime ?? {};\r\n const outDir = config.outDir || './opencroc-output';\r\n\r\n const baseURL = pw.baseURL ? `'${pw.baseURL}'` : \"process.env.BASE_URL || 'http://localhost:3000'\";\r\n const timeout = pw.timeout ?? 30_000;\r\n const workers = pw.workers ?? (null as number | null); // null → use expression\r\n const retries = pw.retries ?? (null as number | null);\r\n const actionTimeout = pw.actionTimeout ?? 10_000;\r\n const navigationTimeout = pw.navigationTimeout ?? timeout;\r\n const storageState = rt.auth?.storageStatePath || 'playwright/.auth/user.json';\r\n\r\n const hasAuth = !!(rt.auth?.loginUrl);\r\n\r\n const lines: string[] = [];\r\n lines.push(`import { defineConfig, devices } from '@playwright/test';`);\r\n lines.push('');\r\n lines.push('export default defineConfig({');\r\n lines.push(` testDir: '${outDir}',`);\r\n lines.push(` testMatch: ['**/*.spec.ts', '**/*.test.ts'],`);\r\n lines.push(` fullyParallel: false,`);\r\n lines.push(` forbidOnly: !!process.env.CI,`);\r\n lines.push(` retries: ${retries !== null ? retries : 'process.env.CI ? 1 : 0'},`);\r\n lines.push(` workers: ${workers !== null ? workers : 'process.env.CI ? 4 : 2'},`);\r\n lines.push(` timeout: ${timeout},`);\r\n lines.push(` globalSetup: './global-setup.ts',`);\r\n lines.push(` globalTeardown: './global-teardown.ts',`);\r\n lines.push(` reporter: [['list'], ['html', { open: 'never' }]],`);\r\n lines.push(` use: {`);\r\n lines.push(` baseURL: ${baseURL},`);\r\n lines.push(` trace: 'retain-on-failure',`);\r\n lines.push(` screenshot: 'only-on-failure',`);\r\n lines.push(` video: 'retain-on-failure',`);\r\n lines.push(` actionTimeout: ${actionTimeout},`);\r\n lines.push(` navigationTimeout: ${navigationTimeout},`);\r\n lines.push(` },`);\r\n\r\n // Projects\r\n lines.push(` projects: [`);\r\n\r\n if (hasAuth) {\r\n lines.push(` {`);\r\n lines.push(` name: 'setup',`);\r\n lines.push(` testMatch: '**/auth.setup.ts',`);\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n lines.push(` },`);\r\n lines.push(` {`);\r\n lines.push(` name: 'chromium',`);\r\n lines.push(` testIgnore: ['**/*.setup.ts'],`);\r\n lines.push(` dependencies: ['setup'],`);\r\n lines.push(` use: {`);\r\n lines.push(` ...devices['Desktop Chrome'],`);\r\n lines.push(` storageState: '${storageState}',`);\r\n lines.push(` },`);\r\n lines.push(` },`);\r\n lines.push(` {`);\r\n lines.push(` name: 'chromium-no-auth',`);\r\n lines.push(` testMatch: '**/login-flow.test.ts',`);\r\n lines.push(` testIgnore: ['**/*.setup.ts'],`);\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n lines.push(` },`);\r\n } else {\r\n lines.push(` {`);\r\n lines.push(` name: 'chromium',`);\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n lines.push(` },`);\r\n }\r\n\r\n // Extra projects from config\r\n if (rt.extraProjects) {\r\n for (const proj of rt.extraProjects) {\r\n lines.push(` {`);\r\n lines.push(` name: '${proj.name}',`);\r\n lines.push(` testMatch: '${proj.testMatch}',`);\r\n if (proj.dependencies?.length) {\r\n lines.push(` dependencies: [${proj.dependencies.map((d) => `'${d}'`).join(', ')}],`);\r\n }\r\n if (proj.useAuth !== false && hasAuth) {\r\n lines.push(` use: {`);\r\n lines.push(` ...devices['Desktop Chrome'],`);\r\n lines.push(` storageState: '${storageState}',`);\r\n lines.push(` },`);\r\n } else {\r\n lines.push(` use: { ...devices['Desktop Chrome'] },`);\r\n }\r\n lines.push(` },`);\r\n }\r\n }\r\n\r\n lines.push(` ],`);\r\n lines.push('});');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate global-setup.ts content for Playwright.\r\n * Handles backend readiness probe, seed endpoint call, and log cleanup.\r\n */\r\nexport function generateGlobalSetup(config: OpenCrocConfig): string {\r\n const pw = config.playwright ?? {};\r\n const rt = config.runtime ?? {};\r\n const baseURL = pw.baseURL || '';\r\n\r\n const hasSeedEndpoint = !!rt.db?.seedEndpoint;\r\n const hasLogEndpoint = !!rt.logEndpoint;\r\n const hasSeedFile = !!rt.db?.seedFile;\r\n\r\n const lines: string[] = [];\r\n\r\n lines.push(`/**`);\r\n lines.push(` * Global setup — generated by OpenCroc.`);\r\n lines.push(` * Runs once before all test projects.`);\r\n lines.push(` */`);\r\n lines.push('');\r\n lines.push(`const BASE_URL = process.env.BASE_URL || '${baseURL || 'http://localhost:3000'}';`);\r\n lines.push('');\r\n\r\n // fetchWithRetry helper\r\n lines.push(`async function fetchWithRetry(`);\r\n lines.push(` url: string,`);\r\n lines.push(` options: RequestInit,`);\r\n lines.push(` retries = 3,`);\r\n lines.push(`): Promise<Response | null> {`);\r\n lines.push(` for (let i = 0; i < retries; i++) {`);\r\n lines.push(` try {`);\r\n lines.push(` const resp = await fetch(url, { ...options, signal: AbortSignal.timeout(10_000) });`);\r\n lines.push(` if (resp.ok) return resp;`);\r\n lines.push(` console.warn(\\`[global-setup] \\${options.method} \\${url} → \\${resp.status} (\\${i + 1}/\\${retries})\\`);`);\r\n lines.push(` } catch (err: unknown) {`);\r\n lines.push(` const msg = err instanceof Error ? err.message : String(err);`);\r\n lines.push(` console.warn(\\`[global-setup] \\${options.method} \\${url} error (\\${i + 1}/\\${retries}): \\${msg}\\`);`);\r\n lines.push(` }`);\r\n lines.push(` if (i < retries - 1) await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, i)));`);\r\n lines.push(` }`);\r\n lines.push(` return null;`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n // waitForBackend helper\r\n lines.push(`async function waitForBackend(origin: string, timeoutMs = 30_000): Promise<void> {`);\r\n lines.push(` const start = Date.now();`);\r\n lines.push(` while (Date.now() - start < timeoutMs) {`);\r\n lines.push(` try {`);\r\n lines.push(` const res = await fetch(\\`\\${origin}/health\\`, { signal: AbortSignal.timeout(3_000) });`);\r\n lines.push(` if (res.ok) return;`);\r\n lines.push(` } catch {`);\r\n lines.push(` // not ready yet`);\r\n lines.push(` }`);\r\n lines.push(` await new Promise((r) => setTimeout(r, 1_000));`);\r\n lines.push(` }`);\r\n lines.push(` throw new Error(\\`Backend not ready after \\${timeoutMs}ms\\`);`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n // Main function\r\n lines.push(`export default async function globalSetup(): Promise<void> {`);\r\n lines.push(` console.log('[global-setup] Starting...');`);\r\n lines.push('');\r\n lines.push(` // Backend readiness probe`);\r\n lines.push(` try {`);\r\n lines.push(` await waitForBackend(BASE_URL, 30_000);`);\r\n lines.push(` console.log('[global-setup] Backend is ready.');`);\r\n lines.push(` } catch (e: unknown) {`);\r\n lines.push(` console.warn(\\`[global-setup] \\${e instanceof Error ? e.message : String(e)}\\`);`);\r\n lines.push(` }`);\r\n lines.push('');\r\n\r\n if (hasSeedEndpoint) {\r\n lines.push(` // Seed data via backend endpoint`);\r\n lines.push(` try {`);\r\n lines.push(` await fetchWithRetry(`);\r\n lines.push(` \\`\\${BASE_URL}${rt.db!.seedEndpoint}\\`,`);\r\n lines.push(` { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' },`);\r\n lines.push(` );`);\r\n lines.push(` console.log('[global-setup] Seed endpoint called.');`);\r\n lines.push(` } catch {`);\r\n lines.push(` console.warn('[global-setup] Seed endpoint failed.');`);\r\n lines.push(` }`);\r\n lines.push('');\r\n }\r\n\r\n if (hasLogEndpoint) {\r\n lines.push(` // Clear test logs`);\r\n lines.push(` try {`);\r\n lines.push(` await fetchWithRetry(\\`\\${BASE_URL}${rt.logEndpoint}\\`, { method: 'DELETE' });`);\r\n lines.push(` console.log('[global-setup] Test logs cleared.');`);\r\n lines.push(` } catch {`);\r\n lines.push(` console.warn('[global-setup] Failed to clear test logs.');`);\r\n lines.push(` }`);\r\n lines.push('');\r\n }\r\n\r\n if (hasSeedFile) {\r\n lines.push(` // SQL seed file execution (placeholder — wire to your DB helper)`);\r\n lines.push(` // import { executeSeedSQL } from './utils/db-helper';`);\r\n lines.push(` // await executeSeedSQL('${rt.db!.seedFile}');`);\r\n lines.push('');\r\n }\r\n\r\n lines.push(` console.log('[global-setup] Done.');`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate global-teardown.ts content for Playwright.\r\n * Handles cleanup endpoint call and optional SQL cleanup.\r\n */\r\nexport function generateGlobalTeardown(config: OpenCrocConfig): string {\r\n const pw = config.playwright ?? {};\r\n const rt = config.runtime ?? {};\r\n const baseURL = pw.baseURL || '';\r\n\r\n const hasCleanupEndpoint = !!rt.db?.cleanupEndpoint;\r\n const hasCleanupFile = !!rt.db?.cleanupFile;\r\n\r\n const lines: string[] = [];\r\n\r\n lines.push(`/**`);\r\n lines.push(` * Global teardown — generated by OpenCroc.`);\r\n lines.push(` * Runs once after all test projects complete.`);\r\n lines.push(` */`);\r\n lines.push('');\r\n lines.push(`const BASE_URL = process.env.BASE_URL || '${baseURL || 'http://localhost:3000'}';`);\r\n lines.push('');\r\n\r\n lines.push(`export default async function globalTeardown(): Promise<void> {`);\r\n lines.push(` console.log('[global-teardown] Starting...');`);\r\n lines.push('');\r\n\r\n if (hasCleanupEndpoint) {\r\n lines.push(` // Cleanup via backend endpoint`);\r\n lines.push(` try {`);\r\n lines.push(` await fetch(\\`\\${BASE_URL}${rt.db!.cleanupEndpoint}\\`, { method: 'POST' });`);\r\n lines.push(` console.log('[global-teardown] Cleanup endpoint called.');`);\r\n lines.push(` } catch {`);\r\n lines.push(` console.warn('[global-teardown] Cleanup endpoint failed.');`);\r\n lines.push(` }`);\r\n lines.push('');\r\n }\r\n\r\n if (hasCleanupFile) {\r\n lines.push(` // SQL cleanup file execution (placeholder — wire to your DB helper)`);\r\n lines.push(` // import { executeSeedSQL } from './utils/db-helper';`);\r\n lines.push(` // await executeSeedSQL('${rt.db!.cleanupFile}');`);\r\n lines.push('');\r\n }\r\n\r\n lines.push(` console.log('[global-teardown] Done.');`);\r\n lines.push(`}`);\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import type { OpenCrocConfig } from '../types.js';\r\n\r\n/**\r\n * Generate auth.setup.ts content for Playwright.\r\n * Creates an authentication setup project that stores state for reuse.\r\n */\r\nexport function generateAuthSetup(config: OpenCrocConfig): string {\r\n const rt = config.runtime ?? {};\r\n const pw = config.playwright ?? {};\r\n const auth = rt.auth ?? {};\r\n const baseURL = pw.baseURL || '';\r\n const storageStatePath = auth.storageStatePath || 'playwright/.auth/user.json';\r\n\r\n const lines: string[] = [];\r\n\r\n lines.push(`import { test as setup, expect } from '@playwright/test';`);\r\n lines.push(`import * as fs from 'fs/promises';`);\r\n lines.push(`import * as path from 'path';`);\r\n lines.push('');\r\n\r\n lines.push(`const authFile = '${storageStatePath}';`);\r\n lines.push('');\r\n\r\n if (auth.loginUrl) {\r\n // API-based authentication\r\n lines.push(`setup('authenticate', async ({ request }) => {`);\r\n lines.push(` const loginUrl = process.env.AUTH_LOGIN_URL || '${auth.loginUrl}';`);\r\n lines.push(` const username = process.env.AUTH_USERNAME || '${auth.username || 'admin'}';`);\r\n lines.push(` const password = process.env.AUTH_PASSWORD || '${auth.password || ''}';`);\r\n lines.push('');\r\n lines.push(` // API login`);\r\n lines.push(` const response = await request.post(loginUrl, {`);\r\n lines.push(` data: { username, password },`);\r\n lines.push(` });`);\r\n lines.push(` expect(response.ok()).toBeTruthy();`);\r\n lines.push('');\r\n lines.push(` const body = await response.json();`);\r\n lines.push(` const token = body.data?.token || body.token || '';`);\r\n lines.push(` console.log('[auth.setup] Login successful, token obtained:', !!token);`);\r\n lines.push('');\r\n lines.push(` // Save storage state with auth cookie/localStorage`);\r\n lines.push(` await fs.mkdir(path.dirname(authFile), { recursive: true });`);\r\n lines.push('');\r\n lines.push(` // Build storage state JSON`);\r\n lines.push(` const baseURL = process.env.BASE_URL || '${baseURL || 'http://localhost:3000'}';`);\r\n lines.push(` const origin = new URL(baseURL).origin;`);\r\n lines.push(` const storageState = {`);\r\n lines.push(` cookies: [],`);\r\n lines.push(` origins: [`);\r\n lines.push(` {`);\r\n lines.push(` origin,`);\r\n lines.push(` localStorage: [`);\r\n lines.push(` { name: 'token', value: token },`);\r\n lines.push(` ],`);\r\n lines.push(` },`);\r\n lines.push(` ],`);\r\n lines.push(` };`);\r\n lines.push('');\r\n lines.push(` await fs.writeFile(authFile, JSON.stringify(storageState, null, 2));`);\r\n lines.push(` console.log(\\`[auth.setup] Storage state saved → \\${authFile}\\`);`);\r\n lines.push(`});`);\r\n } else {\r\n // Browser-based authentication (placeholder)\r\n lines.push(`setup('authenticate', async ({ browser }) => {`);\r\n lines.push(` const context = await browser.newContext();`);\r\n lines.push(` const page = await context.newPage();`);\r\n lines.push('');\r\n lines.push(` // TODO: Implement your login flow here`);\r\n lines.push(` // await page.goto('/login');`);\r\n lines.push(` // await page.fill('[name=username]', 'admin');`);\r\n lines.push(` // await page.fill('[name=password]', 'password');`);\r\n lines.push(` // await page.click('button[type=submit]');`);\r\n lines.push(` // await page.waitForURL('/dashboard');`);\r\n lines.push('');\r\n lines.push(` await fs.mkdir(path.dirname(authFile), { recursive: true });`);\r\n lines.push(` await context.storageState({ path: authFile });`);\r\n lines.push(` console.log(\\`[auth.setup] Storage state saved → \\${authFile}\\`);`);\r\n lines.push('');\r\n lines.push(` await context.close();`);\r\n lines.push(`});`);\r\n }\r\n\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","import chalk from 'chalk';\r\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { generatePlaywrightConfig } from '../../runtime/playwright-config-generator.js';\r\nimport { generateGlobalSetup } from '../../runtime/global-setup-generator.js';\r\nimport { generateGlobalTeardown } from '../../runtime/global-teardown-generator.js';\r\nimport { generateAuthSetup } from '../../runtime/auth-setup-generator.js';\r\n\r\nexport interface InitRuntimeOptions {\r\n output?: string;\r\n force?: boolean;\r\n}\r\n\r\nfunction writeIfNotExists(filePath: string, content: string, force: boolean): boolean {\r\n if (existsSync(filePath) && !force) {\r\n console.log(chalk.yellow(` ⊘ ${filePath} already exists (use --force to overwrite)`));\r\n return false;\r\n }\r\n const dir = resolve(filePath, '..');\r\n mkdirSync(dir, { recursive: true });\r\n writeFileSync(filePath, content, 'utf-8');\r\n console.log(chalk.green(` ✓ ${filePath}`));\r\n return true;\r\n}\r\n\r\nexport async function initRuntime(opts: InitRuntimeOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Initialize Playwright Runtime\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const outDir = resolve(opts.output || '.');\r\n const force = opts.force ?? false;\r\n const hasAuth = !!config.runtime?.auth?.loginUrl;\r\n\r\n let written = 0;\r\n\r\n console.log(chalk.cyan('\\n Generating runtime files...\\n'));\r\n\r\n if (writeIfNotExists(join(outDir, 'playwright.config.ts'), generatePlaywrightConfig(config), force)) written++;\r\n if (writeIfNotExists(join(outDir, 'global-setup.ts'), generateGlobalSetup(config), force)) written++;\r\n if (writeIfNotExists(join(outDir, 'global-teardown.ts'), generateGlobalTeardown(config), force)) written++;\r\n\r\n if (hasAuth) {\r\n if (writeIfNotExists(join(outDir, 'auth.setup.ts'), generateAuthSetup(config), force)) written++;\r\n }\r\n\r\n console.log('');\r\n if (written > 0) {\r\n console.log(chalk.green(` ✓ Generated ${written} runtime file(s) in ${outDir}\\n`));\r\n } else {\r\n console.log(chalk.yellow(` No files written. Use --force to overwrite existing files.\\n`));\r\n }\r\n}\r\n","/**\r\n * Full orchestration engine: generate → execute → analyze → heal → report.\r\n *\r\n * Runs a multi-phase pipeline with per-phase tracking, failure detection,\r\n * token budget management, and structured result reporting.\r\n */\r\n\r\nimport { writeFileSync, mkdirSync, existsSync, readdirSync } from 'node:fs';\r\nimport { dirname, join } from 'node:path';\r\nimport { execFileSync } from 'node:child_process';\r\nimport type {\r\n OpenCrocConfig,\r\n PipelineRunResult,\r\n SelfHealingConfig,\r\n SelfHealingResult,\r\n} from '../types.js';\r\nimport { createPipeline } from '../pipeline/index.js';\r\nimport { createSelfHealingLoop } from '../self-healing/index.js';\r\nimport { generateReports } from '../reporters/index.js';\r\nimport type { ReportOutput } from '../reporters/index.js';\r\n\r\n// ===== Types =====\r\n\r\nexport type PhaseStatus = 'success' | 'warn' | 'error' | 'skipped';\r\n\r\nexport interface PhaseResult<T = unknown> {\r\n phase: string;\r\n status: PhaseStatus;\r\n output?: T;\r\n error?: string;\r\n durationMs: number;\r\n}\r\n\r\nexport interface OrchestrationOptions {\r\n /** Which phases to run (default: all) */\r\n phases?: OrchestrationPhase[];\r\n /** Enable self-healing phase (default: false) */\r\n selfHeal?: boolean;\r\n /** Max self-healing iterations */\r\n maxHealIterations?: number;\r\n /** Report formats to generate */\r\n reportFormats?: ('html' | 'json' | 'markdown')[];\r\n /** Run Playwright in headed mode */\r\n headed?: boolean;\r\n /** Module filter */\r\n module?: string;\r\n /** LLM token budget (0 = unlimited) */\r\n tokenBudget?: number;\r\n /** Abort on phase error (default: false — continue where possible) */\r\n abortOnError?: boolean;\r\n}\r\n\r\nexport type OrchestrationPhase = 'generate' | 'execute' | 'analyze' | 'heal' | 'report';\r\n\r\nexport interface ExecutionMetrics {\r\n passed: number;\r\n failed: number;\r\n skipped: number;\r\n timedOut: number;\r\n}\r\n\r\nexport interface OrchestrationSummary {\r\n overallStatus: 'success' | 'partial-fail' | 'fatal-fail';\r\n phases: PhaseResult[];\r\n totalDurationMs: number;\r\n modules: string[];\r\n executionMetrics?: ExecutionMetrics;\r\n reports?: ReportOutput[];\r\n healingResult?: SelfHealingResult;\r\n recommendation?: string;\r\n}\r\n\r\nconst ALL_PHASES: OrchestrationPhase[] = ['generate', 'execute', 'analyze', 'heal', 'report'];\r\n\r\n// ===== Helpers =====\r\n\r\nfunction discoverTestFiles(outDir: string, moduleFilter?: string): string[] {\r\n const absDir = join(process.cwd(), outDir);\r\n if (!existsSync(absDir)) return [];\r\n\r\n const files: string[] = [];\r\n const entries = readdirSync(absDir, { withFileTypes: true, recursive: true });\r\n for (const entry of entries) {\r\n if (!entry.isFile()) continue;\r\n if (!entry.name.endsWith('.spec.ts') && !entry.name.endsWith('.test.ts')) continue;\r\n const fullPath = join(\r\n entry.parentPath || (entry as unknown as { path: string }).path || absDir,\r\n entry.name,\r\n );\r\n if (moduleFilter && !fullPath.includes(moduleFilter)) continue;\r\n files.push(fullPath);\r\n }\r\n return files;\r\n}\r\n\r\nfunction parsePlaywrightOutput(stderr: string): ExecutionMetrics {\r\n const metrics: ExecutionMetrics = { passed: 0, failed: 0, skipped: 0, timedOut: 0 };\r\n\r\n // Playwright summary line: \"X passed\", \"Y failed\", \"Z skipped\"\r\n const passedMatch = stderr.match(/(\\d+)\\s+passed/);\r\n const failedMatch = stderr.match(/(\\d+)\\s+failed/);\r\n const skippedMatch = stderr.match(/(\\d+)\\s+skipped/);\r\n const timedOutMatch = stderr.match(/(\\d+)\\s+timed?\\s*out/i);\r\n\r\n if (passedMatch) metrics.passed = parseInt(passedMatch[1], 10);\r\n if (failedMatch) metrics.failed = parseInt(failedMatch[1], 10);\r\n if (skippedMatch) metrics.skipped = parseInt(skippedMatch[1], 10);\r\n if (timedOutMatch) metrics.timedOut = parseInt(timedOutMatch[1], 10);\r\n\r\n return metrics;\r\n}\r\n\r\n// ===== Orchestrator =====\r\n\r\nexport function createOrchestrator(config: OpenCrocConfig, options: OrchestrationOptions = {}) {\r\n const {\r\n phases = ALL_PHASES,\r\n selfHeal = false,\r\n maxHealIterations = 3,\r\n reportFormats = ['html', 'json'],\r\n headed = false,\r\n module: moduleFilter,\r\n tokenBudget = 0,\r\n abortOnError = false,\r\n } = options;\r\n\r\n const outDir = config.outDir || './opencroc-output';\r\n const phaseResults: PhaseResult[] = [];\r\n let pipelineResult: PipelineRunResult | undefined;\r\n let executionMetrics: ExecutionMetrics | undefined;\r\n let healingResult: SelfHealingResult | undefined;\r\n let reports: ReportOutput[] | undefined;\r\n let tokensUsed = 0;\r\n\r\n function isBudgetExceeded(): boolean {\r\n return tokenBudget > 0 && tokensUsed >= tokenBudget;\r\n }\r\n\r\n function shouldRun(phase: OrchestrationPhase): boolean {\r\n if (phase === 'heal' && !selfHeal) return false;\r\n return phases.includes(phase);\r\n }\r\n\r\n async function runPhase<T>(\r\n name: string,\r\n fn: () => Promise<T>,\r\n ): Promise<PhaseResult<T>> {\r\n const start = Date.now();\r\n try {\r\n const output = await fn();\r\n const result: PhaseResult<T> = {\r\n phase: name,\r\n status: 'success',\r\n output,\r\n durationMs: Date.now() - start,\r\n };\r\n phaseResults.push(result);\r\n return result;\r\n } catch (err) {\r\n const result: PhaseResult<T> = {\r\n phase: name,\r\n status: 'error',\r\n error: err instanceof Error ? err.message : String(err),\r\n durationMs: Date.now() - start,\r\n };\r\n phaseResults.push(result);\r\n return result;\r\n }\r\n }\r\n\r\n function skipPhase(name: string, reason: string): PhaseResult {\r\n const result: PhaseResult = {\r\n phase: name,\r\n status: 'skipped',\r\n error: reason,\r\n durationMs: 0,\r\n };\r\n phaseResults.push(result);\r\n return result;\r\n }\r\n\r\n return {\r\n async run(): Promise<OrchestrationSummary> {\r\n const orchestrationStart = Date.now();\r\n\r\n // ── Phase 1: Generate ──\r\n if (shouldRun('generate')) {\r\n const genResult = await runPhase('generate', async () => {\r\n if (moduleFilter) config.modules = [moduleFilter];\r\n const pipeline = createPipeline(config);\r\n pipelineResult = await pipeline.run();\r\n\r\n // Write generated files\r\n for (const file of pipelineResult.generatedFiles) {\r\n const dir = dirname(file.filePath);\r\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\r\n writeFileSync(file.filePath, file.content, 'utf-8');\r\n }\r\n\r\n return pipelineResult;\r\n });\r\n\r\n if (genResult.status === 'error' && abortOnError) {\r\n return buildSummary(orchestrationStart);\r\n }\r\n }\r\n\r\n // ── Phase 2: Execute ──\r\n if (shouldRun('execute')) {\r\n const testFiles = discoverTestFiles(outDir, moduleFilter);\r\n\r\n if (testFiles.length === 0) {\r\n skipPhase('execute', 'No test files found');\r\n } else {\r\n const execResult = await runPhase('execute', async () => {\r\n const args = ['test', ...testFiles];\r\n if (!headed) args.push('--reporter=list');\r\n else args.push('--headed');\r\n\r\n const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';\r\n\r\n try {\r\n execFileSync(npxCmd, ['playwright', ...args], {\r\n cwd: process.cwd(),\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n timeout: 300_000,\r\n });\r\n return { passed: testFiles.length, failed: 0, skipped: 0, timedOut: 0 } as ExecutionMetrics;\r\n } catch (err) {\r\n const stderr = (err as { stderr?: Buffer })?.stderr?.toString() ?? '';\r\n const metrics = parsePlaywrightOutput(stderr);\r\n if (metrics.passed === 0 && metrics.failed === 0) {\r\n metrics.failed = testFiles.length;\r\n }\r\n executionMetrics = metrics;\r\n if (metrics.failed > 0) {\r\n throw new Error(`${metrics.failed} test(s) failed, ${metrics.passed} passed`, { cause: err });\r\n }\r\n return metrics;\r\n }\r\n });\r\n\r\n if (!executionMetrics && execResult.output) {\r\n executionMetrics = execResult.output as ExecutionMetrics;\r\n }\r\n\r\n if (execResult.status === 'error' && abortOnError) {\r\n return buildSummary(orchestrationStart);\r\n }\r\n }\r\n }\r\n\r\n // ── Phase 3: Analyze ──\r\n if (shouldRun('analyze')) {\r\n if (!pipelineResult || !executionMetrics || executionMetrics.failed === 0) {\r\n skipPhase('analyze', 'No failures to analyze');\r\n } else {\r\n await runPhase('analyze', async () => {\r\n // Re-run pipeline validation for failure context\r\n const pipeline = createPipeline(config);\r\n const validationResult = await pipeline.run(['validate']);\r\n return {\r\n validationErrors: validationResult.validationErrors,\r\n failedTestCount: executionMetrics!.failed,\r\n };\r\n });\r\n }\r\n }\r\n\r\n // ── Phase 4: Heal ──\r\n if (shouldRun('heal')) {\r\n if (isBudgetExceeded()) {\r\n skipPhase('heal', 'Token budget exceeded');\r\n } else if (!executionMetrics || executionMetrics.failed === 0) {\r\n skipPhase('heal', 'No failures to heal');\r\n } else {\r\n const healResult = await runPhase('heal', async () => {\r\n const healConfig: SelfHealingConfig = {\r\n enabled: true,\r\n maxIterations: maxHealIterations,\r\n mode: config.selfHealing?.mode || 'config-only',\r\n };\r\n\r\n const loop = createSelfHealingLoop(healConfig);\r\n const result = await loop.run(outDir);\r\n tokensUsed += result.totalTokensUsed;\r\n healingResult = result;\r\n return result;\r\n });\r\n\r\n if (healResult.status === 'success' && healingResult && healingResult.remaining.length > 0) {\r\n healResult.status = 'warn';\r\n phaseResults[phaseResults.length - 1] = healResult;\r\n }\r\n }\r\n }\r\n\r\n // ── Phase 5: Report ──\r\n if (shouldRun('report')) {\r\n if (!pipelineResult) {\r\n skipPhase('report', 'No pipeline results to report');\r\n } else {\r\n await runPhase('report', async () => {\r\n reports = generateReports(pipelineResult!, reportFormats);\r\n\r\n if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });\r\n for (const r of reports) {\r\n writeFileSync(join(outDir, r.filename), r.content, 'utf-8');\r\n }\r\n\r\n return reports;\r\n });\r\n }\r\n }\r\n\r\n return buildSummary(orchestrationStart);\r\n },\r\n };\r\n\r\n function buildSummary(startTime: number): OrchestrationSummary {\r\n const errorCount = phaseResults.filter((p) => p.status === 'error').length;\r\n const allSuccess = phaseResults.every((p) => p.status === 'success' || p.status === 'skipped');\r\n\r\n let overallStatus: OrchestrationSummary['overallStatus'];\r\n if (allSuccess) overallStatus = 'success';\r\n else if (errorCount > 1) overallStatus = 'fatal-fail';\r\n else overallStatus = 'partial-fail';\r\n\r\n let recommendation: string | undefined;\r\n if (executionMetrics && executionMetrics.failed > 0 && !selfHeal) {\r\n recommendation = 'Test failures detected. Consider running with --self-heal to attempt automated fixes.';\r\n } else if (healingResult && healingResult.remaining.length > 0) {\r\n recommendation = `${healingResult.remaining.length} issue(s) could not be auto-fixed. Manual review needed.`;\r\n }\r\n\r\n return {\r\n overallStatus,\r\n phases: phaseResults,\r\n totalDurationMs: Date.now() - startTime,\r\n modules: pipelineResult?.modules ?? [],\r\n executionMetrics,\r\n reports,\r\n healingResult,\r\n recommendation,\r\n };\r\n }\r\n}\r\n","/**\r\n * Orchestration summary reporter.\r\n * Writes phase-by-phase JSON and a human-readable console summary.\r\n */\r\n\r\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\r\nimport { join } from 'node:path';\r\nimport type { OrchestrationSummary, PhaseResult } from './index.js';\r\n\r\nexport interface OrchestrationReportOptions {\r\n outputDir: string;\r\n module?: string;\r\n}\r\n\r\n/**\r\n * Write the orchestration summary to a JSON file.\r\n * Returns the written file path.\r\n */\r\nexport function writeOrchestrationSummary(\r\n summary: OrchestrationSummary,\r\n options: OrchestrationReportOptions,\r\n): string {\r\n const { outputDir, module: mod } = options;\r\n if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });\r\n\r\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\r\n const moduleName = mod ?? 'all';\r\n const filename = `orchestration-${moduleName}-${timestamp}.json`;\r\n const filePath = join(outputDir, filename);\r\n\r\n const serializable = {\r\n ...summary,\r\n phases: summary.phases.map((p) => ({\r\n phase: p.phase,\r\n status: p.status,\r\n error: p.error,\r\n durationMs: p.durationMs,\r\n })),\r\n // Strip report content to avoid massive JSON\r\n reports: summary.reports?.map((r) => ({\r\n format: r.format,\r\n filename: r.filename,\r\n })),\r\n };\r\n\r\n writeFileSync(filePath, JSON.stringify(serializable, null, 2), 'utf-8');\r\n return filePath;\r\n}\r\n\r\n/**\r\n * Format a phase result as a single console line.\r\n */\r\nfunction formatPhase(p: PhaseResult): string {\r\n const icons: Record<string, string> = {\r\n success: '✓',\r\n warn: '⚠',\r\n error: '✗',\r\n skipped: '○',\r\n };\r\n const icon = icons[p.status] ?? '?';\r\n const dur = p.durationMs > 0 ? ` (${p.durationMs}ms)` : '';\r\n const err = p.error ? ` — ${p.error}` : '';\r\n return ` ${icon} ${p.phase}${dur}${err}`;\r\n}\r\n\r\n/**\r\n * Print a human-readable console summary.\r\n */\r\nexport function printOrchestrationSummary(summary: OrchestrationSummary): string[] {\r\n const lines: string[] = [];\r\n\r\n lines.push('');\r\n lines.push(' ═══════════════════════════════════════');\r\n lines.push(' Orchestration Summary');\r\n lines.push(' ═══════════════════════════════════════');\r\n lines.push('');\r\n\r\n for (const p of summary.phases) {\r\n lines.push(formatPhase(p));\r\n }\r\n\r\n lines.push('');\r\n lines.push(` Overall : ${summary.overallStatus}`);\r\n lines.push(` Modules : ${summary.modules.join(', ') || '(none)'}`);\r\n lines.push(` Duration : ${summary.totalDurationMs}ms`);\r\n\r\n if (summary.executionMetrics) {\r\n const m = summary.executionMetrics;\r\n lines.push(` Tests : ${m.passed} passed, ${m.failed} failed, ${m.skipped} skipped`);\r\n }\r\n\r\n if (summary.healingResult) {\r\n const h = summary.healingResult;\r\n lines.push(` Healing : ${h.fixed.length} fixed, ${h.remaining.length} remaining (${h.iterations} iterations)`);\r\n }\r\n\r\n if (summary.reports) {\r\n lines.push(` Reports : ${summary.reports.map((r) => r.format).join(', ')}`);\r\n }\r\n\r\n if (summary.recommendation) {\r\n lines.push('');\r\n lines.push(` → ${summary.recommendation}`);\r\n }\r\n\r\n lines.push('');\r\n\r\n return lines;\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport { createOrchestrator } from '../../orchestrator/index.js';\r\nimport { writeOrchestrationSummary, printOrchestrationSummary } from '../../orchestrator/reporter.js';\r\nimport type { OrchestrationPhase } from '../../orchestrator/index.js';\r\n\r\nexport interface RunOptions {\r\n module?: string;\r\n phases?: string;\r\n selfHeal?: boolean;\r\n headed?: boolean;\r\n report?: string;\r\n tokenBudget?: string;\r\n abortOnError?: boolean;\r\n}\r\n\r\nconst VALID_PHASES: OrchestrationPhase[] = ['generate', 'execute', 'analyze', 'heal', 'report'];\r\n\r\nfunction parsePhases(raw?: string): OrchestrationPhase[] | undefined {\r\n if (!raw) return undefined;\r\n const names = raw.split(',').map((s) => s.trim());\r\n for (const name of names) {\r\n if (!VALID_PHASES.includes(name as OrchestrationPhase)) {\r\n console.log(chalk.red(` Unknown phase \"${name}\". Valid: ${VALID_PHASES.join(', ')}`));\r\n process.exitCode = 1;\r\n return undefined;\r\n }\r\n }\r\n return names as OrchestrationPhase[];\r\n}\r\n\r\nexport async function run(opts: RunOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n 🐊 OpenCroc — Full Orchestration\\n'));\r\n\r\n const { config, filepath } = await loadConfig();\r\n console.log(chalk.gray(` Config: ${filepath}`));\r\n\r\n const phases = parsePhases(opts.phases);\r\n if (phases === undefined && opts.phases) return;\r\n\r\n const reportFormats = (opts.report ?? 'html,json').split(',').map((s) => s.trim()) as ('html' | 'json' | 'markdown')[];\r\n\r\n const orchestrator = createOrchestrator(config, {\r\n phases,\r\n selfHeal: opts.selfHeal ?? false,\r\n headed: opts.headed ?? false,\r\n module: opts.module,\r\n reportFormats,\r\n tokenBudget: opts.tokenBudget ? parseInt(opts.tokenBudget, 10) : 0,\r\n abortOnError: opts.abortOnError ?? false,\r\n });\r\n\r\n console.log(chalk.gray(` Phases: ${(phases ?? VALID_PHASES).join(' → ')}`));\r\n if (opts.selfHeal) console.log(chalk.gray(' Self-heal: enabled'));\r\n console.log('');\r\n\r\n const summary = await orchestrator.run();\r\n\r\n // Print console summary\r\n const lines = printOrchestrationSummary(summary);\r\n for (const line of lines) {\r\n const color =\r\n summary.overallStatus === 'success' ? chalk.green :\r\n summary.overallStatus === 'partial-fail' ? chalk.yellow :\r\n chalk.red;\r\n console.log(color(line));\r\n }\r\n\r\n // Write JSON summary\r\n const outDir = config.outDir || './opencroc-output';\r\n const summaryPath = writeOrchestrationSummary(summary, { outputDir: outDir, module: opts.module });\r\n console.log(chalk.gray(` Summary: ${summaryPath}\\n`));\r\n\r\n if (summary.overallStatus !== 'success') {\r\n process.exitCode = 1;\r\n }\r\n}\r\n","import type { FastifyInstance } from 'fastify';\r\nimport type { CrocOffice } from '../croc-office.js';\r\n\r\nexport function registerProjectRoutes(app: FastifyInstance, office: CrocOffice): void {\r\n // GET /api/project — full project info + knowledge graph\r\n app.get('/api/project', async () => {\r\n return office.getProjectInfo();\r\n });\r\n\r\n // GET /api/project/graph — knowledge graph only\r\n app.get('/api/project/graph', async () => {\r\n return office.buildKnowledgeGraph();\r\n });\r\n\r\n // POST /api/project/refresh — invalidate cache and re-scan\r\n app.post('/api/project/refresh', async () => {\r\n office.invalidateCache();\r\n const graph = await office.buildKnowledgeGraph();\r\n office.broadcast('graph:update', graph);\r\n return { ok: true, nodes: graph.nodes.length, edges: graph.edges.length };\r\n });\r\n}\r\n","import type { FastifyInstance } from 'fastify';\r\nimport type { CrocOffice } from '../croc-office.js';\r\n\r\nexport function registerAgentRoutes(app: FastifyInstance, office: CrocOffice): void {\r\n // GET /api/agents — list all croc agents\r\n app.get('/api/agents', async () => {\r\n return office.getAgents();\r\n });\r\n\r\n // GET /api/agents/:id — get specific agent\r\n app.get<{ Params: { id: string } }>('/api/agents/:id', async (req, reply) => {\r\n const agent = office.getAgent(req.params.id);\r\n if (!agent) {\r\n reply.code(404).send({ error: 'Agent not found' });\r\n return;\r\n }\r\n return agent;\r\n });\r\n\r\n // POST /api/scan — trigger project scan (parser croc)\r\n app.post('/api/scan', async (_req, reply) => {\r\n if (office.isRunning()) {\r\n reply.code(409).send({ error: 'A task is already running' });\r\n return;\r\n }\r\n // Run async — don't await, respond immediately\r\n office.runScan().catch(() => { /* errors handled in runScan */ });\r\n return { ok: true, message: 'Scan started' };\r\n });\r\n\r\n // POST /api/pipeline — trigger full pipeline (all crocs)\r\n app.post('/api/pipeline', async (_req, reply) => {\r\n if (office.isRunning()) {\r\n reply.code(409).send({ error: 'A task is already running' });\r\n return;\r\n }\r\n office.runPipeline().catch(() => { /* errors handled in runPipeline */ });\r\n return { ok: true, message: 'Pipeline started' };\r\n });\r\n\r\n // POST /api/reset — reset all agents to idle\r\n app.post('/api/reset', async () => {\r\n office.resetAgents();\r\n return { ok: true };\r\n });\r\n\r\n // GET /api/status — overall status\r\n app.get('/api/status', async () => {\r\n return {\r\n running: office.isRunning(),\r\n agents: office.getAgents(),\r\n };\r\n });\r\n}\r\n","import type { WebSocket } from 'ws';\r\nimport type { OpenCrocConfig } from '../types.js';\r\n\r\nexport interface CrocAgent {\r\n id: string;\r\n name: string;\r\n role: 'parser' | 'analyzer' | 'tester' | 'healer' | 'planner' | 'reporter';\r\n sprite: string;\r\n status: 'idle' | 'working' | 'thinking' | 'done' | 'error';\r\n currentTask?: string;\r\n tokensUsed: number;\r\n progress?: number; // 0-100\r\n}\r\n\r\nexport interface KnowledgeGraphNode {\r\n id: string;\r\n label: string;\r\n type: 'model' | 'controller' | 'api' | 'dto' | 'module';\r\n status: 'idle' | 'testing' | 'passed' | 'failed';\r\n fields?: string[];\r\n module?: string;\r\n}\r\n\r\nexport interface KnowledgeGraphEdge {\r\n source: string;\r\n target: string;\r\n relation: string;\r\n}\r\n\r\nexport interface KnowledgeGraph {\r\n nodes: KnowledgeGraphNode[];\r\n edges: KnowledgeGraphEdge[];\r\n}\r\n\r\nexport interface ProjectInfo {\r\n name: string;\r\n backendRoot: string;\r\n adapter: string;\r\n stats: {\r\n modules: number;\r\n models: number;\r\n endpoints: number;\r\n relations: number;\r\n };\r\n graph: KnowledgeGraph;\r\n agents: CrocAgent[];\r\n}\r\n\r\nexport interface TaskResult {\r\n ok: boolean;\r\n task: string;\r\n duration: number;\r\n details?: Record<string, unknown>;\r\n error?: string;\r\n}\r\n\r\nconst DEFAULT_AGENTS: CrocAgent[] = [\r\n { id: 'parser-croc', name: '解析鳄', role: 'parser', sprite: 'parser', status: 'idle', tokensUsed: 0 },\r\n { id: 'analyzer-croc', name: '分析鳄', role: 'analyzer', sprite: 'analyzer', status: 'idle', tokensUsed: 0 },\r\n { id: 'tester-croc', name: '测试鳄', role: 'tester', sprite: 'tester', status: 'idle', tokensUsed: 0 },\r\n { id: 'healer-croc', name: '修复鳄', role: 'healer', sprite: 'healer', status: 'idle', tokensUsed: 0 },\r\n { id: 'planner-croc', name: '规划鳄', role: 'planner', sprite: 'planner', status: 'idle', tokensUsed: 0 },\r\n { id: 'reporter-croc', name: '汇报鳄', role: 'reporter', sprite: 'reporter', status: 'idle', tokensUsed: 0 },\r\n];\r\n\r\nexport class CrocOffice {\r\n private config: OpenCrocConfig;\r\n private cwd: string;\r\n private clients: Set<WebSocket> = new Set();\r\n private agents: CrocAgent[];\r\n private cachedGraph: KnowledgeGraph | null = null;\r\n private running = false;\r\n\r\n constructor(config: OpenCrocConfig, cwd: string) {\r\n this.config = config;\r\n this.cwd = cwd;\r\n this.agents = DEFAULT_AGENTS.map((a) => ({ ...a }));\r\n }\r\n\r\n addClient(ws: WebSocket): void {\r\n this.clients.add(ws);\r\n }\r\n\r\n removeClient(ws: WebSocket): void {\r\n this.clients.delete(ws);\r\n }\r\n\r\n broadcast(type: string, payload: unknown): void {\r\n const msg = JSON.stringify({ type, payload });\r\n for (const client of this.clients) {\r\n try {\r\n client.send(msg);\r\n } catch {\r\n this.clients.delete(client);\r\n }\r\n }\r\n }\r\n\r\n /** Send a log message to all clients */\r\n log(message: string, level: 'info' | 'warn' | 'error' = 'info'): void {\r\n this.broadcast('log', { message, level, time: Date.now() });\r\n }\r\n\r\n getAgents(): CrocAgent[] {\r\n return this.agents;\r\n }\r\n\r\n getAgent(id: string): CrocAgent | undefined {\r\n return this.agents.find((a) => a.id === id);\r\n }\r\n\r\n updateAgent(id: string, update: Partial<CrocAgent>): void {\r\n const agent = this.agents.find((a) => a.id === id);\r\n if (agent) {\r\n Object.assign(agent, update);\r\n this.broadcast('agent:update', this.agents);\r\n }\r\n }\r\n\r\n isRunning(): boolean {\r\n return this.running;\r\n }\r\n\r\n getConfig(): OpenCrocConfig {\r\n return this.config;\r\n }\r\n\r\n getCwd(): string {\r\n return this.cwd;\r\n }\r\n\r\n // ============ Real Task Dispatch ============\r\n\r\n /** Run the full scan → graph build pipeline */\r\n async runScan(): Promise<TaskResult> {\r\n if (this.running) return { ok: false, task: 'scan', duration: 0, error: 'Another task is running' };\r\n this.running = true;\r\n const start = Date.now();\r\n\r\n try {\r\n this.invalidateCache();\r\n this.updateAgent('parser-croc', { status: 'working', currentTask: 'Scanning project...', progress: 0 });\r\n this.log('🔍 Parser croc is scanning the project...');\r\n\r\n const graph = await this.buildKnowledgeGraph();\r\n\r\n const duration = Date.now() - start;\r\n this.log(`✅ Scan complete: ${graph.nodes.length} nodes, ${graph.edges.length} edges (${duration}ms)`);\r\n return { ok: true, task: 'scan', duration, details: { nodes: graph.nodes.length, edges: graph.edges.length } };\r\n } catch (err) {\r\n this.updateAgent('parser-croc', { status: 'error', currentTask: String(err) });\r\n this.log(`❌ Scan failed: ${err}`, 'error');\r\n return { ok: false, task: 'scan', duration: Date.now() - start, error: String(err) };\r\n } finally {\r\n this.running = false;\r\n }\r\n }\r\n\r\n /** Run the pipeline: scan → er-diagram → api-chain → plan → codegen */\r\n async runPipeline(): Promise<TaskResult> {\r\n if (this.running) return { ok: false, task: 'pipeline', duration: 0, error: 'Another task is running' };\r\n this.running = true;\r\n const start = Date.now();\r\n\r\n try {\r\n // Phase 1: Scan\r\n this.updateAgent('parser-croc', { status: 'working', currentTask: 'Scanning source code...', progress: 10 });\r\n this.log('🐊 解析鳄 is scanning source code...');\r\n this.invalidateCache();\r\n await this.buildKnowledgeGraph();\r\n this.updateNodeStatus('module', 'testing');\r\n\r\n // Phase 2: Analyze\r\n this.updateAgent('parser-croc', { status: 'done', currentTask: 'Scan complete', progress: 100 });\r\n this.updateAgent('analyzer-croc', { status: 'working', currentTask: 'Analyzing API chains...', progress: 0 });\r\n this.log('🐊 分析鳄 is analyzing API dependencies...');\r\n await this.delay(800); // Allow UI to update\r\n this.updateAgent('analyzer-croc', { status: 'done', currentTask: 'Analysis complete', progress: 100 });\r\n\r\n // Phase 3: Plan\r\n this.updateAgent('planner-croc', { status: 'thinking', currentTask: 'Planning test chains...', progress: 0 });\r\n this.log('🐊 规划鳄 is planning test chains...');\r\n await this.delay(600);\r\n this.updateAgent('planner-croc', { status: 'done', currentTask: 'Plan ready', progress: 100 });\r\n\r\n // Phase 4: Generate\r\n this.updateAgent('tester-croc', { status: 'working', currentTask: 'Generating test code...', progress: 0 });\r\n this.log('🐊 测试鳄 is generating test code...');\r\n this.updateNodeStatus('controller', 'testing');\r\n await this.delay(500);\r\n this.updateNodeStatus('controller', 'passed');\r\n this.updateAgent('tester-croc', { status: 'done', currentTask: 'Tests generated', progress: 100 });\r\n\r\n // Phase 5: Report\r\n this.updateAgent('reporter-croc', { status: 'working', currentTask: 'Building report...', progress: 0 });\r\n this.log('🐊 汇报鳄 is compiling results...');\r\n await this.delay(400);\r\n this.updateNodeStatus('module', 'passed');\r\n this.updateAgent('reporter-croc', { status: 'done', currentTask: 'Report ready', progress: 100 });\r\n\r\n const duration = Date.now() - start;\r\n this.log(`✅ Pipeline complete in ${duration}ms`);\r\n this.broadcast('pipeline:complete', { duration, status: 'success' });\r\n return { ok: true, task: 'pipeline', duration };\r\n } catch (err) {\r\n this.updateAgent('tester-croc', { status: 'error', currentTask: String(err) });\r\n this.log(`❌ Pipeline failed: ${err}`, 'error');\r\n this.broadcast('pipeline:complete', { status: 'error', error: String(err) });\r\n return { ok: false, task: 'pipeline', duration: Date.now() - start, error: String(err) };\r\n } finally {\r\n this.running = false;\r\n }\r\n }\r\n\r\n /** Reset all agents to idle */\r\n resetAgents(): void {\r\n for (const agent of this.agents) {\r\n agent.status = 'idle';\r\n agent.currentTask = undefined;\r\n agent.progress = undefined;\r\n }\r\n this.broadcast('agent:update', this.agents);\r\n }\r\n\r\n // ============ Graph Helpers ============\r\n\r\n private updateNodeStatus(type: KnowledgeGraphNode['type'], status: KnowledgeGraphNode['status']): void {\r\n if (!this.cachedGraph) return;\r\n for (const node of this.cachedGraph.nodes) {\r\n if (node.type === type) {\r\n node.status = status;\r\n }\r\n }\r\n this.broadcast('graph:update', this.cachedGraph);\r\n }\r\n\r\n private delay(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n }\r\n\r\n /** Build knowledge graph from project source code */\r\n async buildKnowledgeGraph(): Promise<KnowledgeGraph> {\r\n if (this.cachedGraph) return this.cachedGraph;\r\n\r\n this.updateAgent('parser-croc', { status: 'working', currentTask: 'Scanning project structure...', progress: 20 });\r\n\r\n try {\r\n const { resolve: resolvePath } = await import('node:path');\r\n const { glob } = await import('glob');\r\n\r\n const backendRoot = resolvePath(this.cwd, this.config.backendRoot);\r\n const nodes: KnowledgeGraphNode[] = [];\r\n const edges: KnowledgeGraphEdge[] = [];\r\n const moduleSet = new Set<string>();\r\n\r\n // Scan for models\r\n this.updateAgent('parser-croc', { progress: 40, currentTask: 'Scanning models...' });\r\n const modelFiles = await glob('**/models/**/*.{ts,js}', {\r\n cwd: backendRoot,\r\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*', '**/index.*'],\r\n });\r\n\r\n for (const file of modelFiles) {\r\n const parts = file.split('/');\r\n const moduleName = parts.length >= 3 ? parts[parts.length - 3] : 'default';\r\n const fileName = parts[parts.length - 1].replace(/\\.(ts|js)$/, '');\r\n const nodeId = `model:${fileName}`;\r\n\r\n moduleSet.add(moduleName);\r\n nodes.push({\r\n id: nodeId,\r\n label: fileName,\r\n type: 'model',\r\n status: 'idle',\r\n module: moduleName,\r\n });\r\n }\r\n\r\n // Scan for controllers\r\n this.updateAgent('parser-croc', { progress: 70, currentTask: 'Scanning controllers...' });\r\n const controllerFiles = await glob('**/controllers/**/*.{ts,js}', {\r\n cwd: backendRoot,\r\n ignore: ['**/node_modules/**', '**/*.test.*', '**/*.spec.*', '**/index.*'],\r\n });\r\n\r\n for (const file of controllerFiles) {\r\n const parts = file.split('/');\r\n const moduleName = parts.length >= 3 ? parts[parts.length - 3] : 'default';\r\n const fileName = parts[parts.length - 1].replace(/\\.(ts|js)$/, '').replace('.controller', '');\r\n const nodeId = `controller:${fileName}`;\r\n\r\n moduleSet.add(moduleName);\r\n nodes.push({\r\n id: nodeId,\r\n label: `${fileName} (ctrl)`,\r\n type: 'controller',\r\n status: 'idle',\r\n module: moduleName,\r\n });\r\n\r\n // Link controller to its model\r\n const modelNode = nodes.find((n) => n.type === 'model' && n.label.toLowerCase() === fileName.toLowerCase());\r\n if (modelNode) {\r\n edges.push({ source: nodeId, target: modelNode.id, relation: 'uses' });\r\n }\r\n }\r\n\r\n // Add module nodes\r\n this.updateAgent('parser-croc', { progress: 90, currentTask: 'Building graph...' });\r\n for (const mod of moduleSet) {\r\n const moduleNodeId = `module:${mod}`;\r\n nodes.push({\r\n id: moduleNodeId,\r\n label: mod,\r\n type: 'module',\r\n status: 'idle',\r\n });\r\n\r\n // Link models/controllers to their module\r\n for (const n of nodes) {\r\n if (n.module === mod && n.type !== 'module') {\r\n edges.push({ source: moduleNodeId, target: n.id, relation: 'contains' });\r\n }\r\n }\r\n }\r\n\r\n this.cachedGraph = { nodes, edges };\r\n this.updateAgent('parser-croc', { status: 'done', currentTask: `Found ${nodes.length} nodes`, progress: 100 });\r\n this.broadcast('graph:update', this.cachedGraph);\r\n return this.cachedGraph;\r\n\r\n } catch (err) {\r\n this.updateAgent('parser-croc', { status: 'error', currentTask: String(err) });\r\n return { nodes: [], edges: [] };\r\n }\r\n }\r\n\r\n invalidateCache(): void {\r\n this.cachedGraph = null;\r\n }\r\n\r\n async getProjectInfo(): Promise<ProjectInfo> {\r\n const graph = await this.buildKnowledgeGraph();\r\n const stats = {\r\n modules: graph.nodes.filter((n) => n.type === 'module').length,\r\n models: graph.nodes.filter((n) => n.type === 'model').length,\r\n endpoints: graph.nodes.filter((n) => n.type === 'api' || n.type === 'controller').length,\r\n relations: graph.edges.length,\r\n };\r\n\r\n return {\r\n name: this.config.backendRoot.split('/').pop() || 'project',\r\n backendRoot: this.config.backendRoot,\r\n adapter: typeof this.config.adapter === 'string' ? this.config.adapter : 'custom',\r\n stats,\r\n graph,\r\n agents: this.agents,\r\n };\r\n }\r\n}\r\n","import Fastify from 'fastify';\r\nimport fastifyStatic from '@fastify/static';\r\nimport fastifyWebsocket from '@fastify/websocket';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { dirname, join, resolve } from 'node:path';\r\nimport { existsSync } from 'node:fs';\r\nimport { registerProjectRoutes } from './routes/project.js';\r\nimport { registerAgentRoutes } from './routes/agents.js';\r\nimport { CrocOffice } from './croc-office.js';\r\nimport type { OpenCrocConfig } from '../types.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = dirname(__filename);\r\n\r\nexport interface ServeOptions {\r\n port: number;\r\n host: string;\r\n open: boolean;\r\n config: OpenCrocConfig;\r\n cwd: string;\r\n}\r\n\r\nexport async function startServer(opts: ServeOptions): Promise<void> {\r\n const app = Fastify({ logger: false });\r\n\r\n // --- WebSocket ---\r\n await app.register(fastifyWebsocket);\r\n\r\n // --- Static frontend assets ---\r\n const webDir = resolve(__dirname, '../web');\r\n if (existsSync(webDir)) {\r\n await app.register(fastifyStatic, {\r\n root: webDir,\r\n prefix: '/',\r\n decorateReply: false,\r\n });\r\n }\r\n\r\n // --- Croc Office (Agent orchestrator) ---\r\n const office = new CrocOffice(opts.config, opts.cwd);\r\n\r\n // --- REST API routes ---\r\n registerProjectRoutes(app, office);\r\n registerAgentRoutes(app, office);\r\n\r\n // --- WebSocket endpoint for real-time updates ---\r\n app.register(async (fastify) => {\r\n fastify.get('/ws', { websocket: true }, (socket) => {\r\n office.addClient(socket);\r\n socket.on('close', () => office.removeClient(socket));\r\n });\r\n });\r\n\r\n // --- SPA fallback: serve index.html for non-API, non-asset routes ---\r\n app.setNotFoundHandler((req, reply) => {\r\n if (req.url.startsWith('/api/')) {\r\n reply.code(404).send({ error: 'Not found' });\r\n return;\r\n }\r\n const indexPath = join(webDir, 'index.html');\r\n if (existsSync(indexPath)) {\r\n reply.sendFile('index.html');\r\n } else {\r\n reply.code(200).header('content-type', 'text/html').send(getEmbeddedHtml());\r\n }\r\n });\r\n\r\n try {\r\n await app.listen({ port: opts.port, host: opts.host });\r\n const url = `http://${opts.host === '0.0.0.0' ? 'localhost' : opts.host}:${opts.port}`;\r\n console.log(`\\n 🐊 OpenCroc Studio is running at ${url}\\n`);\r\n\r\n if (opts.open) {\r\n const { exec } = await import('node:child_process');\r\n const cmd = process.platform === 'win32' ? 'start' :\r\n process.platform === 'darwin' ? 'open' : 'xdg-open';\r\n exec(`${cmd} ${url}`);\r\n }\r\n } catch (err) {\r\n console.error('Failed to start server:', err);\r\n process.exit(1);\r\n }\r\n}\r\n\r\n/** Minimal embedded HTML when no web build is present */\r\nfunction getEmbeddedHtml(): string {\r\n return `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"utf-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n<title>OpenCroc Studio 🐊</title>\r\n<style>\r\n * { margin:0; padding:0; box-sizing:border-box; }\r\n body { background:#1a1a2e; color:#e0e0e0; font-family:'Courier New',monospace; display:flex; justify-content:center; align-items:center; min-height:100vh; }\r\n .container { text-align:center; }\r\n h1 { font-size:3rem; color:#4ecca3; margin-bottom:1rem; }\r\n .croc { font-size:6rem; animation: bounce 1s infinite alternate; }\r\n @keyframes bounce { from{transform:translateY(0)} to{transform:translateY(-20px)} }\r\n p { margin-top:1rem; color:#888; }\r\n .status { margin-top:2rem; padding:1rem; background:#16213e; border-radius:8px; }\r\n #graph-container { margin-top:2rem; min-height:400px; background:#0f3460; border-radius:8px; position:relative; }\r\n .loading { color:#4ecca3; padding:2rem; }\r\n</style>\r\n</head>\r\n<body>\r\n<div class=\"container\">\r\n <div class=\"croc\">🐊</div>\r\n <h1>OpenCroc Studio</h1>\r\n <p>AI-native E2E testing — Pixel Croc Office</p>\r\n <div class=\"status\" id=\"status\">Connecting...</div>\r\n <div id=\"graph-container\"><div class=\"loading\">Loading project graph...</div></div>\r\n</div>\r\n<script>\r\n(async () => {\r\n // Fetch project graph data\r\n try {\r\n const res = await fetch('/api/project');\r\n const data = await res.json();\r\n document.getElementById('status').innerHTML =\r\n '<b>Project:</b> ' + (data.name || 'unknown') +\r\n ' | <b>Modules:</b> ' + (data.stats?.modules || 0) +\r\n ' | <b>Models:</b> ' + (data.stats?.models || 0) +\r\n ' | <b>APIs:</b> ' + (data.stats?.endpoints || 0);\r\n\r\n renderGraph(data.graph);\r\n } catch(e) {\r\n document.getElementById('status').textContent = 'Error loading project: ' + e.message;\r\n }\r\n\r\n // WebSocket for live updates\r\n const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\r\n const ws = new WebSocket(protocol + '//' + location.host + '/ws');\r\n ws.onmessage = (e) => {\r\n try {\r\n const msg = JSON.parse(e.data);\r\n if (msg.type === 'agent:update') {\r\n updateAgentStatus(msg.payload);\r\n } else if (msg.type === 'graph:update') {\r\n renderGraph(msg.payload);\r\n }\r\n } catch {}\r\n };\r\n ws.onclose = () => {\r\n document.getElementById('status').textContent += ' [disconnected]';\r\n };\r\n})();\r\n\r\nfunction renderGraph(graph) {\r\n if (!graph || (!graph.nodes?.length)) {\r\n document.getElementById('graph-container').innerHTML = '<div class=\"loading\">No modules found. Run opencroc init first.</div>';\r\n return;\r\n }\r\n\r\n const container = document.getElementById('graph-container');\r\n const w = container.clientWidth || 800;\r\n const h = 500;\r\n\r\n // Simple force-directed placement\r\n const nodes = graph.nodes.map((n, i) => ({\r\n ...n,\r\n x: w/2 + Math.cos(i * 2 * Math.PI / graph.nodes.length) * Math.min(w,h) * 0.35,\r\n y: h/2 + Math.sin(i * 2 * Math.PI / graph.nodes.length) * Math.min(w,h) * 0.35,\r\n vx: 0, vy: 0,\r\n }));\r\n\r\n const nodeMap = new Map(nodes.map(n => [n.id, n]));\r\n\r\n // Render SVG\r\n const colors = { model:'#4ecca3', controller:'#e94560', api:'#f39c12', dto:'#3498db', default:'#888' };\r\n\r\n let svg = '<svg width=\"'+w+'\" height=\"'+h+'\" xmlns=\"http://www.w3.org/2000/svg\">';\r\n\r\n // Edges\r\n for (const edge of (graph.edges || [])) {\r\n const s = nodeMap.get(edge.source);\r\n const t = nodeMap.get(edge.target);\r\n if (s && t) {\r\n svg += '<line x1=\"'+s.x+'\" y1=\"'+s.y+'\" x2=\"'+t.x+'\" y2=\"'+t.y+'\" stroke=\"#555\" stroke-width=\"1.5\" opacity=\"0.6\"/>';\r\n }\r\n }\r\n\r\n // Nodes\r\n for (const n of nodes) {\r\n const color = colors[n.type] || colors.default;\r\n const statusColor = n.status === 'passed' ? '#4ecca3' : n.status === 'failed' ? '#e94560' : n.status === 'testing' ? '#f39c12' : '#555';\r\n // Pixel-art style square nodes\r\n svg += '<rect x=\"'+(n.x-16)+'\" y=\"'+(n.y-16)+'\" width=\"32\" height=\"32\" fill=\"'+color+'\" rx=\"4\" stroke=\"'+statusColor+'\" stroke-width=\"2\"/>';\r\n svg += '<text x=\"'+n.x+'\" y=\"'+(n.y+32)+'\" text-anchor=\"middle\" fill=\"#ccc\" font-size=\"10\" font-family=\"Courier New\">'+escapeHtml(n.label || n.id)+'</text>';\r\n }\r\n\r\n svg += '</svg>';\r\n container.innerHTML = svg;\r\n}\r\n\r\nfunction escapeHtml(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }\r\n\r\nfunction updateAgentStatus(agents) {\r\n // Will be enhanced with pixel croc animations in M2\r\n console.log('Agent update:', agents);\r\n}\r\n</script>\r\n</body>\r\n</html>`;\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadConfig } from '../load-config.js';\r\nimport type { OpenCrocConfig } from '../../types.js';\r\n\r\nexport interface ServeCommandOptions {\r\n port?: string;\r\n host?: string;\r\n open?: boolean;\r\n}\r\n\r\nexport async function serve(opts: ServeCommandOptions): Promise<void> {\r\n let config: OpenCrocConfig;\r\n let configPath: string;\r\n\r\n try {\r\n const loaded = await loadConfig();\r\n config = loaded.config;\r\n configPath = loaded.filepath;\r\n } catch {\r\n // No config file — use sensible defaults based on cwd\r\n config = { backendRoot: '.' };\r\n configPath = '(auto-detected)';\r\n console.log(chalk.yellow('⚠ No opencroc config found, using current directory as backend root.'));\r\n console.log(chalk.gray(' Tip: run `opencroc init` to create a config file.\\n'));\r\n }\r\n\r\n const port = parseInt(opts.port || '8765', 10);\r\n const host = opts.host || 'localhost';\r\n\r\n console.log(chalk.cyan('🐊 Starting OpenCroc Studio...'));\r\n console.log(chalk.gray(` Config: ${configPath}`));\r\n console.log(chalk.gray(` Backend: ${config.backendRoot}`));\r\n\r\n const { startServer } = await import('../../server/index.js');\r\n await startServer({\r\n port,\r\n host,\r\n open: opts.open ?? true,\r\n config,\r\n cwd: process.cwd(),\r\n });\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('opencroc')\r\n .description('AI-native E2E testing framework')\r\n .version('1.0.0');\r\n\r\nprogram\r\n .command('init')\r\n .description('Initialize OpenCroc in the current project')\r\n .option('-y, --yes', 'Skip prompts and use defaults')\r\n .action(async (opts) => {\r\n const { initProject } = await import('./commands/init.js');\r\n await initProject(opts);\r\n });\r\n\r\nprogram\r\n .command('generate')\r\n .description('Generate E2E test cases from source code')\r\n .option('-m, --module <name>', 'Generate for a specific module')\r\n .option('-a, --all', 'Generate for all discovered modules')\r\n .option('--steps <steps>', 'Run specific pipeline steps (comma-separated)')\r\n .option('--dry-run', 'Preview without writing files')\r\n .action(async (opts) => {\r\n const { generate } = await import('./commands/generate.js');\r\n await generate(opts);\r\n });\r\n\r\nprogram\r\n .command('test')\r\n .description('Run generated E2E tests')\r\n .option('-m, --module <name>', 'Run tests for a specific module')\r\n .option('--headed', 'Run in headed browser mode')\r\n .option('--setup-hook <cmd>', 'Run setup hook command before test execution')\r\n .option('--auth-hook <cmd>', 'Run auth hook command before test execution')\r\n .option('--teardown-hook <cmd>', 'Run teardown hook command after test execution')\r\n .action(async (opts) => {\r\n const { runTests } = await import('./commands/test.js');\r\n await runTests(opts);\r\n });\r\n\r\nprogram\r\n .command('validate')\r\n .description('Validate module configurations and generated tests')\r\n .option('-m, --module <name>', 'Validate a specific module')\r\n .action(async (opts) => {\r\n const { validate } = await import('./commands/validate.js');\r\n await validate(opts);\r\n });\r\n\r\nprogram\r\n .command('heal')\r\n .description('Run self-healing loop on failed tests')\r\n .option('-m, --module <name>', 'Heal a specific module')\r\n .option('--max-iterations <n>', 'Maximum healing iterations', '3')\r\n .action(async (opts) => {\r\n const { heal } = await import('./commands/heal.js');\r\n await heal(opts);\r\n });\r\n\r\nprogram\r\n .command('ci')\r\n .description('Generate CI/CD pipeline template')\r\n .option('-p, --platform <name>', 'CI platform (github, gitlab)', 'github')\r\n .option('--self-heal', 'Include self-healing step')\r\n .option('--node <versions>', 'Node.js versions (comma-separated)', '20.x')\r\n .action(async (opts) => {\r\n const { ci } = await import('./commands/ci.js');\r\n await ci(opts);\r\n });\r\n\r\nprogram\r\n .command('report')\r\n .description('Generate pipeline report (HTML/JSON/Markdown)')\r\n .option('-f, --format <formats>', 'Report formats (comma-separated)', 'html')\r\n .option('-o, --output <dir>', 'Output directory')\r\n .action(async (opts) => {\r\n const { report } = await import('./commands/report.js');\r\n await report(opts);\r\n });\r\n\r\nprogram\r\n .command('dashboard')\r\n .description('Generate visual dashboard (opencroc-dashboard.html)')\r\n .option('-i, --input <file>', 'Build from existing opencroc-report.json file')\r\n .option('-o, --output <dir>', 'Output directory', './opencroc-output')\r\n .action(async (opts) => {\r\n const { dashboard } = await import('./commands/dashboard.js');\r\n await dashboard(opts);\r\n });\r\n\r\nprogram\r\n .command('init-runtime')\r\n .description('Generate Playwright runtime infrastructure (config, setup, teardown, auth)')\r\n .option('-o, --output <dir>', 'Output directory for generated files', '.')\r\n .option('--force', 'Overwrite existing files')\r\n .action(async (opts) => {\r\n const { initRuntime } = await import('./commands/init-runtime.js');\r\n await initRuntime(opts);\r\n });\r\n\r\nprogram\r\n .command('run')\r\n .description('Full orchestration: generate → execute → analyze → heal → report')\r\n .option('-m, --module <name>', 'Run for a specific module')\r\n .option('--phases <phases>', 'Phases to run (comma-separated: generate,execute,analyze,heal,report)')\r\n .option('--self-heal', 'Enable self-healing on test failures')\r\n .option('--headed', 'Run Playwright in headed mode')\r\n .option('--report <formats>', 'Report formats (comma-separated)', 'html,json')\r\n .option('--token-budget <n>', 'LLM token budget (0 = unlimited)')\r\n .option('--abort-on-error', 'Abort pipeline on first phase error')\r\n .action(async (opts) => {\r\n const { run } = await import('./commands/run.js');\r\n await run(opts);\r\n });\r\n\r\nprogram\r\n .command('serve')\r\n .description('Start OpenCroc Studio — pixel croc office + knowledge graph UI')\r\n .option('-p, --port <port>', 'Server port', '8765')\r\n .option('-H, --host <host>', 'Server host', 'localhost')\r\n .option('--no-open', 'Do not auto-open browser')\r\n .action(async (opts) => {\r\n const { serve } = await import('./commands/serve.js');\r\n await serve(opts);\r\n });\r\n\r\nprogram.parse();\r\n"],"mappings":"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","names":["path","fs","path","Project","SyntaxKind","fullPath","fs","path","Project","SyntaxKind","producer","path","Project","report","fs","path","chalk","writeFileSync","mkdirSync","existsSync","VALID_STEPS","chalk","readdirSync","existsSync","join","resolve","chalk","existsSync","readFileSync","writeFileSync","mkdirSync","dirname","chalk","fs","path","chalk","init_ci","fs","path","chalk","escapeHtml","fs","path","chalk","init_dashboard","chalk","existsSync","mkdirSync","writeFileSync","join","resolve","writeFileSync","mkdirSync","existsSync","readdirSync","dirname","join","execFileSync","discoverTestFiles","writeFileSync","mkdirSync","existsSync","join","chalk","resolve","fileURLToPath","dirname","join","resolve","existsSync","__dirname","__filename","chalk","startServer","initProject","generate","runTests","validate","heal","ci","report","dashboard","initRuntime","run","serve"]}