@datachonk/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +200 -0
  2. package/dist/__tests__/cache.test.d.ts +2 -0
  3. package/dist/__tests__/cache.test.d.ts.map +1 -0
  4. package/dist/__tests__/cache.test.js +40 -0
  5. package/dist/__tests__/cache.test.js.map +1 -0
  6. package/dist/__tests__/config.test.d.ts +2 -0
  7. package/dist/__tests__/config.test.d.ts.map +1 -0
  8. package/dist/__tests__/config.test.js +113 -0
  9. package/dist/__tests__/config.test.js.map +1 -0
  10. package/dist/__tests__/templates.test.d.ts +2 -0
  11. package/dist/__tests__/templates.test.d.ts.map +1 -0
  12. package/dist/__tests__/templates.test.js +51 -0
  13. package/dist/__tests__/templates.test.js.map +1 -0
  14. package/dist/commands/analyze.d.ts +10 -0
  15. package/dist/commands/analyze.d.ts.map +1 -0
  16. package/dist/commands/analyze.js +156 -0
  17. package/dist/commands/analyze.js.map +1 -0
  18. package/dist/commands/chat.d.ts +3 -0
  19. package/dist/commands/chat.d.ts.map +1 -0
  20. package/dist/commands/chat.js +121 -0
  21. package/dist/commands/chat.js.map +1 -0
  22. package/dist/commands/config.d.ts +2 -0
  23. package/dist/commands/config.d.ts.map +1 -0
  24. package/dist/commands/config.js +287 -0
  25. package/dist/commands/config.js.map +1 -0
  26. package/dist/commands/docs.d.ts +7 -0
  27. package/dist/commands/docs.d.ts.map +1 -0
  28. package/dist/commands/docs.js +208 -0
  29. package/dist/commands/docs.js.map +1 -0
  30. package/dist/commands/generate.d.ts +9 -0
  31. package/dist/commands/generate.d.ts.map +1 -0
  32. package/dist/commands/generate.js +130 -0
  33. package/dist/commands/generate.js.map +1 -0
  34. package/dist/commands/init.d.ts +7 -0
  35. package/dist/commands/init.d.ts.map +1 -0
  36. package/dist/commands/init.js +149 -0
  37. package/dist/commands/init.js.map +1 -0
  38. package/dist/commands/lineage.d.ts +9 -0
  39. package/dist/commands/lineage.d.ts.map +1 -0
  40. package/dist/commands/lineage.js +186 -0
  41. package/dist/commands/lineage.js.map +1 -0
  42. package/dist/commands/migrate.d.ts +3 -0
  43. package/dist/commands/migrate.d.ts.map +1 -0
  44. package/dist/commands/migrate.js +213 -0
  45. package/dist/commands/migrate.js.map +1 -0
  46. package/dist/commands/review.d.ts +8 -0
  47. package/dist/commands/review.d.ts.map +1 -0
  48. package/dist/commands/review.js +215 -0
  49. package/dist/commands/review.js.map +1 -0
  50. package/dist/commands/scan.d.ts +18 -0
  51. package/dist/commands/scan.d.ts.map +1 -0
  52. package/dist/commands/scan.js +335 -0
  53. package/dist/commands/scan.js.map +1 -0
  54. package/dist/commands/test.d.ts +3 -0
  55. package/dist/commands/test.d.ts.map +1 -0
  56. package/dist/commands/test.js +244 -0
  57. package/dist/commands/test.js.map +1 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +232 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/utils/analyzer.d.ts +39 -0
  63. package/dist/utils/analyzer.d.ts.map +1 -0
  64. package/dist/utils/analyzer.js +310 -0
  65. package/dist/utils/analyzer.js.map +1 -0
  66. package/dist/utils/cache.d.ts +98 -0
  67. package/dist/utils/cache.d.ts.map +1 -0
  68. package/dist/utils/cache.js +253 -0
  69. package/dist/utils/cache.js.map +1 -0
  70. package/dist/utils/config.d.ts +56 -0
  71. package/dist/utils/config.d.ts.map +1 -0
  72. package/dist/utils/config.js +108 -0
  73. package/dist/utils/config.js.map +1 -0
  74. package/dist/utils/generators.d.ts +8 -0
  75. package/dist/utils/generators.d.ts.map +1 -0
  76. package/dist/utils/generators.js +265 -0
  77. package/dist/utils/generators.js.map +1 -0
  78. package/dist/utils/plugins.d.ts +94 -0
  79. package/dist/utils/plugins.d.ts.map +1 -0
  80. package/dist/utils/plugins.js +164 -0
  81. package/dist/utils/plugins.js.map +1 -0
  82. package/dist/utils/templates.d.ts +57 -0
  83. package/dist/utils/templates.d.ts.map +1 -0
  84. package/dist/utils/templates.js +458 -0
  85. package/dist/utils/templates.js.map +1 -0
  86. package/package.json +69 -0
