@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.
- package/README.md +200 -0
- package/dist/__tests__/cache.test.d.ts +2 -0
- package/dist/__tests__/cache.test.d.ts.map +1 -0
- package/dist/__tests__/cache.test.js +40 -0
- package/dist/__tests__/cache.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +113 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/templates.test.d.ts +2 -0
- package/dist/__tests__/templates.test.d.ts.map +1 -0
- package/dist/__tests__/templates.test.js +51 -0
- package/dist/__tests__/templates.test.js.map +1 -0
- package/dist/commands/analyze.d.ts +10 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +156 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +121 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +287 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/docs.d.ts +7 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +208 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/generate.d.ts +9 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +130 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +149 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lineage.d.ts +9 -0
- package/dist/commands/lineage.d.ts.map +1 -0
- package/dist/commands/lineage.js +186 -0
- package/dist/commands/lineage.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +213 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/review.d.ts +8 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +215 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/scan.d.ts +18 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +335 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +244 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/analyzer.d.ts +39 -0
- package/dist/utils/analyzer.d.ts.map +1 -0
- package/dist/utils/analyzer.js +310 -0
- package/dist/utils/analyzer.js.map +1 -0
- package/dist/utils/cache.d.ts +98 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +253 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +56 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +108 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/generators.d.ts +8 -0
- package/dist/utils/generators.d.ts.map +1 -0
- package/dist/utils/generators.js +265 -0
- package/dist/utils/generators.js.map +1 -0
- package/dist/utils/plugins.d.ts +94 -0
- package/dist/utils/plugins.d.ts.map +1 -0
- package/dist/utils/plugins.js +164 -0
- package/dist/utils/plugins.js.map +1 -0
- package/dist/utils/templates.d.ts +57 -0
- package/dist/utils/templates.d.ts.map +1 -0
- package/dist/utils/templates.js +458 -0
- package/dist/utils/templates.js.map +1 -0
- 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"}
|