@open-mercato/cli 0.6.7-develop.6553.1.2ec7246e04 → 0.6.7-develop.6566.1.15385fa179
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 +3 -3
- package/AGENTS.md +1 -0
- package/dist/agentic/guides/module-facts.json +53 -53
- package/dist/agentic/guides/modules/ai_assistant.md +1 -1
- package/dist/agentic/guides/modules/api_docs.md +1 -1
- package/dist/agentic/guides/modules/api_keys.md +1 -1
- package/dist/agentic/guides/modules/attachments.md +1 -1
- package/dist/agentic/guides/modules/audit_logs.md +1 -1
- package/dist/agentic/guides/modules/auth.md +1 -1
- package/dist/agentic/guides/modules/business_rules.md +1 -1
- package/dist/agentic/guides/modules/catalog.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail.md +1 -1
- package/dist/agentic/guides/modules/channel_imap.md +1 -1
- package/dist/agentic/guides/modules/checkout.md +1 -1
- package/dist/agentic/guides/modules/communication_channels.md +1 -1
- package/dist/agentic/guides/modules/configs.md +1 -1
- package/dist/agentic/guides/modules/content.md +1 -1
- package/dist/agentic/guides/modules/currencies.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts.md +1 -1
- package/dist/agentic/guides/modules/customers.md +1 -1
- package/dist/agentic/guides/modules/dashboards.md +1 -1
- package/dist/agentic/guides/modules/data_sync.md +1 -1
- package/dist/agentic/guides/modules/dictionaries.md +1 -1
- package/dist/agentic/guides/modules/directory.md +1 -1
- package/dist/agentic/guides/modules/entities.md +1 -1
- package/dist/agentic/guides/modules/events.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe.md +1 -1
- package/dist/agentic/guides/modules/generators.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops.md +1 -1
- package/dist/agentic/guides/modules/integrations.md +1 -1
- package/dist/agentic/guides/modules/messages.md +1 -1
- package/dist/agentic/guides/modules/notifications.md +1 -1
- package/dist/agentic/guides/modules/onboarding.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways.md +1 -1
- package/dist/agentic/guides/modules/perspectives.md +1 -1
- package/dist/agentic/guides/modules/planner.md +1 -1
- package/dist/agentic/guides/modules/portal.md +1 -1
- package/dist/agentic/guides/modules/progress.md +1 -1
- package/dist/agentic/guides/modules/query_index.md +1 -1
- package/dist/agentic/guides/modules/record_locks.md +1 -1
- package/dist/agentic/guides/modules/resources.md +1 -1
- package/dist/agentic/guides/modules/sales.md +1 -1
- package/dist/agentic/guides/modules/scheduler.md +1 -1
- package/dist/agentic/guides/modules/search.md +1 -1
- package/dist/agentic/guides/modules/security.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers.md +1 -1
- package/dist/agentic/guides/modules/sso.md +1 -1
- package/dist/agentic/guides/modules/staff.md +1 -1
- package/dist/agentic/guides/modules/storage_s3.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo.md +1 -1
- package/dist/agentic/guides/modules/sync_excel.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays.md +1 -1
- package/dist/agentic/guides/modules/translations.md +1 -1
- package/dist/agentic/guides/modules/webhooks.md +1 -1
- package/dist/agentic/guides/modules/workflows.md +1 -1
- package/dist/agentic/shared/AGENTS.md.template +1 -1
- package/dist/agentic/shared/ai/skills/tiers.schema.json +15 -0
- package/dist/agentic/shared/scripts/install-skills.sh +244 -37
- package/dist/lib/__integration__/TC-INT-008.spec.js +9 -11
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-setup.js +14 -2
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/db/commands.js +1 -1
- package/dist/lib/db/commands.js.map +1 -1
- package/dist/lib/generate-watch-events.js +106 -0
- package/dist/lib/generate-watch-events.js.map +7 -0
- package/dist/lib/generators/ast/imports.js +1 -1
- package/dist/lib/generators/ast/imports.js.map +1 -1
- package/dist/lib/generators/entity-ids.js +21 -14
- package/dist/lib/generators/entity-ids.js.map +2 -2
- package/dist/lib/generators/extensions/injection-widgets.js +1 -1
- package/dist/lib/generators/extensions/injection-widgets.js.map +1 -1
- package/dist/lib/generators/module-facts.js +1 -1
- package/dist/lib/generators/module-facts.js.map +1 -1
- package/dist/lib/generators/module-registry.js +1 -1
- package/dist/lib/generators/module-registry.js.map +1 -1
- package/dist/lib/generators/openapi.js +114 -12
- package/dist/lib/generators/openapi.js.map +2 -2
- package/dist/lib/in-process-generate-watcher.js +13 -2
- package/dist/lib/in-process-generate-watcher.js.map +2 -2
- package/dist/lib/modules-config.js +1 -1
- package/dist/lib/modules-config.js.map +1 -1
- package/dist/lib/resolver.js +1 -1
- package/dist/lib/resolver.js.map +1 -1
- package/dist/mercato.js +68 -23
- package/dist/mercato.js.map +2 -2
- package/package.json +7 -6
- package/src/__tests__/mercato.test.ts +53 -2
- package/src/lib/__integration__/TC-INT-008.spec.ts +15 -15
- package/src/lib/__tests__/generate-watch-events.test.ts +112 -0
- package/src/lib/__tests__/generate-watch-structure.test.ts +20 -0
- package/src/lib/__tests__/in-process-generate-watcher.test.ts +193 -2
- package/src/lib/agentic-setup.ts +23 -4
- package/src/lib/db/commands.ts +1 -1
- package/src/lib/generate-watch-events.ts +125 -0
- package/src/lib/generators/__tests__/entity-ids.test.ts +98 -1
- package/src/lib/generators/__tests__/openapi.test.ts +174 -0
- package/src/lib/generators/__tests__/structural-contracts.test.ts +1 -1
- package/src/lib/generators/ast/imports.ts +1 -1
- package/src/lib/generators/entity-ids.ts +34 -14
- package/src/lib/generators/extensions/injection-widgets.ts +1 -1
- package/src/lib/generators/module-facts.ts +1 -1
- package/src/lib/generators/module-registry.ts +1 -1
- package/src/lib/generators/openapi.ts +167 -14
- package/src/lib/in-process-generate-watcher.ts +35 -6
- package/src/lib/modules-config.ts +1 -1
- package/src/lib/resolver.ts +1 -1
- package/src/mercato.ts +75 -27
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/agentic-setup.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Agentic setup for the CLI `agentic:init` command.\n *\n * Source files live in packages/create-app/agentic/ and are copied\n * to packages/cli/dist/agentic/ during build (see build.mjs).\n * This module reads those files at runtime \u2014 no embedded string constants.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, symlinkSync, lstatSync, unlinkSync, readdirSync } from 'node:fs'\nimport { spawnSync } from 'node:child_process'\nimport { join, dirname, basename } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { Project, SyntaxKind } from 'ts-morph'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n// In the built output (dist/lib/agentic-setup.js), __dirname is dist/lib/.\n// agentic/ is copied to dist/agentic/ by build.mjs.\nconst AGENTIC_DIR = join(__dirname, '..', 'agentic')\nconst GUIDES_DIR = join(AGENTIC_DIR, 'guides')\n\ntype AskFn = (question: string) => Promise<string>\n\ninterface AgenticSetupOptions {\n tool?: string\n force?: boolean\n}\n\ninterface AgenticConfig {\n projectName: string\n targetDir: string\n}\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction resolvePlaceholders(content: string, config: AgenticConfig): string {\n return content.replace(/\\{\\{PROJECT_NAME\\}\\}/g, config.projectName)\n}\n\nfunction ensureDir(filePath: string): void {\n const dir = dirname(filePath)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n}\n\nfunction writeTemplate(srcDir: string, srcRelative: string, destPath: string, config: AgenticConfig): void {\n const srcPath = join(srcDir, srcRelative)\n const content = readFileSync(srcPath, 'utf-8')\n ensureDir(destPath)\n writeFileSync(destPath, resolvePlaceholders(content, config))\n}\n\nfunction copyFile(srcDir: string, srcRelative: string, destPath: string): void {\n const srcPath = join(srcDir, srcRelative)\n ensureDir(destPath)\n copyFileSync(srcPath, destPath)\n}\n\n// \u2500\u2500\u2500 Module fact-sheet selection (mirrors packages/create-app/src/setup/tools/shared.ts) \u2500\u2500\n\n// AST-parse the static `enabledModules` array literal in the app's src/modules.ts\n// and collect each entry's `id`. Only the static literal is read (conditional\n// .push()/spread entries are intentionally not seen \u2014 see spec D6).\nfunction readEnabledModuleIds(modulesPath: string): string[] {\n if (!existsSync(modulesPath)) return []\n try {\n const project = new Project({ useInMemoryFileSystem: true })\n const sourceFile = project.createSourceFile('modules.ts', readFileSync(modulesPath, 'utf-8'))\n const declaration = sourceFile.getVariableDeclaration('enabledModules')\n const arrayLiteral = declaration?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)\n if (!arrayLiteral) return []\n const ids: string[] = []\n for (const element of arrayLiteral.getElements()) {\n const objectLiteral = element.asKind(SyntaxKind.ObjectLiteralExpression)\n if (!objectLiteral) continue\n const idProperty = objectLiteral.getProperty('id')?.asKind(SyntaxKind.PropertyAssignment)\n const idValue = idProperty?.getInitializerIfKind(SyntaxKind.StringLiteral)?.getLiteralValue()\n if (idValue) ids.push(idValue)\n }\n return ids\n } catch {\n return []\n }\n}\n\n// Resolve which per-module fact-sheets to ship: the intersection of the bundled\n// fact-sheets (the D5 allowlist, materialized by build.mjs) with the app's enabled\n// modules. Falls back to the full bundled set when the enabled set cannot be read\n// (R5 \u2014 degraded, never empty).\nfunction selectModuleFactSheets(targetDir: string, modulesSubdir: string): string[] {\n const available = existsSync(modulesSubdir)\n ? readdirSync(modulesSubdir)\n .filter((file) => file.endsWith('.md'))\n .map((file) => file.replace(/\\.md$/, ''))\n : []\n if (available.length === 0) return []\n const enabled = new Set(readEnabledModuleIds(join(targetDir, 'src', 'modules.ts')))\n const selected = available.filter((moduleId) => enabled.has(moduleId))\n return selected.length > 0 ? selected : available\n}\n\nconst MODULE_GUIDES_START = '<!-- om:module-guides:start -->'\nconst MODULE_GUIDES_END = '<!-- om:module-guides:end -->'\n\n// Read each module's guide label from the bundled `module-facts.json` (emitted by\n// build.mjs from the generator's extraction of each module's own `metadata`). The\n// label falls back description \u2192 title \u2192 generic, so the CLI never re-declares\n// specific module names or descriptions. A missing/malformed sidecar degrades to an\n// empty map (generic labels), never a throw.\nfunction readModuleGuideLabels(guidesDir: string): Record<string, string> {\n const factsPath = join(guidesDir, 'module-facts.json')\n if (!existsSync(factsPath)) return {}\n try {\n const parsed = JSON.parse(readFileSync(factsPath, 'utf-8')) as Record<\n string,\n { description?: unknown; title?: unknown }\n >\n const labels: Record<string, string> = {}\n for (const [moduleId, entry] of Object.entries(parsed)) {\n const label =\n (entry && typeof entry.description === 'string' && entry.description) ||\n (entry && typeof entry.title === 'string' && entry.title) ||\n ''\n if (label) labels[moduleId] = label\n }\n return labels\n } catch {\n return {}\n }\n}\n\nfunction renderModuleGuidesBlock(selected: string[], labels: Record<string, string>): string {\n if (selected.length === 0) return '_No module fact-sheets are bundled for this app._'\n const rows = selected.map((moduleId) => {\n const label = labels[moduleId] ?? `Use the ${moduleId} module`\n return `| ${label} | \\`.ai/guides/modules/${moduleId}.md\\` |`\n })\n return ['| Task | Load |', '|---|---|', ...rows].join('\\n')\n}\n\n// Regenerate the marker-delimited Module-Specific Guides block in the written\n// AGENTS.md from the selected module set. Replaces strictly between the markers so\n// surrounding prose is untouched and repeat runs are idempotent.\nfunction injectModuleGuides(\n agentsMdPath: string,\n selected: string[],\n labels: Record<string, string> = {},\n): void {\n if (!existsSync(agentsMdPath)) return\n const content = readFileSync(agentsMdPath, 'utf-8')\n const startIndex = content.indexOf(MODULE_GUIDES_START)\n const endIndex = content.indexOf(MODULE_GUIDES_END)\n if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {\n console.warn(\n `[agentic] Module-Specific Guides markers (${MODULE_GUIDES_START} \u2026 ${MODULE_GUIDES_END}) not found in ${agentsMdPath}; the per-module guide list was not generated.`,\n )\n return\n }\n const before = content.slice(0, startIndex + MODULE_GUIDES_START.length)\n const after = content.slice(endIndex)\n const next = `${before}\\n${renderModuleGuidesBlock(selected, labels)}\\n${after}`\n if (next !== content) writeFileSync(agentsMdPath, next)\n}\n\n// \u2500\u2500\u2500 Generators \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateShared(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'shared')\n\n // Resolve which per-module fact-sheets this app gets (enabled \u2229 bundled allowlist).\n const selectedModules = selectModuleFactSheets(targetDir, join(GUIDES_DIR, 'modules'))\n const moduleGuideLabels = readModuleGuideLabels(GUIDES_DIR)\n\n // AGENTS.md\n writeTemplate(srcDir, 'AGENTS.md.template', join(targetDir, 'AGENTS.md'), config)\n injectModuleGuides(join(targetDir, 'AGENTS.md'), selectedModules, moduleGuideLabels)\n\n // .ai/ structure\n writeTemplate(srcDir, 'ai/specs/README.md', join(targetDir, '.ai', 'specs', 'README.md'), config)\n copyFile(srcDir, 'ai/specs/SPEC-000-template.md', join(targetDir, '.ai', 'specs', 'SPEC-000-template.md'))\n copyFile(srcDir, 'ai/lessons.md', join(targetDir, '.ai', 'lessons.md'))\n\n // .ai/skills/ \u2014 the shared skills-mixin manifest + tracker + external installer.\n // Skills owned by the external open-mercato/skills collection (code review, spec\n // writing, integration tests, prepare-issue, the auto-* PR family) are NOT copied\n // here; the user installs them with `yarn install-skills` (npx skills add). Only\n // standalone-only + kept-local skills and repo-local override folders are copied.\n copyFile(srcDir, 'ai/skills/tiers.json', join(targetDir, '.ai', 'skills', 'tiers.json'))\n copyFile(srcDir, 'ai/skills/tiers.schema.json', join(targetDir, '.ai', 'skills', 'tiers.schema.json'))\n copyFile(srcDir, 'ai/agentic.config.json', join(targetDir, '.ai', 'agentic.config.json'))\n copyFile(srcDir, 'ai/trackers/github.md', join(targetDir, '.ai', 'trackers', 'github.md'))\n copyFile(srcDir, 'scripts/install-skills.sh', join(targetDir, 'scripts', 'install-skills.sh'))\n\n copyFile(\n srcDir,\n 'ai/skills/om-backend-ui-design/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-backend-ui-design', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-backend-ui-design/references/ui-components.md',\n join(targetDir, '.ai', 'skills', 'om-backend-ui-design', 'references', 'ui-components.md'),\n )\n copyFile(srcDir, 'ai/skills/om-integration-builder/SKILL.md', join(targetDir, '.ai', 'skills', 'om-integration-builder', 'SKILL.md'))\n if (existsSync(join(srcDir, 'ai', 'skills', 'om-integration-builder', 'STANDALONE.md'))) {\n copyFile(\n srcDir,\n 'ai/skills/om-integration-builder/STANDALONE.md',\n join(targetDir, '.ai', 'skills', 'om-integration-builder', 'STANDALONE.md'),\n )\n }\n copyFile(\n srcDir,\n 'ai/skills/om-integration-builder/references/adapter-contracts.md',\n join(targetDir, '.ai', 'skills', 'om-integration-builder', 'references', 'adapter-contracts.md'),\n )\n if (existsSync(join(srcDir, 'ai', 'skills', 'om-integration-builder', 'STANDALONE.md'))) {\n copyFile(\n srcDir,\n 'ai/skills/om-integration-builder/STANDALONE.md',\n join(targetDir, '.ai', 'skills', 'om-integration-builder', 'STANDALONE.md'),\n )\n }\n\n copyFile(\n srcDir,\n 'ai/skills/om-system-extension/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-system-extension', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-system-extension/references/extension-contracts.md',\n join(targetDir, '.ai', 'skills', 'om-system-extension', 'references', 'extension-contracts.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-module-scaffold/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-module-scaffold', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-module-scaffold/references/naming-conventions.md',\n join(targetDir, '.ai', 'skills', 'om-module-scaffold', 'references', 'naming-conventions.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-module-scaffold/references/navigation-patterns.md',\n join(targetDir, '.ai', 'skills', 'om-module-scaffold', 'references', 'navigation-patterns.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-troubleshooter/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-troubleshooter', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-troubleshooter/references/diagnostic-commands.md',\n join(targetDir, '.ai', 'skills', 'om-troubleshooter', 'references', 'diagnostic-commands.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-eject-and-customize/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-eject-and-customize', 'SKILL.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-data-model-design/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-data-model-design', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md',\n join(targetDir, '.ai', 'skills', 'om-data-model-design', 'references', 'mikro-orm-cheatsheet.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-implement-spec/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-implement-spec', 'SKILL.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-help/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-help', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-help/references/skills-catalog.md',\n join(targetDir, '.ai', 'skills', 'om-help', 'references', 'skills-catalog.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-help/references/workflow-sequences.md',\n join(targetDir, '.ai', 'skills', 'om-help', 'references', 'workflow-sequences.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-auto-upgrade-0.4.10-to-0.5.0/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-auto-upgrade-0.4.10-to-0.5.0', 'SKILL.md'),\n )\n\n // Slim repo-local OVERRIDE folders (SKILL.md only) for the external auto-* PR\n // family + single autofix skill. The workflow bodies live in open-mercato/skills\n // (installed via `yarn install-skills`); these overrides adjust them for a\n // standalone app and are read on top of the external skill's built-in workflow.\n // om-prepare-test-env's override carries environment knowledge (cross-platform\n // mercato CLI runner commands) rather than tracker adjustments.\n for (const autoSkill of [\n 'om-auto-create-pr',\n 'om-auto-continue-pr',\n 'om-auto-create-pr-loop',\n 'om-auto-continue-pr-loop',\n 'om-auto-review-pr',\n 'om-auto-fix-issue',\n 'om-prepare-test-env',\n ]) {\n if (!existsSync(join(srcDir, 'ai', 'skills', autoSkill, 'SKILL.md'))) {\n continue\n }\n copyFile(\n srcDir,\n `ai/skills/${autoSkill}/SKILL.md`,\n join(targetDir, '.ai', 'skills', autoSkill, 'SKILL.md'),\n )\n }\n\n copyFile(\n srcDir,\n 'ai/skills/om-trim-unused-modules/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-trim-unused-modules', 'SKILL.md'),\n )\n\n copyFile(srcDir, 'ai/qa/tests/playwright.config.ts', join(targetDir, '.ai', 'qa', 'tests', 'playwright.config.ts'))\n\n // Package & conceptual guides are copied wholesale (framework-wide). Per-module\n // fact-sheets (.ai/guides/modules/<module>.md) are filtered to the app's enabled\n // module set; the combined module-facts.json sidecar is copied as-is.\n if (existsSync(GUIDES_DIR)) {\n const guidesDestDir = join(targetDir, '.ai', 'guides')\n for (const file of readdirSync(GUIDES_DIR)) {\n if (!file.endsWith('.md')) continue\n const srcPath = join(GUIDES_DIR, file)\n const destPath = join(guidesDestDir, file)\n ensureDir(destPath)\n copyFileSync(srcPath, destPath)\n }\n\n const moduleFactsPath = join(GUIDES_DIR, 'module-facts.json')\n if (existsSync(moduleFactsPath)) {\n const destPath = join(guidesDestDir, 'module-facts.json')\n ensureDir(destPath)\n copyFileSync(moduleFactsPath, destPath)\n }\n\n const modulesSubdir = join(GUIDES_DIR, 'modules')\n for (const moduleId of selectedModules) {\n const destPath = join(guidesDestDir, 'modules', `${moduleId}.md`)\n ensureDir(destPath)\n copyFileSync(join(modulesSubdir, `${moduleId}.md`), destPath)\n }\n }\n}\n\nfunction generateClaudeCode(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'claude-code')\n\n writeTemplate(srcDir, 'CLAUDE.md.template', join(targetDir, 'CLAUDE.md'), config)\n copyFile(srcDir, 'settings.json', join(targetDir, '.claude', 'settings.json'))\n copyFile(srcDir, 'hooks/entity-migration-check.ts', join(targetDir, '.claude', 'hooks', 'entity-migration-check.ts'))\n copyFile(srcDir, 'mcp.json.example', join(targetDir, '.mcp.json.example'))\n\n // Symlink .claude/skills \u2192 ../.ai/skills\n ensureSkillsLink(join(targetDir, '.claude', 'skills'), join('..', '.ai', 'skills'))\n}\n\nfunction generateCodex(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'codex')\n\n const agentsPath = join(targetDir, 'AGENTS.md')\n if (existsSync(agentsPath)) {\n const enforcement = readFileSync(join(srcDir, 'enforcement-rules.md'), 'utf-8')\n let agents = readFileSync(agentsPath, 'utf-8')\n const MARKER_START = '<!-- CODEX_ENFORCEMENT_RULES_START -->'\n const MARKER_END = '<!-- CODEX_ENFORCEMENT_RULES_END -->'\n\n if (agents.includes(MARKER_START)) {\n const startIdx = agents.indexOf(MARKER_START)\n const endIdx = agents.indexOf(MARKER_END)\n if (endIdx !== -1) {\n agents = agents.slice(0, startIdx) + enforcement + agents.slice(endIdx + MARKER_END.length)\n }\n } else {\n const firstNewline = agents.indexOf('\\n')\n if (firstNewline !== -1) {\n agents = agents.slice(0, firstNewline + 1) + '\\n' + enforcement + '\\n' + agents.slice(firstNewline + 1)\n } else {\n agents = agents + '\\n\\n' + enforcement\n }\n }\n writeFileSync(agentsPath, agents)\n }\n\n copyFile(srcDir, 'mcp.json.example', join(targetDir, '.codex', 'mcp.json.example'))\n\n // Symlink .codex/skills \u2192 ../.ai/skills\n ensureSkillsLink(join(targetDir, '.codex', 'skills'), join('..', '.ai', 'skills'))\n}\n\nfunction generateCursor(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'cursor')\n\n writeTemplate(srcDir, 'rules/open-mercato.mdc', join(targetDir, '.cursor', 'rules', 'open-mercato.mdc'), config)\n copyFile(srcDir, 'rules/entity-guard.mdc', join(targetDir, '.cursor', 'rules', 'entity-guard.mdc'))\n copyFile(srcDir, 'rules/generated-guard.mdc', join(targetDir, '.cursor', 'rules', 'generated-guard.mdc'))\n copyFile(srcDir, 'hooks.json', join(targetDir, '.cursor', 'hooks.json'))\n copyFile(srcDir, 'hooks/entity-migration-check.mjs', join(targetDir, '.cursor', 'hooks', 'entity-migration-check.mjs'))\n copyFile(srcDir, 'mcp.json.example', join(targetDir, '.cursor', 'mcp.json.example'))\n\n // Symlink .cursor/skills \u2192 ../.ai/skills\n ensureSkillsLink(join(targetDir, '.cursor', 'skills'), join('..', '.ai', 'skills'))\n}\n\nfunction ensureSkillsLink(linkPath: string, target: string): void {\n ensureDir(linkPath)\n if (existsSync(linkPath) && !lstatSync(linkPath).isSymbolicLink()) {\n return\n }\n if (lstatSync(linkPath, { throwIfNoEntry: false })?.isSymbolicLink()) {\n unlinkSync(linkPath)\n }\n symlinkSync(target, linkPath)\n}\n\n// \u2500\u2500\u2500 Wizard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst TOOLS = [\n { key: '1', label: 'Claude Code (Anthropic)', id: 'claude-code' },\n { key: '2', label: 'Codex (OpenAI)', id: 'codex' },\n { key: '3', label: 'Cursor (Anysphere)', id: 'cursor' },\n { key: '4', label: 'Multiple tools (select individually)', id: 'multiple' },\n { key: '5', label: 'Skip \u2014 set up manually later', id: 'skip' },\n] as const\n\nconst SELECTABLE = TOOLS.filter((t) => t.id !== 'multiple' && t.id !== 'skip')\n\nasync function promptSelection(ask: AskFn): Promise<string[]> {\n console.log('')\n console.log('\uD83E\uDD16 Agentic workflow setup')\n console.log('')\n console.log(' Which AI coding tool will you use with this project?')\n console.log('')\n for (const tool of TOOLS) {\n console.log(` ${tool.key}. ${tool.label}`)\n }\n console.log('')\n\n const answer = (await ask(' Enter number(s) separated by comma [1]: ')).trim() || '1'\n\n if (answer === '5') return ['skip']\n\n if (answer === '4') {\n const selected: string[] = []\n for (const tool of SELECTABLE) {\n const yn = await ask(` Include ${tool.label}? [y/N]: `)\n if (yn.toLowerCase() === 'y' || yn.toLowerCase() === 'yes') {\n selected.push(tool.id)\n }\n }\n return selected.length > 0 ? selected : ['skip']\n }\n\n const keys = answer.split(',').map((s) => s.trim())\n const ids: string[] = []\n for (const key of keys) {\n const tool = TOOLS.find((t) => t.key === key)\n if (tool && tool.id !== 'multiple' && tool.id !== 'skip') {\n ids.push(tool.id)\n }\n }\n return ids.length > 0 ? ids : ['skip']\n}\n\nexport async function runAgenticSetup(\n targetDir: string,\n ask: AskFn,\n options?: AgenticSetupOptions,\n): Promise<void> {\n let selectedIds: string[]\n\n if (options?.tool) {\n selectedIds = options.tool.split(',').map((t) => t.trim())\n } else {\n selectedIds = await promptSelection(ask)\n }\n\n if (selectedIds.includes('skip')) {\n console.log('')\n console.log(' Skipped agentic setup.')\n console.log('')\n return\n }\n\n const config: AgenticConfig = {\n projectName: basename(targetDir),\n targetDir,\n }\n\n generateShared(config)\n if (selectedIds.includes('claude-code')) generateClaudeCode(config)\n if (selectedIds.includes('codex')) generateCodex(config)\n if (selectedIds.includes('cursor')) generateCursor(config)\n\n installSkills(targetDir)\n\n console.log('')\n console.log(' Agentic setup complete:')\n if (selectedIds.includes('claude-code')) {\n console.log(' \u2713 Claude Code \u2014 CLAUDE.md, .claude/hooks/, .mcp.json.example')\n }\n if (selectedIds.includes('codex')) {\n console.log(' \u2713 Codex \u2014 AGENTS.md enforcement rules, .codex/mcp.json.example')\n }\n if (selectedIds.includes('cursor')) {\n console.log(' \u2713 Cursor \u2014 .cursor/rules/, .cursor/hooks/, .cursor/mcp.json.example')\n }\n console.log('')\n console.log(' .ai/agentic.config.json ships preconfigured (GitHub tracker, labels off);')\n console.log(' run /om-setup-agent-pipeline in your agent CLI to tailor labels, QA gate,')\n console.log(' tracker, or validation commands. Re-run `yarn install-skills` anytime to')\n console.log(' refresh the external open-mercato/skills subset.')\n console.log('')\n}\n\nfunction installSkills(targetDir: string): void {\n const installScript = join(targetDir, 'scripts', 'install-skills.sh')\n if (!existsSync(installScript)) return\n console.log('')\n console.log(' Installing agent skills (local tiers + external open-mercato/skills subset)...')\n const result = spawnSync('sh', [installScript], { cwd: targetDir, stdio: 'inherit' })\n if (result.error || result.status !== 0) {\n console.log(' \u26A0 Skill installation did not complete; run `yarn install-skills` inside the app when online.')\n }\n}\n"],
|
|
5
|
-
"mappings": "AAQA,SAAS,YAAY,WAAW,cAAc,eAAe,cAAc,aAAa,WAAW,YAAY,mBAAmB;AAClI,SAAS,iBAAiB;AAC1B,SAAS,MAAM,SAAS,gBAAgB;AACxC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,kBAAkB;AAEpC,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,MAAM,cAAc,KAAK,WAAW,MAAM,SAAS;AACnD,MAAM,aAAa,KAAK,aAAa,QAAQ;AAgB7C,SAAS,oBAAoB,SAAiB,QAA+B;AAC3E,SAAO,QAAQ,QAAQ,yBAAyB,OAAO,WAAW;AACpE;AAEA,SAAS,UAAU,UAAwB;AACzC,QAAM,MAAM,QAAQ,QAAQ;AAC5B,MAAI,CAAC,WAAW,GAAG,EAAG,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAC1D;AAEA,SAAS,cAAc,QAAgB,aAAqB,UAAkB,QAA6B;AACzG,QAAM,UAAU,KAAK,QAAQ,WAAW;AACxC,QAAM,UAAU,aAAa,SAAS,OAAO;AAC7C,YAAU,QAAQ;AAClB,gBAAc,UAAU,oBAAoB,SAAS,MAAM,CAAC;AAC9D;AAEA,SAAS,SAAS,QAAgB,aAAqB,UAAwB;AAC7E,QAAM,UAAU,KAAK,QAAQ,WAAW;AACxC,YAAU,QAAQ;AAClB,eAAa,SAAS,QAAQ;AAChC;AAOA,SAAS,qBAAqB,aAA+B;AAC3D,MAAI,CAAC,WAAW,WAAW,EAAG,QAAO,CAAC;AACtC,MAAI;AACF,UAAM,UAAU,IAAI,QAAQ,EAAE,uBAAuB,KAAK,CAAC;AAC3D,UAAM,aAAa,QAAQ,iBAAiB,cAAc,aAAa,aAAa,OAAO,CAAC;AAC5F,UAAM,cAAc,WAAW,uBAAuB,gBAAgB;AACtE,UAAM,eAAe,aAAa,qBAAqB,WAAW,sBAAsB;AACxF,QAAI,CAAC,aAAc,QAAO,CAAC;AAC3B,UAAM,MAAgB,CAAC;AACvB,eAAW,WAAW,aAAa,YAAY,GAAG;AAChD,YAAM,gBAAgB,QAAQ,OAAO,WAAW,uBAAuB;AACvE,UAAI,CAAC,cAAe;AACpB,YAAM,aAAa,cAAc,YAAY,IAAI,GAAG,OAAO,WAAW,kBAAkB;AACxF,YAAM,UAAU,YAAY,qBAAqB,WAAW,aAAa,GAAG,gBAAgB;AAC5F,UAAI,QAAS,KAAI,KAAK,OAAO;AAAA,IAC/B;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAMA,SAAS,uBAAuB,WAAmB,eAAiC;AAClF,QAAM,YAAY,WAAW,aAAa,IACtC,YAAY,aAAa,EACtB,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,CAAC,EACrC,IAAI,CAAC,SAAS,KAAK,QAAQ,SAAS,EAAE,CAAC,IAC1C,CAAC;AACL,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AACpC,QAAM,UAAU,IAAI,IAAI,qBAAqB,KAAK,WAAW,OAAO,YAAY,CAAC,CAAC;AAClF,QAAM,WAAW,UAAU,OAAO,CAAC,aAAa,QAAQ,IAAI,QAAQ,CAAC;AACrE,SAAO,SAAS,SAAS,IAAI,WAAW;AAC1C;AAEA,MAAM,sBAAsB;AAC5B,MAAM,oBAAoB;AAO1B,SAAS,sBAAsB,WAA2C;AACxE,QAAM,YAAY,KAAK,WAAW,mBAAmB;AACrD,MAAI,CAAC,WAAW,SAAS,EAAG,QAAO,CAAC;AACpC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,WAAW,OAAO,CAAC;AAI1D,UAAM,SAAiC,CAAC;AACxC,eAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACtD,YAAM,QACH,SAAS,OAAO,MAAM,gBAAgB,YAAY,MAAM,eACxD,SAAS,OAAO,MAAM,UAAU,YAAY,MAAM,SACnD;AACF,UAAI,MAAO,QAAO,QAAQ,IAAI;AAAA,IAChC;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,wBAAwB,UAAoB,QAAwC;AAC3F,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAM,OAAO,SAAS,IAAI,CAAC,aAAa;AACtC,UAAM,QAAQ,OAAO,QAAQ,KAAK,WAAW,QAAQ;AACrD,WAAO,KAAK,KAAK,2BAA2B,QAAQ;AAAA,EACtD,CAAC;AACD,SAAO,CAAC,mBAAmB,aAAa,GAAG,IAAI,EAAE,KAAK,IAAI;AAC5D;AAKA,SAAS,mBACP,cACA,UACA,SAAiC,CAAC,GAC5B;AACN,MAAI,CAAC,WAAW,YAAY,EAAG;AAC/B,QAAM,UAAU,aAAa,cAAc,OAAO;AAClD,QAAM,aAAa,QAAQ,QAAQ,mBAAmB;AACtD,QAAM,WAAW,QAAQ,QAAQ,iBAAiB;AAClD,MAAI,eAAe,MAAM,aAAa,MAAM,WAAW,YAAY;AACjE,YAAQ;AAAA,MACN,6CAA6C,mBAAmB,WAAM,iBAAiB,kBAAkB,YAAY;AAAA,IACvH;AACA;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,MAAM,GAAG,aAAa,oBAAoB,MAAM;AACvE,QAAM,QAAQ,QAAQ,MAAM,QAAQ;AACpC,QAAM,OAAO,GAAG,MAAM;AAAA,EAAK,wBAAwB,UAAU,MAAM,CAAC;AAAA,EAAK,KAAK;AAC9E,MAAI,SAAS,QAAS,eAAc,cAAc,IAAI;AACxD;AAIA,SAAS,eAAe,QAA6B;AACnD,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,SAAS,KAAK,aAAa,QAAQ;AAGzC,QAAM,kBAAkB,uBAAuB,WAAW,KAAK,YAAY,SAAS,CAAC;AACrF,QAAM,oBAAoB,sBAAsB,UAAU;AAG1D,gBAAc,QAAQ,sBAAsB,KAAK,WAAW,WAAW,GAAG,MAAM;AAChF,qBAAmB,KAAK,WAAW,WAAW,GAAG,iBAAiB,iBAAiB;AAGnF,gBAAc,QAAQ,sBAAsB,KAAK,WAAW,OAAO,SAAS,WAAW,GAAG,MAAM;AAChG,WAAS,QAAQ,iCAAiC,KAAK,WAAW,OAAO,SAAS,sBAAsB,CAAC;AACzG,WAAS,QAAQ,iBAAiB,KAAK,WAAW,OAAO,YAAY,CAAC;AAOtE,WAAS,QAAQ,wBAAwB,KAAK,WAAW,OAAO,UAAU,YAAY,CAAC;AACvF,WAAS,QAAQ,+BAA+B,KAAK,WAAW,OAAO,UAAU,mBAAmB,CAAC;AACrG,WAAS,QAAQ,0BAA0B,KAAK,WAAW,OAAO,qBAAqB,CAAC;AACxF,WAAS,QAAQ,yBAAyB,KAAK,WAAW,OAAO,YAAY,WAAW,CAAC;AACzF,WAAS,QAAQ,6BAA6B,KAAK,WAAW,WAAW,mBAAmB,CAAC;AAE7F;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,wBAAwB,UAAU;AAAA,EACrE;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,wBAAwB,cAAc,kBAAkB;AAAA,EAC3F;AACA,WAAS,QAAQ,6CAA6C,KAAK,WAAW,OAAO,UAAU,0BAA0B,UAAU,CAAC;AACpI,MAAI,WAAW,KAAK,QAAQ,MAAM,UAAU,0BAA0B,eAAe,CAAC,GAAG;AACvF;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,WAAW,OAAO,UAAU,0BAA0B,eAAe;AAAA,IAC5E;AAAA,EACF;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,0BAA0B,cAAc,sBAAsB;AAAA,EACjG;AACA,MAAI,WAAW,KAAK,QAAQ,MAAM,UAAU,0BAA0B,eAAe,CAAC,GAAG;AACvF;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK,WAAW,OAAO,UAAU,0BAA0B,eAAe;AAAA,IAC5E;AAAA,EACF;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,uBAAuB,UAAU;AAAA,EACpE;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,uBAAuB,cAAc,wBAAwB;AAAA,EAChG;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,sBAAsB,UAAU;AAAA,EACnE;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,sBAAsB,cAAc,uBAAuB;AAAA,EAC9F;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,sBAAsB,cAAc,wBAAwB;AAAA,EAC/F;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,qBAAqB,UAAU;AAAA,EAClE;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,qBAAqB,cAAc,wBAAwB;AAAA,EAC9F;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,0BAA0B,UAAU;AAAA,EACvE;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,wBAAwB,UAAU;AAAA,EACrE;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,wBAAwB,cAAc,yBAAyB;AAAA,EAClG;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,qBAAqB,UAAU;AAAA,EAClE;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,WAAW,UAAU;AAAA,EACxD;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,WAAW,cAAc,mBAAmB;AAAA,EAC/E;AACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,WAAW,cAAc,uBAAuB;AAAA,EACnF;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,mCAAmC,UAAU;AAAA,EAChF;AAQA,aAAW,aAAa;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAG;AACD,QAAI,CAAC,WAAW,KAAK,QAAQ,MAAM,UAAU,WAAW,UAAU,CAAC,GAAG;AACpE;AAAA,IACF;AACA;AAAA,MACE;AAAA,MACA,aAAa,SAAS;AAAA,MACtB,KAAK,WAAW,OAAO,UAAU,WAAW,UAAU;AAAA,IACxD;AAAA,EACF;AAEA;AAAA,IACE;AAAA,IACA;AAAA,IACA,KAAK,WAAW,OAAO,UAAU,0BAA0B,UAAU;AAAA,EACvE;AAEA,WAAS,QAAQ,oCAAoC,KAAK,WAAW,OAAO,MAAM,SAAS,sBAAsB,CAAC;AAKlH,MAAI,WAAW,UAAU,GAAG;AAC1B,UAAM,gBAAgB,KAAK,WAAW,OAAO,QAAQ;AACrD,eAAW,QAAQ,YAAY,UAAU,GAAG;AAC1C,UAAI,CAAC,KAAK,SAAS,KAAK,EAAG;AAC3B,YAAM,UAAU,KAAK,YAAY,IAAI;AACrC,YAAM,WAAW,KAAK,eAAe,IAAI;AACzC,gBAAU,QAAQ;AAClB,mBAAa,SAAS,QAAQ;AAAA,IAChC;AAEA,UAAM,kBAAkB,KAAK,YAAY,mBAAmB;AAC5D,QAAI,WAAW,eAAe,GAAG;AAC/B,YAAM,WAAW,KAAK,eAAe,mBAAmB;AACxD,gBAAU,QAAQ;AAClB,mBAAa,iBAAiB,QAAQ;AAAA,IACxC;AAEA,UAAM,gBAAgB,KAAK,YAAY,SAAS;AAChD,eAAW,YAAY,iBAAiB;AACtC,YAAM,WAAW,KAAK,eAAe,WAAW,GAAG,QAAQ,KAAK;AAChE,gBAAU,QAAQ;AAClB,mBAAa,KAAK,eAAe,GAAG,QAAQ,KAAK,GAAG,QAAQ;AAAA,IAC9D;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,QAA6B;AACvD,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,SAAS,KAAK,aAAa,aAAa;AAE9C,gBAAc,QAAQ,sBAAsB,KAAK,WAAW,WAAW,GAAG,MAAM;AAChF,WAAS,QAAQ,iBAAiB,KAAK,WAAW,WAAW,eAAe,CAAC;AAC7E,WAAS,QAAQ,mCAAmC,KAAK,WAAW,WAAW,SAAS,2BAA2B,CAAC;AACpH,WAAS,QAAQ,oBAAoB,KAAK,WAAW,mBAAmB,CAAC;AAGzE,mBAAiB,KAAK,WAAW,WAAW,QAAQ,GAAG,KAAK,MAAM,OAAO,QAAQ,CAAC;AACpF;AAEA,SAAS,cAAc,QAA6B;AAClD,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,SAAS,KAAK,aAAa,OAAO;AAExC,QAAM,aAAa,KAAK,WAAW,WAAW;AAC9C,MAAI,WAAW,UAAU,GAAG;AAC1B,UAAM,cAAc,aAAa,KAAK,QAAQ,sBAAsB,GAAG,OAAO;AAC9E,QAAI,SAAS,aAAa,YAAY,OAAO;AAC7C,UAAM,eAAe;AACrB,UAAM,aAAa;AAEnB,QAAI,OAAO,SAAS,YAAY,GAAG;AACjC,YAAM,WAAW,OAAO,QAAQ,YAAY;AAC5C,YAAM,SAAS,OAAO,QAAQ,UAAU;AACxC,UAAI,WAAW,IAAI;AACjB,iBAAS,OAAO,MAAM,GAAG,QAAQ,IAAI,cAAc,OAAO,MAAM,SAAS,WAAW,MAAM;AAAA,MAC5F;AAAA,IACF,OAAO;AACL,YAAM,eAAe,OAAO,QAAQ,IAAI;AACxC,UAAI,iBAAiB,IAAI;AACvB,iBAAS,OAAO,MAAM,GAAG,eAAe,CAAC,IAAI,OAAO,cAAc,OAAO,OAAO,MAAM,eAAe,CAAC;AAAA,MACxG,OAAO;AACL,iBAAS,SAAS,SAAS;AAAA,MAC7B;AAAA,IACF;AACA,kBAAc,YAAY,MAAM;AAAA,EAClC;AAEA,WAAS,QAAQ,oBAAoB,KAAK,WAAW,UAAU,kBAAkB,CAAC;AAGlF,mBAAiB,KAAK,WAAW,UAAU,QAAQ,GAAG,KAAK,MAAM,OAAO,QAAQ,CAAC;AACnF;AAEA,SAAS,eAAe,QAA6B;AACnD,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,SAAS,KAAK,aAAa,QAAQ;AAEzC,gBAAc,QAAQ,0BAA0B,KAAK,WAAW,WAAW,SAAS,kBAAkB,GAAG,MAAM;AAC/G,WAAS,QAAQ,0BAA0B,KAAK,WAAW,WAAW,SAAS,kBAAkB,CAAC;AAClG,WAAS,QAAQ,6BAA6B,KAAK,WAAW,WAAW,SAAS,qBAAqB,CAAC;AACxG,WAAS,QAAQ,cAAc,KAAK,WAAW,WAAW,YAAY,CAAC;AACvE,WAAS,QAAQ,oCAAoC,KAAK,WAAW,WAAW,SAAS,4BAA4B,CAAC;AACtH,WAAS,QAAQ,oBAAoB,KAAK,WAAW,WAAW,kBAAkB,CAAC;AAGnF,mBAAiB,KAAK,WAAW,WAAW,QAAQ,GAAG,KAAK,MAAM,OAAO,QAAQ,CAAC;AACpF;AAEA,SAAS,iBAAiB,UAAkB,QAAsB;AAChE,YAAU,QAAQ;AAClB,MAAI,WAAW,QAAQ,KAAK,CAAC,UAAU,QAAQ,EAAE,eAAe,GAAG;AACjE;AAAA,EACF;AACA,MAAI,UAAU,UAAU,EAAE,gBAAgB,MAAM,CAAC,GAAG,eAAe,GAAG;AACpE,eAAW,QAAQ;AAAA,EACrB;AACA,cAAY,QAAQ,QAAQ;AAC9B;AAIA,MAAM,QAAQ;AAAA,EACZ,EAAE,KAAK,KAAK,OAAO,+BAA+B,IAAI,cAAc;AAAA,EACpE,EAAE,KAAK,KAAK,OAAO,4BAA4B,IAAI,QAAQ;AAAA,EAC3D,EAAE,KAAK,KAAK,OAAO,+BAA+B,IAAI,SAAS;AAAA,EAC/D,EAAE,KAAK,KAAK,OAAO,yCAAyC,IAAI,WAAW;AAAA,EAC3E,EAAE,KAAK,KAAK,OAAO,qCAAgC,IAAI,OAAO;AAChE;AAEA,MAAM,aAAa,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,cAAc,EAAE,OAAO,MAAM;AAE7E,eAAe,gBAAgB,KAA+B;AAC5D,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,mCAA4B;AACxC,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,yDAAyD;AACrE,UAAQ,IAAI,EAAE;AACd,aAAW,QAAQ,OAAO;AACxB,YAAQ,IAAI,MAAM,KAAK,GAAG,KAAK,KAAK,KAAK,EAAE;AAAA,EAC7C;AACA,UAAQ,IAAI,EAAE;AAEd,QAAM,UAAU,MAAM,IAAI,6CAA6C,GAAG,KAAK,KAAK;AAEpF,MAAI,WAAW,IAAK,QAAO,CAAC,MAAM;AAElC,MAAI,WAAW,KAAK;AAClB,UAAM,WAAqB,CAAC;AAC5B,eAAW,QAAQ,YAAY;AAC7B,YAAM,KAAK,MAAM,IAAI,cAAc,KAAK,KAAK,WAAW;AACxD,UAAI,GAAG,YAAY,MAAM,OAAO,GAAG,YAAY,MAAM,OAAO;AAC1D,iBAAS,KAAK,KAAK,EAAE;AAAA,MACvB;AAAA,IACF;AACA,WAAO,SAAS,SAAS,IAAI,WAAW,CAAC,MAAM;AAAA,EACjD;AAEA,QAAM,OAAO,OAAO,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAClD,QAAM,MAAgB,CAAC;AACvB,aAAW,OAAO,MAAM;AACtB,UAAM,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AAC5C,QAAI,QAAQ,KAAK,OAAO,cAAc,KAAK,OAAO,QAAQ;AACxD,UAAI,KAAK,KAAK,EAAE;AAAA,IAClB;AAAA,EACF;AACA,SAAO,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM;AACvC;AAEA,eAAsB,gBACpB,WACA,KACA,SACe;AACf,MAAI;AAEJ,MAAI,SAAS,MAAM;AACjB,kBAAc,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,EAC3D,OAAO;AACL,kBAAc,MAAM,gBAAgB,GAAG;AAAA,EACzC;AAEA,MAAI,YAAY,SAAS,MAAM,GAAG;AAChC,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,2BAA2B;AACvC,YAAQ,IAAI,EAAE;AACd;AAAA,EACF;AAEA,QAAM,SAAwB;AAAA,IAC5B,aAAa,SAAS,SAAS;AAAA,IAC/B;AAAA,EACF;AAEA,iBAAe,MAAM;AACrB,MAAI,YAAY,SAAS,aAAa,EAAG,oBAAmB,MAAM;AAClE,MAAI,YAAY,SAAS,OAAO,EAAG,eAAc,MAAM;AACvD,MAAI,YAAY,SAAS,QAAQ,EAAG,gBAAe,MAAM;AAEzD,gBAAc,SAAS;AAEvB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,4BAA4B;AACxC,MAAI,YAAY,SAAS,aAAa,GAAG;AACvC,YAAQ,IAAI,2EAAiE;AAAA,EAC/E;AACA,MAAI,YAAY,SAAS,OAAO,GAAG;AACjC,YAAQ,IAAI,6EAAmE;AAAA,EACjF;AACA,MAAI,YAAY,SAAS,QAAQ,GAAG;AAClC,YAAQ,IAAI,kFAAwE;AAAA,EACtF;AACA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,8EAA8E;AAC1F,UAAQ,IAAI,8EAA8E;AAC1F,UAAQ,IAAI,6EAA6E;AACzF,UAAQ,IAAI,qDAAqD;AACjE,UAAQ,IAAI,EAAE;AAChB;AAEA,SAAS,cAAc,WAAyB;AAC9C,QAAM,gBAAgB,KAAK,WAAW,WAAW,mBAAmB;AACpE,MAAI,CAAC,WAAW,aAAa,EAAG;AAChC,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,mFAAmF;AAC/F,QAAM,SAAS,UAAU,MAAM,CAAC,aAAa,GAAG,EAAE,KAAK,WAAW,OAAO,UAAU,CAAC;AACpF,MAAI,OAAO,SAAS,OAAO,WAAW,GAAG;AACvC,YAAQ,IAAI,sGAAiG;AAAA,EAC/G;AACF;",
|
|
4
|
+
"sourcesContent": ["/**\n * Agentic setup for the CLI `agentic:init` command.\n *\n * Source files live in packages/create-app/agentic/ and are copied\n * to packages/cli/dist/agentic/ during build (see build.mjs).\n * This module reads those files at runtime \u2014 no embedded string constants.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, symlinkSync, lstatSync, unlinkSync, readdirSync } from 'node:fs'\nimport { spawnSync } from 'node:child_process'\nimport { join, dirname, basename } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { Project, SyntaxKind } from 'ts-morph'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n// In the built output (dist/lib/agentic-setup.js), __dirname is dist/lib/.\n// agentic/ is copied to dist/agentic/ by build.mjs.\nconst AGENTIC_DIR = join(__dirname, '..', 'agentic')\nconst GUIDES_DIR = join(AGENTIC_DIR, 'guides')\n\ntype AskFn = (question: string) => Promise<string>\n\ninterface AgenticSetupOptions {\n tool?: string\n force?: boolean\n}\n\ninterface AgenticConfig {\n projectName: string\n targetDir: string\n}\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction resolvePlaceholders(content: string, config: AgenticConfig): string {\n return content.replace(/\\{\\{PROJECT_NAME\\}\\}/g, config.projectName)\n}\n\nfunction ensureDir(filePath: string): void {\n const dir = dirname(filePath)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n}\n\nfunction writeTemplate(srcDir: string, srcRelative: string, destPath: string, config: AgenticConfig): void {\n const srcPath = join(srcDir, srcRelative)\n const content = readFileSync(srcPath, 'utf-8')\n ensureDir(destPath)\n writeFileSync(destPath, resolvePlaceholders(content, config))\n}\n\nfunction copyFile(srcDir: string, srcRelative: string, destPath: string): void {\n const srcPath = join(srcDir, srcRelative)\n ensureDir(destPath)\n copyFileSync(srcPath, destPath)\n}\n\n// \u2500\u2500\u2500 Module fact-sheet selection (mirrors packages/create-app/src/setup/tools/shared.ts) \u2500\u2500\n\n// AST-parse the static `enabledModules` array literal in the app's src/modules.ts\n// and collect each entry's `id`. Only the static literal is read (conditional\n// .push()/spread entries are intentionally not seen \u2014 see spec D6).\nfunction readEnabledModuleIds(modulesPath: string): string[] {\n if (!existsSync(modulesPath)) return []\n try {\n const project = new Project({ useInMemoryFileSystem: true })\n const sourceFile = project.createSourceFile('modules.ts', readFileSync(modulesPath, 'utf-8'))\n const declaration = sourceFile.getVariableDeclaration('enabledModules')\n const arrayLiteral = declaration?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)\n if (!arrayLiteral) return []\n const ids: string[] = []\n for (const element of arrayLiteral.getElements()) {\n const objectLiteral = element.asKind(SyntaxKind.ObjectLiteralExpression)\n if (!objectLiteral) continue\n const idProperty = objectLiteral.getProperty('id')?.asKind(SyntaxKind.PropertyAssignment)\n const idValue = idProperty?.getInitializerIfKind(SyntaxKind.StringLiteral)?.getLiteralValue()\n if (idValue) ids.push(idValue)\n }\n return ids\n } catch {\n return []\n }\n}\n\n// Resolve which per-module fact-sheets to ship: the intersection of the bundled\n// fact-sheets (the D5 allowlist, materialized by build.mjs) with the app's enabled\n// modules. Falls back to the full bundled set when the enabled set cannot be read\n// (R5 \u2014 degraded, never empty).\nfunction selectModuleFactSheets(targetDir: string, modulesSubdir: string): string[] {\n const available = existsSync(modulesSubdir)\n ? readdirSync(modulesSubdir)\n .filter((file) => file.endsWith('.md'))\n .map((file) => file.replace(/\\.md$/, ''))\n : []\n if (available.length === 0) return []\n const enabled = new Set(readEnabledModuleIds(join(targetDir, 'src', 'modules.ts')))\n const selected = available.filter((moduleId) => enabled.has(moduleId))\n return selected.length > 0 ? selected : available\n}\n\nconst MODULE_GUIDES_START = '<!-- om:module-guides:start -->'\nconst MODULE_GUIDES_END = '<!-- om:module-guides:end -->'\n\n// Read each module's guide label from the bundled `module-facts.json` (emitted by\n// build.mjs from the generator's extraction of each module's own `metadata`). The\n// label falls back description \u2192 title \u2192 generic, so the CLI never re-declares\n// specific module names or descriptions. A missing/malformed sidecar degrades to an\n// empty map (generic labels), never a throw.\nfunction readModuleGuideLabels(guidesDir: string): Record<string, string> {\n const factsPath = join(guidesDir, 'module-facts.json')\n if (!existsSync(factsPath)) return {}\n try {\n const parsed = JSON.parse(readFileSync(factsPath, 'utf-8')) as Record<\n string,\n { description?: unknown; title?: unknown }\n >\n const labels: Record<string, string> = {}\n for (const [moduleId, entry] of Object.entries(parsed)) {\n const label =\n (entry && typeof entry.description === 'string' && entry.description) ||\n (entry && typeof entry.title === 'string' && entry.title) ||\n ''\n if (label) labels[moduleId] = label\n }\n return labels\n } catch {\n return {}\n }\n}\n\nfunction renderModuleGuidesBlock(selected: string[], labels: Record<string, string>): string {\n if (selected.length === 0) return '_No module fact-sheets are bundled for this app._'\n const rows = selected.map((moduleId) => {\n const label = labels[moduleId] ?? `Use the ${moduleId} module`\n return `| ${label} | \\`.ai/guides/modules/${moduleId}.md\\` |`\n })\n return ['| Task | Load |', '|---|---|', ...rows].join('\\n')\n}\n\n// Regenerate the marker-delimited Module-Specific Guides block in the written\n// AGENTS.md from the selected module set. Replaces strictly between the markers so\n// surrounding prose is untouched and repeat runs are idempotent.\nfunction injectModuleGuides(\n agentsMdPath: string,\n selected: string[],\n labels: Record<string, string> = {},\n): void {\n if (!existsSync(agentsMdPath)) return\n const content = readFileSync(agentsMdPath, 'utf-8')\n const startIndex = content.indexOf(MODULE_GUIDES_START)\n const endIndex = content.indexOf(MODULE_GUIDES_END)\n if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {\n console.warn(\n `[agentic] Module-Specific Guides markers (${MODULE_GUIDES_START} \u2026 ${MODULE_GUIDES_END}) not found in ${agentsMdPath}; the per-module guide list was not generated.`,\n )\n return\n }\n const before = content.slice(0, startIndex + MODULE_GUIDES_START.length)\n const after = content.slice(endIndex)\n const next = `${before}\\n${renderModuleGuidesBlock(selected, labels)}\\n${after}`\n if (next !== content) writeFileSync(agentsMdPath, next)\n}\n\n// \u2500\u2500\u2500 Generators \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction generateShared(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'shared')\n\n // Resolve which per-module fact-sheets this app gets (enabled \u2229 bundled allowlist).\n const selectedModules = selectModuleFactSheets(targetDir, join(GUIDES_DIR, 'modules'))\n const moduleGuideLabels = readModuleGuideLabels(GUIDES_DIR)\n\n // AGENTS.md\n writeTemplate(srcDir, 'AGENTS.md.template', join(targetDir, 'AGENTS.md'), config)\n injectModuleGuides(join(targetDir, 'AGENTS.md'), selectedModules, moduleGuideLabels)\n\n // .ai/ structure\n writeTemplate(srcDir, 'ai/specs/README.md', join(targetDir, '.ai', 'specs', 'README.md'), config)\n copyFile(srcDir, 'ai/specs/SPEC-000-template.md', join(targetDir, '.ai', 'specs', 'SPEC-000-template.md'))\n copyFile(srcDir, 'ai/lessons.md', join(targetDir, '.ai', 'lessons.md'))\n\n // .ai/skills/ \u2014 the shared skills-mixin manifest + tracker + external installer.\n // Skills owned by the external open-mercato/skills collection (code review, spec\n // writing, integration tests, prepare-issue, the auto-* PR family) are NOT copied\n // here; the user installs them with `yarn install-skills` (npx skills add). Only\n // standalone-only + kept-local skills and repo-local override folders are copied.\n copyFile(srcDir, 'ai/skills/tiers.json', join(targetDir, '.ai', 'skills', 'tiers.json'))\n copyFile(srcDir, 'ai/skills/tiers.schema.json', join(targetDir, '.ai', 'skills', 'tiers.schema.json'))\n copyFile(srcDir, 'ai/agentic.config.json', join(targetDir, '.ai', 'agentic.config.json'))\n copyFile(srcDir, 'ai/trackers/github.md', join(targetDir, '.ai', 'trackers', 'github.md'))\n copyFile(srcDir, 'scripts/install-skills.sh', join(targetDir, 'scripts', 'install-skills.sh'))\n\n copyFile(\n srcDir,\n 'ai/skills/om-backend-ui-design/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-backend-ui-design', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-backend-ui-design/references/ui-components.md',\n join(targetDir, '.ai', 'skills', 'om-backend-ui-design', 'references', 'ui-components.md'),\n )\n copyFile(srcDir, 'ai/skills/om-integration-builder/SKILL.md', join(targetDir, '.ai', 'skills', 'om-integration-builder', 'SKILL.md'))\n if (existsSync(join(srcDir, 'ai', 'skills', 'om-integration-builder', 'STANDALONE.md'))) {\n copyFile(\n srcDir,\n 'ai/skills/om-integration-builder/STANDALONE.md',\n join(targetDir, '.ai', 'skills', 'om-integration-builder', 'STANDALONE.md'),\n )\n }\n copyFile(\n srcDir,\n 'ai/skills/om-integration-builder/references/adapter-contracts.md',\n join(targetDir, '.ai', 'skills', 'om-integration-builder', 'references', 'adapter-contracts.md'),\n )\n if (existsSync(join(srcDir, 'ai', 'skills', 'om-integration-builder', 'STANDALONE.md'))) {\n copyFile(\n srcDir,\n 'ai/skills/om-integration-builder/STANDALONE.md',\n join(targetDir, '.ai', 'skills', 'om-integration-builder', 'STANDALONE.md'),\n )\n }\n\n copyFile(\n srcDir,\n 'ai/skills/om-system-extension/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-system-extension', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-system-extension/references/extension-contracts.md',\n join(targetDir, '.ai', 'skills', 'om-system-extension', 'references', 'extension-contracts.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-module-scaffold/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-module-scaffold', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-module-scaffold/references/naming-conventions.md',\n join(targetDir, '.ai', 'skills', 'om-module-scaffold', 'references', 'naming-conventions.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-module-scaffold/references/navigation-patterns.md',\n join(targetDir, '.ai', 'skills', 'om-module-scaffold', 'references', 'navigation-patterns.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-troubleshooter/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-troubleshooter', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-troubleshooter/references/diagnostic-commands.md',\n join(targetDir, '.ai', 'skills', 'om-troubleshooter', 'references', 'diagnostic-commands.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-eject-and-customize/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-eject-and-customize', 'SKILL.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-data-model-design/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-data-model-design', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md',\n join(targetDir, '.ai', 'skills', 'om-data-model-design', 'references', 'mikro-orm-cheatsheet.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-implement-spec/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-implement-spec', 'SKILL.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-help/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-help', 'SKILL.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-help/references/skills-catalog.md',\n join(targetDir, '.ai', 'skills', 'om-help', 'references', 'skills-catalog.md'),\n )\n copyFile(\n srcDir,\n 'ai/skills/om-help/references/workflow-sequences.md',\n join(targetDir, '.ai', 'skills', 'om-help', 'references', 'workflow-sequences.md'),\n )\n\n copyFile(\n srcDir,\n 'ai/skills/om-auto-upgrade-0.4.10-to-0.5.0/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-auto-upgrade-0.4.10-to-0.5.0', 'SKILL.md'),\n )\n\n // Slim repo-local OVERRIDE folders (SKILL.md only) for the external auto-* PR\n // family + single autofix skill. The workflow bodies live in open-mercato/skills\n // (installed via `yarn install-skills`); these overrides adjust them for a\n // standalone app and are read on top of the external skill's built-in workflow.\n // om-prepare-test-env's override carries environment knowledge (cross-platform\n // mercato CLI runner commands) rather than tracker adjustments.\n for (const autoSkill of [\n 'om-auto-create-pr',\n 'om-auto-continue-pr',\n 'om-auto-create-pr-loop',\n 'om-auto-continue-pr-loop',\n 'om-auto-review-pr',\n 'om-auto-fix-issue',\n 'om-prepare-test-env',\n ]) {\n if (!existsSync(join(srcDir, 'ai', 'skills', autoSkill, 'SKILL.md'))) {\n continue\n }\n copyFile(\n srcDir,\n `ai/skills/${autoSkill}/SKILL.md`,\n join(targetDir, '.ai', 'skills', autoSkill, 'SKILL.md'),\n )\n }\n\n copyFile(\n srcDir,\n 'ai/skills/om-trim-unused-modules/SKILL.md',\n join(targetDir, '.ai', 'skills', 'om-trim-unused-modules', 'SKILL.md'),\n )\n\n copyFile(srcDir, 'ai/qa/tests/playwright.config.ts', join(targetDir, '.ai', 'qa', 'tests', 'playwright.config.ts'))\n\n // Package & conceptual guides are copied wholesale (framework-wide). Per-module\n // fact-sheets (.ai/guides/modules/<module>.md) are filtered to the app's enabled\n // module set; the combined module-facts.json sidecar is copied as-is.\n if (existsSync(GUIDES_DIR)) {\n const guidesDestDir = join(targetDir, '.ai', 'guides')\n for (const file of readdirSync(GUIDES_DIR)) {\n if (!file.endsWith('.md')) continue\n const srcPath = join(GUIDES_DIR, file)\n const destPath = join(guidesDestDir, file)\n ensureDir(destPath)\n copyFileSync(srcPath, destPath)\n }\n\n const moduleFactsPath = join(GUIDES_DIR, 'module-facts.json')\n if (existsSync(moduleFactsPath)) {\n const destPath = join(guidesDestDir, 'module-facts.json')\n ensureDir(destPath)\n copyFileSync(moduleFactsPath, destPath)\n }\n\n const modulesSubdir = join(GUIDES_DIR, 'modules')\n for (const moduleId of selectedModules) {\n const destPath = join(guidesDestDir, 'modules', `${moduleId}.md`)\n ensureDir(destPath)\n copyFileSync(join(modulesSubdir, `${moduleId}.md`), destPath)\n }\n }\n}\n\nfunction generateClaudeCode(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'claude-code')\n\n writeTemplate(srcDir, 'CLAUDE.md.template', join(targetDir, 'CLAUDE.md'), config)\n copyFile(srcDir, 'settings.json', join(targetDir, '.claude', 'settings.json'))\n copyFile(srcDir, 'hooks/entity-migration-check.ts', join(targetDir, '.claude', 'hooks', 'entity-migration-check.ts'))\n copyFile(srcDir, 'mcp.json.example', join(targetDir, '.mcp.json.example'))\n\n // Symlink .claude/skills \u2192 ../.ai/skills\n ensureSkillsLink(join(targetDir, '.claude', 'skills'), join('..', '.ai', 'skills'))\n}\n\nfunction generateCodex(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'codex')\n\n const agentsPath = join(targetDir, 'AGENTS.md')\n if (existsSync(agentsPath)) {\n const enforcement = readFileSync(join(srcDir, 'enforcement-rules.md'), 'utf-8')\n let agents = readFileSync(agentsPath, 'utf-8')\n const MARKER_START = '<!-- CODEX_ENFORCEMENT_RULES_START -->'\n const MARKER_END = '<!-- CODEX_ENFORCEMENT_RULES_END -->'\n\n if (agents.includes(MARKER_START)) {\n const startIdx = agents.indexOf(MARKER_START)\n const endIdx = agents.indexOf(MARKER_END)\n if (endIdx !== -1) {\n agents = agents.slice(0, startIdx) + enforcement + agents.slice(endIdx + MARKER_END.length)\n }\n } else {\n const firstNewline = agents.indexOf('\\n')\n if (firstNewline !== -1) {\n agents = agents.slice(0, firstNewline + 1) + '\\n' + enforcement + '\\n' + agents.slice(firstNewline + 1)\n } else {\n agents = agents + '\\n\\n' + enforcement\n }\n }\n writeFileSync(agentsPath, agents)\n }\n\n copyFile(srcDir, 'mcp.json.example', join(targetDir, '.codex', 'mcp.json.example'))\n\n // No .codex/skills directory: Codex reads the canonical .agents/skills/,\n // which scripts/install-skills.sh populates.\n}\n\nfunction generateCursor(config: AgenticConfig): void {\n const { targetDir } = config\n const srcDir = join(AGENTIC_DIR, 'cursor')\n\n writeTemplate(srcDir, 'rules/open-mercato.mdc', join(targetDir, '.cursor', 'rules', 'open-mercato.mdc'), config)\n copyFile(srcDir, 'rules/entity-guard.mdc', join(targetDir, '.cursor', 'rules', 'entity-guard.mdc'))\n copyFile(srcDir, 'rules/generated-guard.mdc', join(targetDir, '.cursor', 'rules', 'generated-guard.mdc'))\n copyFile(srcDir, 'hooks.json', join(targetDir, '.cursor', 'hooks.json'))\n copyFile(srcDir, 'hooks/entity-migration-check.mjs', join(targetDir, '.cursor', 'hooks', 'entity-migration-check.mjs'))\n copyFile(srcDir, 'mcp.json.example', join(targetDir, '.cursor', 'mcp.json.example'))\n\n // No .cursor/skills directory: Cursor reads the canonical .agents/skills/,\n // which scripts/install-skills.sh populates.\n}\n\nfunction ensureSkillsLink(linkPath: string, target: string): void {\n ensureDir(linkPath)\n if (existsSync(linkPath) && !lstatSync(linkPath).isSymbolicLink()) {\n return\n }\n if (lstatSync(linkPath, { throwIfNoEntry: false })?.isSymbolicLink()) {\n unlinkSync(linkPath)\n }\n symlinkSync(target, linkPath)\n}\n\n// \u2500\u2500\u2500 Wizard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst TOOLS = [\n { key: '1', label: 'Claude Code (Anthropic)', id: 'claude-code' },\n { key: '2', label: 'Codex (OpenAI)', id: 'codex' },\n { key: '3', label: 'Cursor (Anysphere)', id: 'cursor' },\n { key: '4', label: 'Multiple tools (select individually)', id: 'multiple' },\n { key: '5', label: 'Skip \u2014 set up manually later', id: 'skip' },\n] as const\n\nconst SELECTABLE = TOOLS.filter((t) => t.id !== 'multiple' && t.id !== 'skip')\n\nasync function promptSelection(ask: AskFn): Promise<string[]> {\n console.log('')\n console.log('\uD83E\uDD16 Agentic workflow setup')\n console.log('')\n console.log(' Which AI coding tool will you use with this project?')\n console.log('')\n for (const tool of TOOLS) {\n console.log(` ${tool.key}. ${tool.label}`)\n }\n console.log('')\n\n const answer = (await ask(' Enter number(s) separated by comma [1]: ')).trim() || '1'\n\n if (answer === '5') return ['skip']\n\n if (answer === '4') {\n const selected: string[] = []\n for (const tool of SELECTABLE) {\n const yn = await ask(` Include ${tool.label}? [y/N]: `)\n if (yn.toLowerCase() === 'y' || yn.toLowerCase() === 'yes') {\n selected.push(tool.id)\n }\n }\n return selected.length > 0 ? selected : ['skip']\n }\n\n const keys = answer.split(',').map((s) => s.trim())\n const ids: string[] = []\n for (const key of keys) {\n const tool = TOOLS.find((t) => t.key === key)\n if (tool && tool.id !== 'multiple' && tool.id !== 'skip') {\n ids.push(tool.id)\n }\n }\n return ids.length > 0 ? ids : ['skip']\n}\n\nexport async function runAgenticSetup(\n targetDir: string,\n ask: AskFn,\n options?: AgenticSetupOptions,\n): Promise<void> {\n let selectedIds: string[]\n\n if (options?.tool) {\n selectedIds = options.tool.split(',').map((t) => t.trim())\n } else {\n selectedIds = await promptSelection(ask)\n }\n\n if (selectedIds.includes('skip')) {\n console.log('')\n console.log(' Skipped agentic setup.')\n console.log('')\n return\n }\n\n const config: AgenticConfig = {\n projectName: basename(targetDir),\n targetDir,\n }\n\n generateShared(config)\n if (selectedIds.includes('claude-code')) generateClaudeCode(config)\n if (selectedIds.includes('codex')) generateCodex(config)\n if (selectedIds.includes('cursor')) generateCursor(config)\n\n persistAgentSelection(targetDir, selectedIds)\n installSkills(targetDir)\n\n console.log('')\n console.log(' Agentic setup complete:')\n if (selectedIds.includes('claude-code')) {\n console.log(' \u2713 Claude Code \u2014 CLAUDE.md, .claude/hooks/, .mcp.json.example')\n }\n if (selectedIds.includes('codex')) {\n console.log(' \u2713 Codex \u2014 AGENTS.md enforcement rules, .codex/mcp.json.example')\n }\n if (selectedIds.includes('cursor')) {\n console.log(' \u2713 Cursor \u2014 .cursor/rules/, .cursor/hooks/, .cursor/mcp.json.example')\n }\n console.log('')\n console.log(' .ai/agentic.config.json ships preconfigured (GitHub tracker, labels off);')\n console.log(' run /om-setup-agent-pipeline in your agent CLI to tailor labels, QA gate,')\n console.log(' tracker, or validation commands. Re-run `yarn install-skills` anytime to')\n console.log(' refresh the external open-mercato/skills subset.')\n console.log('')\n}\n\n/**\n * Persist the agent selection so later `yarn install-skills` runs keep honoring\n * it: agents the user did not pick go into `agents.ignore` in tiers.json and\n * never get a skills directory of their own.\n */\nfunction persistAgentSelection(targetDir: string, selectedIds: string[]): void {\n const manifestPath = join(targetDir, '.ai', 'skills', 'tiers.json')\n if (!existsSync(manifestPath)) return\n const ignore = SELECTABLE.map((tool) => tool.id).filter((id) => !selectedIds.includes(id))\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as Record<string, unknown>\n if (ignore.length > 0) {\n manifest.agents = { ignore }\n } else {\n delete manifest.agents\n }\n writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\nfunction installSkills(targetDir: string): void {\n const installScript = join(targetDir, 'scripts', 'install-skills.sh')\n if (!existsSync(installScript)) return\n console.log('')\n console.log(' Installing agent skills (local tiers + external open-mercato/skills subset)...')\n const result = spawnSync('sh', [installScript], { cwd: targetDir, stdio: 'inherit' })\n if (result.error || result.status !== 0) {\n console.log(' \u26A0 Skill installation did not complete; run `yarn install-skills` inside the app when online.')\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/db/commands.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
-
import ts from "typescript";
|
|
4
|
+
import ts from "typescript-js";
|
|
5
5
|
import { MikroORM, MetadataStorage } from "@mikro-orm/core";
|
|
6
6
|
import { ReflectMetadataProvider } from "@mikro-orm/decorators/legacy";
|
|
7
7
|
import { PostgreSqlDriver } from "@mikro-orm/postgresql";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/db/commands.ts"],
|
|
4
|
-
"sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { pathToFileURL } from 'node:url'\nimport ts from 'typescript'\nimport { MikroORM, MetadataStorage, type Logger } from '@mikro-orm/core'\nimport { ReflectMetadataProvider } from '@mikro-orm/decorators/legacy'\nimport { Migrator } from '@mikro-orm/migrations'\nimport { PostgreSqlDriver } from '@mikro-orm/postgresql'\nimport { getSslConfig } from '@open-mercato/shared/lib/db/ssl'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\nimport { quotePostgresIdentifier } from './identifiers'\n\nconst QUIET_MODE = process.env.OM_CLI_QUIET === '1' || process.env.MERCATO_QUIET === '1'\nconst PROGRESS_EMOJI = ''\n\nfunction formatResult(modId: string, message: string, emoji = '\\u2022') {\n return `${emoji} ${modId}: ${message}`\n}\n\nfunction createProgressRenderer(total: number) {\n const width = 20\n const normalizedTotal = total > 0 ? total : 1\n return (current: number) => {\n const clamped = Math.min(Math.max(current, 0), normalizedTotal)\n const filled = Math.round((clamped / normalizedTotal) * width)\n const bar = `${'='.repeat(filled)}${'.'.repeat(Math.max(width - filled, 0))}`\n return `[${bar}] ${clamped}/${normalizedTotal}`\n }\n}\n\nfunction createMinimalLogger(): Logger {\n return {\n log: () => { },\n error: (_namespace, message) => console.error(message),\n warn: (_namespace, message) => {\n if (!QUIET_MODE) console.warn(message)\n },\n logQuery: () => { },\n setDebugMode: () => { },\n isEnabled: () => false,\n }\n}\n\nfunction getClientUrl(): string {\n const url = process.env.DATABASE_URL\n if (!url) throw new Error('DATABASE_URL is not set')\n return url\n}\n\nfunction sortModules(mods: ModuleEntry[]): ModuleEntry[] {\n // Sort modules alphabetically since they are now isomorphic\n return mods.slice().sort((a, b) => a.id.localeCompare(b.id))\n}\n\n/**\n * Custom dynamic import provider for MikroORM that properly handles Windows paths.\n * MikroORM's built-in handling has a bug where it converts file:// URLs back to\n * Windows paths when the extension isn't in require.extensions (which is always\n * true for .ts files in ESM mode).\n */\nasync function dynamicImportProvider(id: string): Promise<any> {\n // On Windows, convert absolute paths to file:// URLs\n // Check if it's a Windows absolute path (e.g., C:\\... or D:\\...)\n if (process.platform === 'win32' && /^[a-zA-Z]:[\\\\/]/.test(id)) {\n id = pathToFileURL(id).href\n }\n return import(id)\n}\n\n/**\n * Sanitizes a module ID for use in SQL identifiers (table names).\n * Replaces non-alphanumeric characters with underscores to prevent SQL injection.\n * @public Exported for testing\n */\nexport function sanitizeModuleId(modId: string): string {\n return modId.replace(/[^a-z0-9_]/gi, '_')\n}\n\n/**\n * Validates that a table name is safe for use in SQL queries.\n * @throws Error if the table name contains invalid characters.\n * @public Exported for testing\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`)\n }\n}\n\nexport function makeConstraintDropsIdempotent(sql: string): string {\n return sql.replace(/alter table\\s+(\"[^\"]+\"|\\S+)\\s+drop constraint\\s+(\"[^\"]+\"|\\S+);/gi, 'alter table $1 drop constraint if exists $2;')\n}\n\nexport function getMigrationSnapshotName(resolver: Pick<PackageResolver, 'getRootDir'>): string {\n void resolver\n return '.snapshot-open-mercato'\n}\n\nexport function shouldCreateInitialModuleMigration(migrationsPath: string, snapshotName: string): boolean {\n const snapshotPath = path.join(migrationsPath, `${snapshotName}.json`)\n if (fs.existsSync(snapshotPath)) return false\n if (!fs.existsSync(migrationsPath)) return true\n\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => /^Migration.*\\.(ts|js)$/.test(file) && !file.endsWith('.d.ts'))\n\n return migrationFiles.length === 0\n}\n\nexport function resolveGeneratedMigrationPath(fileName: string, migrationsPath: string): string {\n return path.isAbsolute(fileName) ? fileName : path.join(migrationsPath, fileName)\n}\n\nlet tsxLoaderRegistered = false\nlet temporaryModuleCounter = 0\n\nasync function ensureTsxLoaderRegistered() {\n if (tsxLoaderRegistered) return\n try {\n const { register } = await import('tsx/esm/api')\n register()\n tsxLoaderRegistered = true\n } catch {\n // Continue without the loader. Relative TypeScript imports may fail in this case.\n }\n}\n\nasync function importWithTypeScriptFile(filePath: string): Promise<any> {\n await ensureTsxLoaderRegistered()\n const source = fs.readFileSync(filePath, 'utf8')\n const compiled = ts.transpileModule(source, {\n fileName: filePath,\n compilerOptions: {\n module: ts.ModuleKind.ESNext,\n target: ts.ScriptTarget.ES2022,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n esModuleInterop: true,\n resolveJsonModule: true,\n jsx: ts.JsxEmit.ReactJSX,\n experimentalDecorators: true,\n emitDecoratorMetadata: false,\n useDefineForClassFields: false,\n },\n }).outputText\n const tempPath = `${filePath}.mercato-db-generate-${process.pid}-${temporaryModuleCounter++}.mjs`\n fs.writeFileSync(tempPath, compiled, 'utf8')\n try {\n return await import(pathToFileURL(tempPath).href)\n } finally {\n try {\n fs.unlinkSync(tempPath)\n } catch {\n // Ignore cleanup failures for temporary compiled modules.\n }\n }\n}\n\nasync function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver): Promise<any[]> {\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const isAppModule = entry.from === '@app'\n const shouldImportFromSource = isAppModule || resolver.isMonorepo()\n const bases = [\n path.join(roots.appBase, 'data'),\n path.join(roots.pkgBase, 'data'),\n path.join(roots.appBase, 'db'),\n path.join(roots.pkgBase, 'db'),\n ]\n const candidates = ['entities.ts', 'schema.ts']\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const sub = path.basename(base)\n const fromApp = base.startsWith(roots.appBase)\n const importPath = fromApp || shouldImportFromSource\n ? pathToFileURL(p).href\n : `${imps.pkgBase}/${sub}/${f.replace(/\\.ts$/, '')}`\n try {\n const mod = fromApp || shouldImportFromSource\n ? await importWithTypeScriptFile(p)\n : await import(importPath)\n const entities = Object.values(mod).filter((v) => typeof v === 'function')\n if (entities.length) return entities as any[]\n } catch (err) {\n // For @app modules we try a TS loader fallback; otherwise propagate errors\n if (isAppModule) {\n if (process.env.MERCATO_CLI_DEBUG_IMPORTS === '1') {\n console.warn(`[db] failed to load app module entities from ${p}: ${(err as Error)?.message || String(err)}`)\n }\n continue\n }\n throw err\n }\n }\n }\n }\n return []\n}\n\nfunction getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {\n const roots = resolver.getModulePaths(entry)\n\n if (entry.from === '@app') {\n // @app modules: use src/ (user's TypeScript source)\n // Normalize to forward slashes for ESM compatibility on Windows\n return path.join(roots.appBase, 'migrations').replace(/\\\\/g, '/')\n }\n\n // Package modules: in standalone mode, use dist/ (compiled JS) since Node.js\n // can't run TypeScript from node_modules. In monorepo, use src/ (TypeScript).\n if (!resolver.isMonorepo()) {\n // Replace src/modules with dist/modules for standalone apps\n // Use regex to handle both forward and backslashes\n const distPath = roots.pkgBase.replace(/[/\\\\]src[/\\\\]modules[/\\\\]/, '/dist/modules/')\n return path.join(distPath, 'migrations').replace(/\\\\/g, '/')\n }\n\n // Normalize to forward slashes for ESM compatibility on Windows\n return path.join(roots.pkgBase, 'migrations').replace(/\\\\/g, '/')\n}\n\nexport interface DbOptions {\n quiet?: boolean\n}\n\nexport interface GreenfieldOptions extends DbOptions {\n yes: boolean\n}\n\nexport async function dbGenerate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n const sslConfig = getSslConfig()\n const usedFileNames = new Set<string>()\n\n for (const entry of ordered) {\n // Clear the global @Entity() decorator registry before loading this module's\n // entities. MikroORM's migrator reads MetadataStorage at createMigration()\n // time, so without this clear, every module's migration would include every\n // previously-loaded module's tables (see issue #1911).\n MetadataStorage.clear()\n\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n if (!entities.length) {\n if (entry.from === '@app') {\n results.push(formatResult(modId, 'no entities discovered', ''))\n }\n continue\n }\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n const snapshotName = getMigrationSnapshotName(resolver)\n const createInitialMigration = shouldCreateInitialModuleMigration(migrationsPath, snapshotName)\n\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n dynamicImportProvider,\n entities,\n metadataProvider: ReflectMetadataProvider,\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n snapshotName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n // acquireTimeoutMillis removed for v7 (pg.Pool doesn't support it; use connectionTimeoutMillis in driverOptions if needed)\n },\n driverOptions: sslConfig ? {\n ssl: sslConfig,\n } : undefined,\n })\n\n try {\n const diff = await orm.migrator.create(undefined, false, createInitialMigration)\n if (diff && diff.fileName) {\n try {\n const orig = resolveGeneratedMigrationPath(diff.fileName, migrationsPath)\n const base = path.basename(orig)\n const dir = path.dirname(orig)\n const ext = path.extname(base)\n const stem = base.replace(ext, '')\n const suffix = `_${modId}`\n let candidate = stem.endsWith(suffix) ? base : `${stem}${suffix}${ext}`\n let dedupe = 1\n\n while (usedFileNames.has(path.join(dir, candidate))) {\n candidate = `${stem}${suffix}_${dedupe++}${ext}`\n }\n\n const newPath = path.join(dir, candidate)\n let content = fs.readFileSync(orig, 'utf8')\n content = makeConstraintDropsIdempotent(content)\n content = content.replace(\n /export class (Migration\\d+)/,\n `export class $1_${modId.replace(/[^a-zA-Z0-9]/g, '_')}`\n )\n fs.writeFileSync(newPath, content, 'utf8')\n if (newPath !== orig) fs.unlinkSync(orig)\n usedFileNames.add(newPath)\n results.push(formatResult(modId, `generated ${path.basename(newPath)}`, ''))\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n results.push(formatResult(modId, `generated ${path.basename(diff.fileName)} (rename failed: ${message})`, ''))\n }\n } else {\n results.push(formatResult(modId, 'no changes', ''))\n }\n } finally {\n await orm.close(true)\n }\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbMigrate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n // Skip if no entities AND no migrations directory exists\n // (allows @app modules to run migrations even if entities can't be dynamically imported)\n if (!entities.length && !fs.existsSync(migrationsPath)) continue\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n // dbMigrate only runs existing migration files \u2014 entities are intentionally\n // omitted so MikroORM does not compare them against the snapshot and\n // auto-generate a phantom diff migration (that would duplicate tables\n // already created by committed migrations).\n const sslConfig = getSslConfig()\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n dynamicImportProvider,\n entities: [],\n metadataProvider: ReflectMetadataProvider,\n discovery: { warnWhenNoEntities: false },\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n snapshot: false,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n // acquireTimeoutMillis removed for v7 (pg.Pool doesn't support it; use connectionTimeoutMillis in driverOptions if needed)\n },\n driverOptions: sslConfig ? {\n ssl: sslConfig,\n } : undefined,\n })\n\n const migrator = orm.migrator as Migrator\n const pending = await migrator.getPending()\n if (!pending.length) {\n results.push(formatResult(modId, 'no pending migrations', ''))\n } else {\n const renderProgress = createProgressRenderer(pending.length)\n let applied = 0\n if (!QUIET_MODE) {\n process.stdout.write(` ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n for (const migration of pending) {\n const migrationName =\n typeof migration === 'string'\n ? migration\n : (migration as any).name ?? (migration as any).fileName\n await migrator.up(migrationName ? { migrations: [migrationName] } : undefined)\n applied += 1\n if (!QUIET_MODE) {\n process.stdout.write(`\\r ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n }\n if (!QUIET_MODE) process.stdout.write('\\n')\n results.push(\n formatResult(modId, `${pending.length} migration${pending.length === 1 ? '' : 's'} applied`, '')\n )\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbGreenfield(resolver: PackageResolver, options: GreenfieldOptions): Promise<void> {\n if (!options.yes) {\n console.error('This command will DELETE all data. Use --yes to confirm.')\n process.exit(1)\n }\n\n console.log('Cleaning up migrations and snapshots for greenfield setup...')\n\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n const outputDir = resolver.getOutputDir()\n\n for (const entry of ordered) {\n const modId = entry.id\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n if (fs.existsSync(migrationsPath)) {\n // Remove all migration files\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.ts') && file.startsWith('Migration'))\n\n for (const file of migrationFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n // Remove snapshot files\n const snapshotFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.json') && file.includes('snapshot'))\n\n for (const file of snapshotFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n if (migrationFiles.length > 0 || snapshotFiles.length > 0) {\n results.push(\n formatResult(modId, `cleaned ${migrationFiles.length} migrations, ${snapshotFiles.length} snapshots`, '')\n )\n } else {\n results.push(formatResult(modId, 'already clean', ''))\n }\n } else {\n results.push(formatResult(modId, 'no migrations directory', ''))\n }\n\n // Clean up checksum files using glob pattern\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n const checksumFiles = files.filter((file) => file.endsWith('.checksum'))\n\n for (const file of checksumFiles) {\n fs.unlinkSync(path.join(outputDir, file))\n }\n\n if (checksumFiles.length > 0) {\n results.push(formatResult(modId, `cleaned ${checksumFiles.length} checksum files`, ''))\n }\n }\n }\n\n console.log(results.join('\\n'))\n\n // Drop per-module MikroORM migration tables to ensure clean slate\n console.log('Dropping per-module migration tables...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl(), ssl: getSslConfig() })\n await client.connect()\n try {\n await client.query('BEGIN')\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n await client.query(`DROP TABLE IF EXISTS \"${tableName}\"`)\n console.log(` ${modId}: dropped table ${tableName}`)\n }\n await client.query('COMMIT')\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n } finally {\n try {\n await client.end()\n } catch { }\n }\n } catch (e) {\n console.error('Failed to drop migration tables:', (e as any)?.message || e)\n throw e\n }\n\n // Drop all existing user tables to ensure fresh CREATE-only migrations\n console.log('Dropping ALL public tables for true greenfield...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl(), ssl: getSslConfig() })\n await client.connect()\n try {\n const res = await client.query(`SELECT tablename FROM pg_tables WHERE schemaname = current_schema()`)\n const tables: string[] = (res.rows || []).map((r: any) => String(r.tablename))\n if (tables.length) {\n await client.query('BEGIN')\n try {\n await client.query(\"SET session_replication_role = 'replica'\")\n for (const t of tables) {\n await client.query(`DROP TABLE IF EXISTS ${quotePostgresIdentifier(t)} CASCADE`)\n }\n await client.query(\"SET session_replication_role = 'origin'\")\n await client.query('COMMIT')\n console.log(` Dropped ${tables.length} tables.`)\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n }\n } else {\n console.log(' No tables found to drop.')\n }\n } finally {\n try {\n await client.end()\n } catch { }\n }\n } catch (e) {\n console.error('Failed to drop public tables:', (e as any)?.message || e)\n throw e\n }\n\n // Generate fresh migrations for all modules\n console.log('Generating fresh migrations for all modules...')\n await dbGenerate(resolver)\n\n // Apply migrations\n console.log('Applying migrations...')\n await dbMigrate(resolver)\n\n console.log('Greenfield reset complete! Fresh migrations generated and applied.')\n}\n"],
|
|
4
|
+
"sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { pathToFileURL } from 'node:url'\nimport ts from 'typescript-js'\nimport { MikroORM, MetadataStorage, type Logger } from '@mikro-orm/core'\nimport { ReflectMetadataProvider } from '@mikro-orm/decorators/legacy'\nimport { Migrator } from '@mikro-orm/migrations'\nimport { PostgreSqlDriver } from '@mikro-orm/postgresql'\nimport { getSslConfig } from '@open-mercato/shared/lib/db/ssl'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\nimport { quotePostgresIdentifier } from './identifiers'\n\nconst QUIET_MODE = process.env.OM_CLI_QUIET === '1' || process.env.MERCATO_QUIET === '1'\nconst PROGRESS_EMOJI = ''\n\nfunction formatResult(modId: string, message: string, emoji = '\\u2022') {\n return `${emoji} ${modId}: ${message}`\n}\n\nfunction createProgressRenderer(total: number) {\n const width = 20\n const normalizedTotal = total > 0 ? total : 1\n return (current: number) => {\n const clamped = Math.min(Math.max(current, 0), normalizedTotal)\n const filled = Math.round((clamped / normalizedTotal) * width)\n const bar = `${'='.repeat(filled)}${'.'.repeat(Math.max(width - filled, 0))}`\n return `[${bar}] ${clamped}/${normalizedTotal}`\n }\n}\n\nfunction createMinimalLogger(): Logger {\n return {\n log: () => { },\n error: (_namespace, message) => console.error(message),\n warn: (_namespace, message) => {\n if (!QUIET_MODE) console.warn(message)\n },\n logQuery: () => { },\n setDebugMode: () => { },\n isEnabled: () => false,\n }\n}\n\nfunction getClientUrl(): string {\n const url = process.env.DATABASE_URL\n if (!url) throw new Error('DATABASE_URL is not set')\n return url\n}\n\nfunction sortModules(mods: ModuleEntry[]): ModuleEntry[] {\n // Sort modules alphabetically since they are now isomorphic\n return mods.slice().sort((a, b) => a.id.localeCompare(b.id))\n}\n\n/**\n * Custom dynamic import provider for MikroORM that properly handles Windows paths.\n * MikroORM's built-in handling has a bug where it converts file:// URLs back to\n * Windows paths when the extension isn't in require.extensions (which is always\n * true for .ts files in ESM mode).\n */\nasync function dynamicImportProvider(id: string): Promise<any> {\n // On Windows, convert absolute paths to file:// URLs\n // Check if it's a Windows absolute path (e.g., C:\\... or D:\\...)\n if (process.platform === 'win32' && /^[a-zA-Z]:[\\\\/]/.test(id)) {\n id = pathToFileURL(id).href\n }\n return import(id)\n}\n\n/**\n * Sanitizes a module ID for use in SQL identifiers (table names).\n * Replaces non-alphanumeric characters with underscores to prevent SQL injection.\n * @public Exported for testing\n */\nexport function sanitizeModuleId(modId: string): string {\n return modId.replace(/[^a-z0-9_]/gi, '_')\n}\n\n/**\n * Validates that a table name is safe for use in SQL queries.\n * @throws Error if the table name contains invalid characters.\n * @public Exported for testing\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`)\n }\n}\n\nexport function makeConstraintDropsIdempotent(sql: string): string {\n return sql.replace(/alter table\\s+(\"[^\"]+\"|\\S+)\\s+drop constraint\\s+(\"[^\"]+\"|\\S+);/gi, 'alter table $1 drop constraint if exists $2;')\n}\n\nexport function getMigrationSnapshotName(resolver: Pick<PackageResolver, 'getRootDir'>): string {\n void resolver\n return '.snapshot-open-mercato'\n}\n\nexport function shouldCreateInitialModuleMigration(migrationsPath: string, snapshotName: string): boolean {\n const snapshotPath = path.join(migrationsPath, `${snapshotName}.json`)\n if (fs.existsSync(snapshotPath)) return false\n if (!fs.existsSync(migrationsPath)) return true\n\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => /^Migration.*\\.(ts|js)$/.test(file) && !file.endsWith('.d.ts'))\n\n return migrationFiles.length === 0\n}\n\nexport function resolveGeneratedMigrationPath(fileName: string, migrationsPath: string): string {\n return path.isAbsolute(fileName) ? fileName : path.join(migrationsPath, fileName)\n}\n\nlet tsxLoaderRegistered = false\nlet temporaryModuleCounter = 0\n\nasync function ensureTsxLoaderRegistered() {\n if (tsxLoaderRegistered) return\n try {\n const { register } = await import('tsx/esm/api')\n register()\n tsxLoaderRegistered = true\n } catch {\n // Continue without the loader. Relative TypeScript imports may fail in this case.\n }\n}\n\nasync function importWithTypeScriptFile(filePath: string): Promise<any> {\n await ensureTsxLoaderRegistered()\n const source = fs.readFileSync(filePath, 'utf8')\n const compiled = ts.transpileModule(source, {\n fileName: filePath,\n compilerOptions: {\n module: ts.ModuleKind.ESNext,\n target: ts.ScriptTarget.ES2022,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n esModuleInterop: true,\n resolveJsonModule: true,\n jsx: ts.JsxEmit.ReactJSX,\n experimentalDecorators: true,\n emitDecoratorMetadata: false,\n useDefineForClassFields: false,\n },\n }).outputText\n const tempPath = `${filePath}.mercato-db-generate-${process.pid}-${temporaryModuleCounter++}.mjs`\n fs.writeFileSync(tempPath, compiled, 'utf8')\n try {\n return await import(pathToFileURL(tempPath).href)\n } finally {\n try {\n fs.unlinkSync(tempPath)\n } catch {\n // Ignore cleanup failures for temporary compiled modules.\n }\n }\n}\n\nasync function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver): Promise<any[]> {\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const isAppModule = entry.from === '@app'\n const shouldImportFromSource = isAppModule || resolver.isMonorepo()\n const bases = [\n path.join(roots.appBase, 'data'),\n path.join(roots.pkgBase, 'data'),\n path.join(roots.appBase, 'db'),\n path.join(roots.pkgBase, 'db'),\n ]\n const candidates = ['entities.ts', 'schema.ts']\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const sub = path.basename(base)\n const fromApp = base.startsWith(roots.appBase)\n const importPath = fromApp || shouldImportFromSource\n ? pathToFileURL(p).href\n : `${imps.pkgBase}/${sub}/${f.replace(/\\.ts$/, '')}`\n try {\n const mod = fromApp || shouldImportFromSource\n ? await importWithTypeScriptFile(p)\n : await import(importPath)\n const entities = Object.values(mod).filter((v) => typeof v === 'function')\n if (entities.length) return entities as any[]\n } catch (err) {\n // For @app modules we try a TS loader fallback; otherwise propagate errors\n if (isAppModule) {\n if (process.env.MERCATO_CLI_DEBUG_IMPORTS === '1') {\n console.warn(`[db] failed to load app module entities from ${p}: ${(err as Error)?.message || String(err)}`)\n }\n continue\n }\n throw err\n }\n }\n }\n }\n return []\n}\n\nfunction getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {\n const roots = resolver.getModulePaths(entry)\n\n if (entry.from === '@app') {\n // @app modules: use src/ (user's TypeScript source)\n // Normalize to forward slashes for ESM compatibility on Windows\n return path.join(roots.appBase, 'migrations').replace(/\\\\/g, '/')\n }\n\n // Package modules: in standalone mode, use dist/ (compiled JS) since Node.js\n // can't run TypeScript from node_modules. In monorepo, use src/ (TypeScript).\n if (!resolver.isMonorepo()) {\n // Replace src/modules with dist/modules for standalone apps\n // Use regex to handle both forward and backslashes\n const distPath = roots.pkgBase.replace(/[/\\\\]src[/\\\\]modules[/\\\\]/, '/dist/modules/')\n return path.join(distPath, 'migrations').replace(/\\\\/g, '/')\n }\n\n // Normalize to forward slashes for ESM compatibility on Windows\n return path.join(roots.pkgBase, 'migrations').replace(/\\\\/g, '/')\n}\n\nexport interface DbOptions {\n quiet?: boolean\n}\n\nexport interface GreenfieldOptions extends DbOptions {\n yes: boolean\n}\n\nexport async function dbGenerate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n const sslConfig = getSslConfig()\n const usedFileNames = new Set<string>()\n\n for (const entry of ordered) {\n // Clear the global @Entity() decorator registry before loading this module's\n // entities. MikroORM's migrator reads MetadataStorage at createMigration()\n // time, so without this clear, every module's migration would include every\n // previously-loaded module's tables (see issue #1911).\n MetadataStorage.clear()\n\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n if (!entities.length) {\n if (entry.from === '@app') {\n results.push(formatResult(modId, 'no entities discovered', ''))\n }\n continue\n }\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n const snapshotName = getMigrationSnapshotName(resolver)\n const createInitialMigration = shouldCreateInitialModuleMigration(migrationsPath, snapshotName)\n\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n dynamicImportProvider,\n entities,\n metadataProvider: ReflectMetadataProvider,\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n snapshotName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n // acquireTimeoutMillis removed for v7 (pg.Pool doesn't support it; use connectionTimeoutMillis in driverOptions if needed)\n },\n driverOptions: sslConfig ? {\n ssl: sslConfig,\n } : undefined,\n })\n\n try {\n const diff = await orm.migrator.create(undefined, false, createInitialMigration)\n if (diff && diff.fileName) {\n try {\n const orig = resolveGeneratedMigrationPath(diff.fileName, migrationsPath)\n const base = path.basename(orig)\n const dir = path.dirname(orig)\n const ext = path.extname(base)\n const stem = base.replace(ext, '')\n const suffix = `_${modId}`\n let candidate = stem.endsWith(suffix) ? base : `${stem}${suffix}${ext}`\n let dedupe = 1\n\n while (usedFileNames.has(path.join(dir, candidate))) {\n candidate = `${stem}${suffix}_${dedupe++}${ext}`\n }\n\n const newPath = path.join(dir, candidate)\n let content = fs.readFileSync(orig, 'utf8')\n content = makeConstraintDropsIdempotent(content)\n content = content.replace(\n /export class (Migration\\d+)/,\n `export class $1_${modId.replace(/[^a-zA-Z0-9]/g, '_')}`\n )\n fs.writeFileSync(newPath, content, 'utf8')\n if (newPath !== orig) fs.unlinkSync(orig)\n usedFileNames.add(newPath)\n results.push(formatResult(modId, `generated ${path.basename(newPath)}`, ''))\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n results.push(formatResult(modId, `generated ${path.basename(diff.fileName)} (rename failed: ${message})`, ''))\n }\n } else {\n results.push(formatResult(modId, 'no changes', ''))\n }\n } finally {\n await orm.close(true)\n }\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbMigrate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n // Skip if no entities AND no migrations directory exists\n // (allows @app modules to run migrations even if entities can't be dynamically imported)\n if (!entities.length && !fs.existsSync(migrationsPath)) continue\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n // dbMigrate only runs existing migration files \u2014 entities are intentionally\n // omitted so MikroORM does not compare them against the snapshot and\n // auto-generate a phantom diff migration (that would duplicate tables\n // already created by committed migrations).\n const sslConfig = getSslConfig()\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n dynamicImportProvider,\n entities: [],\n metadataProvider: ReflectMetadataProvider,\n discovery: { warnWhenNoEntities: false },\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n snapshot: false,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n // acquireTimeoutMillis removed for v7 (pg.Pool doesn't support it; use connectionTimeoutMillis in driverOptions if needed)\n },\n driverOptions: sslConfig ? {\n ssl: sslConfig,\n } : undefined,\n })\n\n const migrator = orm.migrator as Migrator\n const pending = await migrator.getPending()\n if (!pending.length) {\n results.push(formatResult(modId, 'no pending migrations', ''))\n } else {\n const renderProgress = createProgressRenderer(pending.length)\n let applied = 0\n if (!QUIET_MODE) {\n process.stdout.write(` ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n for (const migration of pending) {\n const migrationName =\n typeof migration === 'string'\n ? migration\n : (migration as any).name ?? (migration as any).fileName\n await migrator.up(migrationName ? { migrations: [migrationName] } : undefined)\n applied += 1\n if (!QUIET_MODE) {\n process.stdout.write(`\\r ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n }\n if (!QUIET_MODE) process.stdout.write('\\n')\n results.push(\n formatResult(modId, `${pending.length} migration${pending.length === 1 ? '' : 's'} applied`, '')\n )\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbGreenfield(resolver: PackageResolver, options: GreenfieldOptions): Promise<void> {\n if (!options.yes) {\n console.error('This command will DELETE all data. Use --yes to confirm.')\n process.exit(1)\n }\n\n console.log('Cleaning up migrations and snapshots for greenfield setup...')\n\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n const outputDir = resolver.getOutputDir()\n\n for (const entry of ordered) {\n const modId = entry.id\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n if (fs.existsSync(migrationsPath)) {\n // Remove all migration files\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.ts') && file.startsWith('Migration'))\n\n for (const file of migrationFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n // Remove snapshot files\n const snapshotFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.json') && file.includes('snapshot'))\n\n for (const file of snapshotFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n if (migrationFiles.length > 0 || snapshotFiles.length > 0) {\n results.push(\n formatResult(modId, `cleaned ${migrationFiles.length} migrations, ${snapshotFiles.length} snapshots`, '')\n )\n } else {\n results.push(formatResult(modId, 'already clean', ''))\n }\n } else {\n results.push(formatResult(modId, 'no migrations directory', ''))\n }\n\n // Clean up checksum files using glob pattern\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n const checksumFiles = files.filter((file) => file.endsWith('.checksum'))\n\n for (const file of checksumFiles) {\n fs.unlinkSync(path.join(outputDir, file))\n }\n\n if (checksumFiles.length > 0) {\n results.push(formatResult(modId, `cleaned ${checksumFiles.length} checksum files`, ''))\n }\n }\n }\n\n console.log(results.join('\\n'))\n\n // Drop per-module MikroORM migration tables to ensure clean slate\n console.log('Dropping per-module migration tables...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl(), ssl: getSslConfig() })\n await client.connect()\n try {\n await client.query('BEGIN')\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n await client.query(`DROP TABLE IF EXISTS \"${tableName}\"`)\n console.log(` ${modId}: dropped table ${tableName}`)\n }\n await client.query('COMMIT')\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n } finally {\n try {\n await client.end()\n } catch { }\n }\n } catch (e) {\n console.error('Failed to drop migration tables:', (e as any)?.message || e)\n throw e\n }\n\n // Drop all existing user tables to ensure fresh CREATE-only migrations\n console.log('Dropping ALL public tables for true greenfield...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl(), ssl: getSslConfig() })\n await client.connect()\n try {\n const res = await client.query(`SELECT tablename FROM pg_tables WHERE schemaname = current_schema()`)\n const tables: string[] = (res.rows || []).map((r: any) => String(r.tablename))\n if (tables.length) {\n await client.query('BEGIN')\n try {\n await client.query(\"SET session_replication_role = 'replica'\")\n for (const t of tables) {\n await client.query(`DROP TABLE IF EXISTS ${quotePostgresIdentifier(t)} CASCADE`)\n }\n await client.query(\"SET session_replication_role = 'origin'\")\n await client.query('COMMIT')\n console.log(` Dropped ${tables.length} tables.`)\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n }\n } else {\n console.log(' No tables found to drop.')\n }\n } finally {\n try {\n await client.end()\n } catch { }\n }\n } catch (e) {\n console.error('Failed to drop public tables:', (e as any)?.message || e)\n throw e\n }\n\n // Generate fresh migrations for all modules\n console.log('Generating fresh migrations for all modules...')\n await dbGenerate(resolver)\n\n // Apply migrations\n console.log('Applying migrations...')\n await dbMigrate(resolver)\n\n console.log('Greenfield reset complete! Fresh migrations generated and applied.')\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function targetKey(target) {
|
|
4
|
+
return JSON.stringify([
|
|
5
|
+
path.resolve(target.directory),
|
|
6
|
+
target.recursive,
|
|
7
|
+
target.fileName ?? ""
|
|
8
|
+
]);
|
|
9
|
+
}
|
|
10
|
+
const defaultWatchDirectory = (target, onChange, onError) => {
|
|
11
|
+
const watcher = fs.watch(
|
|
12
|
+
target.directory,
|
|
13
|
+
{ recursive: target.recursive },
|
|
14
|
+
(_eventType, fileName) => {
|
|
15
|
+
const normalizedName = String(fileName ?? "");
|
|
16
|
+
if (target.fileName && normalizedName && normalizedName !== target.fileName) return;
|
|
17
|
+
onChange(normalizedName || void 0);
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
watcher.on("error", onError);
|
|
21
|
+
return watcher;
|
|
22
|
+
};
|
|
23
|
+
function createGenerateWatchChangeSignal(options) {
|
|
24
|
+
const watchDirectory = options.watchDirectory ?? defaultWatchDirectory;
|
|
25
|
+
const watchers = /* @__PURE__ */ new Map();
|
|
26
|
+
let version = 0;
|
|
27
|
+
let pollingFallback = false;
|
|
28
|
+
let closed = false;
|
|
29
|
+
const closeWatchers = () => {
|
|
30
|
+
for (const watcher of watchers.values()) {
|
|
31
|
+
try {
|
|
32
|
+
watcher.close();
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
watchers.clear();
|
|
37
|
+
};
|
|
38
|
+
const enterPollingFallback = () => {
|
|
39
|
+
if (pollingFallback) return;
|
|
40
|
+
pollingFallback = true;
|
|
41
|
+
version += 1;
|
|
42
|
+
closeWatchers();
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
currentVersion: () => version,
|
|
46
|
+
usesPollingFallback: () => pollingFallback,
|
|
47
|
+
refresh: async () => {
|
|
48
|
+
if (closed || pollingFallback) return;
|
|
49
|
+
let targets;
|
|
50
|
+
try {
|
|
51
|
+
targets = await options.getWatchTargets();
|
|
52
|
+
} catch {
|
|
53
|
+
enterPollingFallback();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const normalizedTargets = /* @__PURE__ */ new Map();
|
|
57
|
+
for (const target of targets) {
|
|
58
|
+
const normalized = {
|
|
59
|
+
...target,
|
|
60
|
+
directory: path.resolve(target.directory)
|
|
61
|
+
};
|
|
62
|
+
normalizedTargets.set(targetKey(normalized), normalized);
|
|
63
|
+
}
|
|
64
|
+
for (const [key, watcher] of watchers) {
|
|
65
|
+
if (normalizedTargets.has(key)) continue;
|
|
66
|
+
try {
|
|
67
|
+
watcher.close();
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
watchers.delete(key);
|
|
71
|
+
}
|
|
72
|
+
for (const [key, target] of normalizedTargets) {
|
|
73
|
+
if (watchers.has(key)) continue;
|
|
74
|
+
try {
|
|
75
|
+
const watcher = watchDirectory(
|
|
76
|
+
target,
|
|
77
|
+
() => {
|
|
78
|
+
version += 1;
|
|
79
|
+
},
|
|
80
|
+
enterPollingFallback
|
|
81
|
+
);
|
|
82
|
+
if (closed || pollingFallback) {
|
|
83
|
+
try {
|
|
84
|
+
watcher.close();
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
watchers.set(key, watcher);
|
|
90
|
+
} catch {
|
|
91
|
+
enterPollingFallback();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
close: () => {
|
|
97
|
+
if (closed) return;
|
|
98
|
+
closed = true;
|
|
99
|
+
closeWatchers();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
createGenerateWatchChangeSignal
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=generate-watch-events.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/generate-watch-events.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { GenerateWatcherChangeSignal } from './in-process-generate-watcher'\n\nexport type GenerateWatchTarget = {\n directory: string\n recursive: boolean\n fileName?: string\n}\n\ntype WatchHandle = {\n close(): void\n}\n\ntype WatchDirectory = (\n target: GenerateWatchTarget,\n onChange: (fileName?: string) => void,\n onError: () => void,\n) => WatchHandle\n\nexport type GenerateWatchChangeSignalOptions = {\n getWatchTargets: () => Promise<GenerateWatchTarget[]> | GenerateWatchTarget[]\n watchDirectory?: WatchDirectory\n}\n\nfunction targetKey(target: GenerateWatchTarget): string {\n return JSON.stringify([\n path.resolve(target.directory),\n target.recursive,\n target.fileName ?? '',\n ])\n}\n\nconst defaultWatchDirectory: WatchDirectory = (target, onChange, onError) => {\n const watcher = fs.watch(\n target.directory,\n { recursive: target.recursive },\n (_eventType, fileName) => {\n const normalizedName = String(fileName ?? '')\n if (target.fileName && normalizedName && normalizedName !== target.fileName) return\n onChange(normalizedName || undefined)\n },\n )\n watcher.on('error', onError)\n return watcher\n}\n\nexport function createGenerateWatchChangeSignal(\n options: GenerateWatchChangeSignalOptions,\n): GenerateWatcherChangeSignal {\n const watchDirectory = options.watchDirectory ?? defaultWatchDirectory\n const watchers = new Map<string, WatchHandle>()\n let version = 0\n let pollingFallback = false\n let closed = false\n\n const closeWatchers = () => {\n for (const watcher of watchers.values()) {\n try { watcher.close() } catch {}\n }\n watchers.clear()\n }\n\n const enterPollingFallback = () => {\n if (pollingFallback) return\n pollingFallback = true\n version += 1\n closeWatchers()\n }\n\n return {\n currentVersion: () => version,\n usesPollingFallback: () => pollingFallback,\n refresh: async () => {\n if (closed || pollingFallback) return\n\n let targets: GenerateWatchTarget[]\n try {\n targets = await options.getWatchTargets()\n } catch {\n enterPollingFallback()\n return\n }\n\n const normalizedTargets = new Map<string, GenerateWatchTarget>()\n for (const target of targets) {\n const normalized: GenerateWatchTarget = {\n ...target,\n directory: path.resolve(target.directory),\n }\n normalizedTargets.set(targetKey(normalized), normalized)\n }\n\n for (const [key, watcher] of watchers) {\n if (normalizedTargets.has(key)) continue\n try { watcher.close() } catch {}\n watchers.delete(key)\n }\n\n for (const [key, target] of normalizedTargets) {\n if (watchers.has(key)) continue\n try {\n const watcher = watchDirectory(\n target,\n () => { version += 1 },\n enterPollingFallback,\n )\n if (closed || pollingFallback) {\n try { watcher.close() } catch {}\n continue\n }\n watchers.set(key, watcher)\n } catch {\n enterPollingFallback()\n return\n }\n }\n },\n close: () => {\n if (closed) return\n closed = true\n closeWatchers()\n },\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAwBjB,SAAS,UAAU,QAAqC;AACtD,SAAO,KAAK,UAAU;AAAA,IACpB,KAAK,QAAQ,OAAO,SAAS;AAAA,IAC7B,OAAO;AAAA,IACP,OAAO,YAAY;AAAA,EACrB,CAAC;AACH;AAEA,MAAM,wBAAwC,CAAC,QAAQ,UAAU,YAAY;AAC3E,QAAM,UAAU,GAAG;AAAA,IACjB,OAAO;AAAA,IACP,EAAE,WAAW,OAAO,UAAU;AAAA,IAC9B,CAAC,YAAY,aAAa;AACxB,YAAM,iBAAiB,OAAO,YAAY,EAAE;AAC5C,UAAI,OAAO,YAAY,kBAAkB,mBAAmB,OAAO,SAAU;AAC7E,eAAS,kBAAkB,MAAS;AAAA,IACtC;AAAA,EACF;AACA,UAAQ,GAAG,SAAS,OAAO;AAC3B,SAAO;AACT;AAEO,SAAS,gCACd,SAC6B;AAC7B,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,WAAW,oBAAI,IAAyB;AAC9C,MAAI,UAAU;AACd,MAAI,kBAAkB;AACtB,MAAI,SAAS;AAEb,QAAM,gBAAgB,MAAM;AAC1B,eAAW,WAAW,SAAS,OAAO,GAAG;AACvC,UAAI;AAAE,gBAAQ,MAAM;AAAA,MAAE,QAAQ;AAAA,MAAC;AAAA,IACjC;AACA,aAAS,MAAM;AAAA,EACjB;AAEA,QAAM,uBAAuB,MAAM;AACjC,QAAI,gBAAiB;AACrB,sBAAkB;AAClB,eAAW;AACX,kBAAc;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,gBAAgB,MAAM;AAAA,IACtB,qBAAqB,MAAM;AAAA,IAC3B,SAAS,YAAY;AACnB,UAAI,UAAU,gBAAiB;AAE/B,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,QAAQ,gBAAgB;AAAA,MAC1C,QAAQ;AACN,6BAAqB;AACrB;AAAA,MACF;AAEA,YAAM,oBAAoB,oBAAI,IAAiC;AAC/D,iBAAW,UAAU,SAAS;AAC5B,cAAM,aAAkC;AAAA,UACtC,GAAG;AAAA,UACH,WAAW,KAAK,QAAQ,OAAO,SAAS;AAAA,QAC1C;AACA,0BAAkB,IAAI,UAAU,UAAU,GAAG,UAAU;AAAA,MACzD;AAEA,iBAAW,CAAC,KAAK,OAAO,KAAK,UAAU;AACrC,YAAI,kBAAkB,IAAI,GAAG,EAAG;AAChC,YAAI;AAAE,kBAAQ,MAAM;AAAA,QAAE,QAAQ;AAAA,QAAC;AAC/B,iBAAS,OAAO,GAAG;AAAA,MACrB;AAEA,iBAAW,CAAC,KAAK,MAAM,KAAK,mBAAmB;AAC7C,YAAI,SAAS,IAAI,GAAG,EAAG;AACvB,YAAI;AACF,gBAAM,UAAU;AAAA,YACd;AAAA,YACA,MAAM;AAAE,yBAAW;AAAA,YAAE;AAAA,YACrB;AAAA,UACF;AACA,cAAI,UAAU,iBAAiB;AAC7B,gBAAI;AAAE,sBAAQ,MAAM;AAAA,YAAE,QAAQ;AAAA,YAAC;AAC/B;AAAA,UACF;AACA,mBAAS,IAAI,KAAK,OAAO;AAAA,QAC3B,QAAQ;AACN,+BAAqB;AACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,OAAO,MAAM;AACX,UAAI,OAAQ;AACZ,eAAS;AACT,oBAAc;AAAA,IAChB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/generators/ast/imports.ts"],
|
|
4
|
-
"sourcesContent": ["import ts from 'typescript'\nimport {\n StructureKind,\n type ImportDeclarationStructure,\n type OptionalKind,\n type SourceFile,\n} from 'ts-morph'\n\nexport type GeneratedImportSpec = OptionalKind<ImportDeclarationStructure>\n\nfunction toImportSpec(statement: string): GeneratedImportSpec {\n const trimmed = statement.trim()\n const sourceFile = ts.createSourceFile('import.ts', trimmed, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)\n const importDeclaration = sourceFile.statements.find(ts.isImportDeclaration)\n if (!importDeclaration) {\n throw new Error(`Expected import declaration, received: ${statement}`)\n }\n\n const moduleSpecifier = importDeclaration.moduleSpecifier\n if (!ts.isStringLiteral(moduleSpecifier)) {\n throw new Error(`Unsupported import declaration: ${statement}`)\n }\n\n const spec: GeneratedImportSpec = {\n kind: StructureKind.ImportDeclaration,\n moduleSpecifier: moduleSpecifier.text,\n }\n\n const clause = importDeclaration.importClause\n if (!clause) {\n return spec\n }\n\n spec.isTypeOnly = clause.isTypeOnly\n\n if (clause.name) {\n spec.defaultImport = clause.name.text\n }\n\n if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {\n spec.namespaceImport = clause.namedBindings.name.text\n }\n\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n spec.namedImports = clause.namedBindings.elements.map((element) => ({\n kind: StructureKind.ImportSpecifier,\n name: element.propertyName?.text ?? element.name.text,\n alias: element.propertyName ? element.name.text : undefined,\n isTypeOnly: element.isTypeOnly,\n }))\n }\n\n return spec\n}\n\nexport function addImportSpec(sourceFile: SourceFile, spec: GeneratedImportSpec): void {\n sourceFile.addImportDeclaration({\n kind: StructureKind.ImportDeclaration,\n ...spec,\n })\n}\n\nexport function addImportSpecs(sourceFile: SourceFile, specs: GeneratedImportSpec[]): void {\n for (const spec of specs) {\n addImportSpec(sourceFile, spec)\n }\n}\n\nexport function addImportStatement(sourceFile: SourceFile, statement: string): void {\n const trimmed = statement.trim()\n if (!trimmed) return\n addImportSpec(sourceFile, toImportSpec(trimmed))\n}\n\nexport function addImportStatements(sourceFile: SourceFile, statements: string[]): void {\n for (const statement of statements) {\n addImportStatement(sourceFile, statement)\n }\n}\n"],
|
|
4
|
+
"sourcesContent": ["import ts from 'typescript-js'\nimport {\n StructureKind,\n type ImportDeclarationStructure,\n type OptionalKind,\n type SourceFile,\n} from 'ts-morph'\n\nexport type GeneratedImportSpec = OptionalKind<ImportDeclarationStructure>\n\nfunction toImportSpec(statement: string): GeneratedImportSpec {\n const trimmed = statement.trim()\n const sourceFile = ts.createSourceFile('import.ts', trimmed, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)\n const importDeclaration = sourceFile.statements.find(ts.isImportDeclaration)\n if (!importDeclaration) {\n throw new Error(`Expected import declaration, received: ${statement}`)\n }\n\n const moduleSpecifier = importDeclaration.moduleSpecifier\n if (!ts.isStringLiteral(moduleSpecifier)) {\n throw new Error(`Unsupported import declaration: ${statement}`)\n }\n\n const spec: GeneratedImportSpec = {\n kind: StructureKind.ImportDeclaration,\n moduleSpecifier: moduleSpecifier.text,\n }\n\n const clause = importDeclaration.importClause\n if (!clause) {\n return spec\n }\n\n spec.isTypeOnly = clause.isTypeOnly\n\n if (clause.name) {\n spec.defaultImport = clause.name.text\n }\n\n if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {\n spec.namespaceImport = clause.namedBindings.name.text\n }\n\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n spec.namedImports = clause.namedBindings.elements.map((element) => ({\n kind: StructureKind.ImportSpecifier,\n name: element.propertyName?.text ?? element.name.text,\n alias: element.propertyName ? element.name.text : undefined,\n isTypeOnly: element.isTypeOnly,\n }))\n }\n\n return spec\n}\n\nexport function addImportSpec(sourceFile: SourceFile, spec: GeneratedImportSpec): void {\n sourceFile.addImportDeclaration({\n kind: StructureKind.ImportDeclaration,\n ...spec,\n })\n}\n\nexport function addImportSpecs(sourceFile: SourceFile, specs: GeneratedImportSpec[]): void {\n for (const spec of specs) {\n addImportSpec(sourceFile, spec)\n }\n}\n\nexport function addImportStatement(sourceFile: SourceFile, statement: string): void {\n const trimmed = statement.trim()\n if (!trimmed) return\n addImportSpec(sourceFile, toImportSpec(trimmed))\n}\n\nexport function addImportStatements(sourceFile: SourceFile, statements: string[]): void {\n for (const statement of statements) {\n addImportStatement(sourceFile, statement)\n }\n}\n"],
|
|
5
5
|
"mappings": "AAAA,OAAO,QAAQ;AACf;AAAA,EACE;AAAA,OAIK;AAIP,SAAS,aAAa,WAAwC;AAC5D,QAAM,UAAU,UAAU,KAAK;AAC/B,QAAM,aAAa,GAAG,iBAAiB,aAAa,SAAS,GAAG,aAAa,QAAQ,MAAM,GAAG,WAAW,EAAE;AAC3G,QAAM,oBAAoB,WAAW,WAAW,KAAK,GAAG,mBAAmB;AAC3E,MAAI,CAAC,mBAAmB;AACtB,UAAM,IAAI,MAAM,0CAA0C,SAAS,EAAE;AAAA,EACvE;AAEA,QAAM,kBAAkB,kBAAkB;AAC1C,MAAI,CAAC,GAAG,gBAAgB,eAAe,GAAG;AACxC,UAAM,IAAI,MAAM,mCAAmC,SAAS,EAAE;AAAA,EAChE;AAEA,QAAM,OAA4B;AAAA,IAChC,MAAM,cAAc;AAAA,IACpB,iBAAiB,gBAAgB;AAAA,EACnC;AAEA,QAAM,SAAS,kBAAkB;AACjC,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,OAAK,aAAa,OAAO;AAEzB,MAAI,OAAO,MAAM;AACf,SAAK,gBAAgB,OAAO,KAAK;AAAA,EACnC;AAEA,MAAI,OAAO,iBAAiB,GAAG,kBAAkB,OAAO,aAAa,GAAG;AACtE,SAAK,kBAAkB,OAAO,cAAc,KAAK;AAAA,EACnD;AAEA,MAAI,OAAO,iBAAiB,GAAG,eAAe,OAAO,aAAa,GAAG;AACnE,SAAK,eAAe,OAAO,cAAc,SAAS,IAAI,CAAC,aAAa;AAAA,MAClE,MAAM,cAAc;AAAA,MACpB,MAAM,QAAQ,cAAc,QAAQ,QAAQ,KAAK;AAAA,MACjD,OAAO,QAAQ,eAAe,QAAQ,KAAK,OAAO;AAAA,MAClD,YAAY,QAAQ;AAAA,IACtB,EAAE;AAAA,EACJ;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,YAAwB,MAAiC;AACrF,aAAW,qBAAqB;AAAA,IAC9B,MAAM,cAAc;AAAA,IACpB,GAAG;AAAA,EACL,CAAC;AACH;AAEO,SAAS,eAAe,YAAwB,OAAoC;AACzF,aAAW,QAAQ,OAAO;AACxB,kBAAc,YAAY,IAAI;AAAA,EAChC;AACF;AAEO,SAAS,mBAAmB,YAAwB,WAAyB;AAClF,QAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,CAAC,QAAS;AACd,gBAAc,YAAY,aAAa,OAAO,CAAC;AACjD;AAEO,SAAS,oBAAoB,YAAwB,YAA4B;AACtF,aAAW,aAAa,YAAY;AAClC,uBAAmB,YAAY,SAAS;AAAA,EAC1C;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import ts from "typescript";
|
|
3
|
+
import ts from "typescript-js";
|
|
4
4
|
import { VariableDeclarationKind } from "ts-morph";
|
|
5
5
|
import { MODULE_CODE_EXTENSIONS } from "./scanner.js";
|
|
6
6
|
import {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
toSnake,
|
|
13
13
|
rimrafDir,
|
|
14
14
|
logGenerationResult,
|
|
15
|
+
writeIfChanged,
|
|
15
16
|
createGeneratorResult
|
|
16
17
|
} from "../utils.js";
|
|
17
18
|
import { addAutoGeneratedComment, createGeneratedSourceFile, elementAccess, getSourceText, identifier, returnStatement } from "./ast/index.js";
|
|
@@ -170,7 +171,14 @@ function parseEntityFieldsFromFile(filePath, exportedClassNames) {
|
|
|
170
171
|
});
|
|
171
172
|
return result;
|
|
172
173
|
}
|
|
173
|
-
function
|
|
174
|
+
function writeTrackedFile(filePath, content, result) {
|
|
175
|
+
if (writeIfChanged(filePath, content)) {
|
|
176
|
+
result.filesWritten.push(filePath);
|
|
177
|
+
} else {
|
|
178
|
+
result.filesUnchanged.push(filePath);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function writePerEntityFieldFiles(outRoot, fieldsByEntity, result) {
|
|
174
182
|
fs.mkdirSync(outRoot, { recursive: true });
|
|
175
183
|
const desiredEntities = new Set(Object.keys(fieldsByEntity));
|
|
176
184
|
for (const [entity, fields] of Object.entries(fieldsByEntity)) {
|
|
@@ -189,16 +197,18 @@ function writePerEntityFieldFiles(outRoot, fieldsByEntity) {
|
|
|
189
197
|
]
|
|
190
198
|
});
|
|
191
199
|
}
|
|
192
|
-
|
|
200
|
+
writeTrackedFile(path.join(entDir, "index.ts"), getSourceText(sourceFile), result);
|
|
193
201
|
}
|
|
194
202
|
const existingEntries = fs.existsSync(outRoot) ? fs.readdirSync(outRoot, { withFileTypes: true }) : [];
|
|
195
203
|
for (const entry of existingEntries) {
|
|
196
204
|
if (!entry.isDirectory()) continue;
|
|
197
205
|
if (desiredEntities.has(entry.name)) continue;
|
|
198
|
-
|
|
206
|
+
const staleDirectory = path.join(outRoot, entry.name);
|
|
207
|
+
rimrafDir(staleDirectory);
|
|
208
|
+
result.filesWritten.push(staleDirectory);
|
|
199
209
|
}
|
|
200
210
|
}
|
|
201
|
-
function writeEntityFieldsRegistry(generatedRoot, fieldsByEntity) {
|
|
211
|
+
function writeEntityFieldsRegistry(generatedRoot, fieldsByEntity, result) {
|
|
202
212
|
const entities = Object.keys(fieldsByEntity).sort((a, b) => a.localeCompare(b));
|
|
203
213
|
const registry = {};
|
|
204
214
|
for (const entity of entities) {
|
|
@@ -229,8 +239,7 @@ function writeEntityFieldsRegistry(generatedRoot, fieldsByEntity) {
|
|
|
229
239
|
statements: [returnStatement(elementAccess(identifier("entityFieldsRegistry"), identifier("slug")))]
|
|
230
240
|
});
|
|
231
241
|
const outPath = path.join(generatedRoot, "entity-fields-registry.ts");
|
|
232
|
-
|
|
233
|
-
fs.writeFileSync(outPath, getSourceText(sourceFile));
|
|
242
|
+
writeTrackedFile(outPath, getSourceText(sourceFile), result);
|
|
234
243
|
}
|
|
235
244
|
async function generateEntityIds(options) {
|
|
236
245
|
const { resolver, quiet = false } = options;
|
|
@@ -362,9 +371,7 @@ async function generateEntityIds(options) {
|
|
|
362
371
|
type: "typeof E"
|
|
363
372
|
});
|
|
364
373
|
const src = getSourceText(groupSourceFile);
|
|
365
|
-
|
|
366
|
-
fs.writeFileSync(out, src);
|
|
367
|
-
result.filesWritten.push(out);
|
|
374
|
+
writeTrackedFile(out, src, result);
|
|
368
375
|
const fieldsRoot = path.join(pkgOutputDir, "entities");
|
|
369
376
|
const fieldsByModule = fieldsByGroup[g] || {};
|
|
370
377
|
const combined = {};
|
|
@@ -374,8 +381,8 @@ async function generateEntityIds(options) {
|
|
|
374
381
|
combined[entity] = Array.from(/* @__PURE__ */ new Set([...combined[entity] || [], ...fields]));
|
|
375
382
|
}
|
|
376
383
|
}
|
|
377
|
-
writePerEntityFieldFiles(fieldsRoot, combined);
|
|
378
|
-
writeEntityFieldsRegistry(pkgOutputDir, combined);
|
|
384
|
+
writePerEntityFieldFiles(fieldsRoot, combined, result);
|
|
385
|
+
writeEntityFieldsRegistry(pkgOutputDir, combined, result);
|
|
379
386
|
}
|
|
380
387
|
const combinedAll = {};
|
|
381
388
|
for (const groupFields of Object.values(fieldsByGroup)) {
|
|
@@ -385,8 +392,8 @@ async function generateEntityIds(options) {
|
|
|
385
392
|
}
|
|
386
393
|
}
|
|
387
394
|
}
|
|
388
|
-
writePerEntityFieldFiles(path.join(outputDir, "entities"), combinedAll);
|
|
389
|
-
writeEntityFieldsRegistry(outputDir, combinedAll);
|
|
395
|
+
writePerEntityFieldFiles(path.join(outputDir, "entities"), combinedAll, result);
|
|
396
|
+
writeEntityFieldsRegistry(outputDir, combinedAll, result);
|
|
390
397
|
return result;
|
|
391
398
|
}
|
|
392
399
|
function resolveConventionFile(baseDir, basename) {
|