@open-mercato/cli 0.4.9-develop-94fb251ed3 → 0.4.9-develop-8d8db18714

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.
Files changed (58) hide show
  1. package/dist/agentic/shared/AGENTS.md.template +2 -0
  2. package/dist/agentic/shared/ai/skills/eject-and-customize/SKILL.md +3 -1
  3. package/dist/bin.js +1 -0
  4. package/dist/bin.js.map +2 -2
  5. package/dist/lib/__fixtures__/official-module-package/src/index.js +1 -0
  6. package/dist/lib/__fixtures__/official-module-package/src/index.js.map +7 -0
  7. package/dist/lib/__fixtures__/official-module-package/src/modules/test_package/index.js +10 -0
  8. package/dist/lib/__fixtures__/official-module-package/src/modules/test_package/index.js.map +7 -0
  9. package/dist/lib/eject.js +30 -38
  10. package/dist/lib/eject.js.map +2 -2
  11. package/dist/lib/generators/index.js +2 -0
  12. package/dist/lib/generators/index.js.map +2 -2
  13. package/dist/lib/generators/module-package-sources.js +45 -0
  14. package/dist/lib/generators/module-package-sources.js.map +7 -0
  15. package/dist/lib/module-install-args.js +40 -0
  16. package/dist/lib/module-install-args.js.map +7 -0
  17. package/dist/lib/module-install.js +157 -0
  18. package/dist/lib/module-install.js.map +7 -0
  19. package/dist/lib/module-package.js +245 -0
  20. package/dist/lib/module-package.js.map +7 -0
  21. package/dist/lib/modules-config.js +255 -0
  22. package/dist/lib/modules-config.js.map +7 -0
  23. package/dist/lib/resolver.js +19 -5
  24. package/dist/lib/resolver.js.map +2 -2
  25. package/dist/lib/testing/integration-discovery.js +20 -9
  26. package/dist/lib/testing/integration-discovery.js.map +2 -2
  27. package/dist/lib/testing/integration.js +86 -47
  28. package/dist/lib/testing/integration.js.map +2 -2
  29. package/dist/mercato.js +120 -43
  30. package/dist/mercato.js.map +3 -3
  31. package/package.json +5 -4
  32. package/src/__tests__/mercato.test.ts +6 -1
  33. package/src/bin.ts +1 -0
  34. package/src/lib/__fixtures__/official-module-package/dist/modules/test_package/index.js +2 -0
  35. package/src/lib/__fixtures__/official-module-package/package.json +33 -0
  36. package/src/lib/__fixtures__/official-module-package/src/index.ts +1 -0
  37. package/src/lib/__fixtures__/official-module-package/src/modules/test_package/index.ts +6 -0
  38. package/src/lib/__fixtures__/official-module-package/src/modules/test_package/widgets/injection/test/widget.tsx +3 -0
  39. package/src/lib/__tests__/eject.test.ts +107 -1
  40. package/src/lib/__tests__/module-install-args.test.ts +35 -0
  41. package/src/lib/__tests__/module-install.test.ts +217 -0
  42. package/src/lib/__tests__/module-package.test.ts +215 -0
  43. package/src/lib/__tests__/modules-config.test.ts +104 -0
  44. package/src/lib/__tests__/resolve-environment.test.ts +141 -0
  45. package/src/lib/eject.ts +45 -55
  46. package/src/lib/generators/__tests__/generators.test.ts +11 -0
  47. package/src/lib/generators/__tests__/module-package-sources.test.ts +121 -0
  48. package/src/lib/generators/index.ts +1 -0
  49. package/src/lib/generators/module-package-sources.ts +59 -0
  50. package/src/lib/module-install-args.ts +50 -0
  51. package/src/lib/module-install.ts +234 -0
  52. package/src/lib/module-package.ts +355 -0
  53. package/src/lib/modules-config.ts +393 -0
  54. package/src/lib/resolver.ts +46 -4
  55. package/src/lib/testing/__tests__/integration-discovery.test.ts +30 -0
  56. package/src/lib/testing/integration-discovery.ts +23 -8
  57. package/src/lib/testing/integration.ts +97 -57
  58. package/src/mercato.ts +128 -49
@@ -290,10 +290,11 @@ function detectMonorepoFromNodeModules(appDir) {
290
290
  }
291
291
  }
292
292
  function createResolver(cwd = process.cwd()) {
293
- const { isMonorepo: _isMonorepo, monorepoRoot } = detectMonorepoFromNodeModules(cwd);
294
- const rootDir = monorepoRoot ?? cwd;
295
- const candidateAppDir = detectAppDir(rootDir, true);
296
- const appDir = _isMonorepo ? candidateAppDir : candidateAppDir !== rootDir && fs.existsSync(candidateAppDir) ? candidateAppDir : cwd;
293
+ const { isMonorepo: _isMonorepo, monorepoRoot, nodeModulesRoot } = detectMonorepoFromNodeModules(cwd);
294
+ const rootDir = monorepoRoot ?? nodeModulesRoot ?? cwd;
295
+ const shouldResolveAppFromRoot = _isMonorepo || nodeModulesRoot !== null && path.resolve(nodeModulesRoot) !== path.resolve(cwd);
296
+ const candidateAppDir = shouldResolveAppFromRoot ? detectAppDir(rootDir, true) : rootDir;
297
+ const appDir = _isMonorepo ? candidateAppDir : shouldResolveAppFromRoot && candidateAppDir !== rootDir && fs.existsSync(candidateAppDir) ? candidateAppDir : cwd;
297
298
  return {
298
299
  isMonorepo: () => _isMonorepo,
299
300
  getRootDir: () => rootDir,
@@ -331,7 +332,20 @@ function createResolver(cwd = process.cwd()) {
331
332
  }
332
333
  };
333
334
  }
335
+ function resolveEnvironment(cwd = process.cwd()) {
336
+ const resolver = createResolver(cwd);
337
+ const _isMonorepo = resolver.isMonorepo();
338
+ const rootDir = resolver.getRootDir();
339
+ const appDir = resolver.getAppDir();
340
+ return {
341
+ mode: _isMonorepo ? "monorepo" : "standalone",
342
+ rootDir,
343
+ appDir,
344
+ packageRoot: (packageName) => pkgRootFor(rootDir, packageName, _isMonorepo)
345
+ };
346
+ }
334
347
  export {
335
- createResolver
348
+ createResolver,
349
+ resolveEnvironment
336
350
  };
337
351
  //# sourceMappingURL=resolver.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/resolver.ts"],
