ddlforge 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 +326 -0
- package/dist/bin/ddlforge.d.ts +6 -0
- package/dist/bin/ddlforge.d.ts.map +1 -0
- package/dist/bin/ddlforge.js +14 -0
- package/dist/bin/ddlforge.js.map +1 -0
- package/dist/src/cli.d.ts +21 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +262 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/engine/analyzer.d.ts +30 -0
- package/dist/src/engine/analyzer.d.ts.map +1 -0
- package/dist/src/engine/analyzer.js +76 -0
- package/dist/src/engine/analyzer.js.map +1 -0
- package/dist/src/engine/locks.d.ts +26 -0
- package/dist/src/engine/locks.d.ts.map +1 -0
- package/dist/src/engine/locks.js +136 -0
- package/dist/src/engine/locks.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +17 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lexer/sqlTokenizer.d.ts +40 -0
- package/dist/src/lexer/sqlTokenizer.d.ts.map +1 -0
- package/dist/src/lexer/sqlTokenizer.js +494 -0
- package/dist/src/lexer/sqlTokenizer.js.map +1 -0
- package/dist/src/lexer/tokens.d.ts +35 -0
- package/dist/src/lexer/tokens.d.ts.map +1 -0
- package/dist/src/lexer/tokens.js +81 -0
- package/dist/src/lexer/tokens.js.map +1 -0
- package/dist/src/reporters/index.d.ts +7 -0
- package/dist/src/reporters/index.d.ts.map +1 -0
- package/dist/src/reporters/index.js +7 -0
- package/dist/src/reporters/index.js.map +1 -0
- package/dist/src/reporters/json.d.ts +20 -0
- package/dist/src/reporters/json.d.ts.map +1 -0
- package/dist/src/reporters/json.js +35 -0
- package/dist/src/reporters/json.js.map +1 -0
- package/dist/src/reporters/markdown.d.ts +6 -0
- package/dist/src/reporters/markdown.d.ts.map +1 -0
- package/dist/src/reporters/markdown.js +70 -0
- package/dist/src/reporters/markdown.js.map +1 -0
- package/dist/src/reporters/terminal.d.ts +10 -0
- package/dist/src/reporters/terminal.d.ts.map +1 -0
- package/dist/src/reporters/terminal.js +109 -0
- package/dist/src/reporters/terminal.js.map +1 -0
- package/dist/src/rules/addColumnNotNull.d.ts +9 -0
- package/dist/src/rules/addColumnNotNull.d.ts.map +1 -0
- package/dist/src/rules/addColumnNotNull.js +131 -0
- package/dist/src/rules/addColumnNotNull.js.map +1 -0
- package/dist/src/rules/foreignKeyNotValid.d.ts +9 -0
- package/dist/src/rules/foreignKeyNotValid.d.ts.map +1 -0
- package/dist/src/rules/foreignKeyNotValid.js +119 -0
- package/dist/src/rules/foreignKeyNotValid.js.map +1 -0
- package/dist/src/rules/index.d.ts +14 -0
- package/dist/src/rules/index.d.ts.map +1 -0
- package/dist/src/rules/index.js +28 -0
- package/dist/src/rules/index.js.map +1 -0
- package/dist/src/rules/indexConcurrently.d.ts +9 -0
- package/dist/src/rules/indexConcurrently.d.ts.map +1 -0
- package/dist/src/rules/indexConcurrently.js +92 -0
- package/dist/src/rules/indexConcurrently.js.map +1 -0
- package/dist/src/rules/prismaRenameDropAdd.d.ts +11 -0
- package/dist/src/rules/prismaRenameDropAdd.d.ts.map +1 -0
- package/dist/src/rules/prismaRenameDropAdd.js +144 -0
- package/dist/src/rules/prismaRenameDropAdd.js.map +1 -0
- package/dist/src/rules/setNotNullFullScan.d.ts +9 -0
- package/dist/src/rules/setNotNullFullScan.d.ts.map +1 -0
- package/dist/src/rules/setNotNullFullScan.js +93 -0
- package/dist/src/rules/setNotNullFullScan.js.map +1 -0
- package/dist/src/rules/transactionTrap.d.ts +9 -0
- package/dist/src/rules/transactionTrap.d.ts.map +1 -0
- package/dist/src/rules/transactionTrap.js +104 -0
- package/dist/src/rules/transactionTrap.js.map +1 -0
- package/dist/src/rules/types.d.ts +36 -0
- package/dist/src/rules/types.d.ts.map +1 -0
- package/dist/src/rules/types.js +5 -0
- package/dist/src/rules/types.js.map +1 -0
- package/dist/src/rules/unbatchedBackfill.d.ts +9 -0
- package/dist/src/rules/unbatchedBackfill.d.ts.map +1 -0
- package/dist/src/rules/unbatchedBackfill.js +58 -0
- package/dist/src/rules/unbatchedBackfill.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Machine-readable JSON output reporter
|
|
3
|
+
*/
|
|
4
|
+
import { AnalysisResult } from '../engine/analyzer.js';
|
|
5
|
+
import { Finding } from '../rules/types.js';
|
|
6
|
+
export interface JsonReport {
|
|
7
|
+
status: 'passed' | 'failed';
|
|
8
|
+
summary: {
|
|
9
|
+
filesScanned: number;
|
|
10
|
+
statementsAnalyzed: number;
|
|
11
|
+
blockers: number;
|
|
12
|
+
warnings: number;
|
|
13
|
+
advisories: number;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
passed: boolean;
|
|
16
|
+
};
|
|
17
|
+
findings: Finding[];
|
|
18
|
+
}
|
|
19
|
+
export declare function formatJson(results: AnalysisResult[], pretty?: boolean): string;
|
|
20
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../src/reporters/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,MAAM,GAAE,OAAc,GAAG,MAAM,CAiCpF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Machine-readable JSON output reporter
|
|
3
|
+
*/
|
|
4
|
+
export function formatJson(results, pretty = true) {
|
|
5
|
+
let filesScanned = results.length;
|
|
6
|
+
let statementsAnalyzed = 0;
|
|
7
|
+
let blockers = 0;
|
|
8
|
+
let warnings = 0;
|
|
9
|
+
let advisories = 0;
|
|
10
|
+
let durationMs = 0;
|
|
11
|
+
const allFindings = [];
|
|
12
|
+
for (const res of results) {
|
|
13
|
+
statementsAnalyzed += res.statementsAnalyzed;
|
|
14
|
+
blockers += res.blockersCount;
|
|
15
|
+
warnings += res.warningsCount;
|
|
16
|
+
advisories += res.advisoriesCount;
|
|
17
|
+
durationMs += res.durationMs;
|
|
18
|
+
allFindings.push(...res.findings);
|
|
19
|
+
}
|
|
20
|
+
const report = {
|
|
21
|
+
status: blockers === 0 ? 'passed' : 'failed',
|
|
22
|
+
summary: {
|
|
23
|
+
filesScanned,
|
|
24
|
+
statementsAnalyzed,
|
|
25
|
+
blockers,
|
|
26
|
+
warnings,
|
|
27
|
+
advisories,
|
|
28
|
+
durationMs: Math.round(durationMs * 100) / 100,
|
|
29
|
+
passed: blockers === 0,
|
|
30
|
+
},
|
|
31
|
+
findings: allFindings,
|
|
32
|
+
};
|
|
33
|
+
return pretty ? JSON.stringify(report, null, 2) : JSON.stringify(report);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/reporters/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmBH,MAAM,UAAU,UAAU,CAAC,OAAyB,EAAE,MAAM,GAAY,IAAI;IAC1E,IAAI,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAClC,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAC3B,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,WAAW,GAAc,EAAE,CAAC;IAElC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,kBAAkB,IAAI,GAAG,CAAC,kBAAkB,CAAC;QAC7C,QAAQ,IAAI,GAAG,CAAC,aAAa,CAAC;QAC9B,QAAQ,IAAI,GAAG,CAAC,aAAa,CAAC;QAC9B,UAAU,IAAI,GAAG,CAAC,eAAe,CAAC;QAClC,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC;QAC7B,WAAW,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,MAAM,GAAe;QACzB,MAAM,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ;QAC5C,OAAO,EAAE;YACP,YAAY;YACZ,kBAAkB;YAClB,QAAQ;YACR,QAAQ;YACR,UAAU;YACV,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,GAAG,GAAG;YAC9C,MAAM,EAAE,QAAQ,KAAK,CAAC;SACvB;QACD,QAAQ,EAAE,WAAW;KACtB,CAAC;IAEF,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/reporters/markdown.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CA6EhE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - CI PR Comment / Markdown Report Formatter
|
|
3
|
+
*/
|
|
4
|
+
export function formatMarkdown(results) {
|
|
5
|
+
let filesScanned = results.length;
|
|
6
|
+
let statementsAnalyzed = 0;
|
|
7
|
+
let blockers = 0;
|
|
8
|
+
let warnings = 0;
|
|
9
|
+
let advisories = 0;
|
|
10
|
+
let durationMs = 0;
|
|
11
|
+
for (const res of results) {
|
|
12
|
+
statementsAnalyzed += res.statementsAnalyzed;
|
|
13
|
+
blockers += res.blockersCount;
|
|
14
|
+
warnings += res.warningsCount;
|
|
15
|
+
advisories += res.advisoriesCount;
|
|
16
|
+
durationMs += res.durationMs;
|
|
17
|
+
}
|
|
18
|
+
const lines = [];
|
|
19
|
+
lines.push('## 🛡️ ddlforge Migration Lock & Safety Report');
|
|
20
|
+
lines.push('');
|
|
21
|
+
const statusEmoji = blockers > 0 ? '❌ **FAILED**' : warnings > 0 ? '⚠️ **PASSED WITH WARNINGS**' : '✅ **PASSED**';
|
|
22
|
+
lines.push(`**Status:** ${statusEmoji} (${durationMs.toFixed(1)}ms)`);
|
|
23
|
+
lines.push('');
|
|
24
|
+
// Summary Table
|
|
25
|
+
lines.push('| Metric | Count |');
|
|
26
|
+
lines.push('|:---|:---|');
|
|
27
|
+
lines.push(`| 🛑 **Blocking Violations** | \`${blockers}\` |`);
|
|
28
|
+
lines.push(`| ⚠️ **Warnings / Advisories** | \`${warnings + advisories}\` |`);
|
|
29
|
+
lines.push(`| 📄 **Files Scanned** | \`${filesScanned}\` |`);
|
|
30
|
+
lines.push(`| ⚡ **Statements Analyzed** | \`${statementsAnalyzed}\` |`);
|
|
31
|
+
lines.push('');
|
|
32
|
+
if (blockers === 0 && warnings === 0 && advisories === 0) {
|
|
33
|
+
lines.push('> ✨ **All migrations are safe!** No dangerous table locks or destructive data loss patterns detected.');
|
|
34
|
+
return lines.join('\n');
|
|
35
|
+
}
|
|
36
|
+
lines.push('### Detailed Findings');
|
|
37
|
+
lines.push('');
|
|
38
|
+
for (const res of results) {
|
|
39
|
+
if (res.findings.length === 0)
|
|
40
|
+
continue;
|
|
41
|
+
lines.push(`#### 📁 \`${res.file}\``);
|
|
42
|
+
lines.push('');
|
|
43
|
+
for (const finding of res.findings) {
|
|
44
|
+
const badge = finding.severity === 'BLOCKER' ? '🛑 `BLOCKER`' : '⚠️ `WARNING`';
|
|
45
|
+
const lock = finding.lockLevel !== 'NONE' ? ` · Lock: \`${finding.lockLevel}\`` : '';
|
|
46
|
+
lines.push(`<details ${finding.severity === 'BLOCKER' ? 'open' : ''}>`);
|
|
47
|
+
lines.push(`<summary>${badge} <b>${finding.message}</b> (Line ${finding.line})${lock}</summary>`);
|
|
48
|
+
lines.push('');
|
|
49
|
+
lines.push(`- **Rule:** \`${finding.ruleId}\``);
|
|
50
|
+
lines.push(`- **Location:** \`${res.file}:${finding.line}:${finding.column}\``);
|
|
51
|
+
lines.push(`- **Impact:** ${finding.detail}`);
|
|
52
|
+
lines.push('');
|
|
53
|
+
lines.push('```sql');
|
|
54
|
+
lines.push(finding.codeSnippet);
|
|
55
|
+
lines.push('```');
|
|
56
|
+
lines.push('');
|
|
57
|
+
lines.push('**Suggested Zero-Downtime Fix:**');
|
|
58
|
+
lines.push('```sql');
|
|
59
|
+
lines.push(finding.suggestion);
|
|
60
|
+
lines.push('```');
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push('</details>');
|
|
63
|
+
lines.push('');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
lines.push('---');
|
|
67
|
+
lines.push('*Automated by [ddlforge](https://github.com/ddlforge/ddlforge) — Zero-runtime-dependency Postgres Lock Linter*');
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/reporters/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Clean, colorized TTY terminal reporter with code spans
|
|
3
|
+
*/
|
|
4
|
+
import { AnalysisResult } from '../engine/analyzer.js';
|
|
5
|
+
export interface TerminalReporterOptions {
|
|
6
|
+
quiet?: boolean;
|
|
7
|
+
color?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function formatTerminal(results: AnalysisResult[], options?: TerminalReporterOptions): string;
|
|
10
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../../src/reporters/terminal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AASD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,OAAO,GAAE,uBAA4B,GAAG,MAAM,CAyGvG"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Clean, colorized TTY terminal reporter with code spans
|
|
3
|
+
*/
|
|
4
|
+
const supportsColor = (override) => {
|
|
5
|
+
if (override !== undefined)
|
|
6
|
+
return override;
|
|
7
|
+
if (process.env.NO_COLOR || process.env.NODE_DISABLE_COLORS)
|
|
8
|
+
return false;
|
|
9
|
+
if (process.env.FORCE_COLOR)
|
|
10
|
+
return true;
|
|
11
|
+
return process.stdout.isTTY ?? false;
|
|
12
|
+
};
|
|
13
|
+
export function formatTerminal(results, options = {}) {
|
|
14
|
+
const useColor = supportsColor(options.color);
|
|
15
|
+
const quiet = options.quiet ?? false;
|
|
16
|
+
const c = {
|
|
17
|
+
reset: useColor ? '\x1b[0m' : '',
|
|
18
|
+
bold: useColor ? '\x1b[1m' : '',
|
|
19
|
+
dim: useColor ? '\x1b[2m' : '',
|
|
20
|
+
red: useColor ? '\x1b[31m' : '',
|
|
21
|
+
green: useColor ? '\x1b[32m' : '',
|
|
22
|
+
yellow: useColor ? '\x1b[33m' : '',
|
|
23
|
+
blue: useColor ? '\x1b[34m' : '',
|
|
24
|
+
magenta: useColor ? '\x1b[35m' : '',
|
|
25
|
+
cyan: useColor ? '\x1b[36m' : '',
|
|
26
|
+
gray: useColor ? '\x1b[90m' : '',
|
|
27
|
+
bgRed: useColor ? '\x1b[41m\x1b[37m' : '',
|
|
28
|
+
bgYellow: useColor ? '\x1b[43m\x1b[30m' : '',
|
|
29
|
+
};
|
|
30
|
+
const lines = [];
|
|
31
|
+
let totalBlockers = 0;
|
|
32
|
+
let totalWarnings = 0;
|
|
33
|
+
let totalAdvisories = 0;
|
|
34
|
+
let totalStatements = 0;
|
|
35
|
+
let totalDuration = 0;
|
|
36
|
+
let totalFilesScanned = results.length;
|
|
37
|
+
for (const res of results) {
|
|
38
|
+
totalBlockers += res.blockersCount;
|
|
39
|
+
totalWarnings += res.warningsCount;
|
|
40
|
+
totalAdvisories += res.advisoriesCount;
|
|
41
|
+
totalStatements += res.statementsAnalyzed;
|
|
42
|
+
totalDuration += res.durationMs;
|
|
43
|
+
const visibleFindings = quiet
|
|
44
|
+
? res.findings.filter(f => f.severity === 'BLOCKER')
|
|
45
|
+
: res.findings;
|
|
46
|
+
if (visibleFindings.length === 0)
|
|
47
|
+
continue;
|
|
48
|
+
lines.push('');
|
|
49
|
+
lines.push(`${c.bold}${c.cyan}📁 ${res.file}${c.reset} ${c.gray}(${res.statementsAnalyzed} statements)${c.reset}`);
|
|
50
|
+
lines.push(c.gray + '─'.repeat(70) + c.reset);
|
|
51
|
+
for (const finding of visibleFindings) {
|
|
52
|
+
const severityBadge = formatSeverityBadge(finding.severity, c);
|
|
53
|
+
const lockBadge = finding.lockLevel !== 'NONE'
|
|
54
|
+
? ` ${c.magenta}[LOCK: ${finding.lockLevel}]${c.reset}`
|
|
55
|
+
: '';
|
|
56
|
+
lines.push(`${severityBadge} ${c.bold}${finding.message}${c.reset}${lockBadge}`);
|
|
57
|
+
lines.push(` ${c.gray}at ${res.file}:${finding.line}:${finding.column}${c.reset} ${c.dim}(rule: ${finding.ruleId})${c.reset}`);
|
|
58
|
+
if (finding.codeSnippet) {
|
|
59
|
+
lines.push('');
|
|
60
|
+
const snippetLines = finding.codeSnippet.split('\n');
|
|
61
|
+
snippetLines.forEach((snipLine, i) => {
|
|
62
|
+
const lineNum = String(finding.line + i).padStart(4, ' ');
|
|
63
|
+
lines.push(` ${c.dim}${lineNum} │${c.reset} ${c.yellow}${snipLine}${c.reset}`);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (finding.detail) {
|
|
67
|
+
lines.push(` ${c.dim}Why:${c.reset} ${finding.detail}`);
|
|
68
|
+
}
|
|
69
|
+
if (finding.suggestion) {
|
|
70
|
+
lines.push(` ${c.green}${c.bold}Fix:${c.reset}`);
|
|
71
|
+
const suggestionLines = finding.suggestion.split('\n');
|
|
72
|
+
for (const sLine of suggestionLines) {
|
|
73
|
+
lines.push(` ${c.green}${sLine}${c.reset}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
lines.push('');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Summary footer
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push(c.gray + '━'.repeat(70) + c.reset);
|
|
82
|
+
const durationStr = `${totalDuration.toFixed(1)}ms`;
|
|
83
|
+
if (totalBlockers > 0) {
|
|
84
|
+
lines.push(`${c.red}${c.bold}✖ FAILED:${c.reset} Found ${c.bold}${c.red}${totalBlockers} blocker(s)${c.reset}` +
|
|
85
|
+
(totalWarnings > 0 ? `, ${c.yellow}${totalWarnings} warning(s)${c.reset}` : '') +
|
|
86
|
+
` across ${totalFilesScanned} file(s) [${durationStr}]`);
|
|
87
|
+
lines.push(`${c.dim}Run with --quiet to display blockers only.${c.reset}`);
|
|
88
|
+
}
|
|
89
|
+
else if (totalWarnings > 0) {
|
|
90
|
+
lines.push(`${c.yellow}${c.bold}⚠ PASSED WITH ADVISORIES:${c.reset} 0 blockers, ${c.yellow}${totalWarnings} warning(s)${c.reset}` +
|
|
91
|
+
` across ${totalFilesScanned} file(s) [${durationStr}]`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.push(`${c.green}${c.bold}✔ ALL CLEAR:${c.reset} Checked ${totalFilesScanned} migration file(s) ` +
|
|
95
|
+
`(${totalStatements} statements) with zero lock or data-loss violations [${durationStr}]`);
|
|
96
|
+
}
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
function formatSeverityBadge(severity, c) {
|
|
100
|
+
switch (severity) {
|
|
101
|
+
case 'BLOCKER':
|
|
102
|
+
return `${c.bgRed} BLOCKER ${c.reset}`;
|
|
103
|
+
case 'WARNING':
|
|
104
|
+
return `${c.bgYellow} WARNING ${c.reset}`;
|
|
105
|
+
case 'ADVISORY':
|
|
106
|
+
return `${c.cyan}[ADVISORY]${c.reset}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../../src/reporters/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addColumnNotNull.d.ts","sourceRoot":"","sources":["../../../src/rules/addColumnNotNull.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAwB,MAAM,YAAY,CAAC;AAoGxD,eAAO,MAAM,oBAAoB,EAAE,IAqDlC,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge Rule: ADD COLUMN NOT NULL without DEFAULT
|
|
3
|
+
*
|
|
4
|
+
* Severity: BLOCKER
|
|
5
|
+
* Lock: ACCESS EXCLUSIVE
|
|
6
|
+
*/
|
|
7
|
+
import { PostgresLockLevel } from '../engine/locks.js';
|
|
8
|
+
function extractAddColumnClauses(tokens) {
|
|
9
|
+
const clauses = [];
|
|
10
|
+
let parenDepth = 0;
|
|
11
|
+
let i = 0;
|
|
12
|
+
while (i < tokens.length) {
|
|
13
|
+
if (tokens[i].value === '(') {
|
|
14
|
+
parenDepth++;
|
|
15
|
+
i++;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (tokens[i].value === ')') {
|
|
19
|
+
parenDepth--;
|
|
20
|
+
i++;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
// Check for ADD [COLUMN] at top level (parenDepth === 0)
|
|
24
|
+
if (parenDepth === 0 && tokens[i].value === 'ADD') {
|
|
25
|
+
let clauseIdx = i + 1;
|
|
26
|
+
if (tokens[clauseIdx]?.value === 'COLUMN') {
|
|
27
|
+
clauseIdx++;
|
|
28
|
+
}
|
|
29
|
+
// If next is IF NOT EXISTS, skip
|
|
30
|
+
if (tokens[clauseIdx]?.value === 'IF' && tokens[clauseIdx + 1]?.value === 'NOT' && tokens[clauseIdx + 2]?.value === 'EXISTS') {
|
|
31
|
+
clauseIdx += 3;
|
|
32
|
+
}
|
|
33
|
+
// Check if next token is CONSTRAINT or standard column definition
|
|
34
|
+
if (tokens[clauseIdx]?.value === 'CONSTRAINT') {
|
|
35
|
+
// Constraint definition, not a column
|
|
36
|
+
i++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const columnName = tokens[clauseIdx]?.raw ?? 'column';
|
|
40
|
+
clauseIdx++;
|
|
41
|
+
// Collect tokens until next top-level comma or end of tokens
|
|
42
|
+
let hasNotNull = false;
|
|
43
|
+
let hasDefault = false;
|
|
44
|
+
let hasGenerated = false;
|
|
45
|
+
const clauseTokens = [];
|
|
46
|
+
let subParen = 0;
|
|
47
|
+
while (clauseIdx < tokens.length) {
|
|
48
|
+
const t = tokens[clauseIdx];
|
|
49
|
+
if (t.value === '(')
|
|
50
|
+
subParen++;
|
|
51
|
+
if (t.value === ')')
|
|
52
|
+
subParen--;
|
|
53
|
+
if (subParen === 0 && t.value === ',') {
|
|
54
|
+
break; // next subcommand
|
|
55
|
+
}
|
|
56
|
+
clauseTokens.push(t);
|
|
57
|
+
if (t.value === 'NOT' && tokens[clauseIdx + 1]?.value === 'NULL') {
|
|
58
|
+
hasNotNull = true;
|
|
59
|
+
}
|
|
60
|
+
if (t.value === 'DEFAULT') {
|
|
61
|
+
hasDefault = true;
|
|
62
|
+
}
|
|
63
|
+
if (t.value === 'GENERATED') {
|
|
64
|
+
hasGenerated = true;
|
|
65
|
+
}
|
|
66
|
+
clauseIdx++;
|
|
67
|
+
}
|
|
68
|
+
clauses.push({
|
|
69
|
+
columnName,
|
|
70
|
+
hasNotNull,
|
|
71
|
+
hasDefault,
|
|
72
|
+
hasGenerated,
|
|
73
|
+
tokens: clauseTokens,
|
|
74
|
+
});
|
|
75
|
+
i = clauseIdx;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
i++;
|
|
79
|
+
}
|
|
80
|
+
return clauses;
|
|
81
|
+
}
|
|
82
|
+
export const addColumnNotNullRule = {
|
|
83
|
+
id: 'add-column-not-null-without-default',
|
|
84
|
+
name: 'ADD COLUMN NOT NULL without DEFAULT',
|
|
85
|
+
description: 'Adding a NOT NULL column without a DEFAULT value will immediately fail on non-empty tables and hold an ACCESS EXCLUSIVE lock.',
|
|
86
|
+
defaultSeverity: 'BLOCKER',
|
|
87
|
+
lockLevel: PostgresLockLevel.ACCESS_EXCLUSIVE,
|
|
88
|
+
check(context) {
|
|
89
|
+
const findings = [];
|
|
90
|
+
for (const stmt of context.statements) {
|
|
91
|
+
if (stmt.hasIgnore(this.id))
|
|
92
|
+
continue;
|
|
93
|
+
const tokens = stmt.tokens;
|
|
94
|
+
if (tokens.length < 5)
|
|
95
|
+
continue;
|
|
96
|
+
if (tokens[0].value !== 'ALTER' || tokens[1].value !== 'TABLE')
|
|
97
|
+
continue;
|
|
98
|
+
// Extract table name
|
|
99
|
+
let idx = 2;
|
|
100
|
+
if (tokens[idx]?.value === 'ONLY')
|
|
101
|
+
idx++;
|
|
102
|
+
if (tokens[idx]?.value === 'IF' && tokens[idx + 1]?.value === 'EXISTS')
|
|
103
|
+
idx += 2;
|
|
104
|
+
const tableName = tokens[idx]?.raw ?? 'table';
|
|
105
|
+
idx++;
|
|
106
|
+
// Scan through subcommands separated by comma (at top parentheses depth)
|
|
107
|
+
const clauses = extractAddColumnClauses(tokens.slice(idx));
|
|
108
|
+
for (const clause of clauses) {
|
|
109
|
+
if (clause.hasNotNull && !clause.hasDefault && !clause.hasGenerated) {
|
|
110
|
+
findings.push({
|
|
111
|
+
ruleId: this.id,
|
|
112
|
+
ruleName: this.name,
|
|
113
|
+
severity: this.defaultSeverity,
|
|
114
|
+
lockLevel: this.lockLevel,
|
|
115
|
+
message: `Column "${clause.columnName}" added as NOT NULL without a DEFAULT value on table "${tableName}".`,
|
|
116
|
+
detail: `Adding a NOT NULL column without a DEFAULT on non-empty table "${tableName}" will immediately fail ` +
|
|
117
|
+
'with `column "..." contains null values` while holding an ACCESS EXCLUSIVE table lock.',
|
|
118
|
+
suggestion: `Provide a DEFAULT value: \`ALTER TABLE ${tableName} ADD COLUMN ${clause.columnName} <type> DEFAULT <val> NOT NULL;\` ` +
|
|
119
|
+
`or add nullable first, backfill rows in batches, and apply a validated NOT NULL constraint.`,
|
|
120
|
+
file: context.filePath,
|
|
121
|
+
line: stmt.startLine,
|
|
122
|
+
column: stmt.startColumn,
|
|
123
|
+
codeSnippet: stmt.raw,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return findings;
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=addColumnNotNull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addColumnNotNull.js","sourceRoot":"","sources":["../../../src/rules/addColumnNotNull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge Rule: ADD CONSTRAINT ... FOREIGN KEY without NOT VALID
|
|
3
|
+
*
|
|
4
|
+
* Severity: BLOCKER
|
|
5
|
+
* Lock: SHARE ROW EXCLUSIVE on child table + SHARE on parent table
|
|
6
|
+
*/
|
|
7
|
+
import { Rule } from './types.js';
|
|
8
|
+
export declare const foreignKeyNotValidRule: Rule;
|
|
9
|
+
//# sourceMappingURL=foreignKeyNotValid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreignKeyNotValid.d.ts","sourceRoot":"","sources":["../../../src/rules/foreignKeyNotValid.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAwB,MAAM,YAAY,CAAC;AA+ExD,eAAO,MAAM,sBAAsB,EAAE,IA2DpC,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge Rule: ADD CONSTRAINT ... FOREIGN KEY without NOT VALID
|
|
3
|
+
*
|
|
4
|
+
* Severity: BLOCKER
|
|
5
|
+
* Lock: SHARE ROW EXCLUSIVE on child table + SHARE on parent table
|
|
6
|
+
*/
|
|
7
|
+
import { PostgresLockLevel } from '../engine/locks.js';
|
|
8
|
+
function extractForeignKeyClauses(tokens) {
|
|
9
|
+
const clauses = [];
|
|
10
|
+
let parenDepth = 0;
|
|
11
|
+
let i = 0;
|
|
12
|
+
while (i < tokens.length) {
|
|
13
|
+
if (tokens[i].value === '(') {
|
|
14
|
+
parenDepth++;
|
|
15
|
+
i++;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (tokens[i].value === ')') {
|
|
19
|
+
parenDepth--;
|
|
20
|
+
i++;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (parenDepth === 0 && tokens[i].value === 'ADD') {
|
|
24
|
+
let currentIdx = i + 1;
|
|
25
|
+
let constraintName;
|
|
26
|
+
if (tokens[currentIdx]?.value === 'CONSTRAINT') {
|
|
27
|
+
currentIdx++;
|
|
28
|
+
constraintName = tokens[currentIdx]?.raw;
|
|
29
|
+
currentIdx++;
|
|
30
|
+
}
|
|
31
|
+
// Check if this clause is FOREIGN KEY
|
|
32
|
+
if (tokens[currentIdx]?.value === 'FOREIGN' && tokens[currentIdx + 1]?.value === 'KEY') {
|
|
33
|
+
// Collect all tokens for this constraint until top-level comma or end
|
|
34
|
+
const clauseTokens = [];
|
|
35
|
+
let hasNotValid = false;
|
|
36
|
+
let subParen = 0;
|
|
37
|
+
while (currentIdx < tokens.length) {
|
|
38
|
+
const t = tokens[currentIdx];
|
|
39
|
+
if (t.value === '(')
|
|
40
|
+
subParen++;
|
|
41
|
+
if (t.value === ')')
|
|
42
|
+
subParen--;
|
|
43
|
+
if (subParen === 0 && t.value === ',') {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
clauseTokens.push(t);
|
|
47
|
+
if (t.value === 'NOT' && tokens[currentIdx + 1]?.value === 'VALID') {
|
|
48
|
+
hasNotValid = true;
|
|
49
|
+
}
|
|
50
|
+
currentIdx++;
|
|
51
|
+
}
|
|
52
|
+
clauses.push({
|
|
53
|
+
constraintName,
|
|
54
|
+
hasNotValid,
|
|
55
|
+
tokens: clauseTokens,
|
|
56
|
+
});
|
|
57
|
+
i = currentIdx;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
return clauses;
|
|
64
|
+
}
|
|
65
|
+
export const foreignKeyNotValidRule = {
|
|
66
|
+
id: 'foreign-key-missing-not-valid',
|
|
67
|
+
name: 'ADD CONSTRAINT FOREIGN KEY without NOT VALID',
|
|
68
|
+
description: 'Adding a foreign key constraint without NOT VALID performs a full table scan under SHARE ROW EXCLUSIVE lock, blocking concurrent writes on both tables.',
|
|
69
|
+
defaultSeverity: 'BLOCKER',
|
|
70
|
+
lockLevel: PostgresLockLevel.SHARE_ROW_EXCLUSIVE,
|
|
71
|
+
check(context) {
|
|
72
|
+
const findings = [];
|
|
73
|
+
for (const stmt of context.statements) {
|
|
74
|
+
if (stmt.hasIgnore(this.id))
|
|
75
|
+
continue;
|
|
76
|
+
const tokens = stmt.tokens;
|
|
77
|
+
if (tokens.length < 6)
|
|
78
|
+
continue;
|
|
79
|
+
if (tokens[0].value !== 'ALTER' || tokens[1].value !== 'TABLE')
|
|
80
|
+
continue;
|
|
81
|
+
// Extract table name
|
|
82
|
+
let idx = 2;
|
|
83
|
+
if (tokens[idx]?.value === 'ONLY')
|
|
84
|
+
idx++;
|
|
85
|
+
if (tokens[idx]?.value === 'IF' && tokens[idx + 1]?.value === 'EXISTS')
|
|
86
|
+
idx += 2;
|
|
87
|
+
const tableName = tokens[idx]?.raw ?? 'table';
|
|
88
|
+
idx++;
|
|
89
|
+
const fkClauses = extractForeignKeyClauses(tokens.slice(idx));
|
|
90
|
+
for (const fk of fkClauses) {
|
|
91
|
+
if (!fk.hasNotValid) {
|
|
92
|
+
const cName = fk.constraintName ? ` "${fk.constraintName}"` : '';
|
|
93
|
+
const validateSuggestion = fk.constraintName
|
|
94
|
+
? `ALTER TABLE ${tableName} VALIDATE CONSTRAINT ${fk.constraintName};`
|
|
95
|
+
: `ALTER TABLE ${tableName} VALIDATE CONSTRAINT <constraint_name>;`;
|
|
96
|
+
findings.push({
|
|
97
|
+
ruleId: this.id,
|
|
98
|
+
ruleName: this.name,
|
|
99
|
+
severity: this.defaultSeverity,
|
|
100
|
+
lockLevel: this.lockLevel,
|
|
101
|
+
message: `Foreign key constraint${cName} on table "${tableName}" added without NOT VALID.`,
|
|
102
|
+
detail: `Validating foreign keys inline holds a SHARE ROW EXCLUSIVE lock on "${tableName}" and a SHARE lock on the referenced table, ` +
|
|
103
|
+
'blocking all concurrent writes (INSERT, UPDATE, DELETE) for the entire duration of the table scan.',
|
|
104
|
+
suggestion: `1. Add constraint with NOT VALID:\n` +
|
|
105
|
+
` ALTER TABLE ${tableName} ADD CONSTRAINT ${fk.constraintName ?? 'fk_name'} FOREIGN KEY (...) REFERENCES (...) NOT VALID;\n` +
|
|
106
|
+
`2. Validate concurrently without blocking writes:\n` +
|
|
107
|
+
` ${validateSuggestion}`,
|
|
108
|
+
file: context.filePath,
|
|
109
|
+
line: stmt.startLine,
|
|
110
|
+
column: stmt.startColumn,
|
|
111
|
+
codeSnippet: stmt.raw,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return findings;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=foreignKeyNotValid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreignKeyNotValid.js","sourceRoot":"","sources":["../../../src/rules/foreignKeyNotValid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Rules registry
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { indexConcurrentlyRule } from './indexConcurrently.js';
|
|
6
|
+
export { transactionTrapRule } from './transactionTrap.js';
|
|
7
|
+
export { addColumnNotNullRule } from './addColumnNotNull.js';
|
|
8
|
+
export { foreignKeyNotValidRule } from './foreignKeyNotValid.js';
|
|
9
|
+
export { prismaRenameDropAddRule } from './prismaRenameDropAdd.js';
|
|
10
|
+
export { setNotNullFullScanRule } from './setNotNullFullScan.js';
|
|
11
|
+
export { unbatchedBackfillRule } from './unbatchedBackfill.js';
|
|
12
|
+
import { Rule } from './types.js';
|
|
13
|
+
export declare const ALL_RULES: Rule[];
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AASlC,eAAO,MAAM,SAAS,EAAE,IAAI,EAQ3B,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ddlforge - Rules registry
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { indexConcurrentlyRule } from './indexConcurrently.js';
|
|
6
|
+
export { transactionTrapRule } from './transactionTrap.js';
|
|
7
|
+
export { addColumnNotNullRule } from './addColumnNotNull.js';
|
|
8
|
+
export { foreignKeyNotValidRule } from './foreignKeyNotValid.js';
|
|
9
|
+
export { prismaRenameDropAddRule } from './prismaRenameDropAdd.js';
|
|
10
|
+
export { setNotNullFullScanRule } from './setNotNullFullScan.js';
|
|
11
|
+
export { unbatchedBackfillRule } from './unbatchedBackfill.js';
|
|
12
|
+
import { indexConcurrentlyRule } from './indexConcurrently.js';
|
|
13
|
+
import { transactionTrapRule } from './transactionTrap.js';
|
|
14
|
+
import { addColumnNotNullRule } from './addColumnNotNull.js';
|
|
15
|
+
import { foreignKeyNotValidRule } from './foreignKeyNotValid.js';
|
|
16
|
+
import { prismaRenameDropAddRule } from './prismaRenameDropAdd.js';
|
|
17
|
+
import { setNotNullFullScanRule } from './setNotNullFullScan.js';
|
|
18
|
+
import { unbatchedBackfillRule } from './unbatchedBackfill.js';
|
|
19
|
+
export const ALL_RULES = [
|
|
20
|
+
indexConcurrentlyRule,
|
|
21
|
+
transactionTrapRule,
|
|
22
|
+
addColumnNotNullRule,
|
|
23
|
+
foreignKeyNotValidRule,
|
|
24
|
+
prismaRenameDropAddRule,
|
|
25
|
+
setNotNullFullScanRule,
|
|
26
|
+
unbatchedBackfillRule,
|
|
27
|
+
];
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,CAAC,MAAM,SAAS,GAAW;IAC/B,qBAAqB;IACrB,mBAAmB;IACnB,oBAAoB;IACpB,sBAAsB;IACtB,uBAAuB;IACvB,sBAAsB;IACtB,qBAAqB;CACtB,CAAC"}
|