@arvoretech/db-diagram-mcp 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 ADDED
@@ -0,0 +1,46 @@
1
+ # @arvoretech/db-diagram-mcp
2
+
3
+ MCP Server that generates Mermaid ER diagrams from SQL DDL.
4
+
5
+ ## Tools
6
+
7
+ | Tool | Description |
8
+ |------|-------------|
9
+ | `generate_erd` | Full ER diagram from DDL. Optionally filter to specific tables. |
10
+ | `generate_domain_map` | BFS from an entry table, showing all related tables within N hops. |
11
+ | `explain_table` | Column details, incoming/outgoing FKs, and a mini neighborhood diagram. |
12
+ | `trace_flow` | Find relationship paths between two tables with a connecting diagram. |
13
+
14
+ ## Usage
15
+
16
+ All tools accept DDL as input (no database connection required).
17
+
18
+ ```json
19
+ {
20
+ "ddl": "CREATE TABLE users (...); CREATE TABLE posts (...);",
21
+ "tables": ["users", "posts"],
22
+ "title": "User Domain"
23
+ }
24
+ ```
25
+
26
+ ## Output
27
+
28
+ All diagram output is Mermaid syntax, ready to render in GitHub, Notion, or any Mermaid-compatible viewer.
29
+
30
+ ## Running
31
+
32
+ ```bash
33
+ # stdio transport (for MCP clients)
34
+ npx @arvoretech/db-diagram-mcp
35
+
36
+ # or via pnpm in this monorepo
37
+ pnpm --filter @arvoretech/db-diagram-mcp dev
38
+ ```
39
+
40
+ ## Composable Pattern
41
+
42
+ This MCP doesn't connect to databases directly. Feed it DDL from your existing database MCPs:
43
+
44
+ 1. Use `mysql-mcp` or `postgresql-mcp` to get schema (`SHOW CREATE TABLE` or `pg_dump --schema-only`)
45
+ 2. Pass the DDL output to `generate_erd` or other tools
46
+ 3. Get Mermaid diagrams back
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ export { DbDiagramMCPServer } from "./server.js";
3
+ export { parseDDL, extractInlineForeignKeys } from "./parser.js";
4
+ export { generateErd, generateDomainMap, explainTable, traceFlow, } from "./mermaid.js";
5
+ export { visualize } from "./visualizer.js";
6
+ export * from "./types.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAaA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { DbDiagramMCPServer } from "./server.js";
3
+ try {
4
+ const server = new DbDiagramMCPServer();
5
+ server.setupGracefulShutdown();
6
+ await server.start();
7
+ }
8
+ catch (error) {
9
+ console.error("Failed to start DB Diagram MCP Server:", error);
10
+ process.exit(1);
11
+ }
12
+ export { DbDiagramMCPServer } from "./server.js";
13
+ export { parseDDL, extractInlineForeignKeys } from "./parser.js";
14
+ export { generateErd, generateDomainMap, explainTable, traceFlow, } from "./mermaid.js";
15
+ export { visualize } from "./visualizer.js";
16
+ export * from "./types.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,IAAI,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACxC,MAAM,CAAC,qBAAqB,EAAE,CAAC;IAC/B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Schema } from "./types.js";
2
+ export declare function generateErd(schema: Schema, options?: {
3
+ tables?: string[];
4
+ title?: string;
5
+ }): string;
6
+ export declare function generateDomainMap(schema: Schema, entryTable: string, depth: number): string;
7
+ export declare function explainTable(schema: Schema, tableName: string): string;
8
+ export declare function traceFlow(schema: Schema, from: string, to: string): string;
9
+ //# sourceMappingURL=mermaid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../src/mermaid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,MAAM,YAAY,CAAC;AAE3C,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,MAAM,CAmDR;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GACZ,MAAM,CAuCR;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAwDtE;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,MAAM,CAgCR"}
@@ -0,0 +1,183 @@
1
+ export function generateErd(schema, options) {
2
+ const filteredTables = options?.tables
3
+ ? schema.tables.filter((t) => options.tables.includes(t.name))
4
+ : schema.tables;
5
+ if (filteredTables.length === 0)
6
+ return "erDiagram";
7
+ const lines = [];
8
+ if (options?.title)
9
+ lines.push(`---\ntitle: ${options.title}\n---`);
10
+ lines.push("erDiagram");
11
+ for (const table of filteredTables) {
12
+ lines.push(` ${table.name} {`);
13
+ for (const col of table.columns) {
14
+ const markers = [];
15
+ if (col.primaryKey)
16
+ markers.push("PK");
17
+ if (col.unique && !col.primaryKey)
18
+ markers.push("UK");
19
+ const isFk = schema.tables.some((t) => t.foreignKeys.some((fk) => fk.referencedTable === table.name &&
20
+ fk.referencedColumns.includes(col.name)));
21
+ const isLocalFk = table.foreignKeys.some((fk) => fk.columns.includes(col.name));
22
+ if (isFk || isLocalFk)
23
+ markers.push("FK");
24
+ const markerStr = markers.length > 0 ? ` ${markers.join(",")}` : "";
25
+ lines.push(` ${normalizeType(col.type)} ${col.name}${markerStr}`);
26
+ }
27
+ lines.push(" }");
28
+ }
29
+ const tableNames = new Set(filteredTables.map((t) => t.name));
30
+ for (const table of filteredTables) {
31
+ for (const fk of table.foreignKeys) {
32
+ if (!tableNames.has(fk.referencedTable))
33
+ continue;
34
+ const cardinality = inferCardinality(table, fk.columns);
35
+ lines.push(` ${fk.referencedTable} ${cardinality} ${table.name} : "${fk.columns.join(", ")}"`);
36
+ }
37
+ }
38
+ return lines.join("\n");
39
+ }
40
+ export function generateDomainMap(schema, entryTable, depth) {
41
+ const visited = new Set();
42
+ const queue = [
43
+ { table: entryTable, level: 0 },
44
+ ];
45
+ while (queue.length > 0) {
46
+ const current = queue.shift();
47
+ if (visited.has(current.table) || current.level > depth)
48
+ continue;
49
+ visited.add(current.table);
50
+ const table = schema.tables.find((t) => t.name === current.table);
51
+ if (!table)
52
+ continue;
53
+ for (const fk of table.foreignKeys) {
54
+ if (!visited.has(fk.referencedTable)) {
55
+ queue.push({ table: fk.referencedTable, level: current.level + 1 });
56
+ }
57
+ }
58
+ for (const other of schema.tables) {
59
+ for (const fk of other.foreignKeys) {
60
+ if (fk.referencedTable === current.table &&
61
+ !visited.has(other.name)) {
62
+ queue.push({ table: other.name, level: current.level + 1 });
63
+ }
64
+ }
65
+ }
66
+ }
67
+ const filteredSchema = {
68
+ tables: schema.tables.filter((t) => visited.has(t.name)),
69
+ };
70
+ return generateErd(filteredSchema, {
71
+ title: `Domain: ${entryTable} (depth ${depth})`,
72
+ });
73
+ }
74
+ export function explainTable(schema, tableName) {
75
+ const table = schema.tables.find((t) => t.name === tableName);
76
+ if (!table)
77
+ return `Table "${tableName}" not found in schema.`;
78
+ const lines = [`# ${tableName}`, ""];
79
+ lines.push("## Columns", "");
80
+ lines.push("| Column | Type | PK | Nullable | Unique | Default |");
81
+ lines.push("|--------|------|----|---------:|--------|---------|");
82
+ for (const col of table.columns) {
83
+ lines.push(`| ${col.name} | ${col.type} | ${col.primaryKey ? "✓" : ""} | ${col.nullable ? "✓" : ""} | ${col.unique ? "✓" : ""} | ${col.defaultValue ?? ""} |`);
84
+ }
85
+ if (table.foreignKeys.length > 0) {
86
+ lines.push("", "## Foreign Keys (outgoing)", "");
87
+ for (const fk of table.foreignKeys) {
88
+ lines.push(`- ${fk.columns.join(", ")} → ${fk.referencedTable}(${fk.referencedColumns.join(", ")})`);
89
+ }
90
+ }
91
+ const incomingFks = schema.tables.filter((t) => t.foreignKeys.some((fk) => fk.referencedTable === tableName));
92
+ if (incomingFks.length > 0) {
93
+ lines.push("", "## Referenced By (incoming)", "");
94
+ for (const t of incomingFks) {
95
+ for (const fk of t.foreignKeys) {
96
+ if (fk.referencedTable === tableName) {
97
+ lines.push(`- ${t.name}(${fk.columns.join(", ")}) → ${tableName}(${fk.referencedColumns.join(", ")})`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ const relatedTables = new Set();
103
+ for (const fk of table.foreignKeys)
104
+ relatedTables.add(fk.referencedTable);
105
+ for (const t of incomingFks)
106
+ relatedTables.add(t.name);
107
+ if (relatedTables.size > 0) {
108
+ const miniSchema = {
109
+ tables: schema.tables.filter((t) => t.name === tableName || relatedTables.has(t.name)),
110
+ };
111
+ lines.push("", "## Diagram", "", "```mermaid");
112
+ lines.push(generateErd(miniSchema, { title: tableName }));
113
+ lines.push("```");
114
+ }
115
+ return lines.join("\n");
116
+ }
117
+ export function traceFlow(schema, from, to) {
118
+ const paths = findPaths(schema, from, to, 6);
119
+ if (paths.length === 0) {
120
+ return `No relationship path found between "${from}" and "${to}" within 6 hops.`;
121
+ }
122
+ const allTables = new Set();
123
+ for (const path of paths) {
124
+ for (const table of path)
125
+ allTables.add(table);
126
+ }
127
+ const lines = [
128
+ `# Flow: ${from} → ${to}`,
129
+ "",
130
+ `Found ${paths.length} path(s):`,
131
+ "",
132
+ ];
133
+ for (let i = 0; i < paths.length; i++) {
134
+ lines.push(`${i + 1}. ${paths[i].join(" → ")}`);
135
+ }
136
+ const filteredSchema = {
137
+ tables: schema.tables.filter((t) => allTables.has(t.name)),
138
+ };
139
+ lines.push("", "```mermaid");
140
+ lines.push(generateErd(filteredSchema, { title: `${from} → ${to}` }));
141
+ lines.push("```");
142
+ return lines.join("\n");
143
+ }
144
+ function findPaths(schema, from, to, maxDepth) {
145
+ const results = [];
146
+ function dfs(current, target, path) {
147
+ if (path.length > maxDepth)
148
+ return;
149
+ if (current === target) {
150
+ results.push([...path]);
151
+ return;
152
+ }
153
+ const table = schema.tables.find((t) => t.name === current);
154
+ if (!table)
155
+ return;
156
+ for (const fk of table.foreignKeys) {
157
+ if (!path.includes(fk.referencedTable)) {
158
+ path.push(fk.referencedTable);
159
+ dfs(fk.referencedTable, target, path);
160
+ path.pop();
161
+ }
162
+ }
163
+ for (const other of schema.tables) {
164
+ for (const fk of other.foreignKeys) {
165
+ if (fk.referencedTable === current && !path.includes(other.name)) {
166
+ path.push(other.name);
167
+ dfs(other.name, target, path);
168
+ path.pop();
169
+ }
170
+ }
171
+ }
172
+ }
173
+ dfs(from, to, [from]);
174
+ return results.slice(0, 5);
175
+ }
176
+ function inferCardinality(table, fkColumns) {
177
+ const hasUnique = table.columns.some((c) => fkColumns.includes(c.name) && (c.unique || c.primaryKey));
178
+ return hasUnique ? "||--||" : "||--o{";
179
+ }
180
+ function normalizeType(type) {
181
+ return type.replace(/[(),\s]/g, "_").replace(/_+$/, "");
182
+ }
183
+ //# sourceMappingURL=mermaid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../src/mermaid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mermaid.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.test.d.ts","sourceRoot":"","sources":["../src/mermaid.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,118 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseDDL, extractInlineForeignKeys } from "./parser.js";
3
+ import { generateErd, generateDomainMap, explainTable, traceFlow, } from "./mermaid.js";
4
+ const sampleDDL = `
5
+ CREATE TABLE users (
6
+ id INT NOT NULL AUTO_INCREMENT,
7
+ email VARCHAR(255) NOT NULL,
8
+ name VARCHAR(100),
9
+ PRIMARY KEY (id),
10
+ UNIQUE KEY (email)
11
+ );
12
+
13
+ CREATE TABLE posts (
14
+ id INT NOT NULL AUTO_INCREMENT,
15
+ user_id INT NOT NULL,
16
+ title VARCHAR(255) NOT NULL,
17
+ PRIMARY KEY (id),
18
+ FOREIGN KEY (user_id) REFERENCES users(id)
19
+ );
20
+
21
+ CREATE TABLE comments (
22
+ id INT NOT NULL AUTO_INCREMENT,
23
+ post_id INT NOT NULL,
24
+ user_id INT NOT NULL,
25
+ content TEXT NOT NULL,
26
+ PRIMARY KEY (id),
27
+ FOREIGN KEY (post_id) REFERENCES posts(id),
28
+ FOREIGN KEY (user_id) REFERENCES users(id)
29
+ );
30
+ `;
31
+ function getSchema() {
32
+ let schema = parseDDL(sampleDDL);
33
+ schema = extractInlineForeignKeys(sampleDDL, schema);
34
+ return schema;
35
+ }
36
+ describe("generateErd", () => {
37
+ it("generates valid mermaid erDiagram", () => {
38
+ const schema = getSchema();
39
+ const diagram = generateErd(schema);
40
+ expect(diagram).toContain("erDiagram");
41
+ expect(diagram).toContain("users {");
42
+ expect(diagram).toContain("posts {");
43
+ expect(diagram).toContain("comments {");
44
+ });
45
+ it("includes relationship lines", () => {
46
+ const schema = getSchema();
47
+ const diagram = generateErd(schema);
48
+ expect(diagram).toContain("users");
49
+ expect(diagram).toContain("posts");
50
+ });
51
+ it("filters to specific tables", () => {
52
+ const schema = getSchema();
53
+ const diagram = generateErd(schema, { tables: ["users", "posts"] });
54
+ expect(diagram).toContain("users {");
55
+ expect(diagram).toContain("posts {");
56
+ expect(diagram).not.toContain("comments {");
57
+ });
58
+ it("adds title when provided", () => {
59
+ const schema = getSchema();
60
+ const diagram = generateErd(schema, { title: "My ERD" });
61
+ expect(diagram).toContain("title: My ERD");
62
+ });
63
+ });
64
+ describe("generateDomainMap", () => {
65
+ it("traverses from entry table", () => {
66
+ const schema = getSchema();
67
+ const diagram = generateDomainMap(schema, "posts", 1);
68
+ expect(diagram).toContain("posts {");
69
+ expect(diagram).toContain("users {");
70
+ expect(diagram).toContain("comments {");
71
+ });
72
+ it("respects depth limit", () => {
73
+ const schema = getSchema();
74
+ const diagram = generateDomainMap(schema, "comments", 0);
75
+ expect(diagram).toContain("comments {");
76
+ });
77
+ });
78
+ describe("explainTable", () => {
79
+ it("shows column details", () => {
80
+ const schema = getSchema();
81
+ const explanation = explainTable(schema, "posts");
82
+ expect(explanation).toContain("# posts");
83
+ expect(explanation).toContain("user_id");
84
+ expect(explanation).toContain("## Foreign Keys");
85
+ expect(explanation).toContain("users");
86
+ });
87
+ it("shows incoming references", () => {
88
+ const schema = getSchema();
89
+ const explanation = explainTable(schema, "users");
90
+ expect(explanation).toContain("## Referenced By");
91
+ expect(explanation).toContain("posts");
92
+ expect(explanation).toContain("comments");
93
+ });
94
+ it("returns error for unknown table", () => {
95
+ const schema = getSchema();
96
+ const explanation = explainTable(schema, "nonexistent");
97
+ expect(explanation).toContain("not found");
98
+ });
99
+ });
100
+ describe("traceFlow", () => {
101
+ it("finds path between tables", () => {
102
+ const schema = getSchema();
103
+ const flow = traceFlow(schema, "comments", "users");
104
+ expect(flow).toContain("# Flow:");
105
+ expect(flow).toContain("comments");
106
+ expect(flow).toContain("users");
107
+ });
108
+ it("returns message when no path exists", () => {
109
+ const ddl = `
110
+ CREATE TABLE a (id INT PRIMARY KEY);
111
+ CREATE TABLE b (id INT PRIMARY KEY);
112
+ `;
113
+ const schema = parseDDL(ddl);
114
+ const flow = traceFlow(schema, "a", "b");
115
+ expect(flow).toContain("No relationship path found");
116
+ });
117
+ });
118
+ //# sourceMappingURL=mermaid.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.test.js","sourceRoot":"","sources":["../src/mermaid.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BjB,CAAC;AAEF,SAAS,SAAS;IAChB,IAAI,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,GAAG,wBAAwB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACxD,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,GAAG,GAAG;;;CAGf,CAAC;QACE,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Schema } from "./types.js";
2
+ export declare function parseDDL(ddl: string): Schema;
3
+ export declare function extractInlineForeignKeys(ddl: string, schema: Schema): Schema;
4
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA6B,MAAM,YAAY,CAAC;AAE/D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyB5C;AAqHD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAwC5E"}
package/dist/parser.js ADDED
@@ -0,0 +1,148 @@
1
+ export function parseDDL(ddl) {
2
+ const tables = [];
3
+ const createTableRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?(\w+)[`"']?\s*\(([\s\S]*?)\)\s*(?:ENGINE|;|\n\n)/gi;
4
+ let match;
5
+ while ((match = createTableRegex.exec(ddl)) !== null) {
6
+ const tableName = match[1];
7
+ const body = match[2];
8
+ const table = parseTableBody(tableName, body);
9
+ tables.push(table);
10
+ }
11
+ if (tables.length === 0) {
12
+ const simpleRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?(\w+)[`"']?\s*\(([\s\S]*?)\)/gi;
13
+ while ((match = simpleRegex.exec(ddl)) !== null) {
14
+ const tableName = match[1];
15
+ const body = match[2];
16
+ const table = parseTableBody(tableName, body);
17
+ tables.push(table);
18
+ }
19
+ }
20
+ return { tables };
21
+ }
22
+ function parseTableBody(tableName, body) {
23
+ const columns = [];
24
+ const foreignKeys = [];
25
+ const primaryKeyColumns = [];
26
+ const lines = splitTableBody(body);
27
+ for (const line of lines) {
28
+ const trimmed = line.trim();
29
+ if (!trimmed)
30
+ continue;
31
+ const pkMatch = trimmed.match(/^\s*(?:CONSTRAINT\s+[`"']?\w+[`"']?\s+)?PRIMARY\s+KEY\s*\(([^)]+)\)/i);
32
+ if (pkMatch) {
33
+ const cols = pkMatch[1]
34
+ .split(",")
35
+ .map((c) => c.trim().replace(/[`"']/g, ""));
36
+ primaryKeyColumns.push(...cols);
37
+ continue;
38
+ }
39
+ const fkMatch = trimmed.match(/^\s*(?:CONSTRAINT\s+[`"']?\w+[`"']?\s+)?FOREIGN\s+KEY\s*\(([^)]+)\)\s*REFERENCES\s+[`"']?(\w+)[`"']?\s*\(([^)]+)\)/i);
40
+ if (fkMatch) {
41
+ foreignKeys.push({
42
+ columns: fkMatch[1]
43
+ .split(",")
44
+ .map((c) => c.trim().replace(/[`"']/g, "")),
45
+ referencedTable: fkMatch[2],
46
+ referencedColumns: fkMatch[3]
47
+ .split(",")
48
+ .map((c) => c.trim().replace(/[`"']/g, "")),
49
+ });
50
+ continue;
51
+ }
52
+ const uniqueConstraint = trimmed.match(/^\s*(?:CONSTRAINT\s+[`"']?\w+[`"']?\s+)?UNIQUE\s+(?:KEY|INDEX)?\s*(?:[`"']?\w+[`"']?\s*)?\(([^)]+)\)/i);
53
+ if (uniqueConstraint)
54
+ continue;
55
+ const indexMatch = trimmed.match(/^\s*(?:KEY|INDEX)\s/i);
56
+ if (indexMatch)
57
+ continue;
58
+ const column = parseColumn(trimmed);
59
+ if (column)
60
+ columns.push(column);
61
+ }
62
+ for (const col of columns) {
63
+ if (primaryKeyColumns.includes(col.name)) {
64
+ col.primaryKey = true;
65
+ }
66
+ }
67
+ return { name: tableName, columns, foreignKeys };
68
+ }
69
+ function splitTableBody(body) {
70
+ const lines = [];
71
+ let current = "";
72
+ let parenDepth = 0;
73
+ for (const char of body) {
74
+ if (char === "(")
75
+ parenDepth++;
76
+ if (char === ")")
77
+ parenDepth--;
78
+ if (char === "," && parenDepth === 0) {
79
+ lines.push(current);
80
+ current = "";
81
+ }
82
+ else {
83
+ current += char;
84
+ }
85
+ }
86
+ if (current.trim())
87
+ lines.push(current);
88
+ return lines;
89
+ }
90
+ function parseColumn(line) {
91
+ const match = line.match(/^\s*[`"']?(\w+)[`"']?\s+([\w(),.]+(?:\s*\(\d+(?:,\s*\d+)?\))?)/i);
92
+ if (!match)
93
+ return null;
94
+ const name = match[1];
95
+ const type = match[2].toUpperCase();
96
+ const upperLine = line.toUpperCase();
97
+ const nullable = !upperLine.includes("NOT NULL");
98
+ const primaryKey = upperLine.includes("PRIMARY KEY") ||
99
+ (upperLine.includes("SERIAL") && !upperLine.includes("BIGSERIAL"));
100
+ const unique = upperLine.includes("UNIQUE") || upperLine.includes("PRIMARY KEY");
101
+ let defaultValue;
102
+ const defaultMatch = line.match(/DEFAULT\s+([^,\s]+)/i);
103
+ if (defaultMatch)
104
+ defaultValue = defaultMatch[1].replace(/['"]/g, "");
105
+ const inlineFkMatch = line.match(/REFERENCES\s+[`"']?(\w+)[`"']?\s*\(([^)]+)\)/i);
106
+ return {
107
+ name,
108
+ type,
109
+ nullable,
110
+ primaryKey,
111
+ unique,
112
+ defaultValue,
113
+ ...(inlineFkMatch ? { _inlineRef: inlineFkMatch } : {}),
114
+ };
115
+ }
116
+ export function extractInlineForeignKeys(ddl, schema) {
117
+ const createTableRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?(\w+)[`"']?\s*\(([\s\S]*?)\)(?:\s*(?:ENGINE|;|\n\n))/gi;
118
+ let match;
119
+ while ((match = createTableRegex.exec(ddl)) !== null) {
120
+ const tableName = match[1];
121
+ const body = match[2];
122
+ const table = schema.tables.find((t) => t.name === tableName);
123
+ if (!table)
124
+ continue;
125
+ const lines = splitTableBody(body);
126
+ for (const line of lines) {
127
+ const colMatch = line.match(/^\s*[`"']?(\w+)[`"']?\s+/);
128
+ const fkMatch = line.match(/REFERENCES\s+[`"']?(\w+)[`"']?\s*\(([^)]+)\)/i);
129
+ if (colMatch && fkMatch) {
130
+ const colName = colMatch[1].replace(/[`"']/g, "");
131
+ const refTable = fkMatch[1];
132
+ const refCols = fkMatch[2]
133
+ .split(",")
134
+ .map((c) => c.trim().replace(/[`"']/g, ""));
135
+ const exists = table.foreignKeys.some((fk) => fk.columns.includes(colName) && fk.referencedTable === refTable);
136
+ if (!exists) {
137
+ table.foreignKeys.push({
138
+ columns: [colName],
139
+ referencedTable: refTable,
140
+ referencedColumns: refCols,
141
+ });
142
+ }
143
+ }
144
+ }
145
+ }
146
+ return schema;
147
+ }
148
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.d.ts","sourceRoot":"","sources":["../src/parser.test.ts"],"names":[],"mappings":""}