4
- "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport ts from 'typescript'\nimport { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\n\nexport type ModuleEntry = {\n id: string\n from?: '@open-mercato/core' | '@app' | string\n}\n\nexport type PackageInfo = {\n name: string\n path: string\n modulesPath: string\n}\n\nexport interface PackageResolver {\n isMonorepo(): boolean\n getRootDir(): string\n getAppDir(): string\n getOutputDir(): string\n getModulesConfigPath(): string\n discoverPackages(): PackageInfo[]\n loadEnabledModules(): ModuleEntry[]\n getModulePaths(entry: ModuleEntry): { appBase: string; pkgBase: string }\n getModuleImportBase(entry: ModuleEntry): { appBase: string; pkgBase: string }\n getPackageOutputDir(packageName: string): string\n getPackageRoot(from?: string): string\n}\n\nfunction pkgDirFor(rootDir: string, from?: string, isMonorepo = true): string {\n if (!isMonorepo) {\n // Production mode: look in node_modules\n // Packages ship with src/ included, so we can read TypeScript source files\n const pkgName = from || '@open-mercato/core'\n return path.join(rootDir, 'node_modules', pkgName, 'src', 'modules')\n }\n\n // Monorepo mode - read from src/modules (TypeScript source)\n if (!from || from === '@open-mercato/core') {\n return path.resolve(rootDir, 'packages/core/src/modules')\n }\n // Support other local packages like '@open-mercato/onboarding' => packages/onboarding/src/modules\n const m = from.match(/^@open-mercato\\/(.+)$/)\n if (m) {\n return path.resolve(rootDir, `packages/${m[1]}/src/modules`)\n }\n // Fallback to core modules path\n return path.resolve(rootDir, 'packages/core/src/modules')\n}\n\nfunction pkgRootFor(rootDir: string, from?: string, isMonorepo = true): string {\n if (!isMonorepo) {\n const pkgName = from || '@open-mercato/core'\n return path.join(rootDir, 'node_modules', pkgName)\n }\n\n if (!from || from === '@open-mercato/core') {\n return path.resolve(rootDir, 'packages/core')\n }\n const m = from.match(/^@open-mercato\\/(.+)$/)\n if (m) {\n return path.resolve(rootDir, `packages/${m[1]}`)\n }\n return path.resolve(rootDir, 'packages/core')\n}\n\nfunction parseModuleEntryFromObjectLiteral(node: ts.ObjectLiteralExpression): ModuleEntry | null {\n let id: string | null = null\n let from: string | null = null\n for (const property of node.properties) {\n if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) continue\n const key = property.name.text\n if (key === 'id' && ts.isStringLiteralLike(property.initializer)) {\n id = property.initializer.text\n }\n if (key === 'from' && ts.isStringLiteralLike(property.initializer)) {\n from = property.initializer.text\n }\n }\n if (!id) return null\n return { id, from: from ?? '@open-mercato/core' }\n}\n\nfunction parseProcessEnvAccess(\n node: ts.Expression,\n env: NodeJS.ProcessEnv,\n): { matched: boolean; value: string | undefined } {\n if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name)) {\n const target = node.expression\n if (\n ts.isPropertyAccessExpression(target)\n && ts.isIdentifier(target.expression)\n && target.expression.text === 'process'\n && target.name.text === 'env'\n ) {\n return { matched: true, value: env[node.name.text] }\n }\n }\n if (\n ts.isElementAccessExpression(node)\n && ts.isPropertyAccessExpression(node.expression)\n && ts.isIdentifier(node.expression.expression)\n && node.expression.expression.text === 'process'\n && node.expression.name.text === 'env'\n && ts.isStringLiteralLike(node.argumentExpression)\n ) {\n return { matched: true, value: env[node.argumentExpression.text] }\n }\n return { matched: false, value: undefined }\n}\n\nfunction evaluateStaticExpression(node: ts.Expression, env: NodeJS.ProcessEnv): unknown {\n return evaluateStaticExpressionWithScope(node, env, new Map())\n}\n\nfunction evaluateStaticExpressionWithScope(\n node: ts.Expression,\n env: NodeJS.ProcessEnv,\n scope: Map<string, unknown>,\n): unknown {\n if (ts.isParenthesizedExpression(node)) return evaluateStaticExpressionWithScope(node.expression, env, scope)\n if (ts.isIdentifier(node)) {\n return scope.get(node.text)\n }\n if (ts.isStringLiteralLike(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text\n if (ts.isNumericLiteral(node)) return Number(node.text)\n if (node.kind === ts.SyntaxKind.TrueKeyword) return true\n if (node.kind === ts.SyntaxKind.FalseKeyword) return false\n if (node.kind === ts.SyntaxKind.NullKeyword) return null\n\n const envAccess = parseProcessEnvAccess(node, env)\n if (envAccess.matched) return envAccess.value\n\n if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {\n return !Boolean(evaluateStaticExpressionWithScope(node.operand, env, scope))\n }\n\n if (ts.isBinaryExpression(node)) {\n if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {\n return Boolean(evaluateStaticExpressionWithScope(node.left, env, scope))\n && Boolean(evaluateStaticExpressionWithScope(node.right, env, scope))\n }\n if (node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {\n return Boolean(evaluateStaticExpressionWithScope(node.left, env, scope))\n || Boolean(evaluateStaticExpressionWithScope(node.right, env, scope))\n }\n if (node.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {\n return evaluateStaticExpressionWithScope(node.left, env, scope) === evaluateStaticExpressionWithScope(node.right, env, scope)\n }\n if (node.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {\n return evaluateStaticExpressionWithScope(node.left, env, scope) !== evaluateStaticExpressionWithScope(node.right, env, scope)\n }\n }\n\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'parseBooleanWithDefault') {\n const rawValueNode = node.arguments[0]\n const fallbackNode = node.arguments[1]\n const rawValue = rawValueNode ? evaluateStaticExpressionWithScope(rawValueNode, env, scope) : undefined\n const fallbackValue = fallbackNode ? evaluateStaticExpressionWithScope(fallbackNode, env, scope) : false\n return parseBooleanWithDefault(typeof rawValue === 'string' ? rawValue : undefined, Boolean(fallbackValue))\n }\n\n return undefined\n}\n\nfunction evaluateStaticCondition(\n node: ts.Expression,\n env: NodeJS.ProcessEnv,\n scope: Map<string, unknown>,\n): boolean {\n const evaluated = evaluateStaticExpressionWithScope(node, env, scope)\n return Boolean(evaluated)\n}\n\nfunction collectPushEntriesFromStatement(\n statement: ts.Statement,\n env: NodeJS.ProcessEnv,\n targetVariableName: string,\n scope: Map<string, unknown>,\n): ModuleEntry[] {\n if (ts.isBlock(statement)) {\n return statement.statements.flatMap((child) => collectPushEntriesFromStatement(child, env, targetVariableName, scope))\n }\n\n if (ts.isIfStatement(statement)) {\n if (evaluateStaticCondition(statement.expression, env, scope)) {\n return collectPushEntriesFromStatement(statement.thenStatement, env, targetVariableName, scope)\n }\n if (statement.elseStatement) {\n return collectPushEntriesFromStatement(statement.elseStatement, env, targetVariableName, scope)\n }\n return []\n }\n\n if (!ts.isExpressionStatement(statement)) return []\n const expression = statement.expression\n if (!ts.isCallExpression(expression)) return []\n if (!ts.isPropertyAccessExpression(expression.expression)) return []\n const pushTarget = expression.expression.expression\n const pushMethod = expression.expression.name\n if (!ts.isIdentifier(pushTarget) || pushTarget.text !== targetVariableName) return []\n if (pushMethod.text !== 'push') return []\n\n return expression.arguments.flatMap((argument) => {\n if (!ts.isObjectLiteralExpression(argument)) return []\n const entry = parseModuleEntryFromObjectLiteral(argument)\n return entry ? [entry] : []\n })\n}\n\nfunction parseModulesFromSource(source: string, env: NodeJS.ProcessEnv = process.env): ModuleEntry[] {\n const sourceFile = ts.createSourceFile('modules.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)\n const modules: ModuleEntry[] = []\n const variableName = 'enabledModules'\n const scope = new Map<string, unknown>()\n let foundDeclaration = false\n\n for (const statement of sourceFile.statements) {\n if (ts.isVariableStatement(statement)) {\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name)) continue\n if (declaration.name.text === variableName) {\n if (!declaration.initializer || !ts.isArrayLiteralExpression(declaration.initializer)) continue\n const fromArray = declaration.initializer.elements.flatMap((element) => {\n if (!ts.isObjectLiteralExpression(element)) return []\n const entry = parseModuleEntryFromObjectLiteral(element)\n return entry ? [entry] : []\n })\n modules.push(...fromArray)\n foundDeclaration = true\n continue\n }\n if (!declaration.initializer) continue\n scope.set(\n declaration.name.text,\n evaluateStaticExpressionWithScope(declaration.initializer, env, scope),\n )\n }\n continue\n }\n if (!foundDeclaration) continue\n modules.push(...collectPushEntriesFromStatement(statement, env, variableName, scope))\n }\n\n return modules\n}\n\nfunction readEnabledModulesFromConfig(cfgPath: string): ModuleEntry[] {\n const source = fs.readFileSync(cfgPath, 'utf8')\n return parseModulesFromSource(source)\n}\n\nfunction loadEnabledModulesFromConfig(appDir: string): ModuleEntry[] {\n const cfgPath = path.resolve(appDir, 'src/modules.ts')\n if (fs.existsSync(cfgPath)) {\n try {\n const loadedModules = readEnabledModulesFromConfig(cfgPath)\n if (loadedModules.length > 0) return loadedModules\n } catch (error) {\n console.warn(\n '[resolver] Failed to read enabled modules from src/modules.ts, falling back to src/modules scan:',\n error,\n )\n }\n }\n // Fallback: scan src/modules/* to keep backward compatibility\n const modulesRoot = path.resolve(appDir, 'src/modules')\n if (!fs.existsSync(modulesRoot)) return []\n const scannedModules = fs\n .readdirSync(modulesRoot, { withFileTypes: true })\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((e) => ({ id: e.name, from: '@app' as const }))\n\n return scannedModules\n}\n\nfunction discoverPackagesInMonorepo(rootDir: string): PackageInfo[] {\n const packagesDir = path.join(rootDir, 'packages')\n if (!fs.existsSync(packagesDir)) return []\n\n const packages: PackageInfo[] = []\n const entries = fs.readdirSync(packagesDir, { withFileTypes: true })\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n const pkgPath = path.join(packagesDir, entry.name)\n const pkgJsonPath = path.join(pkgPath, 'package.json')\n\n if (!fs.existsSync(pkgJsonPath)) continue\n\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))\n // Read from src/modules (TypeScript source)\n const modulesPath = path.join(pkgPath, 'src', 'modules')\n\n if (fs.existsSync(modulesPath)) {\n packages.push({\n name: pkgJson.name || `@open-mercato/${entry.name}`,\n path: pkgPath,\n modulesPath,\n })\n }\n } catch {\n // Skip invalid packages\n }\n }\n\n return packages\n}\n\nfunction discoverPackagesInNodeModules(rootDir: string): PackageInfo[] {\n const nodeModulesPath = path.join(rootDir, 'node_modules', '@open-mercato')\n if (!fs.existsSync(nodeModulesPath)) return []\n\n const packages: PackageInfo[] = []\n const entries = fs.readdirSync(nodeModulesPath, { withFileTypes: true })\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n const pkgPath = path.join(nodeModulesPath, entry.name)\n const pkgJsonPath = path.join(pkgPath, 'package.json')\n\n if (!fs.existsSync(pkgJsonPath)) continue\n\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))\n // Packages ship with src/ included, so we can read TypeScript source files\n const modulesPath = path.join(pkgPath, 'src', 'modules')\n\n if (fs.existsSync(modulesPath)) {\n packages.push({\n name: pkgJson.name || `@open-mercato/${entry.name}`,\n path: pkgPath,\n modulesPath,\n })\n }\n } catch {\n // Skip invalid packages\n }\n }\n\n return packages\n}\n\nfunction detectAppDir(rootDir: string, isMonorepo: boolean): string {\n if (!isMonorepo) {\n // Production mode: app is at root\n return rootDir\n }\n\n // Monorepo mode: look for app in apps/mercato/ or apps/app/\n const mercatoApp = path.join(rootDir, 'apps', 'mercato')\n if (fs.existsSync(mercatoApp)) {\n return mercatoApp\n }\n\n const defaultApp = path.join(rootDir, 'apps', 'app')\n if (fs.existsSync(defaultApp)) {\n return defaultApp\n }\n\n // Fallback: check if apps directory exists and has any app\n const appsDir = path.join(rootDir, 'apps')\n if (fs.existsSync(appsDir)) {\n const entries = fs.readdirSync(appsDir, { withFileTypes: true })\n const appEntry = entries.find(\n (e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'docs'\n )\n if (appEntry) {\n return path.join(appsDir, appEntry.name)\n }\n }\n\n // Final fallback for legacy structure: root is the app\n return rootDir\n}\n\nfunction findNodeModulesRoot(startDir: string): string | null {\n // Walk up to find node_modules/@open-mercato/core\n let dir = startDir\n while (dir !== path.dirname(dir)) {\n const corePkgPath = path.join(dir, 'node_modules', '@open-mercato', 'core')\n if (fs.existsSync(corePkgPath)) {\n return dir\n }\n dir = path.dirname(dir)\n }\n return null\n}\n\nfunction detectMonorepoFromNodeModules(appDir: string): { isMonorepo: boolean; monorepoRoot: string | null; nodeModulesRoot: string | null } {\n // Find where node_modules/@open-mercato/core is located (may be hoisted)\n const nodeModulesRoot = findNodeModulesRoot(appDir)\n if (!nodeModulesRoot) {\n return { isMonorepo: false, monorepoRoot: null, nodeModulesRoot: null }\n }\n\n const corePkgPath = path.join(nodeModulesRoot, 'node_modules', '@open-mercato', 'core')\n\n try {\n const stat = fs.lstatSync(corePkgPath)\n if (stat.isSymbolicLink()) {\n // It's a symlink - we're in monorepo dev mode\n // Resolve the symlink to find the monorepo root\n const realPath = fs.realpathSync(corePkgPath)\n // realPath is something like /path/to/monorepo/packages/core\n // monorepo root is 2 levels up\n const monorepoRoot = path.dirname(path.dirname(realPath))\n return { isMonorepo: true, monorepoRoot, nodeModulesRoot }\n }\n // It's a real directory - production mode\n return { isMonorepo: false, monorepoRoot: null, nodeModulesRoot }\n } catch {\n // Package doesn't exist yet or error reading - assume production mode\n return { isMonorepo: false, monorepoRoot: null, nodeModulesRoot }\n }\n}\n\nexport function createResolver(cwd: string = process.cwd()): PackageResolver {\n // First detect if we're in a monorepo by checking if node_modules packages are symlinks\n const { isMonorepo: _isMonorepo, monorepoRoot } = detectMonorepoFromNodeModules(cwd)\n const rootDir = monorepoRoot ?? cwd\n\n // The app directory depends on context:\n // - In monorepo: use detectAppDir to find apps/mercato or similar\n // - When symlinks not detected (e.g. Docker volume node_modules): still use apps/mercato if present at rootDir\n // - Otherwise: app is at cwd\n const candidateAppDir = detectAppDir(rootDir, true)\n const appDir =\n _isMonorepo\n ? candidateAppDir\n : candidateAppDir !== rootDir && fs.existsSync(candidateAppDir)\n ? candidateAppDir\n : cwd\n\n return {\n isMonorepo: () => _isMonorepo,\n\n getRootDir: () => rootDir,\n\n getAppDir: () => appDir,\n\n getOutputDir: () => {\n // Output is ALWAYS .mercato/generated relative to app directory\n return path.join(appDir, '.mercato', 'generated')\n },\n\n getModulesConfigPath: () => path.join(appDir, 'src', 'modules.ts'),\n\n discoverPackages: () => {\n return _isMonorepo\n ? discoverPackagesInMonorepo(rootDir)\n : discoverPackagesInNodeModules(rootDir)\n },\n\n loadEnabledModules: () => loadEnabledModulesFromConfig(appDir),\n\n getModulePaths: (entry: ModuleEntry) => {\n const appBase = path.resolve(appDir, 'src/modules', entry.id)\n const pkgModulesRoot = pkgDirFor(rootDir, entry.from, _isMonorepo)\n const pkgBase = path.join(pkgModulesRoot, entry.id)\n return { appBase, pkgBase }\n },\n\n getModuleImportBase: (entry: ModuleEntry) => {\n // Prefer @app overrides at import-time; fall back to provided package alias\n const from = entry.from || '@open-mercato/core'\n return {\n appBase: `@/modules/${entry.id}`,\n pkgBase: `${from}/modules/${entry.id}`,\n }\n },\n\n getPackageOutputDir: (packageName: string) => {\n if (packageName === '@app') {\n // App output goes to .mercato/generated\n return path.join(appDir, '.mercato', 'generated')\n }\n const pkgRoot = pkgRootFor(rootDir, packageName, _isMonorepo)\n return path.join(pkgRoot, 'generated')\n },\n\n getPackageRoot: (from?: string) => {\n return pkgRootFor(rootDir, from, _isMonorepo)\n },\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport ts from 'typescript'\nimport { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\n\n/**\n * Resolved execution environment for the CLI.\n * Produced once by resolveEnvironment() and consumed by all subsystems.\n * Eliminates per-subsystem isMonorepo() branching and hardcoded path segments.\n */\nexport type CliEnvironment = {\n /** Whether the CLI is running inside a Yarn/npm workspace monorepo. */\n mode: 'monorepo' | 'standalone'\n /** Workspace or project root (where package.json / node_modules live). */\n rootDir: string\n /** Next.js application directory (contains src/, package.json, next.config.*). */\n appDir: string\n /**\n * Resolve the root directory of an installed @open-mercato package.\n * Monorepo: packages/<pkg>/ Standalone: node_modules/<pkg>/\n */\n packageRoot: (packageName: string) => string\n}\n\nexport type ModuleEntry = {\n id: string\n from?: '@open-mercato/core' | '@app' | string\n}\n\nexport type PackageInfo = {\n name: string\n path: string\n modulesPath: string\n}\n\nexport interface PackageResolver {\n isMonorepo(): boolean\n getRootDir(): string\n getAppDir(): string\n getOutputDir(): string\n getModulesConfigPath(): string\n discoverPackages(): PackageInfo[]\n loadEnabledModules(): ModuleEntry[]\n getModulePaths(entry: ModuleEntry): { appBase: string; pkgBase: string }\n getModuleImportBase(entry: ModuleEntry): { appBase: string; pkgBase: string }\n getPackageOutputDir(packageName: string): string\n getPackageRoot(from?: string): string\n}\n\nfunction pkgDirFor(rootDir: string, from?: string, isMonorepo = true): string {\n if (!isMonorepo) {\n // Production mode: look in node_modules\n // Packages ship with src/ included, so we can read TypeScript source files\n const pkgName = from || '@open-mercato/core'\n return path.join(rootDir, 'node_modules', pkgName, 'src', 'modules')\n }\n\n // Monorepo mode - read from src/modules (TypeScript source)\n if (!from || from === '@open-mercato/core') {\n return path.resolve(rootDir, 'packages/core/src/modules')\n }\n // Support other local packages like '@open-mercato/onboarding' => packages/onboarding/src/modules\n const m = from.match(/^@open-mercato\\/(.+)$/)\n if (m) {\n return path.resolve(rootDir, `packages/${m[1]}/src/modules`)\n }\n // Fallback to core modules path\n return path.resolve(rootDir, 'packages/core/src/modules')\n}\n\nfunction pkgRootFor(rootDir: string, from?: string, isMonorepo = true): string {\n if (!isMonorepo) {\n const pkgName = from || '@open-mercato/core'\n return path.join(rootDir, 'node_modules', pkgName)\n }\n\n if (!from || from === '@open-mercato/core') {\n return path.resolve(rootDir, 'packages/core')\n }\n const m = from.match(/^@open-mercato\\/(.+)$/)\n if (m) {\n return path.resolve(rootDir, `packages/${m[1]}`)\n }\n return path.resolve(rootDir, 'packages/core')\n}\n\nfunction parseModuleEntryFromObjectLiteral(node: ts.ObjectLiteralExpression): ModuleEntry | null {\n let id: string | null = null\n let from: string | null = null\n for (const property of node.properties) {\n if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) continue\n const key = property.name.text\n if (key === 'id' && ts.isStringLiteralLike(property.initializer)) {\n id = property.initializer.text\n }\n if (key === 'from' && ts.isStringLiteralLike(property.initializer)) {\n from = property.initializer.text\n }\n }\n if (!id) return null\n return { id, from: from ?? '@open-mercato/core' }\n}\n\nfunction parseProcessEnvAccess(\n node: ts.Expression,\n env: NodeJS.ProcessEnv,\n): { matched: boolean; value: string | undefined } {\n if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name)) {\n const target = node.expression\n if (\n ts.isPropertyAccessExpression(target)\n && ts.isIdentifier(target.expression)\n && target.expression.text === 'process'\n && target.name.text === 'env'\n ) {\n return { matched: true, value: env[node.name.text] }\n }\n }\n if (\n ts.isElementAccessExpression(node)\n && ts.isPropertyAccessExpression(node.expression)\n && ts.isIdentifier(node.expression.expression)\n && node.expression.expression.text === 'process'\n && node.expression.name.text === 'env'\n && ts.isStringLiteralLike(node.argumentExpression)\n ) {\n return { matched: true, value: env[node.argumentExpression.text] }\n }\n return { matched: false, value: undefined }\n}\n\nfunction evaluateStaticExpression(node: ts.Expression, env: NodeJS.ProcessEnv): unknown {\n return evaluateStaticExpressionWithScope(node, env, new Map())\n}\n\nfunction evaluateStaticExpressionWithScope(\n node: ts.Expression,\n env: NodeJS.ProcessEnv,\n scope: Map<string, unknown>,\n): unknown {\n if (ts.isParenthesizedExpression(node)) return evaluateStaticExpressionWithScope(node.expression, env, scope)\n if (ts.isIdentifier(node)) {\n return scope.get(node.text)\n }\n if (ts.isStringLiteralLike(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text\n if (ts.isNumericLiteral(node)) return Number(node.text)\n if (node.kind === ts.SyntaxKind.TrueKeyword) return true\n if (node.kind === ts.SyntaxKind.FalseKeyword) return false\n if (node.kind === ts.SyntaxKind.NullKeyword) return null\n\n const envAccess = parseProcessEnvAccess(node, env)\n if (envAccess.matched) return envAccess.value\n\n if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {\n return !Boolean(evaluateStaticExpressionWithScope(node.operand, env, scope))\n }\n\n if (ts.isBinaryExpression(node)) {\n if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {\n return Boolean(evaluateStaticExpressionWithScope(node.left, env, scope))\n && Boolean(evaluateStaticExpressionWithScope(node.right, env, scope))\n }\n if (node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {\n return Boolean(evaluateStaticExpressionWithScope(node.left, env, scope))\n || Boolean(evaluateStaticExpressionWithScope(node.right, env, scope))\n }\n if (node.operatorToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {\n return evaluateStaticExpressionWithScope(node.left, env, scope) === evaluateStaticExpressionWithScope(node.right, env, scope)\n }\n if (node.operatorToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {\n return evaluateStaticExpressionWithScope(node.left, env, scope) !== evaluateStaticExpressionWithScope(node.right, env, scope)\n }\n }\n\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'parseBooleanWithDefault') {\n const rawValueNode = node.arguments[0]\n const fallbackNode = node.arguments[1]\n const rawValue = rawValueNode ? evaluateStaticExpressionWithScope(rawValueNode, env, scope) : undefined\n const fallbackValue = fallbackNode ? evaluateStaticExpressionWithScope(fallbackNode, env, scope) : false\n return parseBooleanWithDefault(typeof rawValue === 'string' ? rawValue : undefined, Boolean(fallbackValue))\n }\n\n return undefined\n}\n\nfunction evaluateStaticCondition(\n node: ts.Expression,\n env: NodeJS.ProcessEnv,\n scope: Map<string, unknown>,\n): boolean {\n const evaluated = evaluateStaticExpressionWithScope(node, env, scope)\n return Boolean(evaluated)\n}\n\nfunction collectPushEntriesFromStatement(\n statement: ts.Statement,\n env: NodeJS.ProcessEnv,\n targetVariableName: string,\n scope: Map<string, unknown>,\n): ModuleEntry[] {\n if (ts.isBlock(statement)) {\n return statement.statements.flatMap((child) => collectPushEntriesFromStatement(child, env, targetVariableName, scope))\n }\n\n if (ts.isIfStatement(statement)) {\n if (evaluateStaticCondition(statement.expression, env, scope)) {\n return collectPushEntriesFromStatement(statement.thenStatement, env, targetVariableName, scope)\n }\n if (statement.elseStatement) {\n return collectPushEntriesFromStatement(statement.elseStatement, env, targetVariableName, scope)\n }\n return []\n }\n\n if (!ts.isExpressionStatement(statement)) return []\n const expression = statement.expression\n if (!ts.isCallExpression(expression)) return []\n if (!ts.isPropertyAccessExpression(expression.expression)) return []\n const pushTarget = expression.expression.expression\n const pushMethod = expression.expression.name\n if (!ts.isIdentifier(pushTarget) || pushTarget.text !== targetVariableName) return []\n if (pushMethod.text !== 'push') return []\n\n return expression.arguments.flatMap((argument) => {\n if (!ts.isObjectLiteralExpression(argument)) return []\n const entry = parseModuleEntryFromObjectLiteral(argument)\n return entry ? [entry] : []\n })\n}\n\nfunction parseModulesFromSource(source: string, env: NodeJS.ProcessEnv = process.env): ModuleEntry[] {\n const sourceFile = ts.createSourceFile('modules.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)\n const modules: ModuleEntry[] = []\n const variableName = 'enabledModules'\n const scope = new Map<string, unknown>()\n let foundDeclaration = false\n\n for (const statement of sourceFile.statements) {\n if (ts.isVariableStatement(statement)) {\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name)) continue\n if (declaration.name.text === variableName) {\n if (!declaration.initializer || !ts.isArrayLiteralExpression(declaration.initializer)) continue\n const fromArray = declaration.initializer.elements.flatMap((element) => {\n if (!ts.isObjectLiteralExpression(element)) return []\n const entry = parseModuleEntryFromObjectLiteral(element)\n return entry ? [entry] : []\n })\n modules.push(...fromArray)\n foundDeclaration = true\n continue\n }\n if (!declaration.initializer) continue\n scope.set(\n declaration.name.text,\n evaluateStaticExpressionWithScope(declaration.initializer, env, scope),\n )\n }\n continue\n }\n if (!foundDeclaration) continue\n modules.push(...collectPushEntriesFromStatement(statement, env, variableName, scope))\n }\n\n return modules\n}\n\nfunction readEnabledModulesFromConfig(cfgPath: string): ModuleEntry[] {\n const source = fs.readFileSync(cfgPath, 'utf8')\n return parseModulesFromSource(source)\n}\n\nfunction loadEnabledModulesFromConfig(appDir: string): ModuleEntry[] {\n const cfgPath = path.resolve(appDir, 'src/modules.ts')\n if (fs.existsSync(cfgPath)) {\n try {\n const loadedModules = readEnabledModulesFromConfig(cfgPath)\n if (loadedModules.length > 0) return loadedModules\n } catch (error) {\n console.warn(\n '[resolver] Failed to read enabled modules from src/modules.ts, falling back to src/modules scan:',\n error,\n )\n }\n }\n // Fallback: scan src/modules/* to keep backward compatibility\n const modulesRoot = path.resolve(appDir, 'src/modules')\n if (!fs.existsSync(modulesRoot)) return []\n const scannedModules = fs\n .readdirSync(modulesRoot, { withFileTypes: true })\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((e) => ({ id: e.name, from: '@app' as const }))\n\n return scannedModules\n}\n\nfunction discoverPackagesInMonorepo(rootDir: string): PackageInfo[] {\n const packagesDir = path.join(rootDir, 'packages')\n if (!fs.existsSync(packagesDir)) return []\n\n const packages: PackageInfo[] = []\n const entries = fs.readdirSync(packagesDir, { withFileTypes: true })\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n const pkgPath = path.join(packagesDir, entry.name)\n const pkgJsonPath = path.join(pkgPath, 'package.json')\n\n if (!fs.existsSync(pkgJsonPath)) continue\n\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))\n // Read from src/modules (TypeScript source)\n const modulesPath = path.join(pkgPath, 'src', 'modules')\n\n if (fs.existsSync(modulesPath)) {\n packages.push({\n name: pkgJson.name || `@open-mercato/${entry.name}`,\n path: pkgPath,\n modulesPath,\n })\n }\n } catch {\n // Skip invalid packages\n }\n }\n\n return packages\n}\n\nfunction discoverPackagesInNodeModules(rootDir: string): PackageInfo[] {\n const nodeModulesPath = path.join(rootDir, 'node_modules', '@open-mercato')\n if (!fs.existsSync(nodeModulesPath)) return []\n\n const packages: PackageInfo[] = []\n const entries = fs.readdirSync(nodeModulesPath, { withFileTypes: true })\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n const pkgPath = path.join(nodeModulesPath, entry.name)\n const pkgJsonPath = path.join(pkgPath, 'package.json')\n\n if (!fs.existsSync(pkgJsonPath)) continue\n\n try {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))\n // Packages ship with src/ included, so we can read TypeScript source files\n const modulesPath = path.join(pkgPath, 'src', 'modules')\n\n if (fs.existsSync(modulesPath)) {\n packages.push({\n name: pkgJson.name || `@open-mercato/${entry.name}`,\n path: pkgPath,\n modulesPath,\n })\n }\n } catch {\n // Skip invalid packages\n }\n }\n\n return packages\n}\n\nfunction detectAppDir(rootDir: string, isMonorepo: boolean): string {\n if (!isMonorepo) {\n // Production mode: app is at root\n return rootDir\n }\n\n // Monorepo mode: look for app in apps/mercato/ or apps/app/\n const mercatoApp = path.join(rootDir, 'apps', 'mercato')\n if (fs.existsSync(mercatoApp)) {\n return mercatoApp\n }\n\n const defaultApp = path.join(rootDir, 'apps', 'app')\n if (fs.existsSync(defaultApp)) {\n return defaultApp\n }\n\n // Fallback: check if apps directory exists and has any app\n const appsDir = path.join(rootDir, 'apps')\n if (fs.existsSync(appsDir)) {\n const entries = fs.readdirSync(appsDir, { withFileTypes: true })\n const appEntry = entries.find(\n (e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'docs'\n )\n if (appEntry) {\n return path.join(appsDir, appEntry.name)\n }\n }\n\n // Final fallback for legacy structure: root is the app\n return rootDir\n}\n\nfunction findNodeModulesRoot(startDir: string): string | null {\n // Walk up to find node_modules/@open-mercato/core\n let dir = startDir\n while (dir !== path.dirname(dir)) {\n const corePkgPath = path.join(dir, 'node_modules', '@open-mercato', 'core')\n if (fs.existsSync(corePkgPath)) {\n return dir\n }\n dir = path.dirname(dir)\n }\n return null\n}\n\nfunction detectMonorepoFromNodeModules(appDir: string): { isMonorepo: boolean; monorepoRoot: string | null; nodeModulesRoot: string | null } {\n // Find where node_modules/@open-mercato/core is located (may be hoisted)\n const nodeModulesRoot = findNodeModulesRoot(appDir)\n if (!nodeModulesRoot) {\n return { isMonorepo: false, monorepoRoot: null, nodeModulesRoot: null }\n }\n\n const corePkgPath = path.join(nodeModulesRoot, 'node_modules', '@open-mercato', 'core')\n\n try {\n const stat = fs.lstatSync(corePkgPath)\n if (stat.isSymbolicLink()) {\n // It's a symlink - we're in monorepo dev mode\n // Resolve the symlink to find the monorepo root\n const realPath = fs.realpathSync(corePkgPath)\n // realPath is something like /path/to/monorepo/packages/core\n // monorepo root is 2 levels up\n const monorepoRoot = path.dirname(path.dirname(realPath))\n return { isMonorepo: true, monorepoRoot, nodeModulesRoot }\n }\n // It's a real directory - production mode\n return { isMonorepo: false, monorepoRoot: null, nodeModulesRoot }\n } catch {\n // Package doesn't exist yet or error reading - assume production mode\n return { isMonorepo: false, monorepoRoot: null, nodeModulesRoot }\n }\n}\n\nexport function createResolver(cwd: string = process.cwd()): PackageResolver {\n // First detect if we're in a monorepo by checking if node_modules packages are symlinks\n const { isMonorepo: _isMonorepo, monorepoRoot, nodeModulesRoot } = detectMonorepoFromNodeModules(cwd)\n // In workspaces with hoisted real directories, package sources live under the discovered node_modules root.\n const rootDir = monorepoRoot ?? nodeModulesRoot ?? cwd\n\n // shouldResolveAppFromRoot: true when we have a workspace/install root that differs from cwd\n // (monorepo symlinks detected, or node_modules root found above cwd)\n const shouldResolveAppFromRoot =\n _isMonorepo || (nodeModulesRoot !== null && path.resolve(nodeModulesRoot) !== path.resolve(cwd))\n\n // The app directory depends on context:\n // - In monorepo: use detectAppDir to find apps/mercato or similar\n // - When symlinks not detected (e.g. Docker volume node_modules): still use apps/mercato if present at rootDir\n // - Otherwise: app is at cwd\n const candidateAppDir = shouldResolveAppFromRoot ? detectAppDir(rootDir, true) : rootDir\n const appDir =\n _isMonorepo\n ? candidateAppDir\n : shouldResolveAppFromRoot && candidateAppDir !== rootDir && fs.existsSync(candidateAppDir)\n ? candidateAppDir\n : cwd\n\n return {\n isMonorepo: () => _isMonorepo,\n\n getRootDir: () => rootDir,\n\n getAppDir: () => appDir,\n\n getOutputDir: () => {\n // Output is ALWAYS .mercato/generated relative to app directory\n return path.join(appDir, '.mercato', 'generated')\n },\n\n getModulesConfigPath: () => path.join(appDir, 'src', 'modules.ts'),\n\n discoverPackages: () => {\n return _isMonorepo\n ? discoverPackagesInMonorepo(rootDir)\n : discoverPackagesInNodeModules(rootDir)\n },\n\n loadEnabledModules: () => loadEnabledModulesFromConfig(appDir),\n\n getModulePaths: (entry: ModuleEntry) => {\n const appBase = path.resolve(appDir, 'src/modules', entry.id)\n const pkgModulesRoot = pkgDirFor(rootDir, entry.from, _isMonorepo)\n const pkgBase = path.join(pkgModulesRoot, entry.id)\n return { appBase, pkgBase }\n },\n\n getModuleImportBase: (entry: ModuleEntry) => {\n // Prefer @app overrides at import-time; fall back to provided package alias\n const from = entry.from || '@open-mercato/core'\n return {\n appBase: `@/modules/${entry.id}`,\n pkgBase: `${from}/modules/${entry.id}`,\n }\n },\n\n getPackageOutputDir: (packageName: string) => {\n if (packageName === '@app') {\n // App output goes to .mercato/generated\n return path.join(appDir, '.mercato', 'generated')\n }\n const pkgRoot = pkgRootFor(rootDir, packageName, _isMonorepo)\n return path.join(pkgRoot, 'generated')\n },\n\n getPackageRoot: (from?: string) => {\n return pkgRootFor(rootDir, from, _isMonorepo)\n },\n }\n}\n\n/**\n * Resolve the CLI execution environment as a plain value-object.\n * Call once at subsystem init; use the returned object for all path decisions.\n */\nexport function resolveEnvironment(cwd: string = process.cwd()): CliEnvironment {\n const resolver = createResolver(cwd)\n const _isMonorepo = resolver.isMonorepo()\n const rootDir = resolver.getRootDir()\n const appDir = resolver.getAppDir()\n return {\n mode: _isMonorepo ? 'monorepo' : 'standalone',\n rootDir,\n appDir,\n packageRoot: (packageName: string) => pkgRootFor(rootDir, packageName, _isMonorepo),\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -77,30 +77,39 @@ function collectDirectDirectoryNames(directoryPath) {
77
77
  }