package/dist/index.js ADDED
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import chalk from "chalk";
4
+ import { analyzeCommand } from "./commands/analyze.js";
5
+ import { generateCommand } from "./commands/generate.js";
6
+ import { reviewCommand } from "./commands/review.js";
7
+ import { initCommand } from "./commands/init.js";
8
+ import { configCommand } from "./commands/config.js";
9
+ import { lineageCommand } from "./commands/lineage.js";
10
+ import { docsCommand } from "./commands/docs.js";
11
+ import { chatCommand } from "./commands/chat.js";
12
+ import { testCommand } from "./commands/test.js";
13
+ import { migrateCommand } from "./commands/migrate.js";
14
+ import { scanCommand } from "./commands/scan.js";
15
+ import { initCache, clearAll, clearExpired, printCacheStatus } from "./utils/cache.js";
16
+ import { loadPlugins, getPlugins } from "./utils/plugins.js";
17
+ import { initTemplates, listTemplates, printTemplates } from "./utils/templates.js";
18
+ // Initialize systems
19
+ initCache();
20
+ initTemplates();
21
+ loadPlugins().catch(() => { });
22
+ const program = new Command();
23
+ // ASCII art logo
24
+ const logo = `
25
+ ${chalk.hex("#E8A54B")(" ____ _ _____ _ _ ")}
26
+ ${chalk.hex("#E8A54B")(" | _ \\ __ _| |_ __ _ / ____| |__ ___ _ __ | | __")}
27
+ ${chalk.hex("#E8A54B")(" | | | |/ _\` | __/ _\` | | | '_ \\ / _ \\| '_ \\| |/ /")}
28
+ ${chalk.hex("#E8A54B")(" | |_| | (_| | || (_| | |____| | | | (_) | | | | < ")}
29
+ ${chalk.hex("#E8A54B")(" |____/ \\__,_|\\__\\__,_|\\_____|_| |_|\\___/|_| |_|_|\\_\\")}
30
+ ${chalk.gray(" ")}
31
+ ${chalk.gray(" AI-powered dbt expert • v0.1.0")}
32
+ `;
33
+ program
34
+ .name("datachonk")
35
+ .description("AI-powered dbt expert - analyze, generate, and optimize your dbt projects")
36
+ .version("0.1.0")
37
+ .addHelpText("beforeAll", logo);
38
+ // Initialize a new DataChonk config in a dbt project
39
+ program
40
+ .command("init")
41
+ .description("Initialize DataChonk in your dbt project")
42
+ .option("-w, --warehouse <type>", "Data warehouse type (snowflake|bigquery|redshift|databricks|postgres)")
43
+ .option("--api-key <key>", "DataChonk API key for AI features")
44
+ .action(initCommand);
45
+ // Analyze the dbt project
46
+ program
47
+ .command("analyze")
48
+ .description("Analyze your dbt project for issues, anti-patterns, and optimization opportunities")
49
+ .option("-p, --path <path>", "Path to dbt project", ".")
50
+ .option("-m, --model <model>", "Analyze a specific model")
51
+ .option("--fix", "Automatically fix issues where possible")
52
+ .option("--json", "Output results as JSON")
53
+ .option("-v, --verbose", "Show detailed analysis")
54
+ .action(analyzeCommand);
55
+ // Generate dbt code
56
+ program
57
+ .command("generate")
58
+ .description("Generate dbt models, tests, and documentation using AI")
59
+ .argument("<type>", "What to generate: staging|intermediate|mart|snapshot|source|test|docs")
60
+ .option("-n, --name <name>", "Name for the generated model")
61
+ .option("-s, --source <source>", "Source table or model to base generation on")
62
+ .option("-o, --output <path>", "Output path for generated files")
63
+ .option("--dry-run", "Preview generated code without writing files")
64
+ .action(generateCommand);
65
+ // Review code
66
+ program
67
+ .command("review")
68
+ .description("Get an AI-powered code review of your dbt models")
69
+ .argument("[files...]", "Files to review (defaults to staged git changes)")
70
+ .option("--strict", "Enable strict review mode")
71
+ .option("--json", "Output results as JSON")
72
+ .action(reviewCommand);
73
+ // Database discovery scan
74
+ program
75
+ .command("scan")
76
+ .description("Scan a database to discover tables, columns, and relationships")
77
+ .option("-t, --type <type>", "Database type (postgresql|mysql|snowflake|bigquery|redshift)")
78
+ .option("-h, --host <host>", "Database host")
79
+ .option("-p, --port <port>", "Database port")
80
+ .option("-d, --database <db>", "Database name")
81
+ .option("-u, --user <user>", "Database user")
82
+ .option("--password <pass>", "Database password")
83
+ .option("-w, --warehouse <wh>", "Snowflake warehouse")
84
+ .option("-a, --account <acc>", "Snowflake account")
85
+ .option("--project <proj>", "BigQuery project ID")
86
+ .option("-s, --schemas <schemas>", "Comma-separated schemas to scan")
87
+ .option("-o, --output <file>", "Output results to file")
88
+ .option("--sync", "Sync results to DataChonk web app")
89
+ .option("--json", "Output as JSON")
90
+ .action(scanCommand);
91
+ // Lineage analysis
92
+ program
93
+ .command("lineage")
94
+ .description("Analyze and visualize model lineage")
95
+ .argument("[model]", "Model to analyze lineage for")
96
+ .option("--upstream", "Show only upstream dependencies")
97
+ .option("--downstream", "Show only downstream dependencies")
98
+ .option("--depth <n>", "Maximum depth to traverse", "10")
99
+ .option("--json", "Output as JSON")
100
+ .action(lineageCommand);
101
+ // Generate documentation
102
+ program
103
+ .command("docs")
104
+ .description("Generate or enhance dbt documentation")
105
+ .argument("[models...]", "Models to document (defaults to all)")
106
+ .option("--enhance", "Use AI to enhance existing documentation")
107
+ .option("--missing-only", "Only document models without descriptions")
108
+ .action(docsCommand);
109
+ // Configuration management
110
+ program
111
+ .command("config")
112
+ .description("Manage DataChonk configuration")
113
+ .argument("<action>", "Action: get|set|list|reset")
114
+ .argument("[key]", "Configuration key")
115
+ .argument("[value]", "Configuration value (for set)")
116
+ .action(configCommand);
117
+ // Interactive AI chat
118
+ program.addCommand(chatCommand);
119
+ // Test runner
120
+ program.addCommand(testCommand);
121
+ // SQL migration tool
122
+ program.addCommand(migrateCommand);
123
+ // Cache management
124
+ program
125
+ .command("cache")
126
+ .description("Manage the DataChonk cache")
127
+ .argument("<action>", "Action: status|clear|clear-expired")
128
+ .action(async (action) => {
129
+ switch (action) {
130
+ case "status":
131
+ printCacheStatus();
132
+ break;
133
+ case "clear":
134
+ const cleared = clearAll();
135
+ console.log(chalk.green(`Cleared ${cleared} cache entries`));
136
+ break;
137
+ case "clear-expired":
138
+ const expired = clearExpired();
139
+ console.log(chalk.green(`Cleared ${expired} expired cache entries`));
140
+ break;
141
+ default:
142
+ console.log(chalk.red(`Unknown action: ${action}`));
143
+ console.log(chalk.gray("Valid actions: status, clear, clear-expired"));
144
+ }
145
+ });
146
+ // Plugin management
147
+ program
148
+ .command("plugins")
149
+ .description("List loaded plugins")
150
+ .action(() => {
151
+ const plugins = getPlugins();
152
+ if (plugins.length === 0) {
153
+ console.log(chalk.yellow("\nNo plugins loaded"));
154
+ console.log(chalk.gray("Add plugins to .datachonk/plugins/ or install npm packages starting with datachonk-plugin-"));
155
+ return;
156
+ }
157
+ console.log(chalk.bold("\nLoaded Plugins"));
158
+ console.log(chalk.gray("─".repeat(40)));
159
+ for (const plugin of plugins) {
160
+ console.log(` ${chalk.cyan(plugin.name)} (${plugin.type})`);
161
+ }
162
+ });
163
+ // Template management
164
+ program
165
+ .command("templates")
166
+ .description("List available code templates")
167
+ .option("-c, --category <cat>", "Filter by category")
168
+ .action((options) => {
169
+ if (options.category) {
170
+ const templates = listTemplates(options.category);
171
+ console.log(chalk.bold(`\n${options.category} Templates`));
172
+ for (const t of templates) {
173
+ console.log(` ${chalk.cyan(t.name)} - ${t.description}`);
174
+ }
175
+ }
176
+ else {
177
+ printTemplates();
178
+ }
179
+ });
180
+ // Quick commands
181
+ program
182
+ .command("lint")
183
+ .description("Alias for 'analyze --fix' - lint and fix issues")
184
+ .option("-p, --path <path>", "Path to dbt project", ".")
185
+ .action(async (options) => {
186
+ await analyzeCommand({ ...options, fix: true });
187
+ });
188
+ program
189
+ .command("check")
190
+ .description("Alias for 'analyze' - check for issues without fixing")
191
+ .option("-p, --path <path>", "Path to dbt project", ".")
192
+ .action(analyzeCommand);
193
+ // Ask a quick question
194
+ program
195
+ .command("ask")
196
+ .description("Ask a quick dbt question without entering chat mode")
197
+ .argument("<question...>", "Your question")
198
+ .action(async (question) => {
199
+ const { loadConfig, getApiUrl } = await import("./utils/config.js");
200
+ const config = loadConfig();
201
+ const apiKey = config.ai?.apiKey || process.env.DATACHONK_API_KEY;
202
+ if (!apiKey) {
203
+ console.log(chalk.red("No API key. Run: datachonk config set apiKey <key>"));
204
+ process.exit(1);
205
+ }
206
+ const ora = (await import("ora")).default;
207
+ const spinner = ora("Thinking...").start();
208
+ try {
209
+ const apiUrl = getApiUrl();
210
+ const response = await fetch(`${apiUrl}/api/chat`, {
211
+ method: "POST",
212
+ headers: {
213
+ "Content-Type": "application/json",
214
+ "Authorization": `Bearer ${apiKey}`,
215
+ },
216
+ body: JSON.stringify({
217
+ messages: [{ role: "user", content: question.join(" ") }],
218
+ stream: false,
219
+ }),
220
+ });
221
+ const data = await response.json();
222
+ spinner.stop();
223
+ console.log(chalk.cyan("\n" + (data.content || data.message || "No response") + "\n"));
224
+ }
225
+ catch (error) {
226
+ spinner.fail("Failed");
227
+ console.error(error);
228
+ }
229
+ });
230
+ // Parse and run
231
+ program.parse();
232
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface Issue {
2
+ pattern: string;
3
+ severity: "critical" | "high" | "medium" | "low";
4
+ location: string;
5
+ fix: string;
6
+ explanation: string;
7
+ line?: number;
8
+ }
9
+ export interface ParsedModel {
10
+ name: string;
11
+ type: "staging" | "intermediate" | "fact" | "dimension" | "snapshot" | "unknown";
12
+ materialization: string | null;
13
+ config: Record<string, unknown>;
14
+ refs: string[];
15
+ sources: Array<{
16
+ schema: string;
17
+ table: string;
18
+ }>;
19
+ columns: string[];
20
+ hasTests: boolean;
21
+ cteCount: number;
22
+ lineCount: number;
23
+ }
24
+ export declare function parseModel(content: string, fileName: string): ParsedModel;
25
+ export declare function detectAntiPatterns(model: ParsedModel, warehouse: string): Issue[];
26
+ export declare function detectAntiPatternsFromContent(content: string, model: ParsedModel, warehouse: string): Issue[];
27
+ export declare function reviewCode(model: ParsedModel, content: string, warehouse: string, strict?: boolean): {
28
+ score: number;
29
+ overall: "approve" | "request_changes" | "comment";
30
+ strengths: string[];
31
+ issues: Array<{
32
+ severity: string;
33
+ title: string;
34
+ description: string;
35
+ suggestion: string;
36
+ line?: number;
37
+ }>;
38
+ };
39
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/utils/analyzer.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;IACjF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAqEzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,EAAE,CAQjF;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,MAAM,GAChB,KAAK,EAAE,CAiKT;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,UAAQ,GACb;IACD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;IACnD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ,CA8FA"}
@@ -0,0 +1,310 @@
1
+ export function parseModel(content, fileName) {
2
+ const sqlLower = content.toLowerCase();
3
+ // Determine type from file name
4
+ let type = "unknown";
5
+ if (fileName.startsWith("stg_"))
6
+ type = "staging";
7
+ else if (fileName.startsWith("int_"))
8
+ type = "intermediate";
9
+ else if (fileName.startsWith("fct_"))
10
+ type = "fact";
11
+ else if (fileName.startsWith("dim_"))
12
+ type = "dimension";
13
+ else if (fileName.includes("snapshot"))
14
+ type = "snapshot";
15
+ // Extract config
16
+ const configMatch = content.match(/\{\{\s*config\s*\(([\s\S]*?)\)\s*\}\}/);
17
+ let config = {};
18
+ let materialization = null;
19
+ if (configMatch) {
20
+ const configStr = configMatch[1];
21
+ const matMatch = configStr.match(/materialized\s*=\s*['"](\w+)['"]/);
22
+ if (matMatch)
23
+ materialization = matMatch[1];
24
+ // Parse other config options
25
+ const kvPairs = configStr.matchAll(/(\w+)\s*=\s*(['"]([^'"]+)['"]|(\w+))/g);
26
+ for (const match of kvPairs) {
27
+ config[match[1]] = match[3] || match[4];
28
+ }
29
+ }
30
+ // Extract refs
31
+ const refs = [];
32
+ const refMatches = content.matchAll(/\{\{\s*ref\(['"]([^'"]+)['"]\)\s*\}\}/g);
33
+ for (const match of refMatches) {
34
+ refs.push(match[1]);
35
+ }
36
+ // Extract sources
37
+ const sources = [];
38
+ const sourceMatches = content.matchAll(/\{\{\s*source\(['"]([^'"]+)['"],\s*['"]([^'"]+)['"]\)\s*\}\}/g);
39
+ for (const match of sourceMatches) {
40
+ sources.push({ schema: match[1], table: match[2] });
41
+ }
42
+ // Count CTEs
43
+ const cteCount = (content.match(/\bwith\b/gi) || []).length +
44
+ (content.match(/\),\s*\w+\s+as\s*\(/gi) || []).length;
45
+ // Extract columns from final SELECT
46
+ const columns = [];
47
+ const selectMatch = content.match(/select\s+([\s\S]+?)\s+from/i);
48
+ if (selectMatch && !selectMatch[1].includes("*")) {
49
+ const columnPatterns = selectMatch[1].split(/,(?![^(]*\))/);
50
+ for (const pattern of columnPatterns) {
51
+ const aliasMatch = pattern.trim().match(/(?:as\s+)?(\w+)\s*$/i);
52
+ if (aliasMatch)
53
+ columns.push(aliasMatch[1]);
54
+ }
55
+ }
56
+ return {
57
+ name: fileName,
58
+ type,
59
+ materialization,
60
+ config,
61
+ refs,
62
+ sources,
63
+ columns,
64
+ hasTests: false, // Determined externally
65
+ cteCount,
66
+ lineCount: content.split("\n").length,
67
+ };
68
+ }
69
+ export function detectAntiPatterns(model, warehouse) {
70
+ const issues = [];
71
+ // This would analyze based on the model structure
72
+ // For now, we'll use simple pattern matching on the original content
73
+ // In a real implementation, we'd pass the content as well
74
+ return issues;
75
+ }
76
+ export function detectAntiPatternsFromContent(content, model, warehouse) {
77
+ const issues = [];
78
+ const sqlLower = content.toLowerCase();
79
+ const lines = content.split("\n");
80
+ // SELECT *
81
+ if (sqlLower.includes("select *") && !sqlLower.includes("select * from (")) {
82
+ const lineNum = lines.findIndex(l => l.toLowerCase().includes("select *")) + 1;
83
+ issues.push({
84
+ pattern: "SELECT * in production",
85
+ severity: "high",
86
+ location: `Line ${lineNum}`,
87
+ fix: "List specific columns needed",
88
+ explanation: "SELECT * is fragile (breaks when source adds columns), expensive (transfers unnecessary data), and hides dependencies.",
89
+ line: lineNum,
90
+ });
91
+ }
92
+ // Business logic in staging
93
+ if (model.type === "staging") {
94
+ if (sqlLower.includes(" join ") && !sqlLower.includes("deduplication")) {
95
+ const lineNum = lines.findIndex(l => l.toLowerCase().includes(" join ")) + 1;
96
+ issues.push({
97
+ pattern: "Join in staging model",
98
+ severity: "medium",
99
+ location: `Line ${lineNum}`,
100
+ fix: "Move joins to intermediate or mart layer",
101
+ explanation: "Staging should be 1:1 with source. Joins belong in intermediate models where business logic lives.",
102
+ line: lineNum,
103
+ });
104
+ }
105
+ if (model.refs.length > 0) {
106
+ issues.push({
107
+ pattern: "ref() in staging model",
108
+ severity: "high",
109
+ location: "Model references",
110
+ fix: "Use source() instead of ref() in staging models",
111
+ explanation: "Staging models should only reference sources, not other models.",
112
+ });
113
+ }
114
+ }
115
+ // Missing incremental config
116
+ if (sqlLower.includes("is_incremental()") && !model.config.unique_key) {
117
+ issues.push({
118
+ pattern: "Incremental without unique_key",
119
+ severity: "critical",
120
+ location: "Config block",
121
+ fix: "Add unique_key to config for merge/delete+insert strategies",
122
+ explanation: "Without unique_key, merge strategy won't work correctly and you'll accumulate duplicates.",
123
+ });
124
+ }
125
+ // Unbounded incremental
126
+ if (sqlLower.includes("is_incremental()")) {
127
+ const hasUpperBound = sqlLower.includes("< current") ||
128
+ sqlLower.includes("< getdate") ||
129
+ sqlLower.includes("< now()") ||
130
+ sqlLower.includes("< sysdate");
131
+ if (!hasUpperBound) {
132
+ issues.push({
133
+ pattern: "Unbounded incremental filter",
134
+ severity: "medium",
135
+ location: "Incremental WHERE clause",
136
+ fix: "Add upper bound: AND updated_at < current_timestamp()",
137
+ explanation: "Future-dated rows will be processed repeatedly without an upper bound.",
138
+ });
139
+ }
140
+ }
141
+ // Hardcoded dates
142
+ const hardcodedDateMatch = content.match(/'(20\d{2}-\d{2}-\d{2})'/);
143
+ if (hardcodedDateMatch) {
144
+ const lineNum = lines.findIndex(l => l.includes(hardcodedDateMatch[0])) + 1;
145
+ issues.push({
146
+ pattern: "Hardcoded date literal",
147
+ severity: "medium",
148
+ location: `Line ${lineNum}: ${hardcodedDateMatch[0]}`,
149
+ fix: "Use var() or dbt_date macros",
150
+ explanation: "Hardcoded dates require manual updates and differ between environments.",
151
+ line: lineNum,
152
+ });
153
+ }
154
+ // DISTINCT after JOIN
155
+ if (sqlLower.includes("select distinct") && sqlLower.includes(" join ")) {
156
+ issues.push({
157
+ pattern: "DISTINCT after JOIN (possible fan-out fix)",
158
+ severity: "high",
159
+ location: "SELECT DISTINCT with JOIN",
160
+ fix: "Fix the join condition or deduplicate upstream",
161
+ explanation: "DISTINCT after JOIN often masks a fan-out problem. Fix the root cause instead.",
162
+ });
163
+ }
164
+ // Cross join or Cartesian product
165
+ if (sqlLower.includes("cross join")) {
166
+ const lineNum = lines.findIndex(l => l.toLowerCase().includes("cross join")) + 1;
167
+ issues.push({
168
+ pattern: "CROSS JOIN detected",
169
+ severity: "critical",
170
+ location: `Line ${lineNum}`,
171
+ fix: "Ensure CROSS JOIN is intentional; consider alternatives",
172
+ explanation: "CROSS JOINs create Cartesian products. Even small tables (1K x 1K = 1M rows) can explode.",
173
+ line: lineNum,
174
+ });
175
+ }
176
+ // Function on filter columns
177
+ const funcOnFilterMatch = sqlLower.match(/where\s+(\w+)\(.*?\)\s*=/i) ||
178
+ sqlLower.match(/on\s+(\w+)\(.*?\)\s*=/i);
179
+ if (funcOnFilterMatch) {
180
+ issues.push({
181
+ pattern: "Function on filter/join column",
182
+ severity: "high",
183
+ location: "WHERE/ON clause",
184
+ fix: "Move transformation to other side of comparison or materialize",
185
+ explanation: "Functions on filter columns prevent partition pruning and index usage.",
186
+ });
187
+ }
188
+ // Warehouse-specific checks
189
+ if (warehouse === "snowflake") {
190
+ // Check for non-deterministic functions without proper handling
191
+ if (sqlLower.includes("uuid_string()") && model.materialization === "view") {
192
+ issues.push({
193
+ pattern: "Non-deterministic function in view",
194
+ severity: "medium",
195
+ location: "UUID generation",
196
+ fix: "Materialize as table or use consistent key generation",
197
+ explanation: "UUID_STRING() in views generates new values on each query.",
198
+ });
199
+ }
200
+ }
201
+ if (warehouse === "bigquery") {
202
+ // Check for LIMIT without ORDER BY
203
+ if (sqlLower.includes("limit") && !sqlLower.includes("order by")) {
204
+ issues.push({
205
+ pattern: "LIMIT without ORDER BY",
206
+ severity: "medium",
207
+ location: "LIMIT clause",
208
+ fix: "Add ORDER BY for deterministic results",
209
+ explanation: "BigQuery doesn't guarantee row order without ORDER BY.",
210
+ });
211
+ }
212
+ }
213
+ // Large CTE count
214
+ if (model.cteCount > 7) {
215
+ issues.push({
216
+ pattern: "High CTE count",
217
+ severity: "low",
218
+ location: `${model.cteCount} CTEs detected`,
219
+ fix: "Consider breaking into separate intermediate models",
220
+ explanation: "Many CTEs can be hard to maintain and debug. Consider refactoring.",
221
+ });
222
+ }
223
+ return issues;
224
+ }
225
+ export function reviewCode(model, content, warehouse, strict = false) {
226
+ const strengths = [];
227
+ const issues = [];
228
+ let score = 100;
229
+ const sqlLower = content.toLowerCase();
230
+ // Check for good patterns (add points)
231
+ if (content.includes("config(")) {
232
+ strengths.push("Uses config block for model configuration");
233
+ }
234
+ if (content.includes("-- ") || content.includes("/*")) {
235
+ strengths.push("Code includes comments");
236
+ }
237
+ if (sqlLower.includes("coalesce") || sqlLower.includes("ifnull") || sqlLower.includes("nvl")) {
238
+ strengths.push("Handles null values appropriately");
239
+ }
240
+ if (model.materialization === "incremental") {
241
+ strengths.push("Uses incremental materialization for efficiency");
242
+ }
243
+ if (model.cteCount > 0 && model.cteCount <= 5) {
244
+ strengths.push("Uses CTEs for readable organization");
245
+ }
246
+ // Detect anti-patterns
247
+ const antiPatterns = detectAntiPatternsFromContent(content, model, warehouse);
248
+ for (const ap of antiPatterns) {
249
+ issues.push({
250
+ severity: ap.severity,
251
+ title: ap.pattern,
252
+ description: ap.explanation,
253
+ suggestion: ap.fix,
254
+ line: ap.line,
255
+ });
256
+ // Deduct points based on severity
257
+ switch (ap.severity) {
258
+ case "critical":
259
+ score -= 25;
260
+ break;
261
+ case "high":
262
+ score -= 15;
263
+ break;
264
+ case "medium":
265
+ score -= 10;
266
+ break;
267
+ case "low":
268
+ score -= 5;
269
+ break;
270
+ }
271
+ }
272
+ // Additional strict checks
273
+ if (strict) {
274
+ // Check for missing descriptions
275
+ if (!content.includes("description")) {
276
+ issues.push({
277
+ severity: "low",
278
+ title: "Missing model description",
279
+ description: "Model has no description in config or YAML",
280
+ suggestion: "Add a description explaining the model's purpose",
281
+ });
282
+ score -= 5;
283
+ }
284
+ // Check for consistent naming
285
+ if (model.type !== "unknown" && !model.name.startsWith(model.type.slice(0, 3) + "_")) {
286
+ issues.push({
287
+ severity: "low",
288
+ title: "Inconsistent naming convention",
289
+ description: `Model type appears to be ${model.type} but name doesn't follow convention`,
290
+ suggestion: `Rename to ${model.type.slice(0, 3)}_${model.name}`,
291
+ });
292
+ score -= 5;
293
+ }
294
+ }
295
+ // Ensure score is within bounds
296
+ score = Math.max(0, Math.min(100, score));
297
+ // Determine overall verdict
298
+ let overall = "approve";
299
+ if (issues.some(i => i.severity === "critical")) {
300
+ overall = "request_changes";
301
+ }
302
+ else if (issues.some(i => i.severity === "high")) {
303
+ overall = strict ? "request_changes" : "comment";
304
+ }
305
+ else if (issues.length > 0) {
306
+ overall = "comment";
307
+ }
308
+ return { score, overall, strengths, issues };
309
+ }
310
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/utils/analyzer.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,QAAgB;IAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAEvC,gCAAgC;IAChC,IAAI,IAAI,GAAwB,SAAS,CAAC;IAC1C,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,IAAI,GAAG,SAAS,CAAC;SAC7C,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,IAAI,GAAG,cAAc,CAAC;SACvD,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,IAAI,GAAG,MAAM,CAAC;SAC/C,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,IAAI,GAAG,WAAW,CAAC;SACpD,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,IAAI,GAAG,UAAU,CAAC;IAE1D,iBAAiB;IACjB,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3E,IAAI,MAAM,GAA4B,EAAE,CAAC;IACzC,IAAI,eAAe,GAAkB,IAAI,CAAC;IAE1C,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACrE,IAAI,QAAQ;YAAE,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE5C,6BAA6B;QAC7B,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,CAAC,uCAAuC,CAAC,CAAC;QAC5E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,eAAe;IACf,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,wCAAwC,CAAC,CAAC;IAC9E,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAED,kBAAkB;IAClB,MAAM,OAAO,GAA6C,EAAE,CAAC;IAC7D,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,+DAA+D,CAAC,CAAC;IACxG,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,aAAa;IACb,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM;QAC1C,CAAC,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAEvE,oCAAoC;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjE,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjD,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5D,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAChE,IAAI,UAAU;gBAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI;QACJ,eAAe;QACf,MAAM;QACN,IAAI;QACJ,OAAO;QACP,OAAO;QACP,QAAQ,EAAE,KAAK,EAAE,wBAAwB;QACzC,QAAQ;QACR,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAkB,EAAE,SAAiB;IACtE,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,kDAAkD;IAClD,qEAAqE;IACrE,0DAA0D;IAE1D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,OAAe,EACf,KAAkB,EAClB,SAAiB;IAEjB,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,WAAW;IACX,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC3E,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/E,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,wBAAwB;YACjC,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,QAAQ,OAAO,EAAE;YAC3B,GAAG,EAAE,8BAA8B;YACnC,WAAW,EAAE,wHAAwH;YACrI,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YACvE,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC;YAC7E,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,uBAAuB;gBAChC,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,QAAQ,OAAO,EAAE;gBAC3B,GAAG,EAAE,0CAA0C;gBAC/C,WAAW,EAAE,oGAAoG;gBACjH,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,wBAAwB;gBACjC,QAAQ,EAAE,MAAM;gBAChB,QAAQ,EAAE,kBAAkB;gBAC5B,GAAG,EAAE,iDAAiD;gBACtD,WAAW,EAAE,iEAAiE;aAC/E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,IAAI,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,gCAAgC;YACzC,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,cAAc;YACxB,GAAG,EAAE,6DAA6D;YAClE,WAAW,EAAE,2FAA2F;SACzG,CAAC,CAAC;IACL,CAAC;IAED,wBAAwB;IACxB,IAAI,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC1C,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC9B,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC9B,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC5B,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,8BAA8B;gBACvC,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,0BAA0B;gBACpC,GAAG,EAAE,uDAAuD;gBAC5D,WAAW,EAAE,wEAAwE;aACtF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,MAAM,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACpE,IAAI,kBAAkB,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,wBAAwB;YACjC,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,QAAQ,OAAO,KAAK,kBAAkB,CAAC,CAAC,CAAC,EAAE;YACrD,GAAG,EAAE,8BAA8B;YACnC,WAAW,EAAE,yEAAyE;YACtF,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;IACL,CAAC;IAED,sBAAsB;IACtB,IAAI,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,4CAA4C;YACrD,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,2BAA2B;YACrC,GAAG,EAAE,gDAAgD;YACrD,WAAW,EAAE,gFAAgF;SAC9F,CAAC,CAAC;IACL,CAAC;IAED,kCAAkC;IAClC,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC;QACjF,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,qBAAqB;YAC9B,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,QAAQ,OAAO,EAAE;YAC3B,GAAG,EAAE,yDAAyD;YAC9D,WAAW,EAAE,2FAA2F;YACxG,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;IACL,CAAC;IAED,6BAA6B;IAC7B,MAAM,iBAAiB,GAAG,QAAQ,CAAC,KAAK,CAAC,2BAA2B,CAAC;QAC3C,QAAQ,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACnE,IAAI,iBAAiB,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,gCAAgC;YACzC,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,iBAAiB;YAC3B,GAAG,EAAE,gEAAgE;YACrE,WAAW,EAAE,wEAAwE;SACtF,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QAC9B,gEAAgE;QAChE,IAAI,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,KAAK,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;YAC3E,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,oCAAoC;gBAC7C,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,iBAAiB;gBAC3B,GAAG,EAAE,uDAAuD;gBAC5D,WAAW,EAAE,4DAA4D;aAC1E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC7B,mCAAmC;QACnC,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjE,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,wBAAwB;gBACjC,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,cAAc;gBACxB,GAAG,EAAE,wCAAwC;gBAC7C,WAAW,EAAE,wDAAwD;aACtE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,gBAAgB;YACzB,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,GAAG,KAAK,CAAC,QAAQ,gBAAgB;YAC3C,GAAG,EAAE,qDAAqD;YAC1D,WAAW,EAAE,oEAAoE;SAClF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,KAAkB,EAClB,OAAe,EACf,SAAiB,EACjB,MAAM,GAAG,KAAK;IAad,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAMP,EAAE,CAAC;IAER,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAEvC,uCAAuC;IACvC,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,SAAS,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,SAAS,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7F,SAAS,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,KAAK,CAAC,eAAe,KAAK,aAAa,EAAE,CAAC;QAC5C,SAAS,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;QAC9C,SAAS,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IACxD,CAAC;IAED,uBAAuB;IACvB,MAAM,YAAY,GAAG,6BAA6B,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAE9E,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC;YACV,QAAQ,EAAE,EAAE,CAAC,QAAQ;YACrB,KAAK,EAAE,EAAE,CAAC,OAAO;YACjB,WAAW,EAAE,EAAE,CAAC,WAAW;YAC3B,UAAU,EAAE,EAAE,CAAC,GAAG;YAClB,IAAI,EAAE,EAAE,CAAC,IAAI;SACd,CAAC,CAAC;QAEH,kCAAkC;QAClC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC;YACpB,KAAK,UAAU;gBAAE,KAAK,IAAI,EAAE,CAAC;gBAAC,MAAM;YACpC,KAAK,MAAM;gBAAE,KAAK,IAAI,EAAE,CAAC;gBAAC,MAAM;YAChC,KAAK,QAAQ;gBAAE,KAAK,IAAI,EAAE,CAAC;gBAAC,MAAM;YAClC,KAAK,KAAK;gBAAE,KAAK,IAAI,CAAC,CAAC;gBAAC,MAAM;QAChC,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,IAAI,MAAM,EAAE,CAAC;QACX,iCAAiC;QACjC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,KAAK;gBACf,KAAK,EAAE,2BAA2B;gBAClC,WAAW,EAAE,4CAA4C;gBACzD,UAAU,EAAE,kDAAkD;aAC/D,CAAC,CAAC;YACH,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QAED,8BAA8B;QAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YACrF,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,KAAK;gBACf,KAAK,EAAE,gCAAgC;gBACvC,WAAW,EAAE,4BAA4B,KAAK,CAAC,IAAI,qCAAqC;gBACxF,UAAU,EAAE,aAAa,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE;aAChE,CAAC,CAAC;YACH,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAE1C,4BAA4B;IAC5B,IAAI,OAAO,GAA8C,SAAS,CAAC;IACnE,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,GAAG,iBAAiB,CAAC;IAC9B,CAAC;SAAM,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,CAAC;QACnD,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IACnD,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,SAAS,CAAC;IACtB,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC/C,CAAC"}