yamchart 0.7.0 → 0.7.1
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/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/lineage-XSITWW2O.js +184 -0
- package/dist/lineage-XSITWW2O.js.map +1 -0
- package/dist/{sync-dbt-HGBMYKHL.js → sync-dbt-22QQKT3A.js} +14 -2
- package/dist/sync-dbt-22QQKT3A.js.map +1 -0
- package/dist/templates/default/docs/yamchart-reference.md +8 -2
- package/package.json +4 -4
- package/dist/lineage-TX33DUDP.js +0 -81
- package/dist/lineage-TX33DUDP.js.map +0 -1
- package/dist/sync-dbt-HGBMYKHL.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -123,7 +123,7 @@ program.command("init").description("Create a new yamchart project").argument("[
|
|
|
123
123
|
info(`Run \`cd ${directory === "." ? basename(targetDir) : directory} && yamchart dev\` to start.`);
|
|
124
124
|
});
|
|
125
125
|
program.command("sync-dbt").description("Sync dbt project metadata into AI-readable catalog").option("-s, --source <type>", "Source type: local, github, dbt-cloud", "local").option("-p, --path <dir>", "Path to dbt project (for local source)").option("--repo <repo>", "GitHub repository (for github source)").option("--branch <branch>", "Git branch (for github source)", "main").option("-i, --include <patterns...>", "Include glob patterns").option("-e, --exclude <patterns...>", "Exclude glob patterns").option("-t, --tag <tags...>", "Filter by dbt tags").option("--refresh", "Re-sync using saved configuration").option("--target-database <database>", "Override database in ref() paths (e.g. switch from dev to prod)").action(async (options) => {
|
|
126
|
-
const { syncDbt, loadSyncConfig } = await import("./sync-dbt-
|
|
126
|
+
const { syncDbt, loadSyncConfig } = await import("./sync-dbt-22QQKT3A.js");
|
|
127
127
|
const projectDir = await findProjectRoot(process.cwd());
|
|
128
128
|
if (!projectDir) {
|
|
129
129
|
error("yamchart.yaml not found");
|
|
@@ -160,6 +160,11 @@ program.command("sync-dbt").description("Sync dbt project metadata into AI-reada
|
|
|
160
160
|
if (result.modelsExcluded > 0) {
|
|
161
161
|
detail(`${result.modelsExcluded} models filtered out`);
|
|
162
162
|
}
|
|
163
|
+
if (result.warnings) {
|
|
164
|
+
for (const w of result.warnings) {
|
|
165
|
+
warning(w);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
163
168
|
if (!options.targetDatabase) {
|
|
164
169
|
try {
|
|
165
170
|
const { readFile } = await import("fs/promises");
|
|
@@ -458,7 +463,7 @@ program.command("lineage").description("Show upstream dependencies for a model")
|
|
|
458
463
|
process.exit(2);
|
|
459
464
|
}
|
|
460
465
|
try {
|
|
461
|
-
const { getLineage } = await import("./lineage-
|
|
466
|
+
const { getLineage } = await import("./lineage-XSITWW2O.js");
|
|
462
467
|
const depth = options.depth ? parseInt(options.depth, 10) : void 0;
|
|
463
468
|
const result = await getLineage(projectDir, model, { depth, json: options.json });
|
|
464
469
|
if (options.json) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { resolve, basename, dirname, join } from 'path';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { validateProject } from './commands/validate.js';\nimport { findProjectRoot, loadEnvFile } from './utils/config.js';\nimport pc from 'picocolors';\nimport * as output from './utils/output.js';\nimport { checkForUpdate } from './utils/update-check.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\n\nconst program = new Command();\n\nprogram\n .name('yamchart')\n .description('Git-native business intelligence dashboards')\n .version(pkg.version);\n\nprogram\n .command('validate')\n .description('Validate configuration files')\n .argument('[path]', 'Path to yamchart project', '.')\n .option('--dry-run', 'Connect to database and test queries with EXPLAIN')\n .option('-c, --connection <name>', 'Connection to use for dry-run')\n .option('--json', 'Output as JSON')\n .action(async (path: string, options: { dryRun?: boolean; connection?: string; json?: boolean }) => {\n const startPath = resolve(path);\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: 'yamchart.yaml not found' }));\n } else {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n }\n process.exit(2);\n }\n\n // Load .env file\n loadEnvFile(projectDir);\n\n if (!options.json) {\n output.header('Validating yamchart project...');\n }\n\n const result = await validateProject(projectDir, {\n dryRun: options.dryRun ?? false,\n connection: options.connection,\n });\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n // Print results\n for (const error of result.errors) {\n output.error(error.file);\n output.detail(error.message);\n if (error.suggestion) {\n output.detail(error.suggestion);\n }\n }\n\n for (const warning of result.warnings) {\n output.warning(warning.file);\n output.detail(warning.message);\n }\n\n output.newline();\n\n if (result.success) {\n output.success(`Schema: ${result.stats.passed} passed`);\n } else {\n output.error(`Schema: ${result.stats.passed} passed, ${result.stats.failed} failed`);\n }\n\n if (result.dryRunStats) {\n output.newline();\n if (result.dryRunStats.failed === 0) {\n output.success(`Queries: ${result.dryRunStats.passed} passed (EXPLAIN OK)`);\n } else {\n output.error(`Queries: ${result.dryRunStats.passed} passed, ${result.dryRunStats.failed} failed`);\n }\n }\n\n output.newline();\n\n if (result.success) {\n output.success('Validation passed');\n } else {\n output.error(`Validation failed with ${result.errors.length} error(s)`);\n }\n }\n\n process.exit(result.success ? 0 : 1);\n });\n\nprogram\n .command('dev')\n .description('Start development server with hot reload')\n .argument('[path]', 'Path to yamchart project', '.')\n .option('-p, --port <number>', 'Port to listen on', '3001')\n .option('--api-only', 'Only serve API, no web UI')\n .option('--no-open', 'Do not open browser automatically')\n .action(async (path: string, options: { port: string; apiOnly?: boolean; open: boolean }) => {\n const startPath = resolve(path);\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n const { runDevServer } = await import('./commands/dev.js');\n\n await runDevServer(projectDir, {\n port: parseInt(options.port, 10),\n apiOnly: options.apiOnly ?? false,\n open: options.open,\n });\n });\n\nprogram\n .command('init')\n .description('Create a new yamchart project')\n .argument('[directory]', 'Target directory', '.')\n .option('--example', 'Create full example project with sample database')\n .option('--empty', 'Create only yamchart.yaml (no connections, models, or charts)')\n .option('--force', 'Overwrite existing files')\n .action(async (directory: string, options: { example?: boolean; empty?: boolean; force?: boolean }) => {\n const { initProject } = await import('./commands/init.js');\n const targetDir = resolve(directory);\n\n const result = await initProject(targetDir, options);\n\n if (!result.success) {\n output.error(result.error || 'Failed to create project');\n process.exit(1);\n }\n\n output.newline();\n output.success(`Created ${directory === '.' ? basename(targetDir) : directory}/`);\n for (const file of result.files.slice(0, 10)) {\n output.detail(file);\n }\n if (result.files.length > 10) {\n output.detail(`... and ${result.files.length - 10} more files`);\n }\n output.newline();\n output.info(`Run \\`cd ${directory === '.' ? basename(targetDir) : directory} && yamchart dev\\` to start.`);\n });\n\nprogram\n .command('sync-dbt')\n .description('Sync dbt project metadata into AI-readable catalog')\n .option('-s, --source <type>', 'Source type: local, github, dbt-cloud', 'local')\n .option('-p, --path <dir>', 'Path to dbt project (for local source)')\n .option('--repo <repo>', 'GitHub repository (for github source)')\n .option('--branch <branch>', 'Git branch (for github source)', 'main')\n .option('-i, --include <patterns...>', 'Include glob patterns')\n .option('-e, --exclude <patterns...>', 'Exclude glob patterns')\n .option('-t, --tag <tags...>', 'Filter by dbt tags')\n .option('--refresh', 'Re-sync using saved configuration')\n .option('--target-database <database>', 'Override database in ref() paths (e.g. switch from dev to prod)')\n .action(async (options: {\n source: 'local' | 'github' | 'dbt-cloud';\n path?: string;\n repo?: string;\n branch?: string;\n include?: string[];\n exclude?: string[];\n tag?: string[];\n refresh?: boolean;\n targetDatabase?: string;\n }) => {\n const { syncDbt, loadSyncConfig } = await import('./commands/sync-dbt.js');\n\n // Find project root\n const projectDir = await findProjectRoot(process.cwd());\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n // Handle refresh mode\n if (options.refresh) {\n const savedConfig = await loadSyncConfig(projectDir);\n if (!savedConfig) {\n output.error('No saved sync config found');\n output.detail('Run sync-dbt without --refresh first');\n process.exit(1);\n }\n output.info(`Re-syncing from ${savedConfig.source}:${savedConfig.path || savedConfig.repo}`);\n }\n\n const spin = output.spinner('Syncing dbt metadata...');\n\n const result = await syncDbt(projectDir, {\n source: options.source,\n path: options.path,\n repo: options.repo,\n branch: options.branch,\n include: options.include || [],\n exclude: options.exclude || [],\n tags: options.tag || [],\n refresh: options.refresh,\n targetDatabase: options.targetDatabase,\n });\n\n spin.stop();\n\n if (!result.success) {\n output.error(result.error || 'Sync failed');\n process.exit(1);\n }\n\n output.success(`Synced ${result.modelsIncluded} models to .yamchart/catalog.md`);\n if (result.modelsExcluded > 0) {\n output.detail(`${result.modelsExcluded} models filtered out`);\n }\n\n // Check for database mismatch (skip if --target-database was used)\n if (!options.targetDatabase) {\n try {\n const { readFile } = await import('fs/promises');\n const { resolveConnection } = await import('./commands/connection-utils.js');\n const { detectDatabaseMismatch } = await import('./dbt/rewrite-database.js');\n const catalogJsonStr = await readFile(join(projectDir, '.yamchart', 'catalog.json'), 'utf-8');\n const catalogData = JSON.parse(catalogJsonStr);\n const connection = await resolveConnection(projectDir);\n // Extract database from connection config (varies by type)\n const connDb = (connection as Record<string, any>).config?.database as string | undefined;\n const mismatch = detectDatabaseMismatch(catalogData.models, connDb);\n if (mismatch.mismatch) {\n output.warning(\n `Catalog tables reference database \"${mismatch.catalogDatabase}\" but your default ` +\n `connection uses \"${mismatch.connectionDatabase}\". Models using ref() may query the wrong database.`\n );\n output.detail(\n `To fix: yamchart sync-dbt --refresh --target-database ${mismatch.connectionDatabase}`\n );\n }\n } catch {\n // Connection not configured or unreadable — skip silently\n }\n }\n });\n\nprogram\n .command('generate')\n .description('Generate SQL model stubs from dbt catalog')\n .argument('[model]', 'Specific model to generate (optional)')\n .option('--yolo', 'Skip all prompts, use defaults for everything')\n .action(async (model: string | undefined, options: { yolo?: boolean }) => {\n const { generate } = await import('./commands/generate.js');\n\n const projectDir = await findProjectRoot(process.cwd());\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n const result = await generate(projectDir, {\n model,\n yolo: options.yolo,\n });\n\n if (!result.success) {\n output.error(result.error || 'Generate failed');\n process.exit(1);\n }\n\n output.success(`Generated ${result.filesCreated} model stubs`);\n if (result.filesSkipped > 0) {\n output.detail(`${result.filesSkipped} files skipped`);\n }\n });\n\nprogram\n .command('test')\n .description('Run model tests (@returns schema checks and @tests data assertions)')\n .argument('[model]', 'Specific model to test (optional, tests all if omitted)')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .action(async (model: string | undefined, options: { connection?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: 'yamchart.yaml not found' }));\n } else {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n }\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { testProject, formatTestOutput } = await import('./commands/test.js');\n const result = await testProject(projectDir, model, {\n connection: options.connection,\n json: options.json,\n });\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n formatTestOutput(result, result.connectionName);\n }\n\n process.exit(result.success ? 0 : 1);\n } catch (err) {\n if (options.json) {\n console.log(\n JSON.stringify({ success: false, error: err instanceof Error ? err.message : String(err) }),\n );\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exit(2);\n }\n });\n\nprogram\n .command('update')\n .description('Check for yamchart updates')\n .action(async () => {\n const { runUpdate } = await import('./commands/update.js');\n await runUpdate(pkg.version);\n });\n\nprogram\n .command('reset-password')\n .description('Reset a user password (requires auth to be enabled)')\n .requiredOption('-e, --email <email>', 'Email address of the user')\n .action(async (options: { email: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n const { resetPassword } = await import('./commands/reset-password.js');\n await resetPassword(projectDir, options.email);\n });\n\nprogram\n .command('tables')\n .description('List tables and views in the connected database')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('-s, --schema <name>', 'Filter to schema')\n .option('-d, --database <name>', 'Filter to database (Snowflake/Databricks)')\n .option('--json', 'Output as JSON')\n .action(async (options: { connection?: string; schema?: string; database?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { listTables } = await import('./commands/tables.js');\n const result = await listTables(projectDir, options);\n\n if (options.json) {\n console.log(JSON.stringify(result.tables, null, 2));\n } else {\n output.header(`Tables in ${result.connectionName} (${result.connectionType})`);\n if (result.tables.length === 0) {\n output.info('No tables found');\n } else {\n for (const table of result.tables) {\n const schema = table.schema ? `${table.schema}.` : '';\n const typeLabel = table.type === 'VIEW' ? pc.dim(' (view)') : '';\n console.log(` ${schema}${table.name}${typeLabel}`);\n }\n output.newline();\n output.info(`${result.tables.length} table(s) found (${result.durationMs.toFixed(0)}ms)`);\n }\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('describe')\n .description('Show columns and types for a table')\n .argument('<table>', 'Table name (can be fully qualified, e.g. schema.table)')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .action(async (table: string, options: { connection?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { describeTable } = await import('./commands/describe.js');\n const result = await describeTable(projectDir, table, options);\n\n if (options.json) {\n console.log(JSON.stringify(result.columns, null, 2));\n } else {\n if (result.resolvedFrom) {\n output.detail(`Resolved \"${result.resolvedFrom}\" → ${result.table}`);\n }\n output.header(`${result.table} (${result.connectionName})`);\n\n const nameWidth = Math.max(4, ...result.columns.map((c) => c.name.length));\n const typeWidth = Math.max(4, ...result.columns.map((c) => c.type.length));\n\n console.log(` ${'name'.padEnd(nameWidth)} ${'type'.padEnd(typeWidth)} nullable`);\n console.log(` ${'─'.repeat(nameWidth)} ${'─'.repeat(typeWidth)} ${'─'.repeat(8)}`);\n\n for (const col of result.columns) {\n const nullable = col.nullable === 'YES' ? pc.dim('yes') : 'no';\n console.log(` ${col.name.padEnd(nameWidth)} ${pc.dim(col.type.padEnd(typeWidth))} ${nullable}`);\n }\n\n output.newline();\n output.info(`${result.columns.length} column(s) (${result.durationMs.toFixed(0)}ms)`);\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('query')\n .description('Execute SQL against a connection')\n .argument('<sql>', 'SQL query to execute')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .option('-l, --limit <number>', 'Max rows to return (default: 100)')\n .action(async (sql: string, options: { connection?: string; json?: boolean; limit?: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { executeQuery } = await import('./commands/query.js');\n const { formatTable, formatJSON } = await import('./commands/connection-utils.js');\n\n const limit = options.limit ? parseInt(options.limit, 10) : undefined;\n const result = await executeQuery(projectDir, sql, {\n connection: options.connection,\n json: options.json,\n limit,\n });\n\n if (options.json) {\n console.log(formatJSON(result));\n } else {\n console.log(formatTable(result));\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('sample')\n .description('Show sample rows from a table or dbt model')\n .argument('<table>', 'Table name or dbt model name')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('-l, --limit <number>', 'Rows to show (default: 5)')\n .option('--json', 'Output as JSON')\n .action(async (table: string, options: { connection?: string; json?: boolean; limit?: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { sampleTable } = await import('./commands/sample.js');\n const { formatTable, formatJSON } = await import('./commands/connection-utils.js');\n\n const limit = options.limit ? parseInt(options.limit, 10) : undefined;\n const result = await sampleTable(projectDir, table, {\n connection: options.connection,\n json: options.json,\n limit,\n });\n\n if (options.json) {\n console.log(formatJSON(result));\n } else {\n if (result.resolvedFrom) {\n output.detail(`Resolved \"${result.resolvedFrom}\" → ${result.table}`);\n }\n console.log(formatTable(result));\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('search')\n .description('Search for tables and columns by keyword')\n .argument('<keyword>', 'Keyword to search for')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .action(async (keyword: string, options: { connection?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { searchDatabase } = await import('./commands/search.js');\n const result = await searchDatabase(projectDir, keyword, options);\n\n if (options.json) {\n console.log(JSON.stringify(result.results, null, 2));\n } else {\n output.header(`Search results for \"${result.keyword}\" in ${result.connectionName} (${result.connectionType})`);\n\n const tables = result.results.filter((r) => r.type === 'table');\n const columns = result.results.filter((r) => r.type === 'column');\n\n if (tables.length > 0) {\n output.newline();\n console.log(' Tables:');\n for (const t of tables) {\n const qualified = t.schema ? `${t.schema}.${t.table}` : t.table;\n console.log(` ${qualified}`);\n }\n }\n\n if (columns.length > 0) {\n output.newline();\n console.log(' Columns:');\n const nameWidth = Math.max(...columns.map((c) => {\n const qualified = c.schema ? `${c.schema}.${c.table}.${c.column}` : `${c.table}.${c.column}`;\n return qualified.length;\n }));\n for (const c of columns) {\n const qualified = c.schema ? `${c.schema}.${c.table}.${c.column}` : `${c.table}.${c.column}`;\n const colType = c.columnType ? pc.dim(c.columnType) : '';\n console.log(` ${qualified.padEnd(nameWidth + 2)}${colType}`);\n }\n }\n\n if (result.results.length === 0) {\n output.detail('No matches found');\n }\n\n output.newline();\n output.info(`${result.results.length} result(s) (${result.durationMs.toFixed(0)}ms)`);\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('sync-warehouse')\n .description('Sync warehouse table metadata into the catalog')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('-s, --schema <schemas>', 'Comma-separated schemas to sync')\n .option('-d, --database <name>', 'Database to sync (Snowflake/Databricks)')\n .option('--skip-samples', 'Skip sample row collection')\n .option('--refresh', 'Re-run with saved connection/schema config')\n .option('--full', 'Force full re-sync (ignore incremental state)')\n .option('--json', 'Output sync summary as JSON')\n .action(async (options) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { runSyncWarehouse } = await import('./commands/sync-warehouse.js');\n await runSyncWarehouse(projectDir, options);\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('lineage')\n .description('Show upstream dependencies for a model')\n .argument('<model>', 'Model name to trace lineage for')\n .option('--depth <n>', 'Maximum depth to trace (default: unlimited)')\n .option('--json', 'Output as JSON')\n .action(async (model: string, options: { depth?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n try {\n const { getLineage } = await import('./commands/lineage.js');\n const depth = options.depth ? parseInt(options.depth, 10) : undefined;\n const result = await getLineage(projectDir, model, { depth, json: options.json });\n\n if (options.json) {\n console.log(JSON.stringify(result.tree, null, 2));\n } else {\n console.log(result.rendered);\n }\n } catch (err) {\n if (err instanceof Error && err.message.includes('ENOENT')) {\n output.error('No catalog found. Run `yamchart sync-dbt` first.');\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exit(1);\n }\n });\n\nprogram\n .command('advisor')\n .description('AI-powered dbt model advisor — suggests improvements informed by your BI layer')\n .argument('[question]', 'Question to ask, or \"audit\" for comprehensive analysis')\n .option('--top <n>', 'Limit audit suggestions (default: 5)')\n .option('--json', 'Output as JSON')\n .option('--dbt-path <path>', 'Override the synced dbt project path')\n .option('-c, --connection <name>', 'Connection to use for warehouse introspection')\n .action(async (question: string | undefined, options: { top?: string; json?: boolean; dbtPath?: string; connection?: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: 'yamchart.yaml not found' }));\n } else {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n }\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n const { runAdvisor } = await import('./commands/advisor.js');\n await runAdvisor(projectDir, question, {\n top: options.top ? parseInt(options.top, 10) : 5,\n json: options.json,\n dbtPath: options.dbtPath,\n connection: options.connection,\n });\n });\n\nprogram.parse();\n\n// Passive update check — runs in background, prints on exit if outdated\nlet updateNotification: string | null = null;\n\ncheckForUpdate(pkg.version).then((update) => {\n if (update) {\n updateNotification = `\\n Update available: ${update.current} → ${update.latest}\\n Run: yamchart update to see what's new\\n`;\n }\n});\n\nprocess.on('exit', () => {\n if (updateNotification) {\n // Use dim styling so it doesn't compete with command output\n console.error(`\\n${pc.dim(updateNotification)}`);\n }\n});\n"],"mappings":"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","names":["error","warning"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { resolve, basename, dirname, join } from 'path';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { validateProject } from './commands/validate.js';\nimport { findProjectRoot, loadEnvFile } from './utils/config.js';\nimport pc from 'picocolors';\nimport * as output from './utils/output.js';\nimport { checkForUpdate } from './utils/update-check.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\n\nconst program = new Command();\n\nprogram\n .name('yamchart')\n .description('Git-native business intelligence dashboards')\n .version(pkg.version);\n\nprogram\n .command('validate')\n .description('Validate configuration files')\n .argument('[path]', 'Path to yamchart project', '.')\n .option('--dry-run', 'Connect to database and test queries with EXPLAIN')\n .option('-c, --connection <name>', 'Connection to use for dry-run')\n .option('--json', 'Output as JSON')\n .action(async (path: string, options: { dryRun?: boolean; connection?: string; json?: boolean }) => {\n const startPath = resolve(path);\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: 'yamchart.yaml not found' }));\n } else {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n }\n process.exit(2);\n }\n\n // Load .env file\n loadEnvFile(projectDir);\n\n if (!options.json) {\n output.header('Validating yamchart project...');\n }\n\n const result = await validateProject(projectDir, {\n dryRun: options.dryRun ?? false,\n connection: options.connection,\n });\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n // Print results\n for (const error of result.errors) {\n output.error(error.file);\n output.detail(error.message);\n if (error.suggestion) {\n output.detail(error.suggestion);\n }\n }\n\n for (const warning of result.warnings) {\n output.warning(warning.file);\n output.detail(warning.message);\n }\n\n output.newline();\n\n if (result.success) {\n output.success(`Schema: ${result.stats.passed} passed`);\n } else {\n output.error(`Schema: ${result.stats.passed} passed, ${result.stats.failed} failed`);\n }\n\n if (result.dryRunStats) {\n output.newline();\n if (result.dryRunStats.failed === 0) {\n output.success(`Queries: ${result.dryRunStats.passed} passed (EXPLAIN OK)`);\n } else {\n output.error(`Queries: ${result.dryRunStats.passed} passed, ${result.dryRunStats.failed} failed`);\n }\n }\n\n output.newline();\n\n if (result.success) {\n output.success('Validation passed');\n } else {\n output.error(`Validation failed with ${result.errors.length} error(s)`);\n }\n }\n\n process.exit(result.success ? 0 : 1);\n });\n\nprogram\n .command('dev')\n .description('Start development server with hot reload')\n .argument('[path]', 'Path to yamchart project', '.')\n .option('-p, --port <number>', 'Port to listen on', '3001')\n .option('--api-only', 'Only serve API, no web UI')\n .option('--no-open', 'Do not open browser automatically')\n .action(async (path: string, options: { port: string; apiOnly?: boolean; open: boolean }) => {\n const startPath = resolve(path);\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n const { runDevServer } = await import('./commands/dev.js');\n\n await runDevServer(projectDir, {\n port: parseInt(options.port, 10),\n apiOnly: options.apiOnly ?? false,\n open: options.open,\n });\n });\n\nprogram\n .command('init')\n .description('Create a new yamchart project')\n .argument('[directory]', 'Target directory', '.')\n .option('--example', 'Create full example project with sample database')\n .option('--empty', 'Create only yamchart.yaml (no connections, models, or charts)')\n .option('--force', 'Overwrite existing files')\n .action(async (directory: string, options: { example?: boolean; empty?: boolean; force?: boolean }) => {\n const { initProject } = await import('./commands/init.js');\n const targetDir = resolve(directory);\n\n const result = await initProject(targetDir, options);\n\n if (!result.success) {\n output.error(result.error || 'Failed to create project');\n process.exit(1);\n }\n\n output.newline();\n output.success(`Created ${directory === '.' ? basename(targetDir) : directory}/`);\n for (const file of result.files.slice(0, 10)) {\n output.detail(file);\n }\n if (result.files.length > 10) {\n output.detail(`... and ${result.files.length - 10} more files`);\n }\n output.newline();\n output.info(`Run \\`cd ${directory === '.' ? basename(targetDir) : directory} && yamchart dev\\` to start.`);\n });\n\nprogram\n .command('sync-dbt')\n .description('Sync dbt project metadata into AI-readable catalog')\n .option('-s, --source <type>', 'Source type: local, github, dbt-cloud', 'local')\n .option('-p, --path <dir>', 'Path to dbt project (for local source)')\n .option('--repo <repo>', 'GitHub repository (for github source)')\n .option('--branch <branch>', 'Git branch (for github source)', 'main')\n .option('-i, --include <patterns...>', 'Include glob patterns')\n .option('-e, --exclude <patterns...>', 'Exclude glob patterns')\n .option('-t, --tag <tags...>', 'Filter by dbt tags')\n .option('--refresh', 'Re-sync using saved configuration')\n .option('--target-database <database>', 'Override database in ref() paths (e.g. switch from dev to prod)')\n .action(async (options: {\n source: 'local' | 'github' | 'dbt-cloud';\n path?: string;\n repo?: string;\n branch?: string;\n include?: string[];\n exclude?: string[];\n tag?: string[];\n refresh?: boolean;\n targetDatabase?: string;\n }) => {\n const { syncDbt, loadSyncConfig } = await import('./commands/sync-dbt.js');\n\n // Find project root\n const projectDir = await findProjectRoot(process.cwd());\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n // Handle refresh mode\n if (options.refresh) {\n const savedConfig = await loadSyncConfig(projectDir);\n if (!savedConfig) {\n output.error('No saved sync config found');\n output.detail('Run sync-dbt without --refresh first');\n process.exit(1);\n }\n output.info(`Re-syncing from ${savedConfig.source}:${savedConfig.path || savedConfig.repo}`);\n }\n\n const spin = output.spinner('Syncing dbt metadata...');\n\n const result = await syncDbt(projectDir, {\n source: options.source,\n path: options.path,\n repo: options.repo,\n branch: options.branch,\n include: options.include || [],\n exclude: options.exclude || [],\n tags: options.tag || [],\n refresh: options.refresh,\n targetDatabase: options.targetDatabase,\n });\n\n spin.stop();\n\n if (!result.success) {\n output.error(result.error || 'Sync failed');\n process.exit(1);\n }\n\n output.success(`Synced ${result.modelsIncluded} models to .yamchart/catalog.md`);\n if (result.modelsExcluded > 0) {\n output.detail(`${result.modelsExcluded} models filtered out`);\n }\n\n // Show lineage warnings\n if (result.warnings) {\n for (const w of result.warnings) {\n output.warning(w);\n }\n }\n\n // Check for database mismatch (skip if --target-database was used)\n if (!options.targetDatabase) {\n try {\n const { readFile } = await import('fs/promises');\n const { resolveConnection } = await import('./commands/connection-utils.js');\n const { detectDatabaseMismatch } = await import('./dbt/rewrite-database.js');\n const catalogJsonStr = await readFile(join(projectDir, '.yamchart', 'catalog.json'), 'utf-8');\n const catalogData = JSON.parse(catalogJsonStr);\n const connection = await resolveConnection(projectDir);\n // Extract database from connection config (varies by type)\n const connDb = (connection as Record<string, any>).config?.database as string | undefined;\n const mismatch = detectDatabaseMismatch(catalogData.models, connDb);\n if (mismatch.mismatch) {\n output.warning(\n `Catalog tables reference database \"${mismatch.catalogDatabase}\" but your default ` +\n `connection uses \"${mismatch.connectionDatabase}\". Models using ref() may query the wrong database.`\n );\n output.detail(\n `To fix: yamchart sync-dbt --refresh --target-database ${mismatch.connectionDatabase}`\n );\n }\n } catch {\n // Connection not configured or unreadable — skip silently\n }\n }\n });\n\nprogram\n .command('generate')\n .description('Generate SQL model stubs from dbt catalog')\n .argument('[model]', 'Specific model to generate (optional)')\n .option('--yolo', 'Skip all prompts, use defaults for everything')\n .action(async (model: string | undefined, options: { yolo?: boolean }) => {\n const { generate } = await import('./commands/generate.js');\n\n const projectDir = await findProjectRoot(process.cwd());\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n const result = await generate(projectDir, {\n model,\n yolo: options.yolo,\n });\n\n if (!result.success) {\n output.error(result.error || 'Generate failed');\n process.exit(1);\n }\n\n output.success(`Generated ${result.filesCreated} model stubs`);\n if (result.filesSkipped > 0) {\n output.detail(`${result.filesSkipped} files skipped`);\n }\n });\n\nprogram\n .command('test')\n .description('Run model tests (@returns schema checks and @tests data assertions)')\n .argument('[model]', 'Specific model to test (optional, tests all if omitted)')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .action(async (model: string | undefined, options: { connection?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: 'yamchart.yaml not found' }));\n } else {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n }\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { testProject, formatTestOutput } = await import('./commands/test.js');\n const result = await testProject(projectDir, model, {\n connection: options.connection,\n json: options.json,\n });\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n formatTestOutput(result, result.connectionName);\n }\n\n process.exit(result.success ? 0 : 1);\n } catch (err) {\n if (options.json) {\n console.log(\n JSON.stringify({ success: false, error: err instanceof Error ? err.message : String(err) }),\n );\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exit(2);\n }\n });\n\nprogram\n .command('update')\n .description('Check for yamchart updates')\n .action(async () => {\n const { runUpdate } = await import('./commands/update.js');\n await runUpdate(pkg.version);\n });\n\nprogram\n .command('reset-password')\n .description('Reset a user password (requires auth to be enabled)')\n .requiredOption('-e, --email <email>', 'Email address of the user')\n .action(async (options: { email: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n const { resetPassword } = await import('./commands/reset-password.js');\n await resetPassword(projectDir, options.email);\n });\n\nprogram\n .command('tables')\n .description('List tables and views in the connected database')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('-s, --schema <name>', 'Filter to schema')\n .option('-d, --database <name>', 'Filter to database (Snowflake/Databricks)')\n .option('--json', 'Output as JSON')\n .action(async (options: { connection?: string; schema?: string; database?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { listTables } = await import('./commands/tables.js');\n const result = await listTables(projectDir, options);\n\n if (options.json) {\n console.log(JSON.stringify(result.tables, null, 2));\n } else {\n output.header(`Tables in ${result.connectionName} (${result.connectionType})`);\n if (result.tables.length === 0) {\n output.info('No tables found');\n } else {\n for (const table of result.tables) {\n const schema = table.schema ? `${table.schema}.` : '';\n const typeLabel = table.type === 'VIEW' ? pc.dim(' (view)') : '';\n console.log(` ${schema}${table.name}${typeLabel}`);\n }\n output.newline();\n output.info(`${result.tables.length} table(s) found (${result.durationMs.toFixed(0)}ms)`);\n }\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('describe')\n .description('Show columns and types for a table')\n .argument('<table>', 'Table name (can be fully qualified, e.g. schema.table)')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .action(async (table: string, options: { connection?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { describeTable } = await import('./commands/describe.js');\n const result = await describeTable(projectDir, table, options);\n\n if (options.json) {\n console.log(JSON.stringify(result.columns, null, 2));\n } else {\n if (result.resolvedFrom) {\n output.detail(`Resolved \"${result.resolvedFrom}\" → ${result.table}`);\n }\n output.header(`${result.table} (${result.connectionName})`);\n\n const nameWidth = Math.max(4, ...result.columns.map((c) => c.name.length));\n const typeWidth = Math.max(4, ...result.columns.map((c) => c.type.length));\n\n console.log(` ${'name'.padEnd(nameWidth)} ${'type'.padEnd(typeWidth)} nullable`);\n console.log(` ${'─'.repeat(nameWidth)} ${'─'.repeat(typeWidth)} ${'─'.repeat(8)}`);\n\n for (const col of result.columns) {\n const nullable = col.nullable === 'YES' ? pc.dim('yes') : 'no';\n console.log(` ${col.name.padEnd(nameWidth)} ${pc.dim(col.type.padEnd(typeWidth))} ${nullable}`);\n }\n\n output.newline();\n output.info(`${result.columns.length} column(s) (${result.durationMs.toFixed(0)}ms)`);\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('query')\n .description('Execute SQL against a connection')\n .argument('<sql>', 'SQL query to execute')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .option('-l, --limit <number>', 'Max rows to return (default: 100)')\n .action(async (sql: string, options: { connection?: string; json?: boolean; limit?: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { executeQuery } = await import('./commands/query.js');\n const { formatTable, formatJSON } = await import('./commands/connection-utils.js');\n\n const limit = options.limit ? parseInt(options.limit, 10) : undefined;\n const result = await executeQuery(projectDir, sql, {\n connection: options.connection,\n json: options.json,\n limit,\n });\n\n if (options.json) {\n console.log(formatJSON(result));\n } else {\n console.log(formatTable(result));\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('sample')\n .description('Show sample rows from a table or dbt model')\n .argument('<table>', 'Table name or dbt model name')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('-l, --limit <number>', 'Rows to show (default: 5)')\n .option('--json', 'Output as JSON')\n .action(async (table: string, options: { connection?: string; json?: boolean; limit?: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { sampleTable } = await import('./commands/sample.js');\n const { formatTable, formatJSON } = await import('./commands/connection-utils.js');\n\n const limit = options.limit ? parseInt(options.limit, 10) : undefined;\n const result = await sampleTable(projectDir, table, {\n connection: options.connection,\n json: options.json,\n limit,\n });\n\n if (options.json) {\n console.log(formatJSON(result));\n } else {\n if (result.resolvedFrom) {\n output.detail(`Resolved \"${result.resolvedFrom}\" → ${result.table}`);\n }\n console.log(formatTable(result));\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('search')\n .description('Search for tables and columns by keyword')\n .argument('<keyword>', 'Keyword to search for')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('--json', 'Output as JSON')\n .action(async (keyword: string, options: { connection?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { searchDatabase } = await import('./commands/search.js');\n const result = await searchDatabase(projectDir, keyword, options);\n\n if (options.json) {\n console.log(JSON.stringify(result.results, null, 2));\n } else {\n output.header(`Search results for \"${result.keyword}\" in ${result.connectionName} (${result.connectionType})`);\n\n const tables = result.results.filter((r) => r.type === 'table');\n const columns = result.results.filter((r) => r.type === 'column');\n\n if (tables.length > 0) {\n output.newline();\n console.log(' Tables:');\n for (const t of tables) {\n const qualified = t.schema ? `${t.schema}.${t.table}` : t.table;\n console.log(` ${qualified}`);\n }\n }\n\n if (columns.length > 0) {\n output.newline();\n console.log(' Columns:');\n const nameWidth = Math.max(...columns.map((c) => {\n const qualified = c.schema ? `${c.schema}.${c.table}.${c.column}` : `${c.table}.${c.column}`;\n return qualified.length;\n }));\n for (const c of columns) {\n const qualified = c.schema ? `${c.schema}.${c.table}.${c.column}` : `${c.table}.${c.column}`;\n const colType = c.columnType ? pc.dim(c.columnType) : '';\n console.log(` ${qualified.padEnd(nameWidth + 2)}${colType}`);\n }\n }\n\n if (result.results.length === 0) {\n output.detail('No matches found');\n }\n\n output.newline();\n output.info(`${result.results.length} result(s) (${result.durationMs.toFixed(0)}ms)`);\n }\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('sync-warehouse')\n .description('Sync warehouse table metadata into the catalog')\n .option('-c, --connection <name>', 'Connection to use (overrides default)')\n .option('-s, --schema <schemas>', 'Comma-separated schemas to sync')\n .option('-d, --database <name>', 'Database to sync (Snowflake/Databricks)')\n .option('--skip-samples', 'Skip sample row collection')\n .option('--refresh', 'Re-run with saved connection/schema config')\n .option('--full', 'Force full re-sync (ignore incremental state)')\n .option('--json', 'Output sync summary as JSON')\n .action(async (options) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n try {\n const { runSyncWarehouse } = await import('./commands/sync-warehouse.js');\n await runSyncWarehouse(projectDir, options);\n } catch (err) {\n output.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('lineage')\n .description('Show upstream dependencies for a model')\n .argument('<model>', 'Model name to trace lineage for')\n .option('--depth <n>', 'Maximum depth to trace (default: unlimited)')\n .option('--json', 'Output as JSON')\n .action(async (model: string, options: { depth?: string; json?: boolean }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n process.exit(2);\n }\n\n try {\n const { getLineage } = await import('./commands/lineage.js');\n const depth = options.depth ? parseInt(options.depth, 10) : undefined;\n const result = await getLineage(projectDir, model, { depth, json: options.json });\n\n if (options.json) {\n console.log(JSON.stringify(result.tree, null, 2));\n } else {\n console.log(result.rendered);\n }\n } catch (err) {\n if (err instanceof Error && err.message.includes('ENOENT')) {\n output.error('No catalog found. Run `yamchart sync-dbt` first.');\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exit(1);\n }\n });\n\nprogram\n .command('advisor')\n .description('AI-powered dbt model advisor — suggests improvements informed by your BI layer')\n .argument('[question]', 'Question to ask, or \"audit\" for comprehensive analysis')\n .option('--top <n>', 'Limit audit suggestions (default: 5)')\n .option('--json', 'Output as JSON')\n .option('--dbt-path <path>', 'Override the synced dbt project path')\n .option('-c, --connection <name>', 'Connection to use for warehouse introspection')\n .action(async (question: string | undefined, options: { top?: string; json?: boolean; dbtPath?: string; connection?: string }) => {\n const startPath = resolve('.');\n const projectDir = await findProjectRoot(startPath);\n\n if (!projectDir) {\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: 'yamchart.yaml not found' }));\n } else {\n output.error('yamchart.yaml not found');\n output.detail('Run this command from a yamchart project directory');\n }\n process.exit(2);\n }\n\n loadEnvFile(projectDir);\n\n const { runAdvisor } = await import('./commands/advisor.js');\n await runAdvisor(projectDir, question, {\n top: options.top ? parseInt(options.top, 10) : 5,\n json: options.json,\n dbtPath: options.dbtPath,\n connection: options.connection,\n });\n });\n\nprogram.parse();\n\n// Passive update check — runs in background, prints on exit if outdated\nlet updateNotification: string | null = null;\n\ncheckForUpdate(pkg.version).then((update) => {\n if (update) {\n updateNotification = `\\n Update available: ${update.current} → ${update.latest}\\n Run: yamchart update to see what's new\\n`;\n }\n});\n\nprocess.on('exit', () => {\n if (updateNotification) {\n // Use dim styling so it doesn't compete with command output\n console.error(`\\n${pc.dim(updateNotification)}`);\n }\n});\n"],"mappings":"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","names":["error","warning"]}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/lineage.ts
|
|
4
|
+
import { readFile, readdir, access } from "fs/promises";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
function resolveNodeKey(key) {
|
|
7
|
+
const parts = key.split(".");
|
|
8
|
+
if (parts[0] === "source" && parts.length >= 4) {
|
|
9
|
+
return parts.slice(2).join(".");
|
|
10
|
+
}
|
|
11
|
+
return parts[parts.length - 1];
|
|
12
|
+
}
|
|
13
|
+
function extractSqlDependencies(sql) {
|
|
14
|
+
const refs = [];
|
|
15
|
+
const sources = [];
|
|
16
|
+
const tables = [];
|
|
17
|
+
const refRegex = /\{\{\s*ref\(['"]([^'"]+)['"]\)\s*\}\}/g;
|
|
18
|
+
let match;
|
|
19
|
+
while ((match = refRegex.exec(sql)) !== null) {
|
|
20
|
+
if (match[1]) refs.push(match[1]);
|
|
21
|
+
}
|
|
22
|
+
const sourceRegex = /--\s*@source:\s*(.+)/g;
|
|
23
|
+
while ((match = sourceRegex.exec(sql)) !== null) {
|
|
24
|
+
if (match[1]) sources.push(match[1].trim());
|
|
25
|
+
}
|
|
26
|
+
const cleaned = sql.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\{\{[\s\S]*?\}\}/g, "").replace(/\{%[\s\S]*?%\}/g, "");
|
|
27
|
+
const tableRegex = /\b(?:FROM|JOIN)\s+([\w]+(?:\.[\w]+){1,2})/gi;
|
|
28
|
+
while ((match = tableRegex.exec(cleaned)) !== null) {
|
|
29
|
+
if (match[1]) tables.push(match[1]);
|
|
30
|
+
}
|
|
31
|
+
return { refs, sources, tables };
|
|
32
|
+
}
|
|
33
|
+
function resolveDependencies(deps, catalogModels) {
|
|
34
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
35
|
+
const byName = /* @__PURE__ */ new Map();
|
|
36
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const model of catalogModels) {
|
|
38
|
+
byName.set(model.name.toLowerCase(), model.name);
|
|
39
|
+
if (model.table) {
|
|
40
|
+
byTable.set(model.table.toUpperCase(), model.name);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const ref of deps.refs) {
|
|
44
|
+
const nameMatch = byName.get(ref.toLowerCase());
|
|
45
|
+
if (nameMatch) resolved.add(nameMatch);
|
|
46
|
+
}
|
|
47
|
+
for (const source of deps.sources) {
|
|
48
|
+
const nameMatch = byName.get(source.toLowerCase());
|
|
49
|
+
if (nameMatch) resolved.add(nameMatch);
|
|
50
|
+
}
|
|
51
|
+
for (const table of deps.tables) {
|
|
52
|
+
const upper = table.toUpperCase();
|
|
53
|
+
const exactMatch = byTable.get(upper);
|
|
54
|
+
if (exactMatch) {
|
|
55
|
+
resolved.add(exactMatch);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const parts = table.split(".");
|
|
59
|
+
const lastPart = parts[parts.length - 1];
|
|
60
|
+
const nameMatch = byName.get(lastPart.toLowerCase());
|
|
61
|
+
if (nameMatch) {
|
|
62
|
+
resolved.add(nameMatch);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
for (const [tablePath, modelName] of byTable) {
|
|
66
|
+
if (tablePath.endsWith("." + upper)) {
|
|
67
|
+
resolved.add(modelName);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return Array.from(resolved);
|
|
73
|
+
}
|
|
74
|
+
function buildLineageTree(modelName, models, options) {
|
|
75
|
+
const modelMap = /* @__PURE__ */ new Map();
|
|
76
|
+
for (const model of models) {
|
|
77
|
+
modelMap.set(model.name, model);
|
|
78
|
+
}
|
|
79
|
+
function walk(name, visited, depth) {
|
|
80
|
+
if (visited.has(name)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const entry = modelMap.get(name);
|
|
84
|
+
const node = {
|
|
85
|
+
name,
|
|
86
|
+
children: []
|
|
87
|
+
};
|
|
88
|
+
if (entry?.source === "dbt-source") {
|
|
89
|
+
node.isSource = true;
|
|
90
|
+
}
|
|
91
|
+
if (!entry) {
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
if (options?.maxDepth !== void 0 && depth >= options.maxDepth) {
|
|
95
|
+
return node;
|
|
96
|
+
}
|
|
97
|
+
visited.add(name);
|
|
98
|
+
if (entry.dependsOn) {
|
|
99
|
+
for (const dep of entry.dependsOn) {
|
|
100
|
+
const resolvedName = resolveNodeKey(dep);
|
|
101
|
+
const child = walk(resolvedName, visited, depth + 1);
|
|
102
|
+
if (child) {
|
|
103
|
+
node.children.push(child);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
visited.delete(name);
|
|
108
|
+
return node;
|
|
109
|
+
}
|
|
110
|
+
return walk(modelName, /* @__PURE__ */ new Set(), 0);
|
|
111
|
+
}
|
|
112
|
+
function renderLineageTree(node, indent = 0) {
|
|
113
|
+
const lines = [];
|
|
114
|
+
if (indent === 0) {
|
|
115
|
+
lines.push(node.name);
|
|
116
|
+
} else {
|
|
117
|
+
const padding = " ".repeat(indent);
|
|
118
|
+
const label = node.isSource ? `source: ${node.name}` : node.name;
|
|
119
|
+
lines.push(`${padding}\u2190 ${label}`);
|
|
120
|
+
}
|
|
121
|
+
for (const child of node.children) {
|
|
122
|
+
lines.push(renderLineageTree(child, indent + 1));
|
|
123
|
+
}
|
|
124
|
+
return lines.join("\n");
|
|
125
|
+
}
|
|
126
|
+
async function findSqlFiles(dir) {
|
|
127
|
+
const files = [];
|
|
128
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
const fullPath = join(dir, entry.name);
|
|
131
|
+
if (entry.isDirectory()) {
|
|
132
|
+
files.push(...await findSqlFiles(fullPath));
|
|
133
|
+
} else if (extname(entry.name) === ".sql") {
|
|
134
|
+
files.push(fullPath);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return files;
|
|
138
|
+
}
|
|
139
|
+
async function scanYamchartModelDeps(projectDir, catalogModels) {
|
|
140
|
+
const modelsDir = join(projectDir, "models");
|
|
141
|
+
const entries = [];
|
|
142
|
+
const existingNames = new Set(catalogModels.map((m) => m.name));
|
|
143
|
+
try {
|
|
144
|
+
await access(modelsDir);
|
|
145
|
+
} catch {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const sqlFiles = await findSqlFiles(modelsDir);
|
|
149
|
+
for (const filePath of sqlFiles) {
|
|
150
|
+
const sql = await readFile(filePath, "utf-8");
|
|
151
|
+
const nameMatch = sql.match(/--\s*@name:\s*(.+)/);
|
|
152
|
+
if (!nameMatch?.[1]) continue;
|
|
153
|
+
const name = nameMatch[1].trim();
|
|
154
|
+
if (existingNames.has(name)) continue;
|
|
155
|
+
const deps = extractSqlDependencies(sql);
|
|
156
|
+
const resolved = resolveDependencies(deps, catalogModels);
|
|
157
|
+
entries.push({
|
|
158
|
+
name,
|
|
159
|
+
...resolved.length > 0 ? { dependsOn: resolved } : {}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return entries;
|
|
163
|
+
}
|
|
164
|
+
async function getLineage(projectDir, modelName, options) {
|
|
165
|
+
const catalogPath = join(projectDir, ".yamchart", "catalog.json");
|
|
166
|
+
const content = await readFile(catalogPath, "utf-8");
|
|
167
|
+
const catalog = JSON.parse(content);
|
|
168
|
+
const models = catalog.models || [];
|
|
169
|
+
const yamchartEntries = await scanYamchartModelDeps(projectDir, models);
|
|
170
|
+
const allModels = [...models, ...yamchartEntries];
|
|
171
|
+
const tree = buildLineageTree(modelName, allModels, {
|
|
172
|
+
maxDepth: options?.depth
|
|
173
|
+
});
|
|
174
|
+
const rendered = renderLineageTree(tree);
|
|
175
|
+
return { tree, rendered };
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
buildLineageTree,
|
|
179
|
+
extractSqlDependencies,
|
|
180
|
+
getLineage,
|
|
181
|
+
renderLineageTree,
|
|
182
|
+
resolveDependencies
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=lineage-XSITWW2O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/lineage.ts"],"sourcesContent":["import { readFile, readdir, access } from 'fs/promises';\nimport { join, extname } from 'path';\n\nexport interface LineageNode {\n name: string;\n isSource?: boolean;\n children: LineageNode[];\n}\n\nexport interface CatalogEntry {\n name: string;\n table?: string;\n source?: string;\n dependsOn?: string[];\n}\n\n/**\n * Convert a dbt node key to a model name.\n *\n * Examples:\n * model.project.stg_orders → stg_orders\n * source.project.raw.orders → raw.orders\n */\nfunction resolveNodeKey(key: string): string {\n const parts = key.split('.');\n if (parts[0] === 'source' && parts.length >= 4) {\n // source.project.schema.table → schema.table\n return parts.slice(2).join('.');\n }\n // model.project.name → name\n return parts[parts.length - 1];\n}\n\n/**\n * Extract all dependencies from yamchart model SQL.\n * Finds ref() calls, @source annotations, and FROM/JOIN table references.\n */\nexport function extractSqlDependencies(sql: string): {\n refs: string[];\n sources: string[];\n tables: string[];\n} {\n const refs: string[] = [];\n const sources: string[] = [];\n const tables: string[] = [];\n\n // Extract {{ ref('name') }} calls\n const refRegex = /\\{\\{\\s*ref\\(['\"]([^'\"]+)['\"]\\)\\s*\\}\\}/g;\n let match;\n while ((match = refRegex.exec(sql)) !== null) {\n if (match[1]) refs.push(match[1]);\n }\n\n // Extract all @source: annotations\n const sourceRegex = /--\\s*@source:\\s*(.+)/g;\n while ((match = sourceRegex.exec(sql)) !== null) {\n if (match[1]) sources.push(match[1].trim());\n }\n\n // Remove comments and Jinja for table extraction\n const cleaned = sql\n .replace(/--.*$/gm, '')\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\{\\{[\\s\\S]*?\\}\\}/g, '')\n .replace(/\\{%[\\s\\S]*?%\\}/g, '');\n\n // Extract FROM and JOIN table references\n const tableRegex = /\\b(?:FROM|JOIN)\\s+([\\w]+(?:\\.[\\w]+){1,2})/gi;\n while ((match = tableRegex.exec(cleaned)) !== null) {\n if (match[1]) tables.push(match[1]);\n }\n\n return { refs, sources, tables };\n}\n\n/**\n * Resolve extracted SQL dependencies against catalog entries.\n * Returns an array of catalog model names that were matched.\n */\nexport function resolveDependencies(\n deps: { refs: string[]; sources: string[]; tables: string[] },\n catalogModels: CatalogEntry[],\n): string[] {\n const resolved = new Set<string>();\n\n // Build lookup maps\n const byName = new Map<string, string>();\n const byTable = new Map<string, string>();\n\n for (const model of catalogModels) {\n byName.set(model.name.toLowerCase(), model.name);\n if (model.table) {\n byTable.set(model.table.toUpperCase(), model.name);\n }\n }\n\n // Resolve ref() calls by name\n for (const ref of deps.refs) {\n const nameMatch = byName.get(ref.toLowerCase());\n if (nameMatch) resolved.add(nameMatch);\n }\n\n // Resolve @source by name\n for (const source of deps.sources) {\n const nameMatch = byName.get(source.toLowerCase());\n if (nameMatch) resolved.add(nameMatch);\n }\n\n // Resolve FROM/JOIN tables\n for (const table of deps.tables) {\n // Try exact match on table path (case-insensitive)\n const upper = table.toUpperCase();\n const exactMatch = byTable.get(upper);\n if (exactMatch) {\n resolved.add(exactMatch);\n continue;\n }\n\n // Try matching by name (last segment)\n const parts = table.split('.');\n const lastPart = parts[parts.length - 1]!;\n const nameMatch = byName.get(lastPart.toLowerCase());\n if (nameMatch) {\n resolved.add(nameMatch);\n continue;\n }\n\n // Try suffix match on table paths (for partial qualification like SCHEMA.TABLE)\n for (const [tablePath, modelName] of byTable) {\n if (tablePath.endsWith('.' + upper)) {\n resolved.add(modelName);\n break;\n }\n }\n }\n\n return Array.from(resolved);\n}\n\n/**\n * Build a tree of upstream dependencies from catalog data.\n * Walks `dependsOn` recursively with cycle detection.\n */\nexport function buildLineageTree(\n modelName: string,\n models: CatalogEntry[],\n options?: { maxDepth?: number },\n): LineageNode {\n const modelMap = new Map<string, CatalogEntry>();\n for (const model of models) {\n modelMap.set(model.name, model);\n }\n\n function walk(name: string, visited: Set<string>, depth: number): LineageNode | null {\n // Cycle detection: if already on the current path, skip entirely\n if (visited.has(name)) {\n return null;\n }\n\n const entry = modelMap.get(name);\n const node: LineageNode = {\n name,\n children: [],\n };\n\n if (entry?.source === 'dbt-source') {\n node.isSource = true;\n }\n\n // Unknown model or depth limit reached: return node with no children\n if (!entry) {\n return node;\n }\n\n if (options?.maxDepth !== undefined && depth >= options.maxDepth) {\n return node;\n }\n\n visited.add(name);\n\n if (entry.dependsOn) {\n for (const dep of entry.dependsOn) {\n const resolvedName = resolveNodeKey(dep);\n const child = walk(resolvedName, visited, depth + 1);\n if (child) {\n node.children.push(child);\n }\n }\n }\n\n // Remove from visited so the same node can appear in other branches\n // (cycle detection is path-based, not global)\n visited.delete(name);\n\n return node;\n }\n\n // Root call never returns null (visited set is empty)\n return walk(modelName, new Set<string>(), 0)!;\n}\n\n/**\n * Render a lineage tree as indented text for terminal output.\n *\n * Format:\n * fct_revenue\n * ← stg_orders\n * ← source: raw.orders\n * ← stg_payments\n * ← source: raw.payments\n */\nexport function renderLineageTree(node: LineageNode, indent: number = 0): string {\n const lines: string[] = [];\n\n if (indent === 0) {\n // Root node: just the name\n lines.push(node.name);\n } else {\n const padding = ' '.repeat(indent);\n const label = node.isSource ? `source: ${node.name}` : node.name;\n lines.push(`${padding}\\u2190 ${label}`);\n }\n\n for (const child of node.children) {\n lines.push(renderLineageTree(child, indent + 1));\n }\n\n return lines.join('\\n');\n}\n\ninterface CatalogFile {\n models?: CatalogEntry[];\n}\n\n/**\n * Recursively find all .sql files in a directory.\n */\nasync function findSqlFiles(dir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...await findSqlFiles(fullPath));\n } else if (extname(entry.name) === '.sql') {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\n/**\n * Scan yamchart model .sql files and build catalog entries with resolved dependencies.\n * Only creates entries for models not already in the catalog.\n */\nasync function scanYamchartModelDeps(\n projectDir: string,\n catalogModels: CatalogEntry[],\n): Promise<CatalogEntry[]> {\n const modelsDir = join(projectDir, 'models');\n const entries: CatalogEntry[] = [];\n const existingNames = new Set(catalogModels.map(m => m.name));\n\n try {\n await access(modelsDir);\n } catch {\n return [];\n }\n\n const sqlFiles = await findSqlFiles(modelsDir);\n\n for (const filePath of sqlFiles) {\n const sql = await readFile(filePath, 'utf-8');\n\n // Extract model name from @name annotation\n const nameMatch = sql.match(/--\\s*@name:\\s*(.+)/);\n if (!nameMatch?.[1]) continue;\n\n const name = nameMatch[1].trim();\n\n // Skip if already in catalog (dbt/warehouse entry takes precedence)\n if (existingNames.has(name)) continue;\n\n // Extract and resolve dependencies\n const deps = extractSqlDependencies(sql);\n const resolved = resolveDependencies(deps, catalogModels);\n\n entries.push({\n name,\n ...(resolved.length > 0 ? { dependsOn: resolved } : {}),\n });\n }\n\n return entries;\n}\n\n/**\n * Load catalog.json and run lineage for a model.\n * Scans yamchart model SQL files to resolve dependencies not in the catalog.\n */\nexport async function getLineage(\n projectDir: string,\n modelName: string,\n options?: { depth?: number; json?: boolean },\n): Promise<{ tree: LineageNode; rendered: string }> {\n const catalogPath = join(projectDir, '.yamchart', 'catalog.json');\n const content = await readFile(catalogPath, 'utf-8');\n const catalog: CatalogFile = JSON.parse(content);\n const models = catalog.models || [];\n\n // Scan yamchart model SQL files for dependencies not in the catalog\n const yamchartEntries = await scanYamchartModelDeps(projectDir, models);\n const allModels = [...models, ...yamchartEntries];\n\n const tree = buildLineageTree(modelName, allModels, {\n maxDepth: options?.depth,\n });\n\n const rendered = renderLineageTree(tree);\n\n return { tree, rendered };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -537,11 +537,23 @@ async function syncDbt(projectDir, options) {
|
|
|
537
537
|
};
|
|
538
538
|
const configYaml = stringifyYaml(syncConfig);
|
|
539
539
|
await writeFile(join4(yamchartDir, "dbt-source.yaml"), configYaml, "utf-8");
|
|
540
|
+
const warnings = [];
|
|
541
|
+
const hasLineage = fullModels.some((m) => m.dependsOn && m.dependsOn.length > 0);
|
|
542
|
+
if (!hasLineage && fullModels.length > 0) {
|
|
543
|
+
try {
|
|
544
|
+
await access3(join4(effectiveOptions.path, "target", "manifest.json"));
|
|
545
|
+
} catch {
|
|
546
|
+
warnings.push(
|
|
547
|
+
"No lineage data: target/manifest.json not found in dbt project. Run `dbt compile` to generate it, then re-sync."
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
540
551
|
return {
|
|
541
552
|
success: true,
|
|
542
553
|
modelsIncluded: filteredModels.length,
|
|
543
554
|
modelsExcluded,
|
|
544
|
-
catalogPath
|
|
555
|
+
catalogPath,
|
|
556
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
545
557
|
};
|
|
546
558
|
} catch (err) {
|
|
547
559
|
return {
|
|
@@ -557,4 +569,4 @@ export {
|
|
|
557
569
|
loadSyncConfig,
|
|
558
570
|
syncDbt
|
|
559
571
|
};
|
|
560
|
-
//# sourceMappingURL=sync-dbt-
|
|
572
|
+
//# sourceMappingURL=sync-dbt-22QQKT3A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/sync-dbt.ts","../src/dbt/local-source.ts","../src/dbt/parser.ts","../src/dbt/scanner.ts"],"sourcesContent":["import { mkdir, writeFile, readFile, access } from 'fs/promises';\nimport { join } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { LocalDbtSource } from '../dbt/local-source.js';\nimport { scanYamchartModels } from '../dbt/scanner.js';\nimport { generateCatalogMd, generateCatalogJson, type CatalogData, type CatalogModel } from '../dbt/catalog.js';\nimport { rewriteTableDatabase } from '../dbt/rewrite-database.js';\nimport type { DbtSourceConfig } from '../dbt/types.js';\n\nexport interface SyncDbtOptions {\n source: 'local' | 'github' | 'dbt-cloud';\n path?: string;\n repo?: string;\n branch?: string;\n include: string[];\n exclude: string[];\n tags: string[];\n refresh?: boolean;\n targetDatabase?: string;\n}\n\nexport interface SyncDbtResult {\n success: boolean;\n modelsIncluded: number;\n modelsExcluded: number;\n catalogPath: string;\n error?: string;\n warnings?: string[];\n}\n\n/**\n * Load saved sync config from .yamchart/dbt-source.yaml\n */\nexport async function loadSyncConfig(projectDir: string): Promise<DbtSourceConfig | null> {\n const configPath = join(projectDir, '.yamchart', 'dbt-source.yaml');\n\n try {\n await access(configPath);\n const content = await readFile(configPath, 'utf-8');\n return parseYaml(content) as DbtSourceConfig;\n } catch {\n return null;\n }\n}\n\n/**\n * Sync dbt project metadata to yamchart catalog.\n *\n * This function:\n * 1. Ensures .yamchart directory exists\n * 2. Handles refresh mode (load saved config)\n * 3. Validates source type (only 'local' supported for v1)\n * 4. Creates LocalDbtSource, lists models, applies filters\n * 5. Uses smart defaults if no filters specified\n * 6. Scans yamchart models for cross-references\n * 7. Generates catalog.md and catalog.json\n * 8. Saves sync config to dbt-source.yaml\n */\nexport async function syncDbt(\n projectDir: string,\n options: SyncDbtOptions\n): Promise<SyncDbtResult> {\n const yamchartDir = join(projectDir, '.yamchart');\n const catalogPath = join(yamchartDir, 'catalog.md');\n\n try {\n // Step 1: Ensure .yamchart directory exists\n await mkdir(yamchartDir, { recursive: true });\n\n // Step 2: Handle refresh mode - load saved config\n let effectiveOptions = { ...options };\n if (options.refresh) {\n const savedConfig = await loadSyncConfig(projectDir);\n if (!savedConfig) {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: 'No saved sync config found. Run sync-dbt with --path first.',\n };\n }\n\n effectiveOptions = {\n source: savedConfig.source,\n path: savedConfig.path,\n repo: savedConfig.repo,\n branch: savedConfig.branch,\n include: savedConfig.filters.include,\n exclude: savedConfig.filters.exclude,\n tags: savedConfig.filters.tags,\n targetDatabase: options.targetDatabase || savedConfig.targetDatabase,\n };\n }\n\n // Step 3: Validate source type (only 'local' supported for v1)\n if (effectiveOptions.source !== 'local') {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: `Source type \"${effectiveOptions.source}\" not yet supported. Only \"local\" is available.`,\n };\n }\n\n if (!effectiveOptions.path) {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: 'Path to dbt project is required for local source.',\n };\n }\n\n // Step 4: Create LocalDbtSource, list models\n const dbtSource = new LocalDbtSource(effectiveOptions.path);\n const allModels = await dbtSource.listModels();\n\n // Step 5: Apply filters or use smart defaults\n let hasFilters =\n effectiveOptions.include.length > 0 ||\n effectiveOptions.exclude.length > 0 ||\n effectiveOptions.tags.length > 0;\n\n let filteredModels;\n if (hasFilters) {\n filteredModels = LocalDbtSource.filterModels(allModels, {\n include: effectiveOptions.include,\n exclude: effectiveOptions.exclude,\n tags: effectiveOptions.tags,\n });\n } else {\n // Use smart defaults - prefer marts/reporting, exclude staging/intermediate\n const defaults = LocalDbtSource.getDefaultFilters();\n const withDefaults = LocalDbtSource.filterModels(allModels, defaults);\n\n // If defaults filter out everything, include all models\n filteredModels = withDefaults.length > 0 ? withDefaults : allModels;\n }\n\n const modelsExcluded = allModels.length - filteredModels.length;\n\n // Get full model details for included models\n const modelNames = filteredModels.map(m => m.name);\n const fullModels = await dbtSource.getModels(modelNames);\n\n // Step 6: Scan yamchart models for cross-references\n const yamchartModels = await scanYamchartModels(projectDir);\n\n // Build catalog models with cross-references\n const catalogModels: CatalogModel[] = fullModels.map(model => {\n // Find yamchart models that reference this dbt model\n const referencingModels = yamchartModels.filter(ym =>\n ym.source === model.name || ym.source === model.table\n );\n\n return {\n ...model,\n yamchartModels: referencingModels,\n };\n });\n\n // Rewrite database in table paths if targetDatabase is set\n const targetDatabase = effectiveOptions.targetDatabase;\n if (targetDatabase) {\n for (const model of catalogModels) {\n model.table = rewriteTableDatabase(model.table, targetDatabase);\n }\n }\n\n // Step 7: Generate catalog files\n const catalogData: CatalogData = {\n syncedAt: new Date().toISOString(),\n source: {\n type: effectiveOptions.source,\n path: effectiveOptions.path,\n repo: effectiveOptions.repo,\n },\n stats: {\n modelsIncluded: filteredModels.length,\n modelsExcluded,\n },\n models: catalogModels,\n };\n\n const catalogMd = generateCatalogMd(catalogData);\n const catalogJson = generateCatalogJson(catalogData);\n\n await writeFile(catalogPath, catalogMd, 'utf-8');\n await writeFile(join(yamchartDir, 'catalog.json'), catalogJson, 'utf-8');\n\n // Step 8: Save sync config for re-sync\n const syncConfig: DbtSourceConfig = {\n source: effectiveOptions.source,\n path: effectiveOptions.path,\n repo: effectiveOptions.repo,\n branch: effectiveOptions.branch,\n targetDatabase: effectiveOptions.targetDatabase,\n lastSync: catalogData.syncedAt,\n filters: {\n include: effectiveOptions.include,\n exclude: effectiveOptions.exclude,\n tags: effectiveOptions.tags,\n },\n stats: {\n modelsIncluded: filteredModels.length,\n modelsExcluded,\n },\n };\n\n const configYaml = stringifyYaml(syncConfig);\n await writeFile(join(yamchartDir, 'dbt-source.yaml'), configYaml, 'utf-8');\n\n // Check if lineage data was populated\n const warnings: string[] = [];\n const hasLineage = fullModels.some(m => m.dependsOn && m.dependsOn.length > 0);\n if (!hasLineage && fullModels.length > 0) {\n try {\n await access(join(effectiveOptions.path!, 'target', 'manifest.json'));\n // Manifest exists but no deps — models may have no inter-model dependencies\n } catch {\n warnings.push(\n 'No lineage data: target/manifest.json not found in dbt project. ' +\n 'Run `dbt compile` to generate it, then re-sync.'\n );\n }\n }\n\n return {\n success: true,\n modelsIncluded: filteredModels.length,\n modelsExcluded,\n catalogPath,\n ...(warnings.length > 0 ? { warnings } : {}),\n };\n } catch (err) {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: err instanceof Error ? err.message : 'Unknown error during sync',\n };\n }\n}\n","import { readFile, access } from 'fs/promises';\nimport { join } from 'path';\nimport fg from 'fast-glob';\nimport { minimatch } from 'minimatch';\nimport { parseSchemaYml, parseProjectYml } from './parser.js';\nimport type {\n DbtSource,\n DbtProjectConfig,\n DbtModel,\n DbtModelSummary,\n} from './types.js';\n\nexport interface ModelFilters {\n include?: string[];\n exclude?: string[];\n tags?: string[];\n}\n\n/**\n * LocalDbtSource reads dbt project metadata from a local filesystem.\n * It parses schema.yml files to extract model definitions, columns, and hints.\n */\nexport class LocalDbtSource implements DbtSource {\n readonly type = 'local' as const;\n private projectPath: string;\n private modelsCache: Map<string, DbtModel> | null = null;\n private configCache: DbtProjectConfig | null = null;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n\n /**\n * Read and parse dbt_project.yml\n */\n async getProjectConfig(): Promise<DbtProjectConfig> {\n if (this.configCache) {\n return this.configCache;\n }\n\n const configPath = join(this.projectPath, 'dbt_project.yml');\n const content = await readFile(configPath, 'utf-8');\n const parsed = parseProjectYml(content);\n\n this.configCache = {\n name: parsed.name,\n version: parsed.version,\n profile: parsed.profile,\n model_paths: parsed.modelPaths,\n target_path: 'target',\n vars: parsed.vars,\n };\n\n return this.configCache;\n }\n\n /**\n * Find all schema.yml files and parse models from them.\n * Returns summaries for model selection UI.\n */\n async listModels(): Promise<DbtModelSummary[]> {\n await this.loadModels();\n\n const summaries: DbtModelSummary[] = [];\n for (const model of this.modelsCache!.values()) {\n summaries.push({\n name: model.name,\n path: model.path,\n description: model.description || 'No description',\n tags: model.tags || [],\n });\n }\n\n return summaries;\n }\n\n /**\n * Get full model details by name.\n * @throws Error if model not found\n */\n async getModel(name: string): Promise<DbtModel> {\n await this.loadModels();\n\n const model = this.modelsCache!.get(name);\n if (!model) {\n throw new Error(`Model not found: ${name}`);\n }\n\n return model;\n }\n\n /**\n * Get multiple models by name.\n * @throws Error if any model not found\n */\n async getModels(names: string[]): Promise<DbtModel[]> {\n if (names.length === 0) {\n return [];\n }\n\n const models: DbtModel[] = [];\n for (const name of names) {\n models.push(await this.getModel(name));\n }\n\n return models;\n }\n\n /**\n * Load all models from schema.yml files into cache.\n */\n private async loadModels(): Promise<void> {\n if (this.modelsCache) {\n return;\n }\n\n const config = await this.getProjectConfig();\n this.modelsCache = new Map();\n\n // Find all schema.yml files in model paths\n for (const modelPath of config.model_paths) {\n const pattern = join(this.projectPath, modelPath, '**/*.yml');\n const files = await fg(pattern, {\n ignore: ['**/node_modules/**'],\n });\n\n for (const file of files) {\n const content = await readFile(file, 'utf-8');\n // Get relative path from project root\n const relativePath = file.slice(this.projectPath.length + 1);\n const models = parseSchemaYml(content, relativePath);\n\n for (const model of models) {\n this.modelsCache.set(model.name, model);\n }\n }\n }\n\n // Merge table paths from target/manifest.json (always present after dbt run)\n await this.mergeManifestPaths();\n // Merge column types from target/catalog.json if available (requires dbt docs generate)\n await this.mergeCatalogTypes();\n }\n\n /**\n * Read dbt's target/manifest.json and extract fully-qualified table paths.\n * manifest.json is always present after dbt run/build (unlike catalog.json\n * which requires dbt docs generate).\n */\n private async mergeManifestPaths(): Promise<void> {\n const manifestPath = join(this.projectPath, 'target', 'manifest.json');\n\n try {\n await access(manifestPath);\n } catch {\n return;\n }\n\n try {\n const content = await readFile(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as {\n nodes?: Record<string, {\n relation_name?: string;\n database?: string;\n schema?: string;\n alias?: string;\n name?: string;\n depends_on?: { nodes?: string[] };\n }>;\n sources?: Record<string, {\n database?: string;\n schema?: string;\n name?: string;\n identifier?: string;\n description?: string;\n columns?: Record<string, { name?: string; type?: string; description?: string }>;\n source_name?: string;\n }>;\n };\n\n if (!manifest.nodes) return;\n\n for (const [nodeKey, node] of Object.entries(manifest.nodes)) {\n const parts = nodeKey.split('.');\n const modelName = parts[parts.length - 1];\n\n const model = this.modelsCache!.get(modelName);\n if (!model || model.table) continue;\n\n // Prefer relation_name (e.g. '\"PROD\".\"ANALYTICS\".\"DIM_CUSTOMERS\"')\n if (node.relation_name) {\n // Strip quotes: \"PROD\".\"ANALYTICS\".\"DIM_CUSTOMERS\" → PROD.ANALYTICS.DIM_CUSTOMERS\n model.table = node.relation_name.replace(/\"/g, '');\n continue;\n }\n\n // Fall back to database/schema/alias fields\n const tableName = node.alias || node.name;\n if (node.schema && tableName) {\n model.table = node.database\n ? `${node.database}.${node.schema}.${tableName}`\n : `${node.schema}.${tableName}`;\n }\n }\n\n // Extract depends_on for each model\n for (const [nodeKey, node] of Object.entries(manifest.nodes)) {\n const parts = nodeKey.split('.');\n const modelName = parts[parts.length - 1];\n const model = this.modelsCache!.get(modelName);\n if (!model || !node.depends_on?.nodes?.length) continue;\n model.dependsOn = node.depends_on.nodes;\n }\n\n // Extract dbt sources from manifest.json sources section\n if (manifest.sources) {\n for (const [sourceKey, source] of Object.entries(manifest.sources)) {\n const sourceName = source.source_name\n ? `${source.source_name}.${source.name}`\n : source.name || sourceKey.split('.').slice(-2).join('.');\n\n // Skip if already in cache (model with same name takes precedence)\n if (this.modelsCache!.has(sourceName)) continue;\n\n const tablePath = [source.database, source.schema, source.identifier || source.name]\n .filter(Boolean)\n .join('.');\n\n const columns = source.columns\n ? Object.values(source.columns).map((col) => ({\n name: col.name || '',\n description: col.description || '',\n data_type: col.type || '',\n hints: [] as string[],\n }))\n : [];\n\n this.modelsCache!.set(sourceName, {\n name: sourceName,\n path: '',\n description: source.description || '',\n table: tablePath,\n tags: [],\n meta: {},\n columns,\n source: 'dbt-source',\n });\n }\n }\n } catch {\n // manifest.json unreadable or malformed — silently skip\n }\n }\n\n /**\n * Read dbt's target/catalog.json and merge column data_type, missing columns,\n * and table paths into loaded models.\n */\n private async mergeCatalogTypes(): Promise<void> {\n const catalogPath = join(this.projectPath, 'target', 'catalog.json');\n\n try {\n await access(catalogPath);\n } catch {\n return; // No catalog.json, nothing to merge\n }\n\n try {\n const content = await readFile(catalogPath, 'utf-8');\n const catalog = JSON.parse(content) as {\n nodes?: Record<string, {\n metadata?: { schema?: string; name?: string; database?: string };\n columns?: Record<string, { type?: string; name?: string }>;\n }>;\n };\n\n if (!catalog.nodes) return;\n\n // Build lookups: model name → column types, model name → table path\n const typesByModel = new Map<string, Map<string, string>>();\n const tableByModel = new Map<string, string>();\n\n for (const [nodeKey, node] of Object.entries(catalog.nodes)) {\n // Node keys are like \"model.project_name.model_name\"\n const parts = nodeKey.split('.');\n const modelName = parts[parts.length - 1];\n\n // Build fully-qualified table path from metadata\n if (node.metadata?.schema && node.metadata?.name) {\n const tablePath = node.metadata.database\n ? `${node.metadata.database}.${node.metadata.schema}.${node.metadata.name}`\n : `${node.metadata.schema}.${node.metadata.name}`;\n tableByModel.set(modelName, tablePath);\n }\n\n if (!node.columns) continue;\n\n const colTypes = new Map<string, string>();\n for (const [colName, colInfo] of Object.entries(node.columns)) {\n if (colInfo.type) {\n colTypes.set(colName.toLowerCase(), colInfo.type);\n }\n }\n\n if (colTypes.size > 0) {\n typesByModel.set(modelName, colTypes);\n }\n }\n\n // Merge into cached models\n for (const [modelName, model] of this.modelsCache!) {\n // Set table path from catalog if not already set in schema.yml\n const tablePath = tableByModel.get(modelName);\n if (tablePath && !model.table) {\n model.table = tablePath;\n }\n\n const colTypes = typesByModel.get(modelName);\n if (!colTypes) continue;\n\n // Merge types into existing columns\n const existingCols = new Set(model.columns.map(c => c.name.toLowerCase()));\n for (const column of model.columns) {\n if (!column.data_type) {\n const catalogType = colTypes.get(column.name.toLowerCase());\n if (catalogType) {\n column.data_type = catalogType;\n }\n }\n }\n\n // Add columns from catalog that don't exist in schema.yml\n for (const [colNameLower, colType] of colTypes) {\n if (!existingCols.has(colNameLower)) {\n model.columns.push({\n name: colNameLower,\n description: '',\n data_type: colType,\n hints: [],\n });\n }\n }\n }\n } catch {\n // catalog.json unreadable or malformed — silently skip\n }\n }\n\n /**\n * Filter models by include/exclude glob patterns and tags.\n * Patterns match against model paths.\n */\n static filterModels(\n models: DbtModelSummary[],\n filters: ModelFilters\n ): DbtModelSummary[] {\n let filtered = [...models];\n\n // Apply include patterns (match any)\n if (filters.include && filters.include.length > 0) {\n filtered = filtered.filter((model) =>\n filters.include!.some((pattern) => minimatch(model.path, pattern))\n );\n }\n\n // Apply exclude patterns (exclude any matches)\n if (filters.exclude && filters.exclude.length > 0) {\n filtered = filtered.filter(\n (model) =>\n !filters.exclude!.some((pattern) => minimatch(model.path, pattern))\n );\n }\n\n // Apply tag filters (match any)\n if (filters.tags && filters.tags.length > 0) {\n filtered = filtered.filter((model) =>\n filters.tags!.some((tag) => model.tags.includes(tag))\n );\n }\n\n return filtered;\n }\n\n /**\n * Get default filters that focus on marts/reporting models.\n * These are typically the models most useful for BI dashboards.\n */\n static getDefaultFilters(): Required<ModelFilters> {\n return {\n include: ['**/marts/**', '**/reporting/**'],\n exclude: ['**/staging/**', '**/intermediate/**'],\n tags: [],\n };\n }\n}\n","import { parse as parseYaml } from 'yaml';\nimport { dirname, join } from 'path';\nimport type { DbtModel, DbtColumn } from './types.js';\n\ninterface RawSchemaYml {\n version: number;\n models?: RawModel[];\n}\n\ninterface RawModel {\n name: string;\n description?: string;\n meta?: Record<string, unknown>;\n tags?: string[];\n columns?: RawColumn[];\n}\n\ninterface RawColumn {\n name: string;\n description?: string;\n data_type?: string;\n tests?: (string | Record<string, unknown>)[];\n}\n\n/**\n * Extract hints from dbt column tests.\n * - unique → \"unique\"\n * - not_null → \"required\"\n * - relationships: { to: ref('X') } → \"fk:X\"\n */\nexport function extractHintsFromTests(tests: (string | Record<string, unknown>)[]): string[] {\n const hints: string[] = [];\n\n for (const test of tests) {\n if (typeof test === 'string') {\n if (test === 'unique') {\n hints.push('unique');\n } else if (test === 'not_null') {\n hints.push('required');\n } else if (test === 'primary_key') {\n hints.push('primary_key');\n }\n } else if (typeof test === 'object' && test !== null) {\n // Handle relationships test\n if ('relationships' in test) {\n const rel = test.relationships as { to?: string; field?: string };\n if (rel.to) {\n // Extract table name from ref('table_name')\n const match = rel.to.match(/ref\\(['\"]([^'\"]+)['\"]\\)/);\n if (match) {\n hints.push(`fk:${match[1]}`);\n }\n }\n }\n // Handle dbt_constraints for primary key\n if ('dbt_constraints' in test) {\n const constraint = test.dbt_constraints as { type?: string };\n if (constraint.type === 'primary_key') {\n hints.push('primary_key');\n }\n }\n }\n }\n\n return hints;\n}\n\n/**\n * Parse a dbt schema.yml file and extract model definitions.\n * @param content - Raw YAML content\n * @param schemaPath - Path to the schema file (e.g., \"models/marts/_schema.yml\")\n * @returns Array of parsed models\n */\nexport function parseSchemaYml(content: string, schemaPath: string): DbtModel[] {\n const parsed = parseYaml(content) as RawSchemaYml;\n\n if (!parsed?.models || !Array.isArray(parsed.models)) {\n return [];\n }\n\n const schemaDir = dirname(schemaPath);\n\n return parsed.models.map((model): DbtModel => {\n const columns: DbtColumn[] = (model.columns || []).map((col) => ({\n name: col.name,\n description: col.description || '',\n data_type: col.data_type,\n hints: col.tests ? extractHintsFromTests(col.tests) : [],\n }));\n\n return {\n name: model.name,\n path: join(schemaDir, `${model.name}.sql`),\n description: model.description || 'No description',\n tags: model.tags || [],\n meta: model.meta || {},\n columns,\n };\n });\n}\n\n/**\n * Parse dbt_project.yml to get project-level config.\n */\nexport function parseProjectYml(content: string): {\n name: string;\n version?: string;\n profile?: string;\n modelPaths: string[];\n vars: Record<string, unknown>;\n} {\n const parsed = parseYaml(content) as Record<string, unknown>;\n\n return {\n name: (parsed.name as string) || 'unknown',\n version: parsed.version as string | undefined,\n profile: parsed.profile as string | undefined,\n modelPaths: (parsed['model-paths'] as string[]) || (parsed.model_paths as string[]) || ['models'],\n vars: (parsed.vars as Record<string, unknown>) || {},\n };\n}\n","import { readFile, access, readdir } from 'fs/promises';\nimport { join, extname, relative } from 'path';\n\nexport interface YamchartModel {\n name: string;\n description: string;\n path: string; // relative to project\n source?: string; // dbt table this queries\n}\n\n/**\n * Scan yamchart models directory and extract metadata.\n * Used to cross-reference which yamchart models use which dbt tables.\n */\nexport async function scanYamchartModels(projectDir: string): Promise<YamchartModel[]> {\n const modelsDir = join(projectDir, 'models');\n const models: YamchartModel[] = [];\n\n try {\n await access(modelsDir);\n } catch {\n return [];\n }\n\n await scanModelsRecursive(modelsDir, projectDir, models);\n return models;\n}\n\nasync function scanModelsRecursive(\n dir: string,\n projectDir: string,\n models: YamchartModel[]\n): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n await scanModelsRecursive(fullPath, projectDir, models);\n } else if (extname(entry.name) === '.sql') {\n const content = await readFile(fullPath, 'utf-8');\n const metadata = parseModelMetadata(content);\n\n if (metadata.name) {\n models.push({\n name: metadata.name,\n description: metadata.description || '',\n path: relative(projectDir, fullPath),\n source: metadata.source || extractSourceFromSql(content),\n });\n }\n }\n }\n}\n\ninterface ModelMetadata {\n name?: string;\n description?: string;\n source?: string;\n}\n\n/**\n * Parse yamchart model metadata from SQL comments.\n */\nfunction parseModelMetadata(content: string): ModelMetadata {\n const metadata: ModelMetadata = {};\n\n // Match @name: value\n const nameMatch = content.match(/--\\s*@name:\\s*(.+)/);\n if (nameMatch) {\n metadata.name = nameMatch[1].trim();\n }\n\n // Match @description: value\n const descMatch = content.match(/--\\s*@description:\\s*(.+)/);\n if (descMatch) {\n metadata.description = descMatch[1].trim();\n }\n\n // Match @source: value (explicit dbt table reference)\n const sourceMatch = content.match(/--\\s*@source:\\s*(.+)/);\n if (sourceMatch) {\n metadata.source = sourceMatch[1].trim();\n }\n\n return metadata;\n}\n\n/**\n * Extract the primary table name from SQL FROM clause.\n * This is a best-effort extraction for cross-referencing.\n */\nfunction extractSourceFromSql(sql: string): string | undefined {\n // Remove comments\n const noComments = sql.replace(/--.*$/gm, '').replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n\n // Match FROM table_name (handles schema.table and database.schema.table)\n const fromMatch = noComments.match(/\\bFROM\\s+(\\{\\{\\s*ref\\(['\"]([^'\"]+)['\"]\\)\\s*\\}\\}|[\\w.]+)/i);\n\n if (fromMatch) {\n // If it's a Jinja ref(), extract the table name\n if (fromMatch[2]) {\n return fromMatch[2];\n }\n // Otherwise return the raw table name\n return fromMatch[1];\n }\n\n return undefined;\n}\n"],"mappings":"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","names":["readFile","access","join","parseYaml","join","join","readFile","access","join","join","access","readFile","parseYaml"]}
|
|
@@ -747,10 +747,11 @@ yamchart sync-warehouse --json # JSON output
|
|
|
747
747
|
|
|
748
748
|
## Lineage
|
|
749
749
|
|
|
750
|
-
Traces upstream dependencies for any model
|
|
750
|
+
Traces upstream dependencies for any model — both dbt catalog models and local yamchart SQL models. For dbt models, lineage follows `dependsOn` from `target/manifest.json`. For yamchart models (local `.sql` files), lineage parses SQL to extract `{{ ref() }}` calls, `@source` annotations, and `FROM`/`JOIN` table references, then resolves them against the catalog.
|
|
751
751
|
|
|
752
752
|
```bash
|
|
753
753
|
yamchart lineage fct_revenue # Show upstream dependencies
|
|
754
|
+
yamchart lineage marketing__daily_traffic # Works for yamchart models too
|
|
754
755
|
yamchart lineage fct_revenue --depth 2 # Limit recursion depth
|
|
755
756
|
yamchart lineage fct_revenue --json # JSON output
|
|
756
757
|
```
|
|
@@ -764,7 +765,12 @@ fct_revenue
|
|
|
764
765
|
← source: raw.payments
|
|
765
766
|
```
|
|
766
767
|
|
|
767
|
-
**
|
|
768
|
+
**How yamchart model dependencies are resolved:**
|
|
769
|
+
- `{{ ref('model_name') }}` → matched by catalog model name
|
|
770
|
+
- `-- @source: model_name` → matched by catalog model name
|
|
771
|
+
- `FROM database.schema.table` → matched by catalog table path (case-insensitive)
|
|
772
|
+
|
|
773
|
+
**Note:** dbt lineage data requires `sync-dbt` with a dbt project that has run `dbt compile` or `dbt run` (which generates `target/manifest.json`). If the manifest is missing, `sync-dbt` will warn you. Yamchart model lineage works independently by parsing SQL files directly.
|
|
768
774
|
|
|
769
775
|
## Axis Types
|
|
770
776
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yamchart",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Git-native business intelligence dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -64,12 +64,12 @@
|
|
|
64
64
|
"tsup": "^8.0.0",
|
|
65
65
|
"typescript": "^5.7.0",
|
|
66
66
|
"vitest": "^2.1.0",
|
|
67
|
-
"@yamchart/advisor": "0.1.0",
|
|
68
67
|
"@yamchart/auth-local": "0.1.0",
|
|
68
|
+
"@yamchart/advisor": "0.1.0",
|
|
69
69
|
"@yamchart/config": "0.1.2",
|
|
70
|
-
"@yamchart/query": "0.1.2",
|
|
71
70
|
"@yamchart/schema": "0.1.2",
|
|
72
|
-
"@yamchart/server": "0.1.2"
|
|
71
|
+
"@yamchart/server": "0.1.2",
|
|
72
|
+
"@yamchart/query": "0.1.2"
|
|
73
73
|
},
|
|
74
74
|
"files": [
|
|
75
75
|
"dist",
|
package/dist/lineage-TX33DUDP.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import "./chunk-DGUM43GV.js";
|
|
2
|
-
|
|
3
|
-
// src/commands/lineage.ts
|
|
4
|
-
import { readFile } from "fs/promises";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
function resolveNodeKey(key) {
|
|
7
|
-
const parts = key.split(".");
|
|
8
|
-
if (parts[0] === "source" && parts.length >= 4) {
|
|
9
|
-
return parts.slice(2).join(".");
|
|
10
|
-
}
|
|
11
|
-
return parts[parts.length - 1];
|
|
12
|
-
}
|
|
13
|
-
function buildLineageTree(modelName, models, options) {
|
|
14
|
-
const modelMap = /* @__PURE__ */ new Map();
|
|
15
|
-
for (const model of models) {
|
|
16
|
-
modelMap.set(model.name, model);
|
|
17
|
-
}
|
|
18
|
-
function walk(name, visited, depth) {
|
|
19
|
-
if (visited.has(name)) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
const entry = modelMap.get(name);
|
|
23
|
-
const node = {
|
|
24
|
-
name,
|
|
25
|
-
children: []
|
|
26
|
-
};
|
|
27
|
-
if (entry?.source === "dbt-source") {
|
|
28
|
-
node.isSource = true;
|
|
29
|
-
}
|
|
30
|
-
if (!entry) {
|
|
31
|
-
return node;
|
|
32
|
-
}
|
|
33
|
-
if (options?.maxDepth !== void 0 && depth >= options.maxDepth) {
|
|
34
|
-
return node;
|
|
35
|
-
}
|
|
36
|
-
visited.add(name);
|
|
37
|
-
if (entry.dependsOn) {
|
|
38
|
-
for (const dep of entry.dependsOn) {
|
|
39
|
-
const resolvedName = resolveNodeKey(dep);
|
|
40
|
-
const child = walk(resolvedName, visited, depth + 1);
|
|
41
|
-
if (child) {
|
|
42
|
-
node.children.push(child);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
visited.delete(name);
|
|
47
|
-
return node;
|
|
48
|
-
}
|
|
49
|
-
return walk(modelName, /* @__PURE__ */ new Set(), 0);
|
|
50
|
-
}
|
|
51
|
-
function renderLineageTree(node, indent = 0) {
|
|
52
|
-
const lines = [];
|
|
53
|
-
if (indent === 0) {
|
|
54
|
-
lines.push(node.name);
|
|
55
|
-
} else {
|
|
56
|
-
const padding = " ".repeat(indent);
|
|
57
|
-
const label = node.isSource ? `source: ${node.name}` : node.name;
|
|
58
|
-
lines.push(`${padding}\u2190 ${label}`);
|
|
59
|
-
}
|
|
60
|
-
for (const child of node.children) {
|
|
61
|
-
lines.push(renderLineageTree(child, indent + 1));
|
|
62
|
-
}
|
|
63
|
-
return lines.join("\n");
|
|
64
|
-
}
|
|
65
|
-
async function getLineage(projectDir, modelName, options) {
|
|
66
|
-
const catalogPath = join(projectDir, ".yamchart", "catalog.json");
|
|
67
|
-
const content = await readFile(catalogPath, "utf-8");
|
|
68
|
-
const catalog = JSON.parse(content);
|
|
69
|
-
const models = catalog.models || [];
|
|
70
|
-
const tree = buildLineageTree(modelName, models, {
|
|
71
|
-
maxDepth: options?.depth
|
|
72
|
-
});
|
|
73
|
-
const rendered = renderLineageTree(tree);
|
|
74
|
-
return { tree, rendered };
|
|
75
|
-
}
|
|
76
|
-
export {
|
|
77
|
-
buildLineageTree,
|
|
78
|
-
getLineage,
|
|
79
|
-
renderLineageTree
|
|
80
|
-
};
|
|
81
|
-
//# sourceMappingURL=lineage-TX33DUDP.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/lineage.ts"],"sourcesContent":["import { readFile } from 'fs/promises';\nimport { join } from 'path';\n\nexport interface LineageNode {\n name: string;\n isSource?: boolean;\n children: LineageNode[];\n}\n\nexport interface CatalogEntry {\n name: string;\n table?: string;\n source?: string;\n dependsOn?: string[];\n}\n\n/**\n * Convert a dbt node key to a model name.\n *\n * Examples:\n * model.project.stg_orders → stg_orders\n * source.project.raw.orders → raw.orders\n */\nfunction resolveNodeKey(key: string): string {\n const parts = key.split('.');\n if (parts[0] === 'source' && parts.length >= 4) {\n // source.project.schema.table → schema.table\n return parts.slice(2).join('.');\n }\n // model.project.name → name\n return parts[parts.length - 1];\n}\n\n/**\n * Build a tree of upstream dependencies from catalog data.\n * Walks `dependsOn` recursively with cycle detection.\n */\nexport function buildLineageTree(\n modelName: string,\n models: CatalogEntry[],\n options?: { maxDepth?: number },\n): LineageNode {\n const modelMap = new Map<string, CatalogEntry>();\n for (const model of models) {\n modelMap.set(model.name, model);\n }\n\n function walk(name: string, visited: Set<string>, depth: number): LineageNode | null {\n // Cycle detection: if already on the current path, skip entirely\n if (visited.has(name)) {\n return null;\n }\n\n const entry = modelMap.get(name);\n const node: LineageNode = {\n name,\n children: [],\n };\n\n if (entry?.source === 'dbt-source') {\n node.isSource = true;\n }\n\n // Unknown model or depth limit reached: return node with no children\n if (!entry) {\n return node;\n }\n\n if (options?.maxDepth !== undefined && depth >= options.maxDepth) {\n return node;\n }\n\n visited.add(name);\n\n if (entry.dependsOn) {\n for (const dep of entry.dependsOn) {\n const resolvedName = resolveNodeKey(dep);\n const child = walk(resolvedName, visited, depth + 1);\n if (child) {\n node.children.push(child);\n }\n }\n }\n\n // Remove from visited so the same node can appear in other branches\n // (cycle detection is path-based, not global)\n visited.delete(name);\n\n return node;\n }\n\n // Root call never returns null (visited set is empty)\n return walk(modelName, new Set<string>(), 0)!;\n}\n\n/**\n * Render a lineage tree as indented text for terminal output.\n *\n * Format:\n * fct_revenue\n * ← stg_orders\n * ← source: raw.orders\n * ← stg_payments\n * ← source: raw.payments\n */\nexport function renderLineageTree(node: LineageNode, indent: number = 0): string {\n const lines: string[] = [];\n\n if (indent === 0) {\n // Root node: just the name\n lines.push(node.name);\n } else {\n const padding = ' '.repeat(indent);\n const label = node.isSource ? `source: ${node.name}` : node.name;\n lines.push(`${padding}\\u2190 ${label}`);\n }\n\n for (const child of node.children) {\n lines.push(renderLineageTree(child, indent + 1));\n }\n\n return lines.join('\\n');\n}\n\ninterface CatalogFile {\n models?: CatalogEntry[];\n}\n\n/**\n * Load catalog.json and run lineage for a model.\n */\nexport async function getLineage(\n projectDir: string,\n modelName: string,\n options?: { depth?: number; json?: boolean },\n): Promise<{ tree: LineageNode; rendered: string }> {\n const catalogPath = join(projectDir, '.yamchart', 'catalog.json');\n const content = await readFile(catalogPath, 'utf-8');\n const catalog: CatalogFile = JSON.parse(content);\n const models = catalog.models || [];\n\n const tree = buildLineageTree(modelName, models, {\n maxDepth: options?.depth,\n });\n\n const rendered = renderLineageTree(tree);\n\n return { tree, rendered };\n}\n"],"mappings":";;;AAAA,SAAS,gBAAgB;AACzB,SAAS,YAAY;AAsBrB,SAAS,eAAe,KAAqB;AAC3C,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,MAAI,MAAM,CAAC,MAAM,YAAY,MAAM,UAAU,GAAG;AAE9C,WAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AAAA,EAChC;AAEA,SAAO,MAAM,MAAM,SAAS,CAAC;AAC/B;AAMO,SAAS,iBACd,WACA,QACA,SACa;AACb,QAAM,WAAW,oBAAI,IAA0B;AAC/C,aAAW,SAAS,QAAQ;AAC1B,aAAS,IAAI,MAAM,MAAM,KAAK;AAAA,EAChC;AAEA,WAAS,KAAK,MAAc,SAAsB,OAAmC;AAEnF,QAAI,QAAQ,IAAI,IAAI,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,SAAS,IAAI,IAAI;AAC/B,UAAM,OAAoB;AAAA,MACxB;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAEA,QAAI,OAAO,WAAW,cAAc;AAClC,WAAK,WAAW;AAAA,IAClB;AAGA,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,aAAa,UAAa,SAAS,QAAQ,UAAU;AAChE,aAAO;AAAA,IACT;AAEA,YAAQ,IAAI,IAAI;AAEhB,QAAI,MAAM,WAAW;AACnB,iBAAW,OAAO,MAAM,WAAW;AACjC,cAAM,eAAe,eAAe,GAAG;AACvC,cAAM,QAAQ,KAAK,cAAc,SAAS,QAAQ,CAAC;AACnD,YAAI,OAAO;AACT,eAAK,SAAS,KAAK,KAAK;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAIA,YAAQ,OAAO,IAAI;AAEnB,WAAO;AAAA,EACT;AAGA,SAAO,KAAK,WAAW,oBAAI,IAAY,GAAG,CAAC;AAC7C;AAYO,SAAS,kBAAkB,MAAmB,SAAiB,GAAW;AAC/E,QAAM,QAAkB,CAAC;AAEzB,MAAI,WAAW,GAAG;AAEhB,UAAM,KAAK,KAAK,IAAI;AAAA,EACtB,OAAO;AACL,UAAM,UAAU,KAAK,OAAO,MAAM;AAClC,UAAM,QAAQ,KAAK,WAAW,WAAW,KAAK,IAAI,KAAK,KAAK;AAC5D,UAAM,KAAK,GAAG,OAAO,UAAU,KAAK,EAAE;AAAA,EACxC;AAEA,aAAW,SAAS,KAAK,UAAU;AACjC,UAAM,KAAK,kBAAkB,OAAO,SAAS,CAAC,CAAC;AAAA,EACjD;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AASA,eAAsB,WACpB,YACA,WACA,SACkD;AAClD,QAAM,cAAc,KAAK,YAAY,aAAa,cAAc;AAChE,QAAM,UAAU,MAAM,SAAS,aAAa,OAAO;AACnD,QAAM,UAAuB,KAAK,MAAM,OAAO;AAC/C,QAAM,SAAS,QAAQ,UAAU,CAAC;AAElC,QAAM,OAAO,iBAAiB,WAAW,QAAQ;AAAA,IAC/C,UAAU,SAAS;AAAA,EACrB,CAAC;AAED,QAAM,WAAW,kBAAkB,IAAI;AAEvC,SAAO,EAAE,MAAM,SAAS;AAC1B;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/sync-dbt.ts","../src/dbt/local-source.ts","../src/dbt/parser.ts","../src/dbt/scanner.ts"],"sourcesContent":["import { mkdir, writeFile, readFile, access } from 'fs/promises';\nimport { join } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { LocalDbtSource } from '../dbt/local-source.js';\nimport { scanYamchartModels } from '../dbt/scanner.js';\nimport { generateCatalogMd, generateCatalogJson, type CatalogData, type CatalogModel } from '../dbt/catalog.js';\nimport { rewriteTableDatabase } from '../dbt/rewrite-database.js';\nimport type { DbtSourceConfig } from '../dbt/types.js';\n\nexport interface SyncDbtOptions {\n source: 'local' | 'github' | 'dbt-cloud';\n path?: string;\n repo?: string;\n branch?: string;\n include: string[];\n exclude: string[];\n tags: string[];\n refresh?: boolean;\n targetDatabase?: string;\n}\n\nexport interface SyncDbtResult {\n success: boolean;\n modelsIncluded: number;\n modelsExcluded: number;\n catalogPath: string;\n error?: string;\n}\n\n/**\n * Load saved sync config from .yamchart/dbt-source.yaml\n */\nexport async function loadSyncConfig(projectDir: string): Promise<DbtSourceConfig | null> {\n const configPath = join(projectDir, '.yamchart', 'dbt-source.yaml');\n\n try {\n await access(configPath);\n const content = await readFile(configPath, 'utf-8');\n return parseYaml(content) as DbtSourceConfig;\n } catch {\n return null;\n }\n}\n\n/**\n * Sync dbt project metadata to yamchart catalog.\n *\n * This function:\n * 1. Ensures .yamchart directory exists\n * 2. Handles refresh mode (load saved config)\n * 3. Validates source type (only 'local' supported for v1)\n * 4. Creates LocalDbtSource, lists models, applies filters\n * 5. Uses smart defaults if no filters specified\n * 6. Scans yamchart models for cross-references\n * 7. Generates catalog.md and catalog.json\n * 8. Saves sync config to dbt-source.yaml\n */\nexport async function syncDbt(\n projectDir: string,\n options: SyncDbtOptions\n): Promise<SyncDbtResult> {\n const yamchartDir = join(projectDir, '.yamchart');\n const catalogPath = join(yamchartDir, 'catalog.md');\n\n try {\n // Step 1: Ensure .yamchart directory exists\n await mkdir(yamchartDir, { recursive: true });\n\n // Step 2: Handle refresh mode - load saved config\n let effectiveOptions = { ...options };\n if (options.refresh) {\n const savedConfig = await loadSyncConfig(projectDir);\n if (!savedConfig) {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: 'No saved sync config found. Run sync-dbt with --path first.',\n };\n }\n\n effectiveOptions = {\n source: savedConfig.source,\n path: savedConfig.path,\n repo: savedConfig.repo,\n branch: savedConfig.branch,\n include: savedConfig.filters.include,\n exclude: savedConfig.filters.exclude,\n tags: savedConfig.filters.tags,\n targetDatabase: options.targetDatabase || savedConfig.targetDatabase,\n };\n }\n\n // Step 3: Validate source type (only 'local' supported for v1)\n if (effectiveOptions.source !== 'local') {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: `Source type \"${effectiveOptions.source}\" not yet supported. Only \"local\" is available.`,\n };\n }\n\n if (!effectiveOptions.path) {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: 'Path to dbt project is required for local source.',\n };\n }\n\n // Step 4: Create LocalDbtSource, list models\n const dbtSource = new LocalDbtSource(effectiveOptions.path);\n const allModels = await dbtSource.listModels();\n\n // Step 5: Apply filters or use smart defaults\n let hasFilters =\n effectiveOptions.include.length > 0 ||\n effectiveOptions.exclude.length > 0 ||\n effectiveOptions.tags.length > 0;\n\n let filteredModels;\n if (hasFilters) {\n filteredModels = LocalDbtSource.filterModels(allModels, {\n include: effectiveOptions.include,\n exclude: effectiveOptions.exclude,\n tags: effectiveOptions.tags,\n });\n } else {\n // Use smart defaults - prefer marts/reporting, exclude staging/intermediate\n const defaults = LocalDbtSource.getDefaultFilters();\n const withDefaults = LocalDbtSource.filterModels(allModels, defaults);\n\n // If defaults filter out everything, include all models\n filteredModels = withDefaults.length > 0 ? withDefaults : allModels;\n }\n\n const modelsExcluded = allModels.length - filteredModels.length;\n\n // Get full model details for included models\n const modelNames = filteredModels.map(m => m.name);\n const fullModels = await dbtSource.getModels(modelNames);\n\n // Step 6: Scan yamchart models for cross-references\n const yamchartModels = await scanYamchartModels(projectDir);\n\n // Build catalog models with cross-references\n const catalogModels: CatalogModel[] = fullModels.map(model => {\n // Find yamchart models that reference this dbt model\n const referencingModels = yamchartModels.filter(ym =>\n ym.source === model.name || ym.source === model.table\n );\n\n return {\n ...model,\n yamchartModels: referencingModels,\n };\n });\n\n // Rewrite database in table paths if targetDatabase is set\n const targetDatabase = effectiveOptions.targetDatabase;\n if (targetDatabase) {\n for (const model of catalogModels) {\n model.table = rewriteTableDatabase(model.table, targetDatabase);\n }\n }\n\n // Step 7: Generate catalog files\n const catalogData: CatalogData = {\n syncedAt: new Date().toISOString(),\n source: {\n type: effectiveOptions.source,\n path: effectiveOptions.path,\n repo: effectiveOptions.repo,\n },\n stats: {\n modelsIncluded: filteredModels.length,\n modelsExcluded,\n },\n models: catalogModels,\n };\n\n const catalogMd = generateCatalogMd(catalogData);\n const catalogJson = generateCatalogJson(catalogData);\n\n await writeFile(catalogPath, catalogMd, 'utf-8');\n await writeFile(join(yamchartDir, 'catalog.json'), catalogJson, 'utf-8');\n\n // Step 8: Save sync config for re-sync\n const syncConfig: DbtSourceConfig = {\n source: effectiveOptions.source,\n path: effectiveOptions.path,\n repo: effectiveOptions.repo,\n branch: effectiveOptions.branch,\n targetDatabase: effectiveOptions.targetDatabase,\n lastSync: catalogData.syncedAt,\n filters: {\n include: effectiveOptions.include,\n exclude: effectiveOptions.exclude,\n tags: effectiveOptions.tags,\n },\n stats: {\n modelsIncluded: filteredModels.length,\n modelsExcluded,\n },\n };\n\n const configYaml = stringifyYaml(syncConfig);\n await writeFile(join(yamchartDir, 'dbt-source.yaml'), configYaml, 'utf-8');\n\n return {\n success: true,\n modelsIncluded: filteredModels.length,\n modelsExcluded,\n catalogPath,\n };\n } catch (err) {\n return {\n success: false,\n modelsIncluded: 0,\n modelsExcluded: 0,\n catalogPath,\n error: err instanceof Error ? err.message : 'Unknown error during sync',\n };\n }\n}\n","import { readFile, access } from 'fs/promises';\nimport { join } from 'path';\nimport fg from 'fast-glob';\nimport { minimatch } from 'minimatch';\nimport { parseSchemaYml, parseProjectYml } from './parser.js';\nimport type {\n DbtSource,\n DbtProjectConfig,\n DbtModel,\n DbtModelSummary,\n} from './types.js';\n\nexport interface ModelFilters {\n include?: string[];\n exclude?: string[];\n tags?: string[];\n}\n\n/**\n * LocalDbtSource reads dbt project metadata from a local filesystem.\n * It parses schema.yml files to extract model definitions, columns, and hints.\n */\nexport class LocalDbtSource implements DbtSource {\n readonly type = 'local' as const;\n private projectPath: string;\n private modelsCache: Map<string, DbtModel> | null = null;\n private configCache: DbtProjectConfig | null = null;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n\n /**\n * Read and parse dbt_project.yml\n */\n async getProjectConfig(): Promise<DbtProjectConfig> {\n if (this.configCache) {\n return this.configCache;\n }\n\n const configPath = join(this.projectPath, 'dbt_project.yml');\n const content = await readFile(configPath, 'utf-8');\n const parsed = parseProjectYml(content);\n\n this.configCache = {\n name: parsed.name,\n version: parsed.version,\n profile: parsed.profile,\n model_paths: parsed.modelPaths,\n target_path: 'target',\n vars: parsed.vars,\n };\n\n return this.configCache;\n }\n\n /**\n * Find all schema.yml files and parse models from them.\n * Returns summaries for model selection UI.\n */\n async listModels(): Promise<DbtModelSummary[]> {\n await this.loadModels();\n\n const summaries: DbtModelSummary[] = [];\n for (const model of this.modelsCache!.values()) {\n summaries.push({\n name: model.name,\n path: model.path,\n description: model.description || 'No description',\n tags: model.tags || [],\n });\n }\n\n return summaries;\n }\n\n /**\n * Get full model details by name.\n * @throws Error if model not found\n */\n async getModel(name: string): Promise<DbtModel> {\n await this.loadModels();\n\n const model = this.modelsCache!.get(name);\n if (!model) {\n throw new Error(`Model not found: ${name}`);\n }\n\n return model;\n }\n\n /**\n * Get multiple models by name.\n * @throws Error if any model not found\n */\n async getModels(names: string[]): Promise<DbtModel[]> {\n if (names.length === 0) {\n return [];\n }\n\n const models: DbtModel[] = [];\n for (const name of names) {\n models.push(await this.getModel(name));\n }\n\n return models;\n }\n\n /**\n * Load all models from schema.yml files into cache.\n */\n private async loadModels(): Promise<void> {\n if (this.modelsCache) {\n return;\n }\n\n const config = await this.getProjectConfig();\n this.modelsCache = new Map();\n\n // Find all schema.yml files in model paths\n for (const modelPath of config.model_paths) {\n const pattern = join(this.projectPath, modelPath, '**/*.yml');\n const files = await fg(pattern, {\n ignore: ['**/node_modules/**'],\n });\n\n for (const file of files) {\n const content = await readFile(file, 'utf-8');\n // Get relative path from project root\n const relativePath = file.slice(this.projectPath.length + 1);\n const models = parseSchemaYml(content, relativePath);\n\n for (const model of models) {\n this.modelsCache.set(model.name, model);\n }\n }\n }\n\n // Merge table paths from target/manifest.json (always present after dbt run)\n await this.mergeManifestPaths();\n // Merge column types from target/catalog.json if available (requires dbt docs generate)\n await this.mergeCatalogTypes();\n }\n\n /**\n * Read dbt's target/manifest.json and extract fully-qualified table paths.\n * manifest.json is always present after dbt run/build (unlike catalog.json\n * which requires dbt docs generate).\n */\n private async mergeManifestPaths(): Promise<void> {\n const manifestPath = join(this.projectPath, 'target', 'manifest.json');\n\n try {\n await access(manifestPath);\n } catch {\n return;\n }\n\n try {\n const content = await readFile(manifestPath, 'utf-8');\n const manifest = JSON.parse(content) as {\n nodes?: Record<string, {\n relation_name?: string;\n database?: string;\n schema?: string;\n alias?: string;\n name?: string;\n depends_on?: { nodes?: string[] };\n }>;\n sources?: Record<string, {\n database?: string;\n schema?: string;\n name?: string;\n identifier?: string;\n description?: string;\n columns?: Record<string, { name?: string; type?: string; description?: string }>;\n source_name?: string;\n }>;\n };\n\n if (!manifest.nodes) return;\n\n for (const [nodeKey, node] of Object.entries(manifest.nodes)) {\n const parts = nodeKey.split('.');\n const modelName = parts[parts.length - 1];\n\n const model = this.modelsCache!.get(modelName);\n if (!model || model.table) continue;\n\n // Prefer relation_name (e.g. '\"PROD\".\"ANALYTICS\".\"DIM_CUSTOMERS\"')\n if (node.relation_name) {\n // Strip quotes: \"PROD\".\"ANALYTICS\".\"DIM_CUSTOMERS\" → PROD.ANALYTICS.DIM_CUSTOMERS\n model.table = node.relation_name.replace(/\"/g, '');\n continue;\n }\n\n // Fall back to database/schema/alias fields\n const tableName = node.alias || node.name;\n if (node.schema && tableName) {\n model.table = node.database\n ? `${node.database}.${node.schema}.${tableName}`\n : `${node.schema}.${tableName}`;\n }\n }\n\n // Extract depends_on for each model\n for (const [nodeKey, node] of Object.entries(manifest.nodes)) {\n const parts = nodeKey.split('.');\n const modelName = parts[parts.length - 1];\n const model = this.modelsCache!.get(modelName);\n if (!model || !node.depends_on?.nodes?.length) continue;\n model.dependsOn = node.depends_on.nodes;\n }\n\n // Extract dbt sources from manifest.json sources section\n if (manifest.sources) {\n for (const [sourceKey, source] of Object.entries(manifest.sources)) {\n const sourceName = source.source_name\n ? `${source.source_name}.${source.name}`\n : source.name || sourceKey.split('.').slice(-2).join('.');\n\n // Skip if already in cache (model with same name takes precedence)\n if (this.modelsCache!.has(sourceName)) continue;\n\n const tablePath = [source.database, source.schema, source.identifier || source.name]\n .filter(Boolean)\n .join('.');\n\n const columns = source.columns\n ? Object.values(source.columns).map((col) => ({\n name: col.name || '',\n description: col.description || '',\n data_type: col.type || '',\n hints: [] as string[],\n }))\n : [];\n\n this.modelsCache!.set(sourceName, {\n name: sourceName,\n path: '',\n description: source.description || '',\n table: tablePath,\n tags: [],\n meta: {},\n columns,\n source: 'dbt-source',\n });\n }\n }\n } catch {\n // manifest.json unreadable or malformed — silently skip\n }\n }\n\n /**\n * Read dbt's target/catalog.json and merge column data_type, missing columns,\n * and table paths into loaded models.\n */\n private async mergeCatalogTypes(): Promise<void> {\n const catalogPath = join(this.projectPath, 'target', 'catalog.json');\n\n try {\n await access(catalogPath);\n } catch {\n return; // No catalog.json, nothing to merge\n }\n\n try {\n const content = await readFile(catalogPath, 'utf-8');\n const catalog = JSON.parse(content) as {\n nodes?: Record<string, {\n metadata?: { schema?: string; name?: string; database?: string };\n columns?: Record<string, { type?: string; name?: string }>;\n }>;\n };\n\n if (!catalog.nodes) return;\n\n // Build lookups: model name → column types, model name → table path\n const typesByModel = new Map<string, Map<string, string>>();\n const tableByModel = new Map<string, string>();\n\n for (const [nodeKey, node] of Object.entries(catalog.nodes)) {\n // Node keys are like \"model.project_name.model_name\"\n const parts = nodeKey.split('.');\n const modelName = parts[parts.length - 1];\n\n // Build fully-qualified table path from metadata\n if (node.metadata?.schema && node.metadata?.name) {\n const tablePath = node.metadata.database\n ? `${node.metadata.database}.${node.metadata.schema}.${node.metadata.name}`\n : `${node.metadata.schema}.${node.metadata.name}`;\n tableByModel.set(modelName, tablePath);\n }\n\n if (!node.columns) continue;\n\n const colTypes = new Map<string, string>();\n for (const [colName, colInfo] of Object.entries(node.columns)) {\n if (colInfo.type) {\n colTypes.set(colName.toLowerCase(), colInfo.type);\n }\n }\n\n if (colTypes.size > 0) {\n typesByModel.set(modelName, colTypes);\n }\n }\n\n // Merge into cached models\n for (const [modelName, model] of this.modelsCache!) {\n // Set table path from catalog if not already set in schema.yml\n const tablePath = tableByModel.get(modelName);\n if (tablePath && !model.table) {\n model.table = tablePath;\n }\n\n const colTypes = typesByModel.get(modelName);\n if (!colTypes) continue;\n\n // Merge types into existing columns\n const existingCols = new Set(model.columns.map(c => c.name.toLowerCase()));\n for (const column of model.columns) {\n if (!column.data_type) {\n const catalogType = colTypes.get(column.name.toLowerCase());\n if (catalogType) {\n column.data_type = catalogType;\n }\n }\n }\n\n // Add columns from catalog that don't exist in schema.yml\n for (const [colNameLower, colType] of colTypes) {\n if (!existingCols.has(colNameLower)) {\n model.columns.push({\n name: colNameLower,\n description: '',\n data_type: colType,\n hints: [],\n });\n }\n }\n }\n } catch {\n // catalog.json unreadable or malformed — silently skip\n }\n }\n\n /**\n * Filter models by include/exclude glob patterns and tags.\n * Patterns match against model paths.\n */\n static filterModels(\n models: DbtModelSummary[],\n filters: ModelFilters\n ): DbtModelSummary[] {\n let filtered = [...models];\n\n // Apply include patterns (match any)\n if (filters.include && filters.include.length > 0) {\n filtered = filtered.filter((model) =>\n filters.include!.some((pattern) => minimatch(model.path, pattern))\n );\n }\n\n // Apply exclude patterns (exclude any matches)\n if (filters.exclude && filters.exclude.length > 0) {\n filtered = filtered.filter(\n (model) =>\n !filters.exclude!.some((pattern) => minimatch(model.path, pattern))\n );\n }\n\n // Apply tag filters (match any)\n if (filters.tags && filters.tags.length > 0) {\n filtered = filtered.filter((model) =>\n filters.tags!.some((tag) => model.tags.includes(tag))\n );\n }\n\n return filtered;\n }\n\n /**\n * Get default filters that focus on marts/reporting models.\n * These are typically the models most useful for BI dashboards.\n */\n static getDefaultFilters(): Required<ModelFilters> {\n return {\n include: ['**/marts/**', '**/reporting/**'],\n exclude: ['**/staging/**', '**/intermediate/**'],\n tags: [],\n };\n }\n}\n","import { parse as parseYaml } from 'yaml';\nimport { dirname, join } from 'path';\nimport type { DbtModel, DbtColumn } from './types.js';\n\ninterface RawSchemaYml {\n version: number;\n models?: RawModel[];\n}\n\ninterface RawModel {\n name: string;\n description?: string;\n meta?: Record<string, unknown>;\n tags?: string[];\n columns?: RawColumn[];\n}\n\ninterface RawColumn {\n name: string;\n description?: string;\n data_type?: string;\n tests?: (string | Record<string, unknown>)[];\n}\n\n/**\n * Extract hints from dbt column tests.\n * - unique → \"unique\"\n * - not_null → \"required\"\n * - relationships: { to: ref('X') } → \"fk:X\"\n */\nexport function extractHintsFromTests(tests: (string | Record<string, unknown>)[]): string[] {\n const hints: string[] = [];\n\n for (const test of tests) {\n if (typeof test === 'string') {\n if (test === 'unique') {\n hints.push('unique');\n } else if (test === 'not_null') {\n hints.push('required');\n } else if (test === 'primary_key') {\n hints.push('primary_key');\n }\n } else if (typeof test === 'object' && test !== null) {\n // Handle relationships test\n if ('relationships' in test) {\n const rel = test.relationships as { to?: string; field?: string };\n if (rel.to) {\n // Extract table name from ref('table_name')\n const match = rel.to.match(/ref\\(['\"]([^'\"]+)['\"]\\)/);\n if (match) {\n hints.push(`fk:${match[1]}`);\n }\n }\n }\n // Handle dbt_constraints for primary key\n if ('dbt_constraints' in test) {\n const constraint = test.dbt_constraints as { type?: string };\n if (constraint.type === 'primary_key') {\n hints.push('primary_key');\n }\n }\n }\n }\n\n return hints;\n}\n\n/**\n * Parse a dbt schema.yml file and extract model definitions.\n * @param content - Raw YAML content\n * @param schemaPath - Path to the schema file (e.g., \"models/marts/_schema.yml\")\n * @returns Array of parsed models\n */\nexport function parseSchemaYml(content: string, schemaPath: string): DbtModel[] {\n const parsed = parseYaml(content) as RawSchemaYml;\n\n if (!parsed?.models || !Array.isArray(parsed.models)) {\n return [];\n }\n\n const schemaDir = dirname(schemaPath);\n\n return parsed.models.map((model): DbtModel => {\n const columns: DbtColumn[] = (model.columns || []).map((col) => ({\n name: col.name,\n description: col.description || '',\n data_type: col.data_type,\n hints: col.tests ? extractHintsFromTests(col.tests) : [],\n }));\n\n return {\n name: model.name,\n path: join(schemaDir, `${model.name}.sql`),\n description: model.description || 'No description',\n tags: model.tags || [],\n meta: model.meta || {},\n columns,\n };\n });\n}\n\n/**\n * Parse dbt_project.yml to get project-level config.\n */\nexport function parseProjectYml(content: string): {\n name: string;\n version?: string;\n profile?: string;\n modelPaths: string[];\n vars: Record<string, unknown>;\n} {\n const parsed = parseYaml(content) as Record<string, unknown>;\n\n return {\n name: (parsed.name as string) || 'unknown',\n version: parsed.version as string | undefined,\n profile: parsed.profile as string | undefined,\n modelPaths: (parsed['model-paths'] as string[]) || (parsed.model_paths as string[]) || ['models'],\n vars: (parsed.vars as Record<string, unknown>) || {},\n };\n}\n","import { readFile, access, readdir } from 'fs/promises';\nimport { join, extname, relative } from 'path';\n\nexport interface YamchartModel {\n name: string;\n description: string;\n path: string; // relative to project\n source?: string; // dbt table this queries\n}\n\n/**\n * Scan yamchart models directory and extract metadata.\n * Used to cross-reference which yamchart models use which dbt tables.\n */\nexport async function scanYamchartModels(projectDir: string): Promise<YamchartModel[]> {\n const modelsDir = join(projectDir, 'models');\n const models: YamchartModel[] = [];\n\n try {\n await access(modelsDir);\n } catch {\n return [];\n }\n\n await scanModelsRecursive(modelsDir, projectDir, models);\n return models;\n}\n\nasync function scanModelsRecursive(\n dir: string,\n projectDir: string,\n models: YamchartModel[]\n): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n await scanModelsRecursive(fullPath, projectDir, models);\n } else if (extname(entry.name) === '.sql') {\n const content = await readFile(fullPath, 'utf-8');\n const metadata = parseModelMetadata(content);\n\n if (metadata.name) {\n models.push({\n name: metadata.name,\n description: metadata.description || '',\n path: relative(projectDir, fullPath),\n source: metadata.source || extractSourceFromSql(content),\n });\n }\n }\n }\n}\n\ninterface ModelMetadata {\n name?: string;\n description?: string;\n source?: string;\n}\n\n/**\n * Parse yamchart model metadata from SQL comments.\n */\nfunction parseModelMetadata(content: string): ModelMetadata {\n const metadata: ModelMetadata = {};\n\n // Match @name: value\n const nameMatch = content.match(/--\\s*@name:\\s*(.+)/);\n if (nameMatch) {\n metadata.name = nameMatch[1].trim();\n }\n\n // Match @description: value\n const descMatch = content.match(/--\\s*@description:\\s*(.+)/);\n if (descMatch) {\n metadata.description = descMatch[1].trim();\n }\n\n // Match @source: value (explicit dbt table reference)\n const sourceMatch = content.match(/--\\s*@source:\\s*(.+)/);\n if (sourceMatch) {\n metadata.source = sourceMatch[1].trim();\n }\n\n return metadata;\n}\n\n/**\n * Extract the primary table name from SQL FROM clause.\n * This is a best-effort extraction for cross-referencing.\n */\nfunction extractSourceFromSql(sql: string): string | undefined {\n // Remove comments\n const noComments = sql.replace(/--.*$/gm, '').replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n\n // Match FROM table_name (handles schema.table and database.schema.table)\n const fromMatch = noComments.match(/\\bFROM\\s+(\\{\\{\\s*ref\\(['\"]([^'\"]+)['\"]\\)\\s*\\}\\}|[\\w.]+)/i);\n\n if (fromMatch) {\n // If it's a Jinja ref(), extract the table name\n if (fromMatch[2]) {\n return fromMatch[2];\n }\n // Otherwise return the raw table name\n return fromMatch[1];\n }\n\n return undefined;\n}\n"],"mappings":"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","names":["readFile","access","join","parseYaml","join","join","readFile","access","join","join","access","readFile","parseYaml"]}
|