78
78
  return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
79
79
  }
80
+ function collectModuleIdsFromModulesRoot(modulesRoot, enabledModules) {
81
+ for (const moduleName of collectDirectDirectoryNames(modulesRoot)) {
82
+ enabledModules.add(normalizeModuleId(moduleName));
83
+ }
84
+ }
80
85
  function resolveEnabledModuleIds(projectRoot) {
81
86
  const enabledModules = /* @__PURE__ */ new Set();
87
+ const appModulesRoot = path.join(projectRoot, "src", "modules");
82
88
  const appsRoot = path.join(projectRoot, "apps");
83
89
  const packagesRoot = path.join(projectRoot, "packages");
90
+ const installedPackagesRoot = path.join(projectRoot, "node_modules", "@open-mercato");
84
91
  const enterpriseEnabled = isEnterpriseModulesEnabled();
92
+ collectModuleIdsFromModulesRoot(appModulesRoot, enabledModules);
85
93
  for (const appName of collectDirectDirectoryNames(appsRoot)) {
86
94
  const moduleRoot = path.join(appsRoot, appName, "src", "modules");
87
- for (const moduleName of collectDirectDirectoryNames(moduleRoot)) {
88
- enabledModules.add(normalizeModuleId(moduleName));
89
- }
95
+ collectModuleIdsFromModulesRoot(moduleRoot, enabledModules);
90
96
  }
91
97
  for (const packageName of collectDirectDirectoryNames(packagesRoot)) {
92
98
  if (packageName === "enterprise" && !enterpriseEnabled) {
93
99
  continue;
94
100
  }
95
101
  const moduleRoot = path.join(packagesRoot, packageName, "src", "modules");
96
- for (const moduleName of collectDirectDirectoryNames(moduleRoot)) {
97
- enabledModules.add(normalizeModuleId(moduleName));
102
+ collectModuleIdsFromModulesRoot(moduleRoot, enabledModules);
103
+ }
104
+ for (const packageName of collectDirectDirectoryNames(installedPackagesRoot)) {
105
+ if (packageName === "enterprise" && !enterpriseEnabled) {
106
+ continue;
98
107
  }
108
+ const moduleRoot = path.join(installedPackagesRoot, packageName, "src", "modules");
109
+ collectModuleIdsFromModulesRoot(moduleRoot, enabledModules);
99
110
  }
100
111
  const createAppTemplateModulesRoot = path.join(projectRoot, "packages", "create-app", "template", "src", "modules");
101
- for (const moduleName of collectDirectDirectoryNames(createAppTemplateModulesRoot)) {
102
- enabledModules.add(normalizeModuleId(moduleName));
103
- }
112
+ collectModuleIdsFromModulesRoot(createAppTemplateModulesRoot, enabledModules);
104
113
  return enabledModules;
105
114
  }
106
115
  function extractModuleNameFromIntegrationPath(relativePath) {
@@ -187,8 +196,10 @@ function discoverIntegrationSpecFiles(projectRoot, legacyIntegrationRoot) {
187
196
  const overlayRoot = resolveOverlayRootPath();
188
197
  const enterpriseEnabled = isEnterpriseModulesEnabled();
189
198
  const discoveryRoots = [
199
+ path.join(projectRoot, "src", "modules"),
190
200
  path.join(projectRoot, "apps"),
191
- path.join(projectRoot, "packages")
201
+ path.join(projectRoot, "packages"),
202
+ path.join(projectRoot, "node_modules", "@open-mercato")
192
203
  ];
193
204
  for (const specFile of collectSpecFilesFromDirectory(legacyIntegrationRoot)) {
194
205
  const relativePath = normalizePath(path.relative(projectRoot, specFile));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/testing/integration-discovery.ts"],
4
- "sourcesContent": ["import { readFileSync, readdirSync } from 'node:fs'\nimport path from 'node:path'\n\nexport type IntegrationSpecDiscoveryItem = {\n path: string\n moduleName: string | null\n isOverlay: boolean\n requiredModules: string[]\n}\n\nconst MODULE_INTEGRATION_DIRECTORY_NAME = '__integration__'\nconst DISCOVERY_IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n '.next',\n 'dist',\n '.turbo',\n 'coverage',\n 'test-results',\n '.yarn',\n '.cache',\n 'tmp',\n 'temp',\n '.claude',\n '.codex',\n])\nconst INTEGRATION_META_FILE_NAMES = ['meta.ts', 'index.ts'] as const\nconst INTEGRATION_META_DEPENDENCY_KEYS = ['dependsOnModules', 'requiredModules', 'requiresModules'] as const\nconst DEFAULT_OVERLAY_ROOT = 'packages/enterprise'\n\nexport function normalizePath(filePath: string): string {\n return filePath.split(path.sep).join('/')\n}\n\nfunction normalizeModuleId(moduleId: string): string {\n return moduleId.trim().toLowerCase()\n}\n\nfunction isEnterpriseModulesEnabled(): boolean {\n const rawValue = process.env.OM_ENABLE_ENTERPRISE_MODULES?.trim().toLowerCase()\n return rawValue === 'true' || rawValue === '1' || rawValue === 'yes' || rawValue === 'on'\n}\n\nfunction collectNamedDirectories(rootPath: string, directoryName: string): string[] {\n let entries\n try {\n entries = readdirSync(rootPath, { withFileTypes: true, encoding: 'utf8' })\n } catch {\n return []\n }\n\n const collected: string[] = []\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (DISCOVERY_IGNORED_DIRS.has(entry.name)) continue\n const absolutePath = path.join(rootPath, entry.name)\n if (entry.name === directoryName) {\n collected.push(absolutePath)\n }\n collected.push(...collectNamedDirectories(absolutePath, directoryName))\n }\n return collected\n}\n\nfunction collectSpecFilesFromDirectory(directoryPath: string): string[] {\n let entries\n try {\n entries = readdirSync(directoryPath, { withFileTypes: true, encoding: 'utf8' })\n } catch {\n return []\n }\n\n const collected: string[] = []\n for (const entry of entries) {\n const absolutePath = path.join(directoryPath, entry.name)\n if (entry.isDirectory()) {\n collected.push(...collectSpecFilesFromDirectory(absolutePath))\n continue\n }\n if (entry.isFile() && entry.name.endsWith('.spec.ts')) {\n collected.push(absolutePath)\n }\n }\n return collected\n}\n\nfunction collectDirectDirectoryNames(directoryPath: string): string[] {\n let entries\n try {\n entries = readdirSync(directoryPath, { withFileTypes: true, encoding: 'utf8' })\n } catch {\n return []\n }\n return entries\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => entry.name)\n}\n\nfunction resolveEnabledModuleIds(projectRoot: string): Set<string> {\n const enabledModules = new Set<string>()\n const appsRoot = path.join(projectRoot, 'apps')\n const packagesRoot = path.join(projectRoot, 'packages')\n const enterpriseEnabled = isEnterpriseModulesEnabled()\n\n for (const appName of collectDirectDirectoryNames(appsRoot)) {\n const moduleRoot = path.join(appsRoot, appName, 'src', 'modules')\n for (const moduleName of collectDirectDirectoryNames(moduleRoot)) {\n enabledModules.add(normalizeModuleId(moduleName))\n }\n }\n for (const packageName of collectDirectDirectoryNames(packagesRoot)) {\n if (packageName === 'enterprise' && !enterpriseEnabled) {\n continue\n }\n const moduleRoot = path.join(packagesRoot, packageName, 'src', 'modules')\n for (const moduleName of collectDirectDirectoryNames(moduleRoot)) {\n enabledModules.add(normalizeModuleId(moduleName))\n }\n }\n const createAppTemplateModulesRoot = path.join(projectRoot, 'packages', 'create-app', 'template', 'src', 'modules')\n for (const moduleName of collectDirectDirectoryNames(createAppTemplateModulesRoot)) {\n enabledModules.add(normalizeModuleId(moduleName))\n }\n\n return enabledModules\n}\n\nfunction extractModuleNameFromIntegrationPath(relativePath: string): string | null {\n const segments = normalizePath(relativePath).split('/')\n const integrationDirectoryIndex = segments.indexOf(MODULE_INTEGRATION_DIRECTORY_NAME)\n if (integrationDirectoryIndex <= 0) {\n return null\n }\n const moduleSegment = segments[integrationDirectoryIndex - 1]\n return moduleSegment && moduleSegment.length > 0 ? moduleSegment : null\n}\n\nfunction resolveOverlayRootPath(): string {\n const configured = process.env.OM_INTEGRATION_OVERLAY_ROOT?.trim()\n if (!configured) {\n return DEFAULT_OVERLAY_ROOT\n }\n return configured.replace(/\\/+$/, '')\n}\n\nfunction isOverlayIntegrationPath(relativePath: string, overlayRoot: string): boolean {\n return normalizePath(relativePath).startsWith(`${normalizePath(overlayRoot)}/`)\n}\n\nfunction extractDependencyListFromSource(source: string): string[] {\n const collected = new Set<string>()\n for (const key of INTEGRATION_META_DEPENDENCY_KEYS) {\n const keyPattern = new RegExp(`${key}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`, 'm')\n const keyMatch = source.match(keyPattern)\n if (!keyMatch?.[1]) continue\n const valuesPattern = /['\"`]([a-zA-Z0-9_.-]+)['\"`]/g\n for (const valueMatch of keyMatch[1].matchAll(valuesPattern)) {\n const value = normalizeModuleId(valueMatch[1] ?? '')\n if (value) {\n collected.add(value)\n }\n }\n }\n return Array.from(collected)\n}\n\nfunction readDependenciesFromMetadataFile(absolutePath: string): string[] {\n try {\n return extractDependencyListFromSource(readFileSync(absolutePath, 'utf8'))\n } catch {\n return []\n }\n}\n\nfunction resolveIntegrationRootDirectory(relativeSpecPath: string): string | null {\n const segments = normalizePath(relativeSpecPath).split('/')\n const integrationDirectoryIndex = segments.indexOf(MODULE_INTEGRATION_DIRECTORY_NAME)\n if (integrationDirectoryIndex === -1) {\n return null\n }\n return segments.slice(0, integrationDirectoryIndex + 1).join('/')\n}\n\nfunction resolveRequiredModulesForSpec(projectRoot: string, relativeSpecPath: string): string[] {\n const requiredModules = new Set<string>()\n const normalizedSpecPath = normalizePath(relativeSpecPath)\n const absoluteSpecPath = path.join(projectRoot, normalizedSpecPath)\n const integrationRoot = resolveIntegrationRootDirectory(normalizedSpecPath)\n\n if (integrationRoot) {\n const relativeDirectory = normalizePath(path.dirname(normalizedSpecPath))\n const integrationRootAbsolute = path.join(projectRoot, integrationRoot)\n const directoryAbsolute = path.join(projectRoot, relativeDirectory)\n const relativeFromIntegrationRoot = normalizePath(path.relative(integrationRootAbsolute, directoryAbsolute))\n const pathSegments = relativeFromIntegrationRoot === '.'\n ? []\n : relativeFromIntegrationRoot.split('/').filter(Boolean)\n\n let currentDirectory = integrationRootAbsolute\n const traversal = [currentDirectory, ...pathSegments.map((segment) => {\n currentDirectory = path.join(currentDirectory, segment)\n return currentDirectory\n })]\n\n for (const directoryPath of traversal) {\n for (const metadataFileName of INTEGRATION_META_FILE_NAMES) {\n const metadataPath = path.join(directoryPath, metadataFileName)\n readDependenciesFromMetadataFile(metadataPath).forEach((dependency) => requiredModules.add(dependency))\n }\n }\n }\n\n readDependenciesFromMetadataFile(absoluteSpecPath).forEach((dependency) => requiredModules.add(dependency))\n const specFileName = path.basename(normalizedSpecPath, '.spec.ts')\n const perTestMetadataPath = path.join(path.dirname(absoluteSpecPath), `${specFileName}.meta.ts`)\n readDependenciesFromMetadataFile(perTestMetadataPath).forEach((dependency) => requiredModules.add(dependency))\n\n return Array.from(requiredModules)\n}\n\nexport function discoverIntegrationSpecFiles(projectRoot: string, legacyIntegrationRoot: string): IntegrationSpecDiscoveryItem[] {\n const discoveredByPath = new Map<string, IntegrationSpecDiscoveryItem>()\n const overlayRoot = resolveOverlayRootPath()\n const enterpriseEnabled = isEnterpriseModulesEnabled()\n const discoveryRoots = [\n path.join(projectRoot, 'apps'),\n path.join(projectRoot, 'packages'),\n ]\n\n for (const specFile of collectSpecFilesFromDirectory(legacyIntegrationRoot)) {\n const relativePath = normalizePath(path.relative(projectRoot, specFile))\n discoveredByPath.set(relativePath, {\n path: relativePath,\n moduleName: extractModuleNameFromIntegrationPath(relativePath),\n isOverlay: isOverlayIntegrationPath(relativePath, overlayRoot),\n requiredModules: resolveRequiredModulesForSpec(projectRoot, relativePath),\n })\n }\n\n for (const root of discoveryRoots) {\n for (const integrationDirectory of collectNamedDirectories(root, MODULE_INTEGRATION_DIRECTORY_NAME)) {\n for (const specFile of collectSpecFilesFromDirectory(integrationDirectory)) {\n const relativePath = normalizePath(path.relative(projectRoot, specFile))\n discoveredByPath.set(relativePath, {\n path: relativePath,\n moduleName: extractModuleNameFromIntegrationPath(relativePath),\n isOverlay: isOverlayIntegrationPath(relativePath, overlayRoot),\n requiredModules: resolveRequiredModulesForSpec(projectRoot, relativePath),\n })\n }\n }\n }\n\n const discovered = Array.from(discoveredByPath.values())\n const enabledModules = resolveEnabledModuleIds(projectRoot)\n const enabledModuleNames = new Set<string>()\n for (const file of discovered) {\n if (!file.isOverlay && file.moduleName) {\n enabledModuleNames.add(file.moduleName)\n }\n }\n\n return discovered\n .filter((file) => {\n if (file.requiredModules.some((moduleId) => !enabledModules.has(normalizeModuleId(moduleId)))) {\n return false\n }\n if (!file.isOverlay) {\n return true\n }\n if (!enterpriseEnabled) {\n return false\n }\n if (!file.moduleName) {\n return true\n }\n if (enabledModuleNames.has(file.moduleName)) {\n return true\n }\n return enabledModules.has(normalizeModuleId(file.moduleName))\n })\n .sort((left, right) => left.path.localeCompare(right.path))\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { readFileSync, readdirSync } from 'node:fs'\nimport path from 'node:path'\n\nexport type IntegrationSpecDiscoveryItem = {\n path: string\n moduleName: string | null\n isOverlay: boolean\n requiredModules: string[]\n}\n\nconst MODULE_INTEGRATION_DIRECTORY_NAME = '__integration__'\nconst DISCOVERY_IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n '.next',\n 'dist',\n '.turbo',\n 'coverage',\n 'test-results',\n '.yarn',\n '.cache',\n 'tmp',\n 'temp',\n '.claude',\n '.codex',\n])\nconst INTEGRATION_META_FILE_NAMES = ['meta.ts', 'index.ts'] as const\nconst INTEGRATION_META_DEPENDENCY_KEYS = ['dependsOnModules', 'requiredModules', 'requiresModules'] as const\nconst DEFAULT_OVERLAY_ROOT = 'packages/enterprise'\n\nexport function normalizePath(filePath: string): string {\n return filePath.split(path.sep).join('/')\n}\n\nfunction normalizeModuleId(moduleId: string): string {\n return moduleId.trim().toLowerCase()\n}\n\nfunction isEnterpriseModulesEnabled(): boolean {\n const rawValue = process.env.OM_ENABLE_ENTERPRISE_MODULES?.trim().toLowerCase()\n return rawValue === 'true' || rawValue === '1' || rawValue === 'yes' || rawValue === 'on'\n}\n\nfunction collectNamedDirectories(rootPath: string, directoryName: string): string[] {\n let entries\n try {\n entries = readdirSync(rootPath, { withFileTypes: true, encoding: 'utf8' })\n } catch {\n return []\n }\n\n const collected: string[] = []\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (DISCOVERY_IGNORED_DIRS.has(entry.name)) continue\n const absolutePath = path.join(rootPath, entry.name)\n if (entry.name === directoryName) {\n collected.push(absolutePath)\n }\n collected.push(...collectNamedDirectories(absolutePath, directoryName))\n }\n return collected\n}\n\nfunction collectSpecFilesFromDirectory(directoryPath: string): string[] {\n let entries\n try {\n entries = readdirSync(directoryPath, { withFileTypes: true, encoding: 'utf8' })\n } catch {\n return []\n }\n\n const collected: string[] = []\n for (const entry of entries) {\n const absolutePath = path.join(directoryPath, entry.name)\n if (entry.isDirectory()) {\n collected.push(...collectSpecFilesFromDirectory(absolutePath))\n continue\n }\n if (entry.isFile() && entry.name.endsWith('.spec.ts')) {\n collected.push(absolutePath)\n }\n }\n return collected\n}\n\nfunction collectDirectDirectoryNames(directoryPath: string): string[] {\n let entries\n try {\n entries = readdirSync(directoryPath, { withFileTypes: true, encoding: 'utf8' })\n } catch {\n return []\n }\n return entries\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => entry.name)\n}\n\nfunction collectModuleIdsFromModulesRoot(modulesRoot: string, enabledModules: Set<string>): void {\n for (const moduleName of collectDirectDirectoryNames(modulesRoot)) {\n enabledModules.add(normalizeModuleId(moduleName))\n }\n}\n\nfunction resolveEnabledModuleIds(projectRoot: string): Set<string> {\n const enabledModules = new Set<string>()\n const appModulesRoot = path.join(projectRoot, 'src', 'modules')\n const appsRoot = path.join(projectRoot, 'apps')\n const packagesRoot = path.join(projectRoot, 'packages')\n const installedPackagesRoot = path.join(projectRoot, 'node_modules', '@open-mercato')\n const enterpriseEnabled = isEnterpriseModulesEnabled()\n\n // Standalone app: src/modules at project root\n collectModuleIdsFromModulesRoot(appModulesRoot, enabledModules)\n\n for (const appName of collectDirectDirectoryNames(appsRoot)) {\n const moduleRoot = path.join(appsRoot, appName, 'src', 'modules')\n collectModuleIdsFromModulesRoot(moduleRoot, enabledModules)\n }\n for (const packageName of collectDirectDirectoryNames(packagesRoot)) {\n if (packageName === 'enterprise' && !enterpriseEnabled) {\n continue\n }\n const moduleRoot = path.join(packagesRoot, packageName, 'src', 'modules')\n collectModuleIdsFromModulesRoot(moduleRoot, enabledModules)\n }\n // Standalone app: installed @open-mercato packages in node_modules\n for (const packageName of collectDirectDirectoryNames(installedPackagesRoot)) {\n if (packageName === 'enterprise' && !enterpriseEnabled) {\n continue\n }\n const moduleRoot = path.join(installedPackagesRoot, packageName, 'src', 'modules')\n collectModuleIdsFromModulesRoot(moduleRoot, enabledModules)\n }\n const createAppTemplateModulesRoot = path.join(projectRoot, 'packages', 'create-app', 'template', 'src', 'modules')\n collectModuleIdsFromModulesRoot(createAppTemplateModulesRoot, enabledModules)\n\n return enabledModules\n}\n\nfunction extractModuleNameFromIntegrationPath(relativePath: string): string | null {\n const segments = normalizePath(relativePath).split('/')\n const integrationDirectoryIndex = segments.indexOf(MODULE_INTEGRATION_DIRECTORY_NAME)\n if (integrationDirectoryIndex <= 0) {\n return null\n }\n const moduleSegment = segments[integrationDirectoryIndex - 1]\n return moduleSegment && moduleSegment.length > 0 ? moduleSegment : null\n}\n\nfunction resolveOverlayRootPath(): string {\n const configured = process.env.OM_INTEGRATION_OVERLAY_ROOT?.trim()\n if (!configured) {\n return DEFAULT_OVERLAY_ROOT\n }\n return configured.replace(/\\/+$/, '')\n}\n\nfunction isOverlayIntegrationPath(relativePath: string, overlayRoot: string): boolean {\n return normalizePath(relativePath).startsWith(`${normalizePath(overlayRoot)}/`)\n}\n\nfunction extractDependencyListFromSource(source: string): string[] {\n const collected = new Set<string>()\n for (const key of INTEGRATION_META_DEPENDENCY_KEYS) {\n const keyPattern = new RegExp(`${key}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`, 'm')\n const keyMatch = source.match(keyPattern)\n if (!keyMatch?.[1]) continue\n const valuesPattern = /['\"`]([a-zA-Z0-9_.-]+)['\"`]/g\n for (const valueMatch of keyMatch[1].matchAll(valuesPattern)) {\n const value = normalizeModuleId(valueMatch[1] ?? '')\n if (value) {\n collected.add(value)\n }\n }\n }\n return Array.from(collected)\n}\n\nfunction readDependenciesFromMetadataFile(absolutePath: string): string[] {\n try {\n return extractDependencyListFromSource(readFileSync(absolutePath, 'utf8'))\n } catch {\n return []\n }\n}\n\nfunction resolveIntegrationRootDirectory(relativeSpecPath: string): string | null {\n const segments = normalizePath(relativeSpecPath).split('/')\n const integrationDirectoryIndex = segments.indexOf(MODULE_INTEGRATION_DIRECTORY_NAME)\n if (integrationDirectoryIndex === -1) {\n return null\n }\n return segments.slice(0, integrationDirectoryIndex + 1).join('/')\n}\n\nfunction resolveRequiredModulesForSpec(projectRoot: string, relativeSpecPath: string): string[] {\n const requiredModules = new Set<string>()\n const normalizedSpecPath = normalizePath(relativeSpecPath)\n const absoluteSpecPath = path.join(projectRoot, normalizedSpecPath)\n const integrationRoot = resolveIntegrationRootDirectory(normalizedSpecPath)\n\n if (integrationRoot) {\n const relativeDirectory = normalizePath(path.dirname(normalizedSpecPath))\n const integrationRootAbsolute = path.join(projectRoot, integrationRoot)\n const directoryAbsolute = path.join(projectRoot, relativeDirectory)\n const relativeFromIntegrationRoot = normalizePath(path.relative(integrationRootAbsolute, directoryAbsolute))\n const pathSegments = relativeFromIntegrationRoot === '.'\n ? []\n : relativeFromIntegrationRoot.split('/').filter(Boolean)\n\n let currentDirectory = integrationRootAbsolute\n const traversal = [currentDirectory, ...pathSegments.map((segment) => {\n currentDirectory = path.join(currentDirectory, segment)\n return currentDirectory\n })]\n\n for (const directoryPath of traversal) {\n for (const metadataFileName of INTEGRATION_META_FILE_NAMES) {\n const metadataPath = path.join(directoryPath, metadataFileName)\n readDependenciesFromMetadataFile(metadataPath).forEach((dependency) => requiredModules.add(dependency))\n }\n }\n }\n\n readDependenciesFromMetadataFile(absoluteSpecPath).forEach((dependency) => requiredModules.add(dependency))\n const specFileName = path.basename(normalizedSpecPath, '.spec.ts')\n const perTestMetadataPath = path.join(path.dirname(absoluteSpecPath), `${specFileName}.meta.ts`)\n readDependenciesFromMetadataFile(perTestMetadataPath).forEach((dependency) => requiredModules.add(dependency))\n\n return Array.from(requiredModules)\n}\n\nexport function discoverIntegrationSpecFiles(projectRoot: string, legacyIntegrationRoot: string): IntegrationSpecDiscoveryItem[] {\n const discoveredByPath = new Map<string, IntegrationSpecDiscoveryItem>()\n const overlayRoot = resolveOverlayRootPath()\n const enterpriseEnabled = isEnterpriseModulesEnabled()\n const discoveryRoots = [\n path.join(projectRoot, 'src', 'modules'),\n path.join(projectRoot, 'apps'),\n path.join(projectRoot, 'packages'),\n path.join(projectRoot, 'node_modules', '@open-mercato'),\n ]\n\n for (const specFile of collectSpecFilesFromDirectory(legacyIntegrationRoot)) {\n const relativePath = normalizePath(path.relative(projectRoot, specFile))\n discoveredByPath.set(relativePath, {\n path: relativePath,\n moduleName: extractModuleNameFromIntegrationPath(relativePath),\n isOverlay: isOverlayIntegrationPath(relativePath, overlayRoot),\n requiredModules: resolveRequiredModulesForSpec(projectRoot, relativePath),\n })\n }\n\n for (const root of discoveryRoots) {\n for (const integrationDirectory of collectNamedDirectories(root, MODULE_INTEGRATION_DIRECTORY_NAME)) {\n for (const specFile of collectSpecFilesFromDirectory(integrationDirectory)) {\n const relativePath = normalizePath(path.relative(projectRoot, specFile))\n discoveredByPath.set(relativePath, {\n path: relativePath,\n moduleName: extractModuleNameFromIntegrationPath(relativePath),\n isOverlay: isOverlayIntegrationPath(relativePath, overlayRoot),\n requiredModules: resolveRequiredModulesForSpec(projectRoot, relativePath),\n })\n }\n }\n }\n\n const discovered = Array.from(discoveredByPath.values())\n const enabledModules = resolveEnabledModuleIds(projectRoot)\n const enabledModuleNames = new Set<string>()\n for (const file of discovered) {\n if (!file.isOverlay && file.moduleName) {\n enabledModuleNames.add(file.moduleName)\n }\n }\n\n return discovered\n .filter((file) => {\n if (file.requiredModules.some((moduleId) => !enabledModules.has(normalizeModuleId(moduleId)))) {\n return false\n }\n if (!file.isOverlay) {\n return true\n }\n if (!enterpriseEnabled) {\n return false\n }\n if (!file.moduleName) {\n return true\n }\n if (enabledModuleNames.has(file.moduleName)) {\n return true\n }\n return enabledModules.has(normalizeModuleId(file.moduleName))\n })\n .sort((left, right) => left.path.localeCompare(right.path))\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,12 +1,13 @@
1
1
  import { GenericContainer } from "testcontainers";
2
2
  import { spawn } from "node:child_process";
3
3
  import { createServer } from "node:net";
4
+ import { existsSync, readFileSync } from "node:fs";
4
5
  import { mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
5
6
  import { createHash } from "node:crypto";
6
7
  import path from "node:path";
7
8
  import { createInterface } from "node:readline/promises";
8
9
  import { stdin as input, stdout as output } from "node:process";
9
- import { createResolver } from "../resolver.js";
10
+ import { resolveEnvironment } from "../resolver.js";
10
11
  import { discoverIntegrationSpecFiles as discoverIntegrationSpecFilesShared } from "./integration-discovery.js";
11
12
  import { resolveDockerHostFromContext, runCommandAndCapture } from "./runtime-utils.js";
12
13
  function shouldUseIsolatedPortForFreshEnvironment(options) {
@@ -33,8 +34,44 @@ const PLAYWRIGHT_QUICK_FAILURE_THRESHOLD = 6;
33
34
  const PLAYWRIGHT_QUICK_FAILURE_MAX_DURATION_MS = 1500;
34
35
  const PLAYWRIGHT_HEALTH_PROBE_INTERVAL_MS = 3e3;
35
36
  const ANSI_ESCAPE_REGEX = /\x1b\[[0-?]*[ -/]*[@-~]/g;
36
- const resolver = createResolver();
37
- const projectRootDirectory = resolver.getRootDir();
37
+ const env = resolveEnvironment();
38
+ const projectRootDirectory = env.rootDir;
39
+ const appDirectory = env.appDir;
40
+ const corePackageRootDirectory = env.packageRoot("@open-mercato/core");
41
+ const uiPackageRootDirectory = env.packageRoot("@open-mercato/ui");
42
+ function resolveFirstExistingPath(...candidates) {
43
+ for (const candidate of candidates) {
44
+ if (existsSync(candidate)) {
45
+ return candidate;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ function collectExistingPaths(candidates) {
51
+ const collected = /* @__PURE__ */ new Set();
52
+ for (const candidate of candidates) {
53
+ if (candidate && existsSync(candidate)) {
54
+ collected.add(candidate);
55
+ }
56
+ }
57
+ return Array.from(collected);
58
+ }
59
+ function readPackageScripts(packageRoot) {
60
+ try {
61
+ const raw = JSON.parse(readFileSync(path.join(packageRoot, "package.json"), "utf8"));
62
+ return raw.scripts ?? {};
63
+ } catch {
64
+ return {};
65
+ }
66
+ }
67
+ const projectScripts = readPackageScripts(projectRootDirectory);
68
+ const appNextConfigPath = resolveFirstExistingPath(
69
+ path.join(appDirectory, "next.config.ts"),
70
+ path.join(appDirectory, "next.config.js"),
71
+ path.join(appDirectory, "next.config.mjs")
72
+ );
73
+ const APP_MODULES_CHECKSUM_PATH = path.join(appDirectory, ".mercato", "generated", "modules.generated.checksum");
74
+ const PROJECT_SUPPORTS_PACKAGE_BUILDS = typeof projectScripts["build:packages"] === "string";
38
75
  const EPHEMERAL_ENV_FILE_PATH = path.join(projectRootDirectory, ".ai", "qa", "ephemeral-env.json");
39
76
  const EPHEMERAL_ENV_LOCK_PATH = path.join(projectRootDirectory, ".ai", "qa", "ephemeral-env.lock");
40
77
  const LEGACY_EPHEMERAL_ENV_FILE_PATH = path.join(projectRootDirectory, ".ai", "qa", "ephemeral-env.md");
@@ -43,26 +80,26 @@ const PLAYWRIGHT_INTEGRATION_CONFIG_PATH = ".ai/qa/tests/playwright.config.ts";
43
80
  const PLAYWRIGHT_RESULTS_JSON_PATH = path.join(projectRootDirectory, ".ai", "qa", "test-results", "results.json");
44
81
  const LEGACY_INTEGRATION_TEST_ROOT = path.join(projectRootDirectory, ".ai", "qa", "tests");
45
82
  const APP_BUILD_ARTIFACTS = [
46
- path.join(projectRootDirectory, "apps", "mercato", ".mercato", "next", "BUILD_ID"),
47
- path.join(projectRootDirectory, "apps", "mercato", ".mercato", "generated", "modules.generated.ts"),
48
- path.join(projectRootDirectory, "packages", "core", "dist", "index.js"),
49
- path.join(projectRootDirectory, "packages", "ui", "dist", "index.js")
83
+ path.join(appDirectory, ".mercato", "next", "BUILD_ID"),
84
+ path.join(appDirectory, ".mercato", "generated", "modules.generated.ts"),
85
+ path.join(corePackageRootDirectory, "dist", "index.js"),
86
+ path.join(uiPackageRootDirectory, "dist", "index.js")
50
87
  ];
51
- const APP_BUILD_INPUT_PATHS = [
52
- path.join(projectRootDirectory, "apps", "mercato", "src"),
53
- path.join(projectRootDirectory, "apps", "mercato", "package.json"),
54
- path.join(projectRootDirectory, "apps", "mercato", "next.config.ts"),
55
- path.join(projectRootDirectory, "apps", "mercato", "tsconfig.json"),
56
- path.join(projectRootDirectory, "packages", "core", "src"),
57
- path.join(projectRootDirectory, "packages", "core", "package.json"),
58
- path.join(projectRootDirectory, "packages", "core", "tsconfig.json"),
59
- path.join(projectRootDirectory, "packages", "ui", "src"),
60
- path.join(projectRootDirectory, "packages", "ui", "package.json"),
61
- path.join(projectRootDirectory, "packages", "ui", "tsconfig.json"),
88
+ const APP_BUILD_INPUT_PATHS = collectExistingPaths([
89
+ path.join(appDirectory, "src"),
90
+ path.join(appDirectory, "package.json"),
91
+ appNextConfigPath,
92
+ path.join(appDirectory, "tsconfig.json"),
93
+ resolveFirstExistingPath(path.join(corePackageRootDirectory, "src"), path.join(corePackageRootDirectory, "dist")),
94
+ path.join(corePackageRootDirectory, "package.json"),
95
+ path.join(corePackageRootDirectory, "tsconfig.json"),
96
+ resolveFirstExistingPath(path.join(uiPackageRootDirectory, "src"), path.join(uiPackageRootDirectory, "dist")),
97
+ path.join(uiPackageRootDirectory, "package.json"),
98
+ path.join(uiPackageRootDirectory, "tsconfig.json"),
62
99
  path.join(projectRootDirectory, "package.json"),
63
100
  path.join(projectRootDirectory, "tsconfig.base.json"),
64
101
  path.join(projectRootDirectory, "yarn.lock")
65
- ];
102
+ ]);
66
103
  const EXPECTED_TEST_FOLDERS = ["auth", "catalog", "crm", "sales", "admin", "api", "integration"];
67
104
  const FOLDER_TO_CATEGORY_CODE = {
68
105
  admin: "ADMIN",
@@ -96,8 +133,8 @@ function buildEnvironment(overrides) {
96
133
  ...overrides
97
134
  };
98
135
  }
99
- function runYarnCommand(args, environment, opts = {}) {
100
- return runYarnRawCommand(["run", ...args], environment, opts);
136
+ function runYarnCommand(args, environment, opts = {}, cwd = projectRootDirectory) {
137
+ return runYarnRawCommand(["run", ...args], environment, opts, cwd);
101
138
  }
102
139
  async function runTimedStep(logPrefix, label, options, task) {
103
140
  const startedAt = Date.now();
@@ -283,11 +320,11 @@ async function runNpxCommandWithOutputMonitoring(args, environment, opts = {}) {
283
320
  }
284
321
  throw createMonitoredCommandError("npx", args, result);
285
322
  }
286
- function runYarnRawCommand(commandArgs, environment, opts = {}) {
323
+ function runYarnRawCommand(commandArgs, environment, opts = {}, cwd = projectRootDirectory) {
287
324
  return new Promise((resolve, reject) => {
288
325
  const outputMode = opts.silent ? ["ignore", "pipe", "pipe"] : "inherit";
289
326
  const command = spawn(resolveYarnBinary(), commandArgs, {
290
- cwd: projectRootDirectory,
327
+ cwd,
291
328
  env: environment,
292
329
  stdio: outputMode
293
330
  });
@@ -331,13 +368,10 @@ function runNpxCommand(args, environment) {
331
368
  });
332
369
  });
333
370
  }
334
- function runYarnWorkspaceCommand(workspaceName, commandName, commandArgs, environment, opts = {}) {
335
- return runYarnRawCommand(["workspace", workspaceName, commandName, ...commandArgs], environment, opts);
336
- }
337
- function startYarnRawCommand(commandArgs, environment, opts = {}) {
371
+ function startYarnRawCommand(commandArgs, environment, opts = {}, cwd = projectRootDirectory) {
338
372
  const outputMode = opts.silent ? ["ignore", "pipe", "pipe"] : "inherit";
339
373
  const processHandle = spawn(resolveYarnBinary(), commandArgs, {
340
- cwd: projectRootDirectory,
374
+ cwd,
341
375
  env: environment,
342
376
  stdio: outputMode
343
377
  });
@@ -349,8 +383,8 @@ function startYarnRawCommand(commandArgs, environment, opts = {}) {
349
383
  }
350
384
  return processHandle;
351
385
  }
352
- function startYarnWorkspaceCommand(workspaceName, commandName, commandArgs, environment, opts = {}) {
353
- return startYarnRawCommand(["workspace", workspaceName, commandName, ...commandArgs], environment, opts);
386
+ function startYarnCommand(args, environment, opts = {}, cwd = projectRootDirectory) {
387
+ return startYarnRawCommand(["run", ...args], environment, opts, cwd);
354
388
  }
355
389
  async function assertContainerRuntimeAvailable() {
356
390
  const dockerInfoResult = await runCommandAndCapture("docker", ["info"]);
@@ -1981,7 +2015,6 @@ async function startEphemeralEnvironment(options) {
1981
2015
  return existingEnvironment;
1982
2016
  }
1983
2017
  }
1984
- const appWorkspace = "@open-mercato/app";
1985
2018
  const shouldUseIsolatedPort = shouldUseIsolatedPortForFreshEnvironment({
1986
2019
  reuseExisting: options.reuseExisting,
1987
2020
  existingStateBeforeReuseAttempt
@@ -2073,9 +2106,9 @@ async function startEphemeralEnvironment(options) {
2073
2106
  }
2074
2107
  console.log(`[${options.logPrefix}] Ephemeral database ready at ${databaseHost}:${databasePort}`);
2075
2108
  console.log(`[${options.logPrefix}] Initializing application data (includes migrations)...`);
2076
- await runTimedStep(options.logPrefix, "Initializing application data", { expectedSeconds: 45 }, async () => runYarnWorkspaceCommand(appWorkspace, "initialize", [], commandEnvironment, {
2109
+ await runTimedStep(options.logPrefix, "Initializing application data", { expectedSeconds: 45 }, async () => runYarnCommand(["initialize"], commandEnvironment, {
2077
2110
  silent: !options.verbose
2078
- }));
2111
+ }, appDirectory));
2079
2112
  if (!needsBuild) {
2080
2113
  console.log(
2081
2114
  `[${options.logPrefix}] Build cache valid (within ${BUILD_CACHE_TTL_ENV_VAR}=${buildCacheTtlSeconds}s). Skipping build pipeline.`
@@ -2086,33 +2119,39 @@ async function startEphemeralEnvironment(options) {
2086
2119
  } else {
2087
2120
  console.log(`[${options.logPrefix}] Build artifacts missing, stale, or out of date; rebuilding artifacts.`);
2088
2121
  }
2089
- console.log(`[${options.logPrefix}] Building packages...`);
2090
- await runTimedStep(options.logPrefix, "Building packages", { expectedSeconds: 20 }, async () => runYarnCommand(["build:packages"], commandEnvironment, {
2091
- silent: !options.verbose
2092
- }));
2122
+ if (PROJECT_SUPPORTS_PACKAGE_BUILDS) {
2123
+ console.log(`[${options.logPrefix}] Building packages...`);
2124
+ await runTimedStep(options.logPrefix, "Building packages", { expectedSeconds: 20 }, async () => runYarnCommand(["build:packages"], commandEnvironment, {
2125
+ silent: !options.verbose
2126
+ }));
2127
+ } else {
2128
+ console.log(`[${options.logPrefix}] Skipping package build step (no build:packages script at project root).`);
2129
+ }
2093
2130
  console.log(`[${options.logPrefix}] Regenerating module artifacts...`);
2094
- await rm(path.join(projectRootDirectory, "apps", "mercato", ".mercato", "generated", "modules.generated.checksum"), {
2131
+ await rm(APP_MODULES_CHECKSUM_PATH, {
2095
2132
  force: true
2096
2133
  });
2097
2134
  await runTimedStep(options.logPrefix, "Regenerating module artifacts", { expectedSeconds: 8 }, async () => runYarnCommand(["generate"], commandEnvironment, {
2098
2135
  silent: !options.verbose
2099
- }));
2100
- console.log(`[${options.logPrefix}] Rebuilding packages after generation...`);
2101
- await runTimedStep(options.logPrefix, "Rebuilding packages after generation", { expectedSeconds: 20 }, async () => runYarnCommand(["build:packages"], commandEnvironment, {
2102
- silent: !options.verbose
2103
- }));
2136
+ }, appDirectory));
2137
+ if (PROJECT_SUPPORTS_PACKAGE_BUILDS) {
2138
+ console.log(`[${options.logPrefix}] Rebuilding packages after generation...`);
2139
+ await runTimedStep(options.logPrefix, "Rebuilding packages after generation", { expectedSeconds: 20 }, async () => runYarnCommand(["build:packages"], commandEnvironment, {
2140
+ silent: !options.verbose
2141
+ }));
2142
+ }
2104
2143
  console.log(`[${options.logPrefix}] Building application...`);
2105
- await runTimedStep(options.logPrefix, "Building application", { expectedSeconds: 76 }, async () => runYarnWorkspaceCommand(appWorkspace, "build", [], commandEnvironment, {
2144
+ await runTimedStep(options.logPrefix, "Building application", { expectedSeconds: 76 }, async () => runYarnCommand(["build"], commandEnvironment, {
2106
2145
  silent: !options.verbose
2107
- }));
2146
+ }, appDirectory));
2108
2147
  }
2109
2148
  if (shouldPersistBuildCache && sourceFingerprintValue) {
2110
2149
  await writeBuildCacheState(sourceFingerprintValue);
2111
2150
  }
2112
2151
  console.log(`[${options.logPrefix}] Starting application on ${applicationBaseUrl}...`);
2113
- const startedAppProcess = startYarnWorkspaceCommand(appWorkspace, "start", [], commandEnvironment, {
2152
+ const startedAppProcess = startYarnCommand(["start"], commandEnvironment, {
2114
2153
  silent: !options.verbose
2115
- });
2154
+ }, appDirectory);
2116
2155
  applicationProcess = startedAppProcess;
2117
2156
  await runTimedStep(
2118
2157
  options.logPrefix,