mgcheck 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/LICENSE +21 -0
- package/README.md +416 -0
- package/dist/activity-log-ETNHCZ7B.js +13 -0
- package/dist/activity-log-ETNHCZ7B.js.map +1 -0
- package/dist/applier-TLGJ6SZ2.js +9 -0
- package/dist/applier-TLGJ6SZ2.js.map +1 -0
- package/dist/chunk-3G3R2NM3.js +73 -0
- package/dist/chunk-3G3R2NM3.js.map +1 -0
- package/dist/chunk-4YTQB62X.js +261 -0
- package/dist/chunk-4YTQB62X.js.map +1 -0
- package/dist/chunk-7ENQ5WVM.js +97 -0
- package/dist/chunk-7ENQ5WVM.js.map +1 -0
- package/dist/chunk-7JBFSZBD.js +1212 -0
- package/dist/chunk-7JBFSZBD.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +99 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +232 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/installer-KGWDJ6OR.js +323 -0
- package/dist/installer-KGWDJ6OR.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +158 -0
- package/dist/mcp/server.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// src/reporter/terminal.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { basename } from "path";
|
|
4
|
+
function renderTerminalReport(report) {
|
|
5
|
+
const lines = [];
|
|
6
|
+
const { migration, execution, violations, passed, hasDestructive } = report;
|
|
7
|
+
lines.push("");
|
|
8
|
+
lines.push(chalk.bold.cyan(" mgcheck") + chalk.dim(" v0.1.0 \u2014 Migration Guardian"));
|
|
9
|
+
lines.push("");
|
|
10
|
+
const formatLabel = migration.format === "raw-sql" ? "Raw SQL" : migration.format === "prisma" ? "Prisma" : "Drizzle";
|
|
11
|
+
lines.push(chalk.dim(" \u{1F4C1} Detected: ") + chalk.white(formatLabel + " migration"));
|
|
12
|
+
for (const file of migration.files) {
|
|
13
|
+
lines.push(chalk.dim(" \u{1F4C4} File: ") + chalk.white(basename(file.path)));
|
|
14
|
+
}
|
|
15
|
+
lines.push("");
|
|
16
|
+
if (report.statements && report.statements.length > 0) {
|
|
17
|
+
lines.push(chalk.bold(` \u{1F4CB} Inspected Statements (${report.statements.length})`));
|
|
18
|
+
for (let i = 0; i < report.statements.length; i++) {
|
|
19
|
+
const s = report.statements[i];
|
|
20
|
+
const isLast = i === report.statements.length - 1;
|
|
21
|
+
const prefix = isLast ? " \u2514\u2500 " : " \u251C\u2500 ";
|
|
22
|
+
const cleanSql = s.raw.replace(/\s+/g, " ").trim();
|
|
23
|
+
const truncated = cleanSql.length > 70 ? cleanSql.substring(0, 67) + "..." : cleanSql;
|
|
24
|
+
lines.push(chalk.dim(prefix) + chalk.cyan(`[Line ${s.line}] `) + chalk.white(truncated));
|
|
25
|
+
}
|
|
26
|
+
lines.push("");
|
|
27
|
+
}
|
|
28
|
+
if (execution) {
|
|
29
|
+
lines.push(chalk.bold(" \u26A1 Shadow Database"));
|
|
30
|
+
if (report.mode === "run") {
|
|
31
|
+
lines.push(chalk.dim(" \u251C\u2500 Provider: ") + chalk.white("Shadow Postgres"));
|
|
32
|
+
}
|
|
33
|
+
if (execution.success) {
|
|
34
|
+
lines.push(
|
|
35
|
+
chalk.dim(" \u2514\u2500 Execution: ") + chalk.green("\u2705 Success") + chalk.dim(
|
|
36
|
+
` (${execution.executionTimeMs}ms, ${execution.statementsExecuted} statement${execution.statementsExecuted !== 1 ? "s" : ""})`
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
} else {
|
|
40
|
+
lines.push(chalk.dim(" \u2514\u2500 Execution: ") + chalk.red("\u274C Failed"));
|
|
41
|
+
lines.push("");
|
|
42
|
+
lines.push(chalk.red(" Error:"));
|
|
43
|
+
const errorLines = (execution.error || "Unknown error").split("\n");
|
|
44
|
+
for (const errorLine of errorLines) {
|
|
45
|
+
lines.push(chalk.red(" " + errorLine));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
lines.push("");
|
|
49
|
+
}
|
|
50
|
+
if (violations.length > 0) {
|
|
51
|
+
lines.push(chalk.bold(" \u{1F50D} Rule Analysis"));
|
|
52
|
+
lines.push("");
|
|
53
|
+
const errors = violations.filter((v) => v.severity === "error");
|
|
54
|
+
const warnings = violations.filter((v) => v.severity === "warning");
|
|
55
|
+
if (errors.length > 0 || warnings.length > 0) {
|
|
56
|
+
lines.push(
|
|
57
|
+
chalk.dim(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")
|
|
58
|
+
);
|
|
59
|
+
lines.push(
|
|
60
|
+
chalk.dim(" \u2502") + chalk.bold(" Rule ") + chalk.dim("\u2502") + chalk.bold(" Issue ") + chalk.dim("\u2502") + chalk.bold(" Severity ") + chalk.dim("\u2502")
|
|
61
|
+
);
|
|
62
|
+
lines.push(
|
|
63
|
+
chalk.dim(" \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524")
|
|
64
|
+
);
|
|
65
|
+
for (const v of violations) {
|
|
66
|
+
const ruleCol = v.ruleId.padEnd(7);
|
|
67
|
+
const issueCol = truncate(v.ruleName, 38).padEnd(38);
|
|
68
|
+
const sevIcon = v.severity === "error" ? chalk.red("\u274C error") : chalk.yellow("\u26A0 warn");
|
|
69
|
+
const sevCol = v.severity === "error" ? sevIcon + " " : sevIcon + " ";
|
|
70
|
+
lines.push(
|
|
71
|
+
chalk.dim(" \u2502 ") + chalk.white(ruleCol) + chalk.dim(" \u2502 ") + chalk.white(issueCol) + chalk.dim(" \u2502 ") + sevCol + chalk.dim("\u2502")
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
lines.push(
|
|
75
|
+
chalk.dim(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518")
|
|
76
|
+
);
|
|
77
|
+
lines.push("");
|
|
78
|
+
}
|
|
79
|
+
for (const v of violations) {
|
|
80
|
+
const icon = v.severity === "error" ? chalk.red("\u274C") : chalk.yellow("\u26A0\uFE0F");
|
|
81
|
+
lines.push(
|
|
82
|
+
` ${icon} ${chalk.bold(v.ruleId)}: ${chalk.bold(v.ruleName)}`
|
|
83
|
+
);
|
|
84
|
+
if (v.line && v.sql && v.sql !== "(no down migration file found)") {
|
|
85
|
+
lines.push(
|
|
86
|
+
chalk.dim(` Line ${v.line}: `) + chalk.gray(truncate(v.sql, 80))
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
lines.push("");
|
|
90
|
+
lines.push(chalk.dim(" Why this is dangerous:"));
|
|
91
|
+
const msgLines = wordWrap(v.message, 70);
|
|
92
|
+
for (const ml of msgLines) {
|
|
93
|
+
lines.push(chalk.white(" " + ml));
|
|
94
|
+
}
|
|
95
|
+
if (v.suggestion) {
|
|
96
|
+
lines.push("");
|
|
97
|
+
lines.push(chalk.dim(" Suggested fix:"));
|
|
98
|
+
const sugLines = v.suggestion.split("\n");
|
|
99
|
+
for (const sl of sugLines) {
|
|
100
|
+
lines.push(chalk.green(" " + sl));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
105
|
+
lines.push("");
|
|
106
|
+
}
|
|
107
|
+
} else if (report.mode === "analyze") {
|
|
108
|
+
lines.push(chalk.bold(" \u{1F50D} Rule Analysis"));
|
|
109
|
+
lines.push("");
|
|
110
|
+
lines.push(chalk.green(" \u2705 No rule violations found"));
|
|
111
|
+
lines.push("");
|
|
112
|
+
}
|
|
113
|
+
if (hasDestructive) {
|
|
114
|
+
lines.push(
|
|
115
|
+
chalk.bgRed.white.bold(" \u26A0 DESTRUCTIVE OPERATIONS DETECTED ") + chalk.red(" Use --confirm-destructive to proceed")
|
|
116
|
+
);
|
|
117
|
+
lines.push("");
|
|
118
|
+
}
|
|
119
|
+
const errorCount = violations.filter((v) => v.severity === "error").length;
|
|
120
|
+
const warnCount = violations.filter((v) => v.severity === "warning").length;
|
|
121
|
+
if (passed) {
|
|
122
|
+
lines.push(
|
|
123
|
+
chalk.bgGreen.black.bold(" \u2705 PASSED ") + chalk.green(
|
|
124
|
+
warnCount > 0 ? ` (${warnCount} warning${warnCount !== 1 ? "s" : ""})` : " All checks passed"
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
} else {
|
|
128
|
+
lines.push(
|
|
129
|
+
chalk.bgRed.white.bold(" \u274C FAILED ") + chalk.red(
|
|
130
|
+
` (${errorCount} error${errorCount !== 1 ? "s" : ""}, ${warnCount} warning${warnCount !== 1 ? "s" : ""})`
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
lines.push("");
|
|
135
|
+
return lines.join("\n");
|
|
136
|
+
}
|
|
137
|
+
function truncate(str, maxLen) {
|
|
138
|
+
const clean = str.replace(/\n/g, " ").trim();
|
|
139
|
+
if (clean.length <= maxLen) return clean;
|
|
140
|
+
return clean.substring(0, maxLen - 3) + "...";
|
|
141
|
+
}
|
|
142
|
+
function wordWrap(str, maxLen) {
|
|
143
|
+
const words = str.split(" ");
|
|
144
|
+
const lines = [];
|
|
145
|
+
let current = "";
|
|
146
|
+
for (const word of words) {
|
|
147
|
+
if (current.length + word.length + 1 > maxLen && current.length > 0) {
|
|
148
|
+
lines.push(current);
|
|
149
|
+
current = word;
|
|
150
|
+
} else {
|
|
151
|
+
current = current ? current + " " + word : word;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (current) lines.push(current);
|
|
155
|
+
return lines;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/reporter/json.ts
|
|
159
|
+
function renderJsonReport(report) {
|
|
160
|
+
return JSON.stringify(report, null, 2);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/reporter/markdown.ts
|
|
164
|
+
import { basename as basename2 } from "path";
|
|
165
|
+
function renderMarkdownReport(report) {
|
|
166
|
+
const lines = [];
|
|
167
|
+
const { migration, execution, violations, passed, hasDestructive } = report;
|
|
168
|
+
lines.push("## \u{1F6E1}\uFE0F Migration Guardian Report");
|
|
169
|
+
lines.push("");
|
|
170
|
+
if (passed) {
|
|
171
|
+
lines.push("**Status: \u2705 PASSED**");
|
|
172
|
+
} else {
|
|
173
|
+
lines.push("**Status: \u274C FAILED**");
|
|
174
|
+
}
|
|
175
|
+
lines.push("");
|
|
176
|
+
const formatLabel = migration.format === "raw-sql" ? "Raw SQL" : migration.format === "prisma" ? "Prisma" : "Drizzle";
|
|
177
|
+
lines.push(`**Format:** ${formatLabel}`);
|
|
178
|
+
lines.push(`**Files:** ${migration.files.map((f) => "`" + basename2(f.path) + "`").join(", ")}`);
|
|
179
|
+
lines.push("");
|
|
180
|
+
if (execution) {
|
|
181
|
+
lines.push("### \u26A1 Execution");
|
|
182
|
+
lines.push("");
|
|
183
|
+
if (execution.success) {
|
|
184
|
+
lines.push(
|
|
185
|
+
`\u2705 Executed successfully (${execution.statementsExecuted} statements, ${execution.executionTimeMs}ms)`
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
lines.push("\u274C Execution failed");
|
|
189
|
+
lines.push("");
|
|
190
|
+
lines.push("```");
|
|
191
|
+
lines.push(execution.error || "Unknown error");
|
|
192
|
+
lines.push("```");
|
|
193
|
+
}
|
|
194
|
+
lines.push("");
|
|
195
|
+
}
|
|
196
|
+
if (violations.length > 0) {
|
|
197
|
+
lines.push("### \u{1F50D} Rule Violations");
|
|
198
|
+
lines.push("");
|
|
199
|
+
lines.push("| Rule | Issue | Severity |");
|
|
200
|
+
lines.push("|------|-------|----------|");
|
|
201
|
+
for (const v of violations) {
|
|
202
|
+
const sevIcon = v.severity === "error" ? "\u274C error" : "\u26A0\uFE0F warning";
|
|
203
|
+
lines.push(`| ${v.ruleId} | ${v.ruleName} | ${sevIcon} |`);
|
|
204
|
+
}
|
|
205
|
+
lines.push("");
|
|
206
|
+
for (const v of violations) {
|
|
207
|
+
const icon = v.severity === "error" ? "\u274C" : "\u26A0\uFE0F";
|
|
208
|
+
lines.push(`#### ${icon} ${v.ruleId}: ${v.ruleName}`);
|
|
209
|
+
lines.push("");
|
|
210
|
+
lines.push(v.message);
|
|
211
|
+
if (v.suggestion) {
|
|
212
|
+
lines.push("");
|
|
213
|
+
lines.push("**Suggested fix:**");
|
|
214
|
+
lines.push("");
|
|
215
|
+
lines.push("```sql");
|
|
216
|
+
lines.push(v.suggestion);
|
|
217
|
+
lines.push("```");
|
|
218
|
+
}
|
|
219
|
+
lines.push("");
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
lines.push("### \u{1F50D} Rule Analysis");
|
|
223
|
+
lines.push("");
|
|
224
|
+
lines.push("\u2705 No violations found");
|
|
225
|
+
lines.push("");
|
|
226
|
+
}
|
|
227
|
+
if (hasDestructive) {
|
|
228
|
+
lines.push("> \u26A0\uFE0F **DESTRUCTIVE OPERATIONS DETECTED** \u2014 Use `--confirm-destructive` to proceed");
|
|
229
|
+
lines.push("");
|
|
230
|
+
}
|
|
231
|
+
lines.push("---");
|
|
232
|
+
lines.push(`*Generated by [mgcheck](https://github.com/yourusername/mgcheck) at ${report.timestamp}*`);
|
|
233
|
+
return lines.join("\n");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/reporter/index.ts
|
|
237
|
+
function renderReport(report, format) {
|
|
238
|
+
switch (format) {
|
|
239
|
+
case "terminal":
|
|
240
|
+
return renderTerminalReport(report);
|
|
241
|
+
case "json":
|
|
242
|
+
return renderJsonReport(report);
|
|
243
|
+
case "markdown":
|
|
244
|
+
return renderMarkdownReport(report);
|
|
245
|
+
default:
|
|
246
|
+
return renderTerminalReport(report);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function getExitCode(report) {
|
|
250
|
+
if (report.passed) {
|
|
251
|
+
const hasWarnings = report.violations.some((v) => v.severity === "warning");
|
|
252
|
+
return hasWarnings ? 2 : 0;
|
|
253
|
+
}
|
|
254
|
+
return 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export {
|
|
258
|
+
renderReport,
|
|
259
|
+
getExitCode
|
|
260
|
+
};
|
|
261
|
+
//# sourceMappingURL=chunk-4YTQB62X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reporter/terminal.ts","../src/reporter/json.ts","../src/reporter/markdown.ts","../src/reporter/index.ts"],"sourcesContent":["import type { MigrationReport } from '../core/types.js';\nimport chalk from 'chalk';\nimport { basename } from 'node:path';\n\n/**\n * Rich terminal reporter with colors, symbols, and structured output.\n */\nexport function renderTerminalReport(report: MigrationReport): string {\n const lines: string[] = [];\n const { migration, execution, violations, passed, hasDestructive } = report;\n\n // Header\n lines.push('');\n lines.push(chalk.bold.cyan(' mgcheck') + chalk.dim(' v0.1.0 â Migration Guardian'));\n lines.push('');\n\n // Migration info\n const formatLabel =\n migration.format === 'raw-sql'\n ? 'Raw SQL'\n : migration.format === 'prisma'\n ? 'Prisma'\n : 'Drizzle';\n\n lines.push(chalk.dim(' đ Detected: ') + chalk.white(formatLabel + ' migration'));\n\n for (const file of migration.files) {\n lines.push(chalk.dim(' đ File: ') + chalk.white(basename(file.path)));\n }\n\n lines.push('');\n\n // Inspected Statements\n if (report.statements && report.statements.length > 0) {\n lines.push(chalk.bold(` đ Inspected Statements (${report.statements.length})`));\n for (let i = 0; i < report.statements.length; i++) {\n const s = report.statements[i];\n const isLast = i === report.statements.length - 1;\n const prefix = isLast ? ' ââ ' : ' ââ ';\n const cleanSql = s.raw.replace(/\\s+/g, ' ').trim();\n const truncated = cleanSql.length > 70 ? cleanSql.substring(0, 67) + '...' : cleanSql;\n lines.push(chalk.dim(prefix) + chalk.cyan(`[Line ${s.line}] `) + chalk.white(truncated));\n }\n lines.push('');\n }\n\n // Shadow DB Execution\n if (execution) {\n lines.push(chalk.bold(' ⥠Shadow Database'));\n\n if (report.mode === 'run') {\n lines.push(chalk.dim(' ââ Provider: ') + chalk.white('Shadow Postgres'));\n }\n\n if (execution.success) {\n lines.push(\n chalk.dim(' ââ Execution: ') +\n chalk.green('â
Success') +\n chalk.dim(\n ` (${execution.executionTimeMs}ms, ${execution.statementsExecuted} statement${execution.statementsExecuted !== 1 ? 's' : ''})`\n )\n );\n } else {\n lines.push(chalk.dim(' ââ Execution: ') + chalk.red('â Failed'));\n lines.push('');\n lines.push(chalk.red(' Error:'));\n const errorLines = (execution.error || 'Unknown error').split('\\n');\n for (const errorLine of errorLines) {\n lines.push(chalk.red(' ' + errorLine));\n }\n }\n\n lines.push('');\n }\n\n // Rule Analysis\n if (violations.length > 0) {\n lines.push(chalk.bold(' đ Rule Analysis'));\n lines.push('');\n\n // Summary table\n const errors = violations.filter((v) => v.severity === 'error');\n const warnings = violations.filter((v) => v.severity === 'warning');\n\n if (errors.length > 0 || warnings.length > 0) {\n lines.push(\n chalk.dim(' âââââââââââŦâââââââââââââââââââââââââââââââââââââââââŦâââââââââââ')\n );\n lines.push(\n chalk.dim(' â') +\n chalk.bold(' Rule ') +\n chalk.dim('â') +\n chalk.bold(' Issue ') +\n chalk.dim('â') +\n chalk.bold(' Severity ') +\n chalk.dim('â')\n );\n lines.push(\n chalk.dim(' âââââââââââŧâââââââââââââââââââââââââââââââââââââââââŧâââââââââââ¤')\n );\n\n for (const v of violations) {\n const ruleCol = v.ruleId.padEnd(7);\n const issueCol = truncate(v.ruleName, 38).padEnd(38);\n const sevIcon = v.severity === 'error' ? chalk.red('â error') : chalk.yellow('â warn');\n const sevCol = v.severity === 'error' ? sevIcon + ' ' : sevIcon + ' ';\n\n lines.push(\n chalk.dim(' â ') + chalk.white(ruleCol) + chalk.dim(' â ') + chalk.white(issueCol) + chalk.dim(' â ') + sevCol + chalk.dim('â')\n );\n }\n\n lines.push(\n chalk.dim(' âââââââââââ´âââââââââââââââââââââââââââââââââââââââââ´âââââââââââ')\n );\n lines.push('');\n }\n\n // Detailed violations\n for (const v of violations) {\n const icon = v.severity === 'error' ? chalk.red('â') : chalk.yellow('â ī¸');\n lines.push(\n ` ${icon} ${chalk.bold(v.ruleId)}: ${chalk.bold(v.ruleName)}`\n );\n\n if (v.line && v.sql && v.sql !== '(no down migration file found)') {\n lines.push(\n chalk.dim(` Line ${v.line}: `) + chalk.gray(truncate(v.sql, 80))\n );\n }\n\n lines.push('');\n lines.push(chalk.dim(' Why this is dangerous:'));\n const msgLines = wordWrap(v.message, 70);\n for (const ml of msgLines) {\n lines.push(chalk.white(' ' + ml));\n }\n\n if (v.suggestion) {\n lines.push('');\n lines.push(chalk.dim(' Suggested fix:'));\n const sugLines = v.suggestion.split('\\n');\n for (const sl of sugLines) {\n lines.push(chalk.green(' ' + sl));\n }\n }\n\n lines.push('');\n lines.push(chalk.dim(' ââââââââââââââââââââââââââââââââââââââââââ'));\n lines.push('');\n }\n } else if (report.mode === 'analyze') {\n lines.push(chalk.bold(' đ Rule Analysis'));\n lines.push('');\n lines.push(chalk.green(' â
No rule violations found'));\n lines.push('');\n }\n\n // Destructive warning\n if (hasDestructive) {\n lines.push(\n chalk.bgRed.white.bold(' â DESTRUCTIVE OPERATIONS DETECTED ') +\n chalk.red(' Use --confirm-destructive to proceed')\n );\n lines.push('');\n }\n\n // Final result\n const errorCount = violations.filter((v) => v.severity === 'error').length;\n const warnCount = violations.filter((v) => v.severity === 'warning').length;\n\n if (passed) {\n lines.push(\n chalk.bgGreen.black.bold(' â
PASSED ') +\n chalk.green(\n warnCount > 0\n ? ` (${warnCount} warning${warnCount !== 1 ? 's' : ''})`\n : ' All checks passed'\n )\n );\n } else {\n lines.push(\n chalk.bgRed.white.bold(' â FAILED ') +\n chalk.red(\n ` (${errorCount} error${errorCount !== 1 ? 's' : ''}, ${warnCount} warning${warnCount !== 1 ? 's' : ''})`\n )\n );\n }\n\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction truncate(str: string, maxLen: number): string {\n // Remove newlines for table display\n const clean = str.replace(/\\n/g, ' ').trim();\n if (clean.length <= maxLen) return clean;\n return clean.substring(0, maxLen - 3) + '...';\n}\n\nfunction wordWrap(str: string, maxLen: number): string[] {\n const words = str.split(' ');\n const lines: string[] = [];\n let current = '';\n\n for (const word of words) {\n if (current.length + word.length + 1 > maxLen && current.length > 0) {\n lines.push(current);\n current = word;\n } else {\n current = current ? current + ' ' + word : word;\n }\n }\n\n if (current) lines.push(current);\n return lines;\n}\n","import type { MigrationReport } from '../core/types.js';\n\n/**\n * JSON reporter â machine-readable output for CI/CD pipelines.\n * Outputs the full MigrationReport as formatted JSON.\n */\nexport function renderJsonReport(report: MigrationReport): string {\n return JSON.stringify(report, null, 2);\n}\n","import type { MigrationReport } from '../core/types.js';\nimport { basename } from 'node:path';\n\n/**\n * Markdown reporter â generates a report suitable for PR comments\n * or GitHub Action annotations.\n */\nexport function renderMarkdownReport(report: MigrationReport): string {\n const lines: string[] = [];\n const { migration, execution, violations, passed, hasDestructive } = report;\n\n // Header\n lines.push('## đĄī¸ Migration Guardian Report');\n lines.push('');\n\n // Status badge\n if (passed) {\n lines.push('**Status: â
PASSED**');\n } else {\n lines.push('**Status: â FAILED**');\n }\n lines.push('');\n\n // Migration info\n const formatLabel =\n migration.format === 'raw-sql'\n ? 'Raw SQL'\n : migration.format === 'prisma'\n ? 'Prisma'\n : 'Drizzle';\n\n lines.push(`**Format:** ${formatLabel}`);\n lines.push(`**Files:** ${migration.files.map((f) => '`' + basename(f.path) + '`').join(', ')}`);\n lines.push('');\n\n // Execution\n if (execution) {\n lines.push('### ⥠Execution');\n lines.push('');\n\n if (execution.success) {\n lines.push(\n `â
Executed successfully (${execution.statementsExecuted} statements, ${execution.executionTimeMs}ms)`\n );\n } else {\n lines.push('â Execution failed');\n lines.push('');\n lines.push('```');\n lines.push(execution.error || 'Unknown error');\n lines.push('```');\n }\n\n lines.push('');\n }\n\n // Violations\n if (violations.length > 0) {\n lines.push('### đ Rule Violations');\n lines.push('');\n lines.push('| Rule | Issue | Severity |');\n lines.push('|------|-------|----------|');\n\n for (const v of violations) {\n const sevIcon = v.severity === 'error' ? 'â error' : 'â ī¸ warning';\n lines.push(`| ${v.ruleId} | ${v.ruleName} | ${sevIcon} |`);\n }\n\n lines.push('');\n\n // Details\n for (const v of violations) {\n const icon = v.severity === 'error' ? 'â' : 'â ī¸';\n lines.push(`#### ${icon} ${v.ruleId}: ${v.ruleName}`);\n lines.push('');\n lines.push(v.message);\n\n if (v.suggestion) {\n lines.push('');\n lines.push('**Suggested fix:**');\n lines.push('');\n lines.push('```sql');\n lines.push(v.suggestion);\n lines.push('```');\n }\n\n lines.push('');\n }\n } else {\n lines.push('### đ Rule Analysis');\n lines.push('');\n lines.push('â
No violations found');\n lines.push('');\n }\n\n // Destructive warning\n if (hasDestructive) {\n lines.push('> â ī¸ **DESTRUCTIVE OPERATIONS DETECTED** â Use `--confirm-destructive` to proceed');\n lines.push('');\n }\n\n // Footer\n lines.push('---');\n lines.push(`*Generated by [mgcheck](https://github.com/yourusername/mgcheck) at ${report.timestamp}*`);\n\n return lines.join('\\n');\n}\n","import type { MigrationReport, OutputFormat } from '../core/types.js';\nimport { renderTerminalReport } from './terminal.js';\nimport { renderJsonReport } from './json.js';\nimport { renderMarkdownReport } from './markdown.js';\n\n/**\n * Render a migration report in the specified format.\n */\nexport function renderReport(report: MigrationReport, format: OutputFormat): string {\n switch (format) {\n case 'terminal':\n return renderTerminalReport(report);\n case 'json':\n return renderJsonReport(report);\n case 'markdown':\n return renderMarkdownReport(report);\n default:\n return renderTerminalReport(report);\n }\n}\n\n/**\n * Get the exit code for a migration report.\n *\n * 0 = passed\n * 1 = failed (execution error or blocking violation)\n * 2 = warnings only\n */\nexport function getExitCode(report: MigrationReport): number {\n if (report.passed) {\n const hasWarnings = report.violations.some((v) => v.severity === 'warning');\n return hasWarnings ? 2 : 0;\n }\n return 1;\n}\n\nexport { renderTerminalReport } from './terminal.js';\nexport { renderJsonReport } from './json.js';\nexport { renderMarkdownReport } from './markdown.js';\n"],"mappings":"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","names":["basename"]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/core/applier.ts
|
|
2
|
+
import pg from "pg";
|
|
3
|
+
import * as readline from "readline/promises";
|
|
4
|
+
import { stdin as input, stdout as output } from "process";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
async function applyToRealDatabase(sql, dbUrl) {
|
|
7
|
+
const client = new pg.Client({ connectionString: dbUrl });
|
|
8
|
+
const start = Date.now();
|
|
9
|
+
try {
|
|
10
|
+
await client.connect();
|
|
11
|
+
await client.query("BEGIN");
|
|
12
|
+
await client.query(sql);
|
|
13
|
+
await client.query("COMMIT");
|
|
14
|
+
const executionTimeMs = Date.now() - start;
|
|
15
|
+
return { success: true, executionTimeMs };
|
|
16
|
+
} catch (err) {
|
|
17
|
+
try {
|
|
18
|
+
await client.query("ROLLBACK");
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
const executionTimeMs = Date.now() - start;
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
error: err.message,
|
|
25
|
+
executionTimeMs
|
|
26
|
+
};
|
|
27
|
+
} finally {
|
|
28
|
+
await client.end().catch(() => {
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function promptAndApplyIfConfirmed(sql, options = {}) {
|
|
33
|
+
if (options.isCi || !process.stdin.isTTY) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
console.log("\n" + chalk.bold.cyan(" \u{1F4DC} Verified Safe SQL Query:"));
|
|
37
|
+
console.log(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
38
|
+
const lines = sql.trim().split("\n");
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
console.log(` ${chalk.cyan("\u2502")} ${chalk.white(line)}`);
|
|
41
|
+
}
|
|
42
|
+
console.log(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
43
|
+
let shouldApply = options.autoApply ?? false;
|
|
44
|
+
if (!shouldApply) {
|
|
45
|
+
const rl = readline.createInterface({ input, output });
|
|
46
|
+
try {
|
|
47
|
+
const answer = await rl.question(
|
|
48
|
+
chalk.bold.yellow(" \u{1F680} Apply this migration to your real database now? ") + chalk.dim("[y/N] ")
|
|
49
|
+
);
|
|
50
|
+
const trimmed = answer.trim().toLowerCase();
|
|
51
|
+
shouldApply = trimmed === "y" || trimmed === "yes";
|
|
52
|
+
} finally {
|
|
53
|
+
rl.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!shouldApply) {
|
|
57
|
+
console.log(chalk.dim("\n \u2139\uFE0F Real database untouched. Continuing IDE agent workflow...\n"));
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
let targetUrl = options.targetDbUrl || process.env.TARGET_DATABASE_URL || process.env.DATABASE_URL;
|
|
61
|
+
if (!targetUrl) {
|
|
62
|
+
const rl = readline.createInterface({ input, output });
|
|
63
|
+
try {
|
|
64
|
+
const enteredUrl = await rl.question(
|
|
65
|
+
chalk.bold.cyan(" \u{1F517} Enter target database URL ") + chalk.dim("(e.g., postgresql://user:pass@localhost:5432/mydb): ")
|
|
66
|
+
);
|
|
67
|
+
targetUrl = enteredUrl.trim();
|
|
68
|
+
} finally {
|
|
69
|
+
rl.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!targetUrl) {
|
|
73
|
+
console.log(chalk.yellow("\n \u26A0\uFE0F No target database URL provided. Skipped real DB application.\n"));
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
console.log(chalk.dim(`
|
|
77
|
+
Connecting to target database and applying migration...`));
|
|
78
|
+
const res = await applyToRealDatabase(sql, targetUrl);
|
|
79
|
+
if (res.success) {
|
|
80
|
+
console.log(chalk.bold.green(`
|
|
81
|
+
\u2705 Successfully applied migration to target database!`) + chalk.dim(` (${res.executionTimeMs}ms)
|
|
82
|
+
`));
|
|
83
|
+
return true;
|
|
84
|
+
} else {
|
|
85
|
+
console.log(chalk.bold.red(`
|
|
86
|
+
\u274C Failed to apply migration to target database:`));
|
|
87
|
+
console.log(chalk.red(` ${res.error}
|
|
88
|
+
`));
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
applyToRealDatabase,
|
|
95
|
+
promptAndApplyIfConfirmed
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=chunk-7ENQ5WVM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/applier.ts"],"sourcesContent":["import pg from 'pg';\nimport * as readline from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport chalk from 'chalk';\n\nexport interface ApplyResult {\n success: boolean;\n error?: string;\n executionTimeMs: number;\n}\n\n/**\n * Executes verified SQL migration directly against a target real PostgreSQL database.\n */\nexport async function applyToRealDatabase(sql: string, dbUrl: string): Promise<ApplyResult> {\n const client = new pg.Client({ connectionString: dbUrl });\n const start = Date.now();\n\n try {\n await client.connect();\n await client.query('BEGIN');\n await client.query(sql);\n await client.query('COMMIT');\n const executionTimeMs = Date.now() - start;\n return { success: true, executionTimeMs };\n } catch (err) {\n try {\n await client.query('ROLLBACK');\n } catch {\n // Ignore rollback errors\n }\n const executionTimeMs = Date.now() - start;\n return {\n success: false,\n error: (err as Error).message,\n executionTimeMs,\n };\n } finally {\n await client.end().catch(() => {});\n }\n}\n\n/**\n * Interactive prompt: displays the verified SQL and asks the user\n * whether they want to apply it to their real database.\n */\nexport async function promptAndApplyIfConfirmed(\n sql: string,\n options: {\n targetDbUrl?: string;\n autoApply?: boolean;\n isCi?: boolean;\n } = {}\n): Promise<boolean> {\n // Never prompt in non-interactive CI environments\n if (options.isCi || !process.stdin.isTTY) {\n return false;\n }\n\n console.log('\\n' + chalk.bold.cyan(' đ Verified Safe SQL Query:'));\n console.log(chalk.dim(' âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ'));\n const lines = sql.trim().split('\\n');\n for (const line of lines) {\n console.log(` ${chalk.cyan('â')} ${chalk.white(line)}`);\n }\n console.log(chalk.dim(' âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ\\n'));\n\n let shouldApply = options.autoApply ?? false;\n\n if (!shouldApply) {\n const rl = readline.createInterface({ input, output });\n try {\n const answer = await rl.question(\n chalk.bold.yellow(' đ Apply this migration to your real database now? ') + chalk.dim('[y/N] ')\n );\n const trimmed = answer.trim().toLowerCase();\n shouldApply = trimmed === 'y' || trimmed === 'yes';\n } finally {\n rl.close();\n }\n }\n\n if (!shouldApply) {\n console.log(chalk.dim('\\n âšī¸ Real database untouched. Continuing IDE agent workflow...\\n'));\n return false;\n }\n\n // Determine target database URL\n let targetUrl =\n options.targetDbUrl ||\n process.env.TARGET_DATABASE_URL ||\n process.env.DATABASE_URL;\n\n if (!targetUrl) {\n const rl = readline.createInterface({ input, output });\n try {\n const enteredUrl = await rl.question(\n chalk.bold.cyan(' đ Enter target database URL ') + chalk.dim('(e.g., postgresql://user:pass@localhost:5432/mydb): ')\n );\n targetUrl = enteredUrl.trim();\n } finally {\n rl.close();\n }\n }\n\n if (!targetUrl) {\n console.log(chalk.yellow('\\n â ī¸ No target database URL provided. Skipped real DB application.\\n'));\n return false;\n }\n\n console.log(chalk.dim(`\\n Connecting to target database and applying migration...`));\n const res = await applyToRealDatabase(sql, targetUrl);\n\n if (res.success) {\n console.log(chalk.bold.green(`\\n â
Successfully applied migration to target database!`) + chalk.dim(` (${res.executionTimeMs}ms)\\n`));\n return true;\n } else {\n console.log(chalk.bold.red(`\\n â Failed to apply migration to target database:`));\n console.log(chalk.red(` ${res.error}\\n`));\n return false;\n }\n}\n"],"mappings":"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","names":[]}
|