@open-mercato/cli 0.6.4-develop.4382.1.6b4f656b77 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/agentic/guides/core.customers.md +1 -1
- package/dist/agentic/shared/AGENTS.md.template +56 -33
- package/dist/agentic/shared/ai/skills/{auto-continue-pr → om-auto-continue-pr}/SKILL.md +19 -19
- package/dist/agentic/shared/ai/skills/{auto-create-pr → om-auto-continue-pr}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-continue-pr-loop → om-auto-continue-pr-loop}/SKILL.md +32 -32
- package/dist/agentic/shared/ai/skills/{auto-create-pr → om-auto-create-pr}/SKILL.md +24 -24
- package/dist/agentic/shared/ai/skills/{auto-fix-github → om-auto-create-pr}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-create-pr-loop → om-auto-create-pr-loop}/SKILL.md +40 -40
- package/dist/agentic/shared/ai/skills/{auto-fix-github → om-auto-fix-github}/SKILL.md +3 -3
- package/dist/agentic/shared/ai/skills/{auto-continue-pr → om-auto-fix-github}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-review-pr → om-auto-review-pr}/SKILL.md +5 -5
- package/dist/agentic/shared/ai/skills/{auto-review-pr → om-auto-review-pr}/STANDALONE.md +1 -1
- package/dist/agentic/shared/ai/skills/{auto-upgrade-0.4.10-to-0.5.0 → om-auto-upgrade-0.4.10-to-0.5.0}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{backend-ui-design → om-backend-ui-design}/SKILL.md +2 -2
- package/dist/agentic/shared/ai/skills/{code-review → om-code-review}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{code-review → om-code-review}/references/review-checklist.md +3 -0
- package/dist/agentic/shared/ai/skills/{data-model-design → om-data-model-design}/SKILL.md +4 -2
- package/dist/agentic/shared/ai/skills/{eject-and-customize → om-eject-and-customize}/SKILL.md +2 -2
- package/dist/agentic/shared/ai/skills/{implement-spec → om-implement-spec}/SKILL.md +6 -6
- package/dist/agentic/shared/ai/skills/{integration-builder → om-integration-builder}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{integration-tests → om-integration-tests}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{module-scaffold → om-module-scaffold}/SKILL.md +5 -3
- package/dist/agentic/shared/ai/skills/{module-scaffold → om-module-scaffold}/references/naming-conventions.md +7 -8
- package/dist/agentic/shared/ai/skills/om-prepare-issue/SKILL.md +202 -0
- package/dist/agentic/shared/ai/skills/{spec-writing → om-spec-writing}/SKILL.md +6 -6
- package/dist/agentic/shared/ai/skills/{spec-writing → om-spec-writing}/references/spec-checklist.md +3 -3
- package/dist/agentic/shared/ai/skills/{system-extension → om-system-extension}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{trim-unused-modules → om-trim-unused-modules}/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/{troubleshooter → om-troubleshooter}/SKILL.md +1 -1
- package/dist/bin.js +1 -0
- package/dist/bin.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-008.spec.js +1 -1
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-setup.js +52 -51
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.meta.js +7 -0
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.meta.js.map +7 -0
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.spec.js +63 -0
- package/dist/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.spec.js.map +7 -0
- package/dist/lib/deploy/railway/env.js +98 -0
- package/dist/lib/deploy/railway/env.js.map +7 -0
- package/dist/lib/deploy/railway/graphql-client.js +99 -0
- package/dist/lib/deploy/railway/graphql-client.js.map +7 -0
- package/dist/lib/deploy/railway/index.js +1115 -0
- package/dist/lib/deploy/railway/index.js.map +7 -0
- package/dist/lib/deploy/railway/operations.js +249 -0
- package/dist/lib/deploy/railway/operations.js.map +7 -0
- package/dist/lib/deploy/railway/options.js +155 -0
- package/dist/lib/deploy/railway/options.js.map +7 -0
- package/dist/lib/deploy/railway/railway-cli.js +61 -0
- package/dist/lib/deploy/railway/railway-cli.js.map +7 -0
- package/dist/lib/deploy/railway/redaction.js +88 -0
- package/dist/lib/deploy/railway/redaction.js.map +7 -0
- package/dist/lib/deploy/railway/source.js +128 -0
- package/dist/lib/deploy/railway/source.js.map +7 -0
- package/dist/lib/deploy/railway/state.js +41 -0
- package/dist/lib/deploy/railway/state.js.map +7 -0
- package/dist/lib/deploy/railway/token.js +46 -0
- package/dist/lib/deploy/railway/token.js.map +7 -0
- package/dist/lib/deploy/railway/types.js +1 -0
- package/dist/lib/deploy/railway/types.js.map +7 -0
- package/dist/lib/generators/openapi.js +4 -0
- package/dist/lib/generators/openapi.js.map +2 -2
- package/dist/lib/testing/integration.js +57 -26
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/lib/testing/runtime-utils.js +16 -8
- package/dist/lib/testing/runtime-utils.js.map +2 -2
- package/dist/mercato.js +23 -8
- package/dist/mercato.js.map +2 -2
- package/package.json +11 -12
- package/src/bin.ts +1 -0
- package/src/lib/__integration__/TC-INT-008.spec.ts +1 -1
- package/src/lib/agentic-setup.ts +52 -51
- package/src/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.meta.ts +3 -0
- package/src/lib/deploy/railway/__integration__/TC-CLI-RAILWAY-001.spec.ts +71 -0
- package/src/lib/deploy/railway/__tests__/dry-run.test.ts +61 -0
- package/src/lib/deploy/railway/__tests__/env.test.ts +55 -0
- package/src/lib/deploy/railway/__tests__/graphql-client.test.ts +80 -0
- package/src/lib/deploy/railway/__tests__/operations.test.ts +21 -0
- package/src/lib/deploy/railway/__tests__/options.test.ts +78 -0
- package/src/lib/deploy/railway/__tests__/redaction.test.ts +62 -0
- package/src/lib/deploy/railway/__tests__/source.test.ts +130 -0
- package/src/lib/deploy/railway/__tests__/state.test.ts +24 -0
- package/src/lib/deploy/railway/__tests__/token.test.ts +39 -0
- package/src/lib/deploy/railway/env.ts +112 -0
- package/src/lib/deploy/railway/graphql-client.ts +128 -0
- package/src/lib/deploy/railway/index.ts +1454 -0
- package/src/lib/deploy/railway/operations.ts +248 -0
- package/src/lib/deploy/railway/options.ts +167 -0
- package/src/lib/deploy/railway/railway-cli.ts +65 -0
- package/src/lib/deploy/railway/redaction.ts +91 -0
- package/src/lib/deploy/railway/source.ts +143 -0
- package/src/lib/deploy/railway/state.ts +45 -0
- package/src/lib/deploy/railway/token.ts +49 -0
- package/src/lib/deploy/railway/types.ts +87 -0
- package/src/lib/generators/openapi.ts +3 -0
- package/src/lib/testing/__tests__/integration.test.ts +79 -0
- package/src/lib/testing/__tests__/runtime-utils.test.ts +45 -1
- package/src/lib/testing/integration.ts +74 -28
- package/src/lib/testing/runtime-utils.ts +17 -8
- package/src/mercato.ts +33 -8
- /package/dist/agentic/shared/ai/skills/{backend-ui-design → om-backend-ui-design}/references/ui-components.md +0 -0
- /package/dist/agentic/shared/ai/skills/{data-model-design → om-data-model-design}/references/mikro-orm-cheatsheet.md +0 -0
- /package/dist/agentic/shared/ai/skills/{integration-builder → om-integration-builder}/references/adapter-contracts.md +0 -0
- /package/dist/agentic/shared/ai/skills/{module-scaffold → om-module-scaffold}/references/navigation-patterns.md +0 -0
- /package/dist/agentic/shared/ai/skills/{spec-writing → om-spec-writing}/references/spec-template.md +0 -0
- /package/dist/agentic/shared/ai/skills/{system-extension → om-system-extension}/references/extension-contracts.md +0 -0
- /package/dist/agentic/shared/ai/skills/{troubleshooter → om-troubleshooter}/references/diagnostic-commands.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/generators/openapi.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * OpenAPI JSON Generator\n *\n * Generates a static openapi.generated.json file at build time.\n * This allows CLI tools (like MCP dev server) to access API endpoint\n * information without requiring a running Next.js app.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport { isModuleRouteFile } from './scanner'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface GenerateOpenApiOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\ninterface ApiRouteInfo {\n path: string\n methods: HttpMethod[]\n openApiPath: string\n}\n\nfunction resolveExistingPath(paths: string[]): string | null {\n for (const candidate of paths) {\n if (fs.existsSync(candidate)) return candidate\n }\n return null\n}\n\n/**\n * Find all API route files and extract their OpenAPI specs.\n */\nasync function findApiRoutes(resolver: PackageResolver): Promise<ApiRouteInfo[]> {\n const routes: ApiRouteInfo[] = []\n const enabled = resolver.loadEnabledModules()\n\n for (const entry of enabled) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n\n const apiApp = path.join(roots.appBase, 'api')\n const apiPkg = path.join(roots.pkgBase, 'api')\n\n // Scan route files\n const routeFiles: Array<{ relativePath: string; fullPath: string }> = []\n\n const walkDir = (dir: string, rel: string[] = []) => {\n if (!fs.existsSync(dir)) return\n for (const e of fs.readdirSync(dir, { withFileTypes: true })) {\n if (e.isDirectory()) {\n if (e.name === '__tests__' || e.name === '__mocks__') continue\n walkDir(path.join(dir, e.name), [...rel, e.name])\n } else if (e.isFile() && isModuleRouteFile(e.name)) {\n routeFiles.push({\n relativePath: [...rel].join('/'),\n fullPath: path.join(dir, e.name),\n })\n }\n }\n }\n\n // Scan package first, then app (app overrides)\n if (fs.existsSync(apiPkg)) walkDir(apiPkg)\n if (fs.existsSync(apiApp)) walkDir(apiApp)\n\n // Process unique routes (later app files overwrite earlier package files)\n const filesByRelativePath = new Map<string, string>()\n for (const { relativePath, fullPath } of routeFiles) {\n filesByRelativePath.set(relativePath, fullPath)\n }\n\n for (const [relativePath, fullPath] of filesByRelativePath) {\n\n // Build API path\n const routeSegs = relativePath ? relativePath.split('/') : []\n const apiPath = `/api/${modId}${routeSegs.length ? '/' + routeSegs.join('/') : ''}`\n // Convert [param] to {param} for OpenAPI format\n .replace(/\\[([^\\]]+)\\]/g, '{$1}')\n\n routes.push({\n path: fullPath,\n methods: await detectMethods(fullPath),\n openApiPath: apiPath,\n })\n }\n }\n\n return routes\n}\n\n/**\n * Detect which HTTP methods are exported from a route file.\n */\nasync function detectMethods(filePath: string): Promise<HttpMethod[]> {\n const methods: HttpMethod[] = []\n const content = fs.readFileSync(filePath, 'utf-8')\n\n const methodPatterns: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n for (const method of methodPatterns) {\n // Check for export { GET }, export const GET, export async function GET\n const patterns = [\n new RegExp(`export\\\\s+(const|async\\\\s+function|function)\\\\s+${method}\\\\b`),\n new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${method}\\\\b[^}]*\\\\}`),\n ]\n if (patterns.some((p) => p.test(content))) {\n methods.push(method)\n }\n }\n\n return methods\n}\n\n/**\n * Parse openApi export from route file source code statically.\n * This extracts basic operation info without needing to compile the file.\n */\nfunction parseOpenApiFromSource(filePath: string): Record<string, any> | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n\n // Check if file exports openApi\n if (!content.includes('export const openApi') && !content.includes('export { openApi')) {\n return null\n }\n\n // Extract operationId, summary, description from the source\n const result: Record<string, any> = {}\n const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n\n for (const method of methods) {\n // Look for method specs in the openApi object\n // Pattern: GET: { operationId: '...', summary: '...', ... }\n const methodPattern = new RegExp(\n `${method}\\\\s*:\\\\s*\\\\{([^}]+(?:\\\\{[^}]*\\\\}[^}]*)*)\\\\}`,\n 's'\n )\n const methodMatch = content.match(methodPattern)\n\n if (methodMatch) {\n const methodContent = methodMatch[1]\n const spec: Record<string, any> = {}\n\n // Extract operationId\n const opIdMatch = methodContent.match(/operationId\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (opIdMatch) spec.operationId = opIdMatch[1]\n\n // Extract summary\n const summaryMatch = methodContent.match(/summary\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (summaryMatch) spec.summary = summaryMatch[1]\n\n // Extract description\n const descMatch = methodContent.match(/description\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (descMatch) spec.description = descMatch[1]\n\n // Extract tags\n const tagsMatch = methodContent.match(/tags\\s*:\\s*\\[([^\\]]*)\\]/)\n if (tagsMatch) {\n const tagsContent = tagsMatch[1]\n const tags = tagsContent.match(/['\"]([^'\"]+)['\"]/g)\n if (tags) {\n spec.tags = tags.map(t => t.replace(/['\"]/g, ''))\n }\n }\n\n if (Object.keys(spec).length > 0) {\n result[method] = spec\n }\n }\n }\n\n return Object.keys(result).length > 0 ? result : null\n } catch {\n return null\n }\n}\n\n/**\n * Generate a complete OpenAPI document by bundling route files with esbuild\n * and executing the bundle to call buildOpenApiDocument from @open-mercato/shared.\n *\n * esbuild compiles TypeScript with legacy decorator support (reads experimentalDecorators\n * from tsconfig.json), avoiding the TC39 decorator mismatch that breaks tsx-based imports.\n * External packages (zod, mikro-orm, etc.) are resolved from node_modules at runtime.\n */\nasync function generateOpenApiViaBundle(\n routes: ApiRouteInfo[],\n resolver: PackageResolver,\n quiet: boolean\n): Promise<Record<string, any> | null> {\n let esbuild: typeof import('esbuild')\n try {\n esbuild = await import('esbuild')\n } catch {\n if (!quiet) console.log('[OpenAPI] esbuild not available, skipping bundle approach')\n return null\n }\n\n const { execFileSync } = await import('node:child_process')\n\n const rootDir = resolver.getRootDir()\n const appDir = resolver.getAppDir()\n const sharedPackageRoot = resolver.getPackageRoot('@open-mercato/shared')\n const corePackageRoot = resolver.getPackageRoot('@open-mercato/core')\n const tsconfigPath = resolveExistingPath([\n path.join(appDir, 'tsconfig.json'),\n path.join(rootDir, 'tsconfig.base.json'),\n path.join(rootDir, 'tsconfig.json'),\n ])\n const generatorPath = path.join(sharedPackageRoot, 'src', 'lib', 'openapi', 'generator.ts')\n const coreGeneratedRoot = path.join(corePackageRoot, 'generated')\n\n if (!fs.existsSync(generatorPath)) {\n if (!quiet) {\n console.log(`[OpenAPI] Generator source not found at ${generatorPath}, skipping bundle approach`)\n }\n return null\n }\n\n const cacheDir = path.join(rootDir, 'node_modules', '.cache')\n if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true })\n\n const bundlePath = path.join(cacheDir, '_openapi-bundle.mjs')\n\n // Build the entry script that imports all routes and calls buildOpenApiDocument\n const importLines: string[] = [\n `import { buildOpenApiDocument } from ${JSON.stringify(generatorPath)};`,\n ]\n const routeMapLines: string[] = []\n\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i]\n importLines.push(`import * as R${i} from ${JSON.stringify(route.path)};`)\n // Use [param] format so normalizePath in buildOpenApiDocument extracts path params\n const bracketPath = route.openApiPath.replace(/\\{([^}]+)\\}/g, '[$1]')\n routeMapLines.push(` [${JSON.stringify(bracketPath)}, R${i}],`)\n }\n\n const entryScript = `${importLines.join('\\n')}\n\nconst routeEntries = [\n${routeMapLines.join('\\n')}\n];\n\nconst modules = new Map();\nfor (const [apiPath, mod] of routeEntries) {\n const moduleId = apiPath.replace(/^\\\\/api\\\\//, '').split('/')[0];\n if (!modules.has(moduleId)) modules.set(moduleId, { id: moduleId, apis: [] });\n modules.get(moduleId).apis.push({\n path: apiPath,\n handlers: mod,\n metadata: mod.metadata,\n });\n}\n\nconst doc = buildOpenApiDocument([...modules.values()], {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n});\n\n// Deep-clone to break shared object references before serializing.\n// The zodToJsonSchema memo cache returns the same object instance for\n// fields like currencyCode that appear on both parent and child schemas.\n// A naive WeakSet-based circular-ref guard would drop the second occurrence,\n// causing properties to vanish from the generated spec (while the field\n// still appears in the 'required' array, since those are plain strings).\nconst deepClone = (v, ancestors = []) => {\n if (v === null || typeof v !== 'object') return v;\n if (typeof v === 'bigint') return Number(v);\n if (typeof v === 'function') return undefined;\n if (ancestors.includes(v)) return undefined; // true circular ref\n const next = [...ancestors, v];\n if (Array.isArray(v)) return v.map((item) => deepClone(item, next));\n const out = {};\n for (const [k, val] of Object.entries(v)) {\n const cloned = deepClone(val, next);\n if (cloned !== undefined) out[k] = cloned;\n }\n return out;\n};\nprocess.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>\n typeof v === 'bigint' ? Number(v) : v\n));\n`\n\n // Plugin: stub next/* imports (not available outside Next.js app context)\n const stubNextPlugin = {\n name: 'stub-next',\n setup(build: any) {\n build.onResolve({ filter: /^next($|\\/)/ }, () => ({\n path: 'next-stub',\n namespace: 'next-stub',\n }))\n build.onLoad({ filter: /.*/, namespace: 'next-stub' }, () => ({\n contents: [\n 'const p = new Proxy(function(){}, {',\n ' get(_, k) { return k === \"__esModule\" ? true : k === \"default\" ? p : p; },',\n ' apply() { return p; },',\n ' construct() { return p; },',\n '});',\n 'export default p;',\n 'export const NextRequest = p, NextResponse = p, headers = p, cookies = p;',\n 'export const redirect = p, notFound = p, useRouter = p, usePathname = p;',\n 'export const useSearchParams = p, permanentRedirect = p, revalidatePath = p;',\n ].join('\\n'),\n loader: 'js' as const,\n }))\n },\n }\n\n // Plugin: resolve workspace imports, aliases, and subpath imports\n const resolveProjectImportsPlugin = {\n name: 'resolve-project-imports',\n setup(build: any) {\n // @open-mercato/<pkg>/<path> \u2192 packageRoot/src/<path>.ts\n build.onResolve({ filter: /^@open-mercato\\// }, (args: any) => {\n const withoutScope = args.path.slice('@open-mercato/'.length)\n const slashIdx = withoutScope.indexOf('/')\n const pkg = slashIdx === -1 ? withoutScope : withoutScope.slice(0, slashIdx)\n const rest = slashIdx === -1 ? '' : withoutScope.slice(slashIdx + 1)\n const packageName = `@open-mercato/${pkg}`\n const packageRoot = resolver.getPackageRoot(packageName)\n\n const base = rest\n ? path.join(packageRoot, 'src', rest)\n : path.join(packageRoot, 'src', 'index')\n\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/.mercato/* \u2192 app/.mercato/* (tsconfig paths)\n build.onResolve({ filter: /^@\\/\\.mercato\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length) // '.mercato/generated/...'\n const base = path.join(appDir, rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx', '']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/* \u2192 app/src/* (tsconfig paths)\n build.onResolve({ filter: /^@\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length)\n const base = path.join(appDir, 'src', rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // #generated/* \u2192 core package generated/* (Node subpath imports)\n build.onResolve({ filter: /^#generated\\// }, (args: any) => {\n const rest = args.path.slice('#generated/'.length)\n const base = path.join(coreGeneratedRoot, rest)\n for (const ext of ['.ts', '/index.ts']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n },\n }\n\n // Plugin: externalize installed packages, stub missing ones\n const nodeBuiltins = new Set([\n 'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',\n 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2',\n 'https', 'module', 'net', 'os', 'path', 'perf_hooks', 'process',\n 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',\n 'sys', 'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi',\n 'worker_threads', 'zlib', 'async_hooks', 'diagnostics_channel', 'inspector',\n 'trace_events',\n ])\n const externalNonWorkspacePlugin = {\n name: 'external-non-workspace',\n setup(build: any) {\n build.onResolve({ filter: /^[^./]/ }, (args: any) => {\n if (path.isAbsolute(args.path)) return undefined\n if (args.path.startsWith('@open-mercato/')) return undefined\n if (args.path.startsWith('@/')) return undefined\n if (args.path.startsWith('#generated/')) return undefined\n if (args.path.startsWith('next')) return undefined\n // Let esbuild handle Node builtins (with or without node: prefix)\n if (args.path.startsWith('node:')) return undefined\n const topLevel = args.path.split('/')[0]\n if (nodeBuiltins.has(topLevel)) return undefined\n\n // Extract package name (handle scoped packages like @mikro-orm/core)\n const pkgName = args.path.startsWith('@')\n ? args.path.split('/').slice(0, 2).join('/')\n : topLevel\n const pkgDir = path.join(rootDir, 'node_modules', pkgName)\n if (fs.existsSync(pkgDir)) return { external: true }\n\n // Package not installed \u2014 provide CJS stub (allows any named import)\n return { path: args.path, namespace: 'missing-pkg' }\n })\n build.onLoad({ filter: /.*/, namespace: 'missing-pkg' }, () => ({\n contents: 'var h={get:(_,k)=>k===\"__esModule\"?true:p};var p=new Proxy(function(){return p},{get:h.get,apply:()=>p,construct:()=>p});module.exports=p;',\n loader: 'js' as const,\n }))\n },\n }\n\n try {\n await esbuild.build({\n stdin: {\n contents: entryScript,\n resolveDir: appDir,\n sourcefile: 'openapi-entry.ts',\n loader: 'ts',\n },\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n outfile: bundlePath,\n write: true,\n ...(tsconfigPath ? { tsconfig: tsconfigPath } : {}),\n logLevel: 'silent',\n jsx: 'automatic',\n plugins: [stubNextPlugin, resolveProjectImportsPlugin, externalNonWorkspacePlugin],\n })\n\n const stdout = execFileSync(process.execPath, [bundlePath], {\n timeout: 60_000,\n maxBuffer: 20 * 1024 * 1024,\n encoding: 'utf-8',\n env: { ...process.env, NODE_NO_WARNINGS: '1' },\n cwd: rootDir,\n })\n\n const lastLine = stdout.trim().split('\\n').pop()!\n const doc = JSON.parse(lastLine) as Record<string, any>\n\n if (!quiet) {\n const pathCount = Object.keys(doc.paths || {}).length\n const withBody = Object.values(doc.paths || {}).reduce((n: number, methods: any) => {\n for (const m of Object.values(methods)) {\n if ((m as any)?.requestBody) n++\n }\n return n\n }, 0)\n console.log(`[OpenAPI] Bundle approach: ${pathCount} paths, ${withBody} with requestBody schemas`)\n }\n\n return doc\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err)\n const stderr = (err as any)?.stderr\n const esbuildErrors = (err as any)?.errors as Array<{ text: string; location?: { file: string } }> | undefined\n if (!quiet) {\n console.log(`[OpenAPI] Bundle approach failed, will use static fallback: ${errMsg.split('\\n')[0]}`)\n if (esbuildErrors?.length) {\n const unique = new Map<string, string>()\n for (const e of esbuildErrors) {\n const key = e.text\n if (!unique.has(key)) unique.set(key, e.location?.file ?? '')\n }\n for (const [text, file] of [...unique.entries()].slice(0, 10)) {\n console.log(`[OpenAPI] ${text}${file ? ` (${path.basename(file)})` : ''}`)\n }\n if (unique.size > 10) console.log(`[OpenAPI] ... and ${unique.size - 10} more`)\n }\n if (stderr) {\n for (const line of String(stderr).trim().split('\\n').slice(0, 3)) {\n console.log(`[OpenAPI] ${line}`)\n }\n }\n }\n return null\n } finally {\n // Clean up old files from previous tsx-based approach\n for (const file of ['_openapi-register.mjs', '_openapi-loader.mjs', '_next-stub.cjs']) {\n try { fs.unlinkSync(path.join(cacheDir, file)) } catch {}\n }\n }\n}\n\n/**\n * Build OpenAPI paths from discovered routes.\n * Extracts basic operation info from route files statically.\n */\nfunction buildOpenApiPaths(routes: ApiRouteInfo[]): Record<string, any> {\n const paths: Record<string, any> = {}\n\n for (const route of routes) {\n const pathEntry: Record<string, any> = {}\n\n // Try to extract OpenAPI specs from source\n const openApiSpec = parseOpenApiFromSource(route.path)\n\n for (const method of route.methods) {\n const methodLower = method.toLowerCase()\n const spec = openApiSpec?.[method]\n\n // Generate a default operationId if not found\n const pathSegments = route.openApiPath\n .replace(/^\\/api\\//, '')\n .replace(/\\{[^}]+\\}/g, 'by_id')\n .split('/')\n .filter(Boolean)\n .join('_')\n const defaultOperationId = `${methodLower}_${pathSegments}`\n\n pathEntry[methodLower] = {\n operationId: spec?.operationId || defaultOperationId,\n summary: spec?.summary || `${method} ${route.openApiPath}`,\n description: spec?.description || `${method} operation for ${route.openApiPath}`,\n tags: spec?.tags || [route.openApiPath.split('/')[2] || 'api'],\n responses: {\n '200': {\n description: 'Successful response',\n },\n },\n }\n }\n\n if (Object.keys(pathEntry).length > 0) {\n paths[route.openApiPath] = pathEntry\n }\n }\n\n return paths\n}\n\n/**\n * Generate the OpenAPI JSON file.\n */\nexport async function generateOpenApi(options: GenerateOpenApiOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'openapi.generated.json')\n const checksumFile = path.join(outputDir, 'openapi.generated.checksum')\n\n // Ensure output directory exists\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true })\n }\n\n // Find all API routes\n const routes = await findApiRoutes(resolver)\n\n if (!quiet) {\n console.log(`[OpenAPI] Found ${routes.length} API route files`)\n }\n\n // Determine project root (cli package is at packages/cli/src/lib/generators/)\n // Try esbuild bundle approach first \u2014 produces full requestBody/response schemas\n let doc: Record<string, any> | null = await generateOpenApiViaBundle(routes, resolver, quiet)\n\n // Fallback to static regex approach (extracts operationId/summary/tags but no schemas)\n if (!doc) {\n if (!quiet) {\n console.log('[OpenAPI] Falling back to static regex approach')\n }\n const paths = buildOpenApiPaths(routes)\n doc = {\n openapi: '3.1.0',\n info: {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n },\n servers: [\n { url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' },\n ],\n paths,\n components: {\n securitySchemes: {\n bearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: 'Send an `Authorization: Bearer <token>` header with a valid API token.',\n },\n },\n },\n }\n }\n\n const output = JSON.stringify(doc, null, 2)\n const checksum = calculateChecksum(output)\n\n // Check if unchanged\n const existingChecksums = readChecksumRecord(checksumFile)\n if (existingChecksums && existingChecksums.content === checksum && fs.existsSync(outFile)) {\n result.filesUnchanged.push(outFile)\n if (!quiet) {\n console.log(`[OpenAPI] Skipped (unchanged): ${outFile}`)\n }\n return result\n }\n\n // Write the file\n fs.writeFileSync(outFile, output)\n writeChecksumRecord(checksumFile, { content: checksum, structure: '' })\n\n result.filesWritten.push(outFile)\n\n if (!quiet) {\n logGenerationResult(outFile, true)\n const pathCount = Object.keys(doc.paths || {}).length\n console.log(`[OpenAPI] Generated ${pathCount} API paths`)\n }\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * OpenAPI JSON Generator\n *\n * Generates a static openapi.generated.json file at build time.\n * This allows CLI tools (like MCP dev server) to access API endpoint\n * information without requiring a running Next.js app.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport { isModuleRouteFile } from './scanner'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface GenerateOpenApiOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\ninterface ApiRouteInfo {\n path: string\n methods: HttpMethod[]\n openApiPath: string\n}\n\nfunction resolveExistingPath(paths: string[]): string | null {\n for (const candidate of paths) {\n if (fs.existsSync(candidate)) return candidate\n }\n return null\n}\n\n/**\n * Find all API route files and extract their OpenAPI specs.\n */\nasync function findApiRoutes(resolver: PackageResolver): Promise<ApiRouteInfo[]> {\n const routes: ApiRouteInfo[] = []\n const enabled = resolver.loadEnabledModules()\n\n for (const entry of enabled) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n\n const apiApp = path.join(roots.appBase, 'api')\n const apiPkg = path.join(roots.pkgBase, 'api')\n\n // Scan route files\n const routeFiles: Array<{ relativePath: string; fullPath: string }> = []\n\n const walkDir = (dir: string, rel: string[] = []) => {\n if (!fs.existsSync(dir)) return\n for (const e of fs.readdirSync(dir, { withFileTypes: true })) {\n if (e.isDirectory()) {\n if (e.name === '__tests__' || e.name === '__mocks__') continue\n walkDir(path.join(dir, e.name), [...rel, e.name])\n } else if (e.isFile() && isModuleRouteFile(e.name)) {\n routeFiles.push({\n relativePath: [...rel].join('/'),\n fullPath: path.join(dir, e.name),\n })\n }\n }\n }\n\n // Scan package first, then app (app overrides)\n if (fs.existsSync(apiPkg)) walkDir(apiPkg)\n if (fs.existsSync(apiApp)) walkDir(apiApp)\n\n // Process unique routes (later app files overwrite earlier package files)\n const filesByRelativePath = new Map<string, string>()\n for (const { relativePath, fullPath } of routeFiles) {\n filesByRelativePath.set(relativePath, fullPath)\n }\n\n for (const [relativePath, fullPath] of filesByRelativePath) {\n\n // Build API path\n const routeSegs = relativePath ? relativePath.split('/') : []\n const apiPath = `/api/${modId}${routeSegs.length ? '/' + routeSegs.join('/') : ''}`\n // Convert [param] to {param} for OpenAPI format\n .replace(/\\[([^\\]]+)\\]/g, '{$1}')\n\n routes.push({\n path: fullPath,\n methods: await detectMethods(fullPath),\n openApiPath: apiPath,\n })\n }\n }\n\n return routes\n}\n\n/**\n * Detect which HTTP methods are exported from a route file.\n */\nasync function detectMethods(filePath: string): Promise<HttpMethod[]> {\n const methods: HttpMethod[] = []\n const content = fs.readFileSync(filePath, 'utf-8')\n\n const methodPatterns: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n for (const method of methodPatterns) {\n // Check for export { GET }, export const GET, export async function GET\n const patterns = [\n new RegExp(`export\\\\s+(const|async\\\\s+function|function)\\\\s+${method}\\\\b`),\n new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${method}\\\\b[^}]*\\\\}`),\n ]\n if (patterns.some((p) => p.test(content))) {\n methods.push(method)\n }\n }\n\n return methods\n}\n\n/**\n * Parse openApi export from route file source code statically.\n * This extracts basic operation info without needing to compile the file.\n */\nfunction parseOpenApiFromSource(filePath: string): Record<string, any> | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n\n // Check if file exports openApi\n if (!content.includes('export const openApi') && !content.includes('export { openApi')) {\n return null\n }\n\n // Extract operationId, summary, description from the source\n const result: Record<string, any> = {}\n const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n\n for (const method of methods) {\n // Look for method specs in the openApi object\n // Pattern: GET: { operationId: '...', summary: '...', ... }\n const methodPattern = new RegExp(\n `${method}\\\\s*:\\\\s*\\\\{([^}]+(?:\\\\{[^}]*\\\\}[^}]*)*)\\\\}`,\n 's'\n )\n const methodMatch = content.match(methodPattern)\n\n if (methodMatch) {\n const methodContent = methodMatch[1]\n const spec: Record<string, any> = {}\n\n // Extract operationId\n const opIdMatch = methodContent.match(/operationId\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (opIdMatch) spec.operationId = opIdMatch[1]\n\n // Extract summary\n const summaryMatch = methodContent.match(/summary\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (summaryMatch) spec.summary = summaryMatch[1]\n\n // Extract description\n const descMatch = methodContent.match(/description\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (descMatch) spec.description = descMatch[1]\n\n // Extract tags\n const tagsMatch = methodContent.match(/tags\\s*:\\s*\\[([^\\]]*)\\]/)\n if (tagsMatch) {\n const tagsContent = tagsMatch[1]\n const tags = tagsContent.match(/['\"]([^'\"]+)['\"]/g)\n if (tags) {\n spec.tags = tags.map(t => t.replace(/['\"]/g, ''))\n }\n }\n\n if (Object.keys(spec).length > 0) {\n result[method] = spec\n }\n }\n }\n\n return Object.keys(result).length > 0 ? result : null\n } catch {\n return null\n }\n}\n\n/**\n * Generate a complete OpenAPI document by bundling route files with esbuild\n * and executing the bundle to call buildOpenApiDocument from @open-mercato/shared.\n *\n * esbuild compiles TypeScript with legacy decorator support (reads experimentalDecorators\n * from tsconfig.json), avoiding the TC39 decorator mismatch that breaks tsx-based imports.\n * External packages (zod, mikro-orm, etc.) are resolved from node_modules at runtime.\n */\nasync function generateOpenApiViaBundle(\n routes: ApiRouteInfo[],\n resolver: PackageResolver,\n quiet: boolean\n): Promise<Record<string, any> | null> {\n let esbuild: typeof import('esbuild')\n try {\n esbuild = await import('esbuild')\n } catch {\n if (!quiet) console.log('[OpenAPI] esbuild not available, skipping bundle approach')\n return null\n }\n\n const { execFileSync } = await import('node:child_process')\n\n const rootDir = resolver.getRootDir()\n const appDir = resolver.getAppDir()\n const sharedPackageRoot = resolver.getPackageRoot('@open-mercato/shared')\n const corePackageRoot = resolver.getPackageRoot('@open-mercato/core')\n const tsconfigPath = resolveExistingPath([\n path.join(appDir, 'tsconfig.json'),\n path.join(rootDir, 'tsconfig.base.json'),\n path.join(rootDir, 'tsconfig.json'),\n ])\n const generatorPath = path.join(sharedPackageRoot, 'src', 'lib', 'openapi', 'generator.ts')\n const coreGeneratedRoot = path.join(corePackageRoot, 'generated')\n\n if (!fs.existsSync(generatorPath)) {\n if (!quiet) {\n console.log(`[OpenAPI] Generator source not found at ${generatorPath}, skipping bundle approach`)\n }\n return null\n }\n\n const cacheDir = path.join(rootDir, 'node_modules', '.cache')\n if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true })\n\n const bundlePath = path.join(cacheDir, '_openapi-bundle.mjs')\n\n // Build the entry script that imports all routes and calls buildOpenApiDocument\n const importLines: string[] = [\n `import { buildOpenApiDocument } from ${JSON.stringify(generatorPath)};`,\n ]\n const routeMapLines: string[] = []\n\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i]\n importLines.push(`import * as R${i} from ${JSON.stringify(route.path)};`)\n // Use [param] format so normalizePath in buildOpenApiDocument extracts path params\n const bracketPath = route.openApiPath.replace(/\\{([^}]+)\\}/g, '[$1]')\n routeMapLines.push(` [${JSON.stringify(bracketPath)}, R${i}],`)\n }\n\n const entryScript = `${importLines.join('\\n')}\n\nconst routeEntries = [\n${routeMapLines.join('\\n')}\n];\n\nconst modules = new Map();\nfor (const [apiPath, mod] of routeEntries) {\n const moduleId = apiPath.replace(/^\\\\/api\\\\//, '').split('/')[0];\n if (!modules.has(moduleId)) modules.set(moduleId, { id: moduleId, apis: [] });\n modules.get(moduleId).apis.push({\n path: apiPath,\n handlers: mod,\n metadata: mod.metadata,\n });\n}\n\nconst doc = buildOpenApiDocument([...modules.values()], {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n});\n\n// Deep-clone to break shared object references before serializing.\n// The zodToJsonSchema memo cache returns the same object instance for\n// fields like currencyCode that appear on both parent and child schemas.\n// A naive WeakSet-based circular-ref guard would drop the second occurrence,\n// causing properties to vanish from the generated spec (while the field\n// still appears in the 'required' array, since those are plain strings).\nconst deepClone = (v, ancestors = []) => {\n if (v === null || typeof v !== 'object') return v;\n if (typeof v === 'bigint') return Number(v);\n if (typeof v === 'function') return undefined;\n if (ancestors.includes(v)) return undefined; // true circular ref\n const next = [...ancestors, v];\n if (Array.isArray(v)) return v.map((item) => deepClone(item, next));\n const out = {};\n for (const [k, val] of Object.entries(v)) {\n const cloned = deepClone(val, next);\n if (cloned !== undefined) out[k] = cloned;\n }\n return out;\n};\nprocess.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>\n typeof v === 'bigint' ? Number(v) : v\n));\n`\n\n // Plugin: stub next/* imports (not available outside Next.js app context)\n const stubNextPlugin = {\n name: 'stub-next',\n setup(build: any) {\n build.onResolve({ filter: /^next($|\\/)/ }, () => ({\n path: 'next-stub',\n namespace: 'next-stub',\n }))\n build.onLoad({ filter: /.*/, namespace: 'next-stub' }, () => ({\n contents: [\n 'const p = new Proxy(function(){}, {',\n ' get(_, k) { return k === \"__esModule\" ? true : k === \"default\" ? p : p; },',\n ' apply() { return p; },',\n ' construct() { return p; },',\n '});',\n 'export default p;',\n 'export const NextRequest = p, NextResponse = p, headers = p, cookies = p;',\n 'export const redirect = p, notFound = p, useRouter = p, usePathname = p;',\n 'export const useSearchParams = p, permanentRedirect = p, revalidatePath = p;',\n ].join('\\n'),\n loader: 'js' as const,\n }))\n },\n }\n\n // Plugin: resolve workspace imports, aliases, and subpath imports\n const resolveProjectImportsPlugin = {\n name: 'resolve-project-imports',\n setup(build: any) {\n // @open-mercato/<pkg>/<path> \u2192 packageRoot/src/<path>.ts\n build.onResolve({ filter: /^@open-mercato\\// }, (args: any) => {\n const withoutScope = args.path.slice('@open-mercato/'.length)\n const slashIdx = withoutScope.indexOf('/')\n const pkg = slashIdx === -1 ? withoutScope : withoutScope.slice(0, slashIdx)\n const rest = slashIdx === -1 ? '' : withoutScope.slice(slashIdx + 1)\n const packageName = `@open-mercato/${pkg}`\n const packageRoot = resolver.getPackageRoot(packageName)\n\n const base = rest\n ? path.join(packageRoot, 'src', rest)\n : path.join(packageRoot, 'src', 'index')\n\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/.mercato/* \u2192 app/.mercato/* (tsconfig paths)\n build.onResolve({ filter: /^@\\/\\.mercato\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length) // '.mercato/generated/...'\n const base = path.join(appDir, rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx', '']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/* \u2192 app/src/* (tsconfig paths)\n build.onResolve({ filter: /^@\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length)\n const base = path.join(appDir, 'src', rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // #generated/* \u2192 core package generated/* (Node subpath imports)\n build.onResolve({ filter: /^#generated\\// }, (args: any) => {\n const rest = args.path.slice('#generated/'.length)\n const base = path.join(coreGeneratedRoot, rest)\n for (const ext of ['.ts', '/index.ts']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n },\n }\n\n // Plugin: externalize installed packages, stub missing ones\n const nodeBuiltins = new Set([\n 'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',\n 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2',\n 'https', 'module', 'net', 'os', 'path', 'perf_hooks', 'process',\n 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',\n 'sys', 'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi',\n 'worker_threads', 'zlib', 'async_hooks', 'diagnostics_channel', 'inspector',\n 'trace_events',\n ])\n const externalNonWorkspacePlugin = {\n name: 'external-non-workspace',\n setup(build: any) {\n build.onResolve({ filter: /^[^./]/ }, (args: any) => {\n if (path.isAbsolute(args.path)) return undefined\n if (args.path.startsWith('@open-mercato/')) return undefined\n if (args.path.startsWith('@/')) return undefined\n if (args.path.startsWith('#generated/')) return undefined\n if (args.path.startsWith('next')) return undefined\n // Let esbuild handle Node builtins (with or without node: prefix)\n if (args.path.startsWith('node:')) return undefined\n const topLevel = args.path.split('/')[0]\n if (nodeBuiltins.has(topLevel)) return undefined\n\n // Extract package name (handle scoped packages like @mikro-orm/core)\n const pkgName = args.path.startsWith('@')\n ? args.path.split('/').slice(0, 2).join('/')\n : topLevel\n const pkgDir = path.join(rootDir, 'node_modules', pkgName)\n if (fs.existsSync(pkgDir)) return { external: true }\n\n // Package not installed \u2014 provide CJS stub (allows any named import)\n return { path: args.path, namespace: 'missing-pkg' }\n })\n build.onLoad({ filter: /.*/, namespace: 'missing-pkg' }, () => ({\n contents: 'var h={get:(_,k)=>k===\"__esModule\"?true:p};var p=new Proxy(function(){return p},{get:h.get,apply:()=>p,construct:()=>p});module.exports=p;',\n loader: 'js' as const,\n }))\n },\n }\n\n try {\n await esbuild.build({\n stdin: {\n contents: entryScript,\n resolveDir: appDir,\n sourcefile: 'openapi-entry.ts',\n loader: 'ts',\n },\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n outfile: bundlePath,\n write: true,\n ...(tsconfigPath ? { tsconfig: tsconfigPath } : {}),\n logLevel: 'silent',\n jsx: 'automatic',\n plugins: [stubNextPlugin, resolveProjectImportsPlugin, externalNonWorkspacePlugin],\n })\n\n const stdout = execFileSync(process.execPath, [bundlePath], {\n timeout: 60_000,\n maxBuffer: 20 * 1024 * 1024,\n encoding: 'utf-8',\n env: { ...process.env, NODE_NO_WARNINGS: '1' },\n cwd: rootDir,\n })\n\n const lastLine = stdout.trim().split('\\n').pop()!\n const doc = JSON.parse(lastLine) as Record<string, any>\n\n if (!quiet) {\n const pathCount = Object.keys(doc.paths || {}).length\n const withBody = Object.values(doc.paths || {}).reduce((n: number, methods: any) => {\n for (const m of Object.values(methods)) {\n if ((m as any)?.requestBody) n++\n }\n return n\n }, 0)\n console.log(`[OpenAPI] Bundle approach: ${pathCount} paths, ${withBody} with requestBody schemas`)\n }\n\n return doc\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err)\n const stderr = (err as any)?.stderr\n const esbuildErrors = (err as any)?.errors as Array<{ text: string; location?: { file: string } }> | undefined\n if (!quiet) {\n console.log(`[OpenAPI] Bundle approach failed, will use static fallback: ${errMsg.split('\\n')[0]}`)\n if (esbuildErrors?.length) {\n const unique = new Map<string, string>()\n for (const e of esbuildErrors) {\n const key = e.text\n if (!unique.has(key)) unique.set(key, e.location?.file ?? '')\n }\n for (const [text, file] of [...unique.entries()].slice(0, 10)) {\n console.log(`[OpenAPI] ${text}${file ? ` (${path.basename(file)})` : ''}`)\n }\n if (unique.size > 10) console.log(`[OpenAPI] ... and ${unique.size - 10} more`)\n }\n if (stderr) {\n for (const line of String(stderr).trim().split('\\n').slice(0, 3)) {\n console.log(`[OpenAPI] ${line}`)\n }\n }\n }\n return null\n } finally {\n // Shut down esbuild's persistent Go service so it does not deadlock at\n // process exit when a plugin request is still in flight.\n try { await esbuild.stop() } catch {}\n // Clean up old files from previous tsx-based approach\n for (const file of ['_openapi-register.mjs', '_openapi-loader.mjs', '_next-stub.cjs']) {\n try { fs.unlinkSync(path.join(cacheDir, file)) } catch {}\n }\n }\n}\n\n/**\n * Build OpenAPI paths from discovered routes.\n * Extracts basic operation info from route files statically.\n */\nfunction buildOpenApiPaths(routes: ApiRouteInfo[]): Record<string, any> {\n const paths: Record<string, any> = {}\n\n for (const route of routes) {\n const pathEntry: Record<string, any> = {}\n\n // Try to extract OpenAPI specs from source\n const openApiSpec = parseOpenApiFromSource(route.path)\n\n for (const method of route.methods) {\n const methodLower = method.toLowerCase()\n const spec = openApiSpec?.[method]\n\n // Generate a default operationId if not found\n const pathSegments = route.openApiPath\n .replace(/^\\/api\\//, '')\n .replace(/\\{[^}]+\\}/g, 'by_id')\n .split('/')\n .filter(Boolean)\n .join('_')\n const defaultOperationId = `${methodLower}_${pathSegments}`\n\n pathEntry[methodLower] = {\n operationId: spec?.operationId || defaultOperationId,\n summary: spec?.summary || `${method} ${route.openApiPath}`,\n description: spec?.description || `${method} operation for ${route.openApiPath}`,\n tags: spec?.tags || [route.openApiPath.split('/')[2] || 'api'],\n responses: {\n '200': {\n description: 'Successful response',\n },\n },\n }\n }\n\n if (Object.keys(pathEntry).length > 0) {\n paths[route.openApiPath] = pathEntry\n }\n }\n\n return paths\n}\n\n/**\n * Generate the OpenAPI JSON file.\n */\nexport async function generateOpenApi(options: GenerateOpenApiOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'openapi.generated.json')\n const checksumFile = path.join(outputDir, 'openapi.generated.checksum')\n\n // Ensure output directory exists\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true })\n }\n\n // Find all API routes\n const routes = await findApiRoutes(resolver)\n\n if (!quiet) {\n console.log(`[OpenAPI] Found ${routes.length} API route files`)\n }\n\n // Determine project root (cli package is at packages/cli/src/lib/generators/)\n // Try esbuild bundle approach first \u2014 produces full requestBody/response schemas\n let doc: Record<string, any> | null = await generateOpenApiViaBundle(routes, resolver, quiet)\n\n // Fallback to static regex approach (extracts operationId/summary/tags but no schemas)\n if (!doc) {\n if (!quiet) {\n console.log('[OpenAPI] Falling back to static regex approach')\n }\n const paths = buildOpenApiPaths(routes)\n doc = {\n openapi: '3.1.0',\n info: {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n },\n servers: [\n { url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' },\n ],\n paths,\n components: {\n securitySchemes: {\n bearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: 'Send an `Authorization: Bearer <token>` header with a valid API token.',\n },\n },\n },\n }\n }\n\n const output = JSON.stringify(doc, null, 2)\n const checksum = calculateChecksum(output)\n\n // Check if unchanged\n const existingChecksums = readChecksumRecord(checksumFile)\n if (existingChecksums && existingChecksums.content === checksum && fs.existsSync(outFile)) {\n result.filesUnchanged.push(outFile)\n if (!quiet) {\n console.log(`[OpenAPI] Skipped (unchanged): ${outFile}`)\n }\n return result\n }\n\n // Write the file\n fs.writeFileSync(outFile, output)\n writeChecksumRecord(checksumFile, { content: checksum, structure: '' })\n\n result.filesWritten.push(outFile)\n\n if (!quiet) {\n logGenerationResult(outFile, true)\n const pathCount = Object.keys(doc.paths || {}).length\n console.log(`[OpenAPI] Generated ${pathCount} API paths`)\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,6 +7,7 @@ import path from "node:path";
|
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { stdin as input, stdout as output } from "node:process";
|
|
9
9
|
import spawn from "cross-spawn";
|
|
10
|
+
import { fetchWithTimeout } from "@open-mercato/shared/lib/http/fetchWithTimeout";
|
|
10
11
|
import { resolveEnvironment } from "../resolver.js";
|
|
11
12
|
import { resolveSpawnCommand } from "../spawn.js";
|
|
12
13
|
import { discoverIntegrationSpecFiles as discoverIntegrationSpecFilesShared } from "./integration-discovery.js";
|
|
@@ -468,6 +469,10 @@ function delay(milliseconds) {
|
|
|
468
469
|
setTimeout(resolve, milliseconds);
|
|
469
470
|
});
|
|
470
471
|
}
|
|
472
|
+
const READINESS_PROBE_FETCH_TIMEOUT_MS = 15e3;
|
|
473
|
+
function probeFetch(input2, init = {}) {
|
|
474
|
+
return fetchWithTimeout(input2, { timeoutMs: READINESS_PROBE_FETCH_TIMEOUT_MS, ...init });
|
|
475
|
+
}
|
|
471
476
|
function resolveBuildCacheTtlSeconds(logPrefix) {
|
|
472
477
|
const rawValue = process.env[BUILD_CACHE_TTL_ENV_VAR];
|
|
473
478
|
if (!rawValue) {
|
|
@@ -748,10 +753,17 @@ async function shouldRebuildBuildArtifacts(ttlSeconds, logPrefix, options = {})
|
|
|
748
753
|
return !await shouldReuseBuildArtifacts(ttlSeconds, logPrefix, options);
|
|
749
754
|
}
|
|
750
755
|
async function getFreePort() {
|
|
751
|
-
|
|
756
|
+
const tryListen = (host) => new Promise((resolve, reject) => {
|
|
752
757
|
const server = createServer();
|
|
753
|
-
server.on("error",
|
|
754
|
-
|
|
758
|
+
server.on("error", (error) => {
|
|
759
|
+
const errorCode = error.code;
|
|
760
|
+
if (errorCode === "EAFNOSUPPORT") {
|
|
761
|
+
resolve(null);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
reject(error);
|
|
765
|
+
});
|
|
766
|
+
server.listen(0, host, () => {
|
|
755
767
|
const address = server.address();
|
|
756
768
|
if (!address || typeof address === "string") {
|
|
757
769
|
server.close();
|
|
@@ -768,6 +780,7 @@ async function getFreePort() {
|
|
|
768
780
|
});
|
|
769
781
|
});
|
|
770
782
|
});
|
|
783
|
+
return await tryListen("::") ?? await tryListen("127.0.0.1") ?? Promise.reject(new Error("Unable to allocate free port"));
|
|
771
784
|
}
|
|
772
785
|
async function isPortAvailable(port) {
|
|
773
786
|
const canBind = (host) => new Promise((resolve) => {
|
|
@@ -786,15 +799,15 @@ async function isPortAvailable(port) {
|
|
|
786
799
|
});
|
|
787
800
|
});
|
|
788
801
|
});
|
|
789
|
-
const
|
|
790
|
-
if (
|
|
802
|
+
const wildcardIpv6Availability = await canBind("::");
|
|
803
|
+
if (wildcardIpv6Availability === false) {
|
|
791
804
|
return false;
|
|
792
805
|
}
|
|
793
|
-
const
|
|
794
|
-
if (
|
|
806
|
+
const ipv4Availability = await canBind("127.0.0.1");
|
|
807
|
+
if (ipv4Availability === false) {
|
|
795
808
|
return false;
|
|
796
809
|
}
|
|
797
|
-
return
|
|
810
|
+
return wildcardIpv6Availability === true || ipv4Availability === true;
|
|
798
811
|
}
|
|
799
812
|
async function getPreferredPort(preferredPort) {
|
|
800
813
|
if (await isPortAvailable(preferredPort)) {
|
|
@@ -885,7 +898,7 @@ function isSuccessfulBrowserNavigationStatus(status) {
|
|
|
885
898
|
}
|
|
886
899
|
async function probeLoginPage(baseUrl) {
|
|
887
900
|
try {
|
|
888
|
-
const response = await
|
|
901
|
+
const response = await probeFetch(`${baseUrl}/login`, {
|
|
889
902
|
method: "GET",
|
|
890
903
|
redirect: "manual"
|
|
891
904
|
});
|
|
@@ -930,7 +943,7 @@ async function probeBackendLoginEndpoint(baseUrl) {
|
|
|
930
943
|
const form = new URLSearchParams();
|
|
931
944
|
form.set("email", "integration-healthcheck@example.invalid");
|
|
932
945
|
form.set("password", "invalid-password");
|
|
933
|
-
const response = await
|
|
946
|
+
const response = await probeFetch(`${baseUrl}/api/auth/login`, {
|
|
934
947
|
method: "POST",
|
|
935
948
|
redirect: "manual",
|
|
936
949
|
headers: {
|
|
@@ -958,7 +971,7 @@ async function probeAuthenticatedApi(baseUrl) {
|
|
|
958
971
|
const form = new URLSearchParams();
|
|
959
972
|
form.set("email", "admin@acme.com");
|
|
960
973
|
form.set("password", "secret");
|
|
961
|
-
const loginResponse = await
|
|
974
|
+
const loginResponse = await probeFetch(`${baseUrl}/api/auth/login`, {
|
|
962
975
|
method: "POST",
|
|
963
976
|
redirect: "manual",
|
|
964
977
|
headers: {
|
|
@@ -984,7 +997,7 @@ async function probeAuthenticatedApi(baseUrl) {
|
|
|
984
997
|
detail: loginResponse.ok ? "POST /api/auth/login did not return an auth token" : `POST /api/auth/login returned ${loginResponse.status}`
|
|
985
998
|
};
|
|
986
999
|
}
|
|
987
|
-
const apiResponse = await
|
|
1000
|
+
const apiResponse = await probeFetch(`${baseUrl}/api/customers/people?pageSize=1`, {
|
|
988
1001
|
headers: {
|
|
989
1002
|
Authorization: `Bearer ${token}`
|
|
990
1003
|
}
|
|
@@ -1288,22 +1301,33 @@ async function tryReuseExistingEnvironment(options) {
|
|
|
1288
1301
|
}
|
|
1289
1302
|
async function waitForApplicationReadiness(baseUrl, appProcess, options) {
|
|
1290
1303
|
const startTimestamp = Date.now();
|
|
1291
|
-
const
|
|
1292
|
-
const readinessStabilizationMs = 600;
|
|
1304
|
+
const intervalMs = options.intervalMs ?? APP_READY_INTERVAL_MS;
|
|
1305
|
+
const readinessStabilizationMs = options.stabilizationMs ?? 600;
|
|
1293
1306
|
let lastProbe = null;
|
|
1307
|
+
let exitCode = null;
|
|
1308
|
+
const exitSignal = getProcessExitPromise(appProcess).then(
|
|
1309
|
+
(code) => {
|
|
1310
|
+
exitCode = code ?? null;
|
|
1311
|
+
return { exited: true };
|
|
1312
|
+
},
|
|
1313
|
+
() => {
|
|
1314
|
+
exitCode = null;
|
|
1315
|
+
return { exited: true };
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
const exitError = () => new Error(`Application process exited before readiness check (exit ${exitCode ?? "unknown"})`);
|
|
1294
1319
|
while (Date.now() - startTimestamp < options.timeoutMs) {
|
|
1295
|
-
const
|
|
1296
|
-
probeApplicationReadiness(baseUrl).then((probe) => ({
|
|
1297
|
-
|
|
1298
|
-
delay(APP_READY_INTERVAL_MS).then(() => ({ kind: "timeout" }))
|
|
1320
|
+
const cycle = await Promise.race([
|
|
1321
|
+
probeApplicationReadiness(baseUrl).then((probe) => ({ probe })),
|
|
1322
|
+
exitSignal
|
|
1299
1323
|
]);
|
|
1300
|
-
if (
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1324
|
+
if ("exited" in cycle) {
|
|
1325
|
+
throw exitError();
|
|
1326
|
+
}
|
|
1327
|
+
lastProbe = cycle.probe;
|
|
1328
|
+
if (cycle.probe.ready) {
|
|
1305
1329
|
const processExited = await Promise.race([
|
|
1306
|
-
|
|
1330
|
+
exitSignal.then(() => true),
|
|
1307
1331
|
delay(readinessStabilizationMs).then(() => false)
|
|
1308
1332
|
]);
|
|
1309
1333
|
if (processExited) {
|
|
@@ -1311,8 +1335,14 @@ async function waitForApplicationReadiness(baseUrl, appProcess, options) {
|
|
|
1311
1335
|
}
|
|
1312
1336
|
return;
|
|
1313
1337
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1338
|
+
const remainingMs = options.timeoutMs - (Date.now() - startTimestamp);
|
|
1339
|
+
if (remainingMs <= 0) break;
|
|
1340
|
+
const waited = await Promise.race([
|
|
1341
|
+
exitSignal,
|
|
1342
|
+
delay(Math.min(intervalMs, remainingMs)).then(() => null)
|
|
1343
|
+
]);
|
|
1344
|
+
if (waited && "exited" in waited) {
|
|
1345
|
+
throw exitError();
|
|
1316
1346
|
}
|
|
1317
1347
|
}
|
|
1318
1348
|
const lastFrontendDetail = lastProbe?.frontend.detail ?? "GET /login was never observed";
|
|
@@ -2738,6 +2768,7 @@ export {
|
|
|
2738
2768
|
shouldUseIsolatedPortForFreshEnvironment,
|
|
2739
2769
|
startEphemeralEnvironment,
|
|
2740
2770
|
tryReuseExistingEnvironment,
|
|
2771
|
+
waitForApplicationReadiness,
|
|
2741
2772
|
writeEphemeralEnvironmentState
|
|
2742
2773
|
};
|
|
2743
2774
|
//# sourceMappingURL=integration.js.map
|