db-scalability-guardian 1.0.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 +165 -0
- package/dist/analyzers/postgres/migrations.d.ts +4 -0
- package/dist/analyzers/postgres/migrations.d.ts.map +1 -0
- package/dist/analyzers/postgres/migrations.js +96 -0
- package/dist/analyzers/postgres/migrations.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +125 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/collectors/git.d.ts +6 -0
- package/dist/collectors/git.d.ts.map +1 -0
- package/dist/collectors/git.js +51 -0
- package/dist/collectors/git.js.map +1 -0
- package/dist/collectors/prisma.d.ts +8 -0
- package/dist/collectors/prisma.d.ts.map +1 -0
- package/dist/collectors/prisma.js +29 -0
- package/dist/collectors/prisma.js.map +1 -0
- package/dist/config/defaults.d.ts +24 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +56 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/loader.d.ts +3 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +69 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +3 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +51 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/gate/gate.d.ts +6 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +11 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/mistral.d.ts +6 -0
- package/dist/llm/mistral.d.ts.map +1 -0
- package/dist/llm/mistral.js +34 -0
- package/dist/llm/mistral.js.map +1 -0
- package/dist/llm/prompts.d.ts +6 -0
- package/dist/llm/prompts.d.ts.map +1 -0
- package/dist/llm/prompts.js +50 -0
- package/dist/llm/prompts.js.map +1 -0
- package/dist/reporters/json.d.ts +3 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +7 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/markdown.d.ts +3 -0
- package/dist/reporters/markdown.d.ts.map +1 -0
- package/dist/reporters/markdown.js +38 -0
- package/dist/reporters/markdown.js.map +1 -0
- package/dist/types/config.d.ts +21 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/report.d.ts +37 -0
- package/dist/types/report.d.ts.map +1 -0
- package/dist/types/report.js +3 -0
- package/dist/types/report.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# DB Scalability Guardian
|
|
2
|
+
|
|
3
|
+
A CLI tool for analyzing database migrations and schema changes for scalability and evolvability risks. Designed for PostgreSQL and Prisma, with optional LLM-powered architectural interpretation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
DB Scalability Guardian performs deterministic analysis of database migrations to identify potential scalability issues, deployment blockers, and future feature blockers. It can optionally use LLM interpretation to provide architectural insights.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Deterministic Migration Analysis**: Detects risky patterns in SQL migrations (NOT NULL additions, type changes, drops, index creation, foreign keys, enums)
|
|
12
|
+
- **Git Integration**: Automatically compares branches to identify changed migrations
|
|
13
|
+
- **Prisma Support**: Works with Prisma migrations and schema files
|
|
14
|
+
- **Configurable Gate Rules**: Fail CI/CD pipelines on HIGH or CRITICAL findings
|
|
15
|
+
- **Optional LLM Interpretation**: Uses Mistral AI for architectural analysis (optional, never required)
|
|
16
|
+
- **Multiple Report Formats**: Generates JSON and Markdown reports
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install --save-dev db-scalability-guardian
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or use directly with npx:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx db-scalability-guardian analyze
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Basic Usage
|
|
33
|
+
|
|
34
|
+
Run the analysis from your repository root:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx db-scalability-guardian analyze
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This will:
|
|
41
|
+
1. Compare `main` → `develop` branches (configurable)
|
|
42
|
+
2. Analyze changed Prisma migrations
|
|
43
|
+
3. Generate reports in `guardian-reports/`
|
|
44
|
+
4. Exit with code 1 if blocking findings are detected
|
|
45
|
+
|
|
46
|
+
### Configuration
|
|
47
|
+
|
|
48
|
+
Create a `guardian.config.js` or `guardian.config.json` in your repository root:
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
// guardian.config.js
|
|
52
|
+
module.exports = {
|
|
53
|
+
baseBranch: "main",
|
|
54
|
+
headBranch: "develop",
|
|
55
|
+
database: {
|
|
56
|
+
engine: "postgres",
|
|
57
|
+
orm: "prisma",
|
|
58
|
+
migrationsPath: "prisma/migrations"
|
|
59
|
+
},
|
|
60
|
+
futureConstraints: [
|
|
61
|
+
"Multi-country deployment (currency, taxes, locale)",
|
|
62
|
+
"Machine-specific pricing and configuration"
|
|
63
|
+
],
|
|
64
|
+
llm: {
|
|
65
|
+
provider: "mistral",
|
|
66
|
+
model: "mistral-medium-2508",
|
|
67
|
+
optional: true,
|
|
68
|
+
apiKey: process.env.MISTRAL_API_KEY
|
|
69
|
+
},
|
|
70
|
+
gate: {
|
|
71
|
+
failOn: ["HIGH", "CRITICAL"]
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Or as JSON:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"baseBranch": "main",
|
|
81
|
+
"headBranch": "develop",
|
|
82
|
+
"database": {
|
|
83
|
+
"engine": "postgres",
|
|
84
|
+
"orm": "prisma",
|
|
85
|
+
"migrationsPath": "prisma/migrations"
|
|
86
|
+
},
|
|
87
|
+
"gate": {
|
|
88
|
+
"failOn": ["HIGH", "CRITICAL"]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### GitHub Actions Integration
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
name: DB Scalability Check
|
|
97
|
+
|
|
98
|
+
on:
|
|
99
|
+
pull_request:
|
|
100
|
+
branches: [main]
|
|
101
|
+
|
|
102
|
+
jobs:
|
|
103
|
+
guardian:
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
steps:
|
|
106
|
+
- uses: actions/checkout@v3
|
|
107
|
+
with:
|
|
108
|
+
fetch-depth: 0 # Required for git diff
|
|
109
|
+
|
|
110
|
+
- name: Setup Node.js
|
|
111
|
+
uses: actions/setup-node@v3
|
|
112
|
+
with:
|
|
113
|
+
node-version: '18'
|
|
114
|
+
|
|
115
|
+
- name: Run DB Scalability Guardian
|
|
116
|
+
run: npx db-scalability-guardian analyze
|
|
117
|
+
env:
|
|
118
|
+
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} # Optional
|
|
119
|
+
|
|
120
|
+
- name: Upload reports
|
|
121
|
+
uses: actions/upload-artifact@v3
|
|
122
|
+
if: always()
|
|
123
|
+
with:
|
|
124
|
+
name: guardian-reports
|
|
125
|
+
path: guardian-reports/
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### LLM Integration (Optional)
|
|
129
|
+
|
|
130
|
+
The tool supports optional LLM interpretation using Mistral AI. To enable:
|
|
131
|
+
|
|
132
|
+
1. Set `MISTRAL_API_KEY` environment variable, or
|
|
133
|
+
2. Configure `llm.apiKey` in your config file
|
|
134
|
+
|
|
135
|
+
The LLM analysis is **never required** - the tool works fully without it. When enabled, it generates an additional `guardian-llm-analysis.md` report with architectural insights.
|
|
136
|
+
|
|
137
|
+
## Report Output
|
|
138
|
+
|
|
139
|
+
The tool generates reports in the `guardian-reports/` directory:
|
|
140
|
+
|
|
141
|
+
- **guardian-report.json**: Machine-readable report with all findings
|
|
142
|
+
- **guardian-report.md**: Human-readable Markdown report
|
|
143
|
+
- **guardian-llm-analysis.md**: LLM-generated architectural analysis (if enabled)
|
|
144
|
+
|
|
145
|
+
## Exit Codes
|
|
146
|
+
|
|
147
|
+
- `0`: Success, no blocking findings
|
|
148
|
+
- `1`: Blocking findings detected (based on `gate.failOn` configuration)
|
|
149
|
+
|
|
150
|
+
## Architecture
|
|
151
|
+
|
|
152
|
+
The tool is structured as a modular library:
|
|
153
|
+
|
|
154
|
+
- **Collectors**: Gather context (git diff, Prisma schema)
|
|
155
|
+
- **Analyzers**: Perform deterministic analysis (migration heuristics)
|
|
156
|
+
- **Reporters**: Generate output (JSON, Markdown)
|
|
157
|
+
- **Gate**: Apply severity-based rules
|
|
158
|
+
- **LLM**: Optional architectural interpretation
|
|
159
|
+
|
|
160
|
+
All analysis is deterministic and does not require LLM. The LLM component is purely additive for enhanced insights.
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
ISC
|
|
165
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../src/analyzers/postgres/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGhD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAmFrE;AAED,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAcrE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeMigration = analyzeMigration;
|
|
4
|
+
exports.analyzeMigrations = analyzeMigrations;
|
|
5
|
+
const prisma_js_1 = require("../../collectors/prisma.js");
|
|
6
|
+
function analyzeMigration(sql, file) {
|
|
7
|
+
const s = sql.toLowerCase();
|
|
8
|
+
const findings = [];
|
|
9
|
+
// 1) Add NOT NULL column (high risk on existing table)
|
|
10
|
+
if (s.match(/alter\s+table[\s\S]*add\s+column[\s\S]*not\s+null/)) {
|
|
11
|
+
findings.push({
|
|
12
|
+
id: "MIG_NOT_NULL_ADD",
|
|
13
|
+
severity: "HIGH",
|
|
14
|
+
title: "ADD COLUMN ... NOT NULL (risk of failure / blocking on existing data)",
|
|
15
|
+
evidence: "Detected `ALTER TABLE ... ADD COLUMN ... NOT NULL`.",
|
|
16
|
+
impact: "On a table with existing rows, this can fail or require full table rewrite / locks, blocking deployments.",
|
|
17
|
+
mitigation: "Use a safe 3-step approach: (1) add nullable column, (2) backfill in batches, (3) add NOT NULL constraint.",
|
|
18
|
+
file,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// 2) Alter column type (often locking + risky)
|
|
22
|
+
if (s.match(/alter\s+table[\s\S]*alter\s+column[\s\S]*type/)) {
|
|
23
|
+
findings.push({
|
|
24
|
+
id: "MIG_ALTER_TYPE",
|
|
25
|
+
severity: "HIGH",
|
|
26
|
+
title: "ALTER COLUMN TYPE (often locks / rewrite)",
|
|
27
|
+
evidence: "Detected `ALTER TABLE ... ALTER COLUMN ... TYPE ...`.",
|
|
28
|
+
impact: "May rewrite the table and take long locks depending on the type change and table size.",
|
|
29
|
+
mitigation: "Prefer expand-and-contract migrations: add new column, dual-write, backfill, swap, then drop old column.",
|
|
30
|
+
file,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// 3) Drop column/table (data loss)
|
|
34
|
+
if (s.match(/drop\s+column/) || s.match(/drop\s+table/)) {
|
|
35
|
+
findings.push({
|
|
36
|
+
id: "MIG_DROP_DATA",
|
|
37
|
+
severity: "CRITICAL",
|
|
38
|
+
title: "DROP COLUMN/TABLE (data loss risk)",
|
|
39
|
+
evidence: "Detected DROP operation.",
|
|
40
|
+
impact: "Irreversible data loss unless you have backups and explicit migration strategy.",
|
|
41
|
+
mitigation: "Use deprecation: stop writes, archive data, keep column/table for at least one release cycle, then remove.",
|
|
42
|
+
file,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// 4) Create index without concurrently (can lock writes)
|
|
46
|
+
if (s.match(/create\s+index/) && !s.match(/concurrently/)) {
|
|
47
|
+
findings.push({
|
|
48
|
+
id: "MIG_INDEX_NOT_CONCURRENT",
|
|
49
|
+
severity: "MEDIUM",
|
|
50
|
+
title: "CREATE INDEX without CONCURRENTLY (potential write lock)",
|
|
51
|
+
evidence: "Detected CREATE INDEX without CONCURRENTLY.",
|
|
52
|
+
impact: "On large tables, index creation may block writes and extend deployment time.",
|
|
53
|
+
mitigation: "For production large tables, prefer `CREATE INDEX CONCURRENTLY` and run outside transaction if needed.",
|
|
54
|
+
file,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// 5) Add foreign key constraints (can lock / scan)
|
|
58
|
+
if (s.match(/add\s+constraint[\s\S]*foreign\s+key/)) {
|
|
59
|
+
findings.push({
|
|
60
|
+
id: "MIG_ADD_FK",
|
|
61
|
+
severity: "MEDIUM",
|
|
62
|
+
title: "ADD FOREIGN KEY constraint (locks / full scan)",
|
|
63
|
+
evidence: "Detected ADD CONSTRAINT ... FOREIGN KEY.",
|
|
64
|
+
impact: "Postgres may scan the table and lock rows; risky on large tables.",
|
|
65
|
+
mitigation: "Ensure supporting indexes exist; consider adding constraint as NOT VALID then VALIDATE in a later step.",
|
|
66
|
+
file,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// 6) Create enum / alter enum (can be tricky)
|
|
70
|
+
if (s.match(/create\s+type[\s\S]*as\s+enum/)) {
|
|
71
|
+
findings.push({
|
|
72
|
+
id: "MIG_ENUM_CREATE",
|
|
73
|
+
severity: "LOW",
|
|
74
|
+
title: "ENUM type created (evolvability considerations)",
|
|
75
|
+
evidence: "Detected CREATE TYPE ... AS ENUM.",
|
|
76
|
+
impact: "Enums are harder to evolve than lookup tables; future changes require careful migrations.",
|
|
77
|
+
mitigation: "Prefer lookup table for frequently changing values; if enum, plan a safe ALTER TYPE strategy.",
|
|
78
|
+
file,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return findings;
|
|
82
|
+
}
|
|
83
|
+
function analyzeMigrations(migrationFiles) {
|
|
84
|
+
const findings = [];
|
|
85
|
+
for (const mig of migrationFiles) {
|
|
86
|
+
// mig might be folder or file; normalize:
|
|
87
|
+
const isFolder = !mig.endsWith(".sql");
|
|
88
|
+
const sqlFile = isFolder ? `${mig}/migration.sql` : mig;
|
|
89
|
+
const sql = (0, prisma_js_1.loadMigrationSQL)(sqlFile);
|
|
90
|
+
if (!sql)
|
|
91
|
+
continue;
|
|
92
|
+
findings.push(...analyzeMigration(sql, sqlFile));
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../../src/analyzers/postgres/migrations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const loader_js_1 = require("../config/loader.js");
|
|
10
|
+
const git_js_1 = require("../collectors/git.js");
|
|
11
|
+
const prisma_js_1 = require("../collectors/prisma.js");
|
|
12
|
+
const migrations_js_1 = require("../analyzers/postgres/migrations.js");
|
|
13
|
+
const markdown_js_1 = require("../reporters/markdown.js");
|
|
14
|
+
const json_js_1 = require("../reporters/json.js");
|
|
15
|
+
const gate_js_1 = require("../gate/gate.js");
|
|
16
|
+
const prompts_js_1 = require("../llm/prompts.js");
|
|
17
|
+
const mistral_js_1 = require("../llm/mistral.js");
|
|
18
|
+
async function main() {
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
const config = (0, loader_js_1.loadConfig)(cwd);
|
|
21
|
+
console.log("🔍 DB Scalability Guardian");
|
|
22
|
+
console.log(`Comparing ${config.baseBranch} → ${config.headBranch}\n`);
|
|
23
|
+
// 1. Collect git diff context
|
|
24
|
+
console.log("📊 Collecting git diff...");
|
|
25
|
+
const diffResult = (0, git_js_1.diffFiles)(config.baseBranch, config.headBranch);
|
|
26
|
+
console.log(` Found ${diffResult.files.length} changed files`);
|
|
27
|
+
// 2. Collect Prisma schema + migrations
|
|
28
|
+
console.log("🗄️ Collecting Prisma context...");
|
|
29
|
+
const migrationsPath = config.database.migrationsPath;
|
|
30
|
+
if (!migrationsPath) {
|
|
31
|
+
throw new Error("migrationsPath is required but not configured");
|
|
32
|
+
}
|
|
33
|
+
const prismaContext = (0, prisma_js_1.collectPrismaContext)(migrationsPath, diffResult.files);
|
|
34
|
+
console.log(` Found ${prismaContext.migrationFiles.length} modified migrations`);
|
|
35
|
+
// 3. Build context
|
|
36
|
+
const meta = {
|
|
37
|
+
baseBranch: config.baseBranch,
|
|
38
|
+
headBranch: config.headBranch,
|
|
39
|
+
comparedRange: diffResult.range,
|
|
40
|
+
changedFilesCount: diffResult.files.length,
|
|
41
|
+
generatedAt: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
const engine = config.database.engine;
|
|
44
|
+
const orm = config.database.orm;
|
|
45
|
+
if (!engine || !orm) {
|
|
46
|
+
throw new Error("database.engine and database.orm are required but not configured");
|
|
47
|
+
}
|
|
48
|
+
const context = {
|
|
49
|
+
meta,
|
|
50
|
+
database: {
|
|
51
|
+
engine,
|
|
52
|
+
orm,
|
|
53
|
+
migrationsPath,
|
|
54
|
+
modifiedMigrations: prismaContext.migrationFiles,
|
|
55
|
+
},
|
|
56
|
+
prisma: {
|
|
57
|
+
schemaPath: prismaContext.schemaPath,
|
|
58
|
+
schema: prismaContext.schema,
|
|
59
|
+
},
|
|
60
|
+
backendHints: diffResult.files.filter((f) => f.startsWith("src/") &&
|
|
61
|
+
(f.includes(".service.") ||
|
|
62
|
+
f.includes(".repository.") ||
|
|
63
|
+
f.includes(".module.") ||
|
|
64
|
+
f.includes(".controller."))),
|
|
65
|
+
changedFiles: diffResult.files,
|
|
66
|
+
};
|
|
67
|
+
// 4. Run Postgres migration analysis
|
|
68
|
+
console.log("🔬 Analyzing migrations...");
|
|
69
|
+
const findings = (0, migrations_js_1.analyzeMigrations)(prismaContext.migrationFiles);
|
|
70
|
+
console.log(` Found ${findings.length} findings`);
|
|
71
|
+
// 5. Generate reports
|
|
72
|
+
console.log("📝 Generating reports...");
|
|
73
|
+
const report = {
|
|
74
|
+
meta,
|
|
75
|
+
findings,
|
|
76
|
+
};
|
|
77
|
+
const reportDir = path_1.default.join(cwd, "guardian-reports");
|
|
78
|
+
fs_1.default.mkdirSync(reportDir, { recursive: true });
|
|
79
|
+
const jsonReport = (0, json_js_1.renderJSON)(report);
|
|
80
|
+
const markdownReport = (0, markdown_js_1.renderMarkdown)(findings, meta);
|
|
81
|
+
fs_1.default.writeFileSync(path_1.default.join(reportDir, "guardian-report.json"), jsonReport);
|
|
82
|
+
fs_1.default.writeFileSync(path_1.default.join(reportDir, "guardian-report.md"), markdownReport);
|
|
83
|
+
console.log(` ✓ guardian-report.json`);
|
|
84
|
+
console.log(` ✓ guardian-report.md`);
|
|
85
|
+
// 6. Optional LLM interpretation
|
|
86
|
+
if (config.llm.optional && config.llm.apiKey) {
|
|
87
|
+
console.log("🤖 Running LLM interpretation...");
|
|
88
|
+
try {
|
|
89
|
+
const prompts = (0, prompts_js_1.buildLLMPrompt)(context, markdownReport);
|
|
90
|
+
const llmOutput = await (0, mistral_js_1.callMistral)({
|
|
91
|
+
apiKey: config.llm.apiKey,
|
|
92
|
+
model: config.llm.model,
|
|
93
|
+
}, prompts.system, prompts.user);
|
|
94
|
+
fs_1.default.writeFileSync(path_1.default.join(reportDir, "guardian-llm-analysis.md"), llmOutput);
|
|
95
|
+
console.log(` ✓ guardian-llm-analysis.md`);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.warn(" ⚠️ LLM analysis failed:", error instanceof Error ? error.message : String(error));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (config.llm.optional) {
|
|
102
|
+
console.log(" ⏭️ Skipping LLM (no API key provided)");
|
|
103
|
+
}
|
|
104
|
+
// 7. Apply gate rules
|
|
105
|
+
console.log("\n🚦 Checking gate rules...");
|
|
106
|
+
const failOn = config.gate.failOn;
|
|
107
|
+
if (!failOn) {
|
|
108
|
+
throw new Error("gate.failOn is required but not configured");
|
|
109
|
+
}
|
|
110
|
+
const gateResult = (0, gate_js_1.checkGate)(findings, failOn);
|
|
111
|
+
if (!gateResult.passed) {
|
|
112
|
+
console.error(`\n❌ Blocking findings detected (${gateResult.blocking.length}):`);
|
|
113
|
+
for (const finding of gateResult.blocking) {
|
|
114
|
+
console.error(` [${finding.severity}] ${finding.title} (${finding.file || "unknown"})`);
|
|
115
|
+
}
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
console.log("✅ No blocking findings");
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
main().catch((error) => {
|
|
122
|
+
console.error("Fatal error:", error);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/collectors/git.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CA+BhE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.diffFiles = diffFiles;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
function sh(cmd) {
|
|
6
|
+
try {
|
|
7
|
+
return (0, child_process_1.execSync)(cmd, { stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const stderr = error && typeof error === "object" && "stderr" in error
|
|
11
|
+
? error.stderr.toString()
|
|
12
|
+
: "";
|
|
13
|
+
throw new Error(`Command failed: ${cmd}\n${stderr}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function diffFiles(base, head) {
|
|
17
|
+
// Try to fetch remote refs first
|
|
18
|
+
try {
|
|
19
|
+
sh("git fetch origin --prune");
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Ignore fetch errors
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// Compare origin/base..origin/head
|
|
26
|
+
const range = `origin/${base}...origin/${head}`;
|
|
27
|
+
const out = sh(`git diff --name-only ${range}`);
|
|
28
|
+
return { files: out.split("\n").filter(Boolean), range };
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Fallback to local branch comparison
|
|
32
|
+
try {
|
|
33
|
+
const range = `${base}...${head}`;
|
|
34
|
+
const out = sh(`git diff --name-only ${range}`);
|
|
35
|
+
return { files: out.split("\n").filter(Boolean), range };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Last resort: compare current HEAD with base
|
|
39
|
+
try {
|
|
40
|
+
const range = `${base}...HEAD`;
|
|
41
|
+
const out = sh(`git diff --name-only ${range}`);
|
|
42
|
+
return { files: out.split("\n").filter(Boolean), range };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
console.warn("Could not determine changed files. Returning empty array.");
|
|
46
|
+
return { files: [], range: "unknown" };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/collectors/git.ts"],"names":[],"mappings":";;AAkBA,8BA+BC;AAjDD,iDAAyC;AAEzC,SAAS,EAAE,CAAC,GAAW;IACrB,IAAI,CAAC;QACH,OAAO,IAAA,wBAAQ,EAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;IAChF,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK;YACpE,CAAC,CAAE,KAAK,CAAC,MAAiC,CAAC,QAAQ,EAAE;YACrD,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,KAAK,MAAM,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAOD,SAAgB,SAAS,CAAC,IAAY,EAAE,IAAY;IAClD,iCAAiC;IACjC,IAAI,CAAC;QACH,EAAE,CAAC,0BAA0B,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IAED,IAAI,CAAC;QACH,mCAAmC;QACnC,MAAM,KAAK,GAAG,UAAU,IAAI,aAAa,IAAI,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,EAAE,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;QAChD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;QACtC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,IAAI,MAAM,IAAI,EAAE,CAAC;YAClC,MAAM,GAAG,GAAG,EAAE,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;YAChD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;YAC9C,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,GAAG,IAAI,SAAS,CAAC;gBAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;gBAChD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;YAC3D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;gBAC1E,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface PrismaContext {
|
|
2
|
+
schemaPath: string;
|
|
3
|
+
schema: string | null;
|
|
4
|
+
migrationFiles: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function collectPrismaContext(migrationsPath: string, changedFiles: string[]): PrismaContext;
|
|
7
|
+
export declare function loadMigrationSQL(migrationPath: string): string | null;
|
|
8
|
+
//# sourceMappingURL=prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/collectors/prisma.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EAAE,GACrB,aAAa,CAWf;AAED,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQrE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.collectPrismaContext = collectPrismaContext;
|
|
7
|
+
exports.loadMigrationSQL = loadMigrationSQL;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
function collectPrismaContext(migrationsPath, changedFiles) {
|
|
11
|
+
const schemaPath = "prisma/schema.prisma";
|
|
12
|
+
const schema = fs_1.default.existsSync(schemaPath) ? fs_1.default.readFileSync(schemaPath, "utf-8") : null;
|
|
13
|
+
const migrationFiles = changedFiles.filter((f) => f.startsWith(`${migrationsPath}/`));
|
|
14
|
+
return {
|
|
15
|
+
schemaPath,
|
|
16
|
+
schema,
|
|
17
|
+
migrationFiles,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function loadMigrationSQL(migrationPath) {
|
|
21
|
+
// If migrationPath is already the sql file, load it; if folder, load migration.sql
|
|
22
|
+
const full = migrationPath.endsWith(".sql")
|
|
23
|
+
? migrationPath
|
|
24
|
+
: path_1.default.join(migrationPath, "migration.sql");
|
|
25
|
+
if (!fs_1.default.existsSync(full))
|
|
26
|
+
return null;
|
|
27
|
+
return fs_1.default.readFileSync(full, "utf-8");
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/collectors/prisma.ts"],"names":[],"mappings":";;;;;AASA,oDAcC;AAED,4CAQC;AAjCD,4CAAoB;AACpB,gDAAwB;AAQxB,SAAgB,oBAAoB,CAClC,cAAsB,EACtB,YAAsB;IAEtB,MAAM,UAAU,GAAG,sBAAsB,CAAC;IAC1C,MAAM,MAAM,GAAG,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvF,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC;IAEtF,OAAO;QACL,UAAU;QACV,MAAM;QACN,cAAc;KACf,CAAC;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAAC,aAAqB;IACpD,mFAAmF;IACnF,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC;QACzC,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IAE9C,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,YAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const DEFAULT_CONFIG: {
|
|
2
|
+
baseBranch: string;
|
|
3
|
+
headBranch: string;
|
|
4
|
+
database: {
|
|
5
|
+
engine: string;
|
|
6
|
+
orm: string;
|
|
7
|
+
migrationsPath: string;
|
|
8
|
+
};
|
|
9
|
+
futureConstraints: never[];
|
|
10
|
+
llm: {
|
|
11
|
+
provider: string;
|
|
12
|
+
model: string;
|
|
13
|
+
optional: boolean;
|
|
14
|
+
apiKey: undefined;
|
|
15
|
+
};
|
|
16
|
+
gate: {
|
|
17
|
+
failOn: string[];
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare const DEFAULT_DB_RULES = "DB Architecture Rules (baseline):\n1. No migration adds a NOT NULL column on an existing table without a safe backfill strategy.\n2. High-cardinality tables must have appropriate indexes for expected access patterns.\n3. Foreign keys must not prevent soft delete or archival strategies.\n4. Pricing/config/business rules must not be hardcoded at entity level; must be contextual (tenant/machine/country).\n5. Entities that represent business concepts should support future versioning (history, effective dates).\n6. Avoid blocking DDL on large tables (Postgres: rewrite/locks).\n7. Many-to-many relations must be extensible (join table supports metadata).\n8. JSON/JSONB usage must be justified and versioned (schema evolution plan).";
|
|
21
|
+
export declare const DEFAULT_FUTURE_CONSTRAINTS = "Known future directions:\n- Multi-country deployment (currency, taxes, locale)\n- Machine-specific pricing and configuration\n- Feature flags per tenant / per machine\n- Audit history and long-term retention\n- Potential white-label / multi-tenant clients";
|
|
22
|
+
export declare const DEFAULT_SYSTEM_PROMPT = "You are a senior database and backend architect.\nYou audit schemas, migrations and backend code for scalability and evolvability risks.\nYou do not modify code. You only report risks and mitigations.\nYou must be conservative: if unsure, raise a warning.\nYou must output a structured report with severities: LOW, MEDIUM, HIGH, CRITICAL.\nFocus on: migration safety, future feature blockers, indexing, coupling, data model rigidity.";
|
|
23
|
+
export declare const DEFAULT_ANALYSIS_PROMPT = "Analyze the repository context provided below.\n\nTasks:\n1) Review migrations for risks: locking, table rewrites, irreversible changes, data loss, unsafe NOT NULL.\n2) Review schema/ORM model for scalability & evolvability: indexes, cardinalities, versioning, soft delete, multitenancy.\n3) Identify \"future feature blockers\" based on future-constraints.md.\n4) Output a report in Markdown with:\n - Executive summary\n - Detected issues (with severity)\n - Evidence (file + snippet or explanation)\n - Impact (what feature becomes hard)\n - Mitigation (safe path)";
|
|
24
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;CAkB1B,CAAC;AAEF,eAAO,MAAM,gBAAgB,kuBAQgD,CAAC;AAE9E,eAAO,MAAM,0BAA0B,oQAKQ,CAAC;AAEhD,eAAO,MAAM,qBAAqB,sbAK4D,CAAC;AAE/F,eAAO,MAAM,uBAAuB,skBAWR,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_ANALYSIS_PROMPT = exports.DEFAULT_SYSTEM_PROMPT = exports.DEFAULT_FUTURE_CONSTRAINTS = exports.DEFAULT_DB_RULES = exports.DEFAULT_CONFIG = void 0;
|
|
4
|
+
exports.DEFAULT_CONFIG = {
|
|
5
|
+
baseBranch: "main",
|
|
6
|
+
headBranch: "develop",
|
|
7
|
+
database: {
|
|
8
|
+
engine: "postgres",
|
|
9
|
+
orm: "prisma",
|
|
10
|
+
migrationsPath: "prisma/migrations",
|
|
11
|
+
},
|
|
12
|
+
futureConstraints: [],
|
|
13
|
+
llm: {
|
|
14
|
+
provider: "mistral",
|
|
15
|
+
model: "mistral-medium-2508",
|
|
16
|
+
optional: true,
|
|
17
|
+
apiKey: undefined,
|
|
18
|
+
},
|
|
19
|
+
gate: {
|
|
20
|
+
failOn: ["HIGH", "CRITICAL"],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
exports.DEFAULT_DB_RULES = `DB Architecture Rules (baseline):
|
|
24
|
+
1. No migration adds a NOT NULL column on an existing table without a safe backfill strategy.
|
|
25
|
+
2. High-cardinality tables must have appropriate indexes for expected access patterns.
|
|
26
|
+
3. Foreign keys must not prevent soft delete or archival strategies.
|
|
27
|
+
4. Pricing/config/business rules must not be hardcoded at entity level; must be contextual (tenant/machine/country).
|
|
28
|
+
5. Entities that represent business concepts should support future versioning (history, effective dates).
|
|
29
|
+
6. Avoid blocking DDL on large tables (Postgres: rewrite/locks).
|
|
30
|
+
7. Many-to-many relations must be extensible (join table supports metadata).
|
|
31
|
+
8. JSON/JSONB usage must be justified and versioned (schema evolution plan).`;
|
|
32
|
+
exports.DEFAULT_FUTURE_CONSTRAINTS = `Known future directions:
|
|
33
|
+
- Multi-country deployment (currency, taxes, locale)
|
|
34
|
+
- Machine-specific pricing and configuration
|
|
35
|
+
- Feature flags per tenant / per machine
|
|
36
|
+
- Audit history and long-term retention
|
|
37
|
+
- Potential white-label / multi-tenant clients`;
|
|
38
|
+
exports.DEFAULT_SYSTEM_PROMPT = `You are a senior database and backend architect.
|
|
39
|
+
You audit schemas, migrations and backend code for scalability and evolvability risks.
|
|
40
|
+
You do not modify code. You only report risks and mitigations.
|
|
41
|
+
You must be conservative: if unsure, raise a warning.
|
|
42
|
+
You must output a structured report with severities: LOW, MEDIUM, HIGH, CRITICAL.
|
|
43
|
+
Focus on: migration safety, future feature blockers, indexing, coupling, data model rigidity.`;
|
|
44
|
+
exports.DEFAULT_ANALYSIS_PROMPT = `Analyze the repository context provided below.
|
|
45
|
+
|
|
46
|
+
Tasks:
|
|
47
|
+
1) Review migrations for risks: locking, table rewrites, irreversible changes, data loss, unsafe NOT NULL.
|
|
48
|
+
2) Review schema/ORM model for scalability & evolvability: indexes, cardinalities, versioning, soft delete, multitenancy.
|
|
49
|
+
3) Identify "future feature blockers" based on future-constraints.md.
|
|
50
|
+
4) Output a report in Markdown with:
|
|
51
|
+
- Executive summary
|
|
52
|
+
- Detected issues (with severity)
|
|
53
|
+
- Evidence (file + snippet or explanation)
|
|
54
|
+
- Impact (what feature becomes hard)
|
|
55
|
+
- Mitigation (safe path)`;
|
|
56
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":";;;AAGa,QAAA,cAAc,GAAG;IAC5B,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,SAAS;IACrB,QAAQ,EAAE;QACR,MAAM,EAAE,UAAU;QAClB,GAAG,EAAE,QAAQ;QACb,cAAc,EAAE,mBAAmB;KACpC;IACD,iBAAiB,EAAE,EAAE;IACrB,GAAG,EAAE;QACH,QAAQ,EAAE,SAAS;QACnB,KAAK,EAAE,qBAAqB;QAC5B,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,SAAS;KAClB;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;KAC7B;CACF,CAAC;AAEW,QAAA,gBAAgB,GAAG;;;;;;;;6EAQ6C,CAAC;AAEjE,QAAA,0BAA0B,GAAG;;;;;+CAKK,CAAC;AAEnC,QAAA,qBAAqB,GAAG;;;;;8FAKyD,CAAC;AAElF,QAAA,uBAAuB,GAAG;;;;;;;;;;;4BAWX,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAAC,cAAc,CAAC,CA2DhF"}
|