@xubylele/schema-forge-core 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.
Files changed (83) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +254 -0
  3. package/dist/core/diff.d.ts +22 -0
  4. package/dist/core/diff.d.ts.map +1 -0
  5. package/dist/core/diff.js +248 -0
  6. package/dist/core/diff.js.map +1 -0
  7. package/dist/core/errors.d.ts +4 -0
  8. package/dist/core/errors.d.ts.map +1 -0
  9. package/dist/core/errors.js +7 -0
  10. package/dist/core/errors.js.map +1 -0
  11. package/dist/core/fs.d.ts +31 -0
  12. package/dist/core/fs.d.ts.map +1 -0
  13. package/dist/core/fs.js +104 -0
  14. package/dist/core/fs.js.map +1 -0
  15. package/dist/core/normalize.d.ts +10 -0
  16. package/dist/core/normalize.d.ts.map +1 -0
  17. package/dist/core/normalize.js +152 -0
  18. package/dist/core/normalize.js.map +1 -0
  19. package/dist/core/parser.d.ts +25 -0
  20. package/dist/core/parser.d.ts.map +1 -0
  21. package/dist/core/parser.js +210 -0
  22. package/dist/core/parser.js.map +1 -0
  23. package/dist/core/paths.d.ts +29 -0
  24. package/dist/core/paths.d.ts.map +1 -0
  25. package/dist/core/paths.js +41 -0
  26. package/dist/core/paths.js.map +1 -0
  27. package/dist/core/sql/apply-ops.d.ts +3 -0
  28. package/dist/core/sql/apply-ops.d.ts.map +1 -0
  29. package/dist/core/sql/apply-ops.js +174 -0
  30. package/dist/core/sql/apply-ops.js.map +1 -0
  31. package/dist/core/sql/load-migrations.d.ts +6 -0
  32. package/dist/core/sql/load-migrations.d.ts.map +1 -0
  33. package/dist/core/sql/load-migrations.js +26 -0
  34. package/dist/core/sql/load-migrations.js.map +1 -0
  35. package/dist/core/sql/parse-migration.d.ts +11 -0
  36. package/dist/core/sql/parse-migration.d.ts.map +1 -0
  37. package/dist/core/sql/parse-migration.js +509 -0
  38. package/dist/core/sql/parse-migration.js.map +1 -0
  39. package/dist/core/sql/schema-to-dsl.d.ts +3 -0
  40. package/dist/core/sql/schema-to-dsl.d.ts.map +1 -0
  41. package/dist/core/sql/schema-to-dsl.js +33 -0
  42. package/dist/core/sql/schema-to-dsl.js.map +1 -0
  43. package/dist/core/sql/split-statements.d.ts +11 -0
  44. package/dist/core/sql/split-statements.d.ts.map +1 -0
  45. package/dist/core/sql/split-statements.js +111 -0
  46. package/dist/core/sql/split-statements.js.map +1 -0
  47. package/dist/core/state-manager.d.ts +17 -0
  48. package/dist/core/state-manager.d.ts.map +1 -0
  49. package/dist/core/state-manager.js +48 -0
  50. package/dist/core/state-manager.js.map +1 -0
  51. package/dist/core/utils.d.ts +30 -0
  52. package/dist/core/utils.d.ts.map +1 -0
  53. package/dist/core/utils.js +45 -0
  54. package/dist/core/utils.js.map +1 -0
  55. package/dist/core/validate.d.ts +20 -0
  56. package/dist/core/validate.d.ts.map +1 -0
  57. package/dist/core/validate.js +154 -0
  58. package/dist/core/validate.js.map +1 -0
  59. package/dist/core/validator.d.ts +16 -0
  60. package/dist/core/validator.d.ts.map +1 -0
  61. package/dist/core/validator.js +126 -0
  62. package/dist/core/validator.js.map +1 -0
  63. package/dist/diff/diff.d.ts +7 -0
  64. package/dist/diff/diff.d.ts.map +1 -0
  65. package/dist/diff/diff.js +75 -0
  66. package/dist/diff/diff.js.map +1 -0
  67. package/dist/generator/sql-generator.d.ts +8 -0
  68. package/dist/generator/sql-generator.d.ts.map +1 -0
  69. package/dist/generator/sql-generator.js +126 -0
  70. package/dist/generator/sql-generator.js.map +1 -0
  71. package/dist/index.d.ts +22 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +29 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/parser/parser.d.ts +3 -0
  76. package/dist/parser/parser.d.ts.map +1 -0
  77. package/dist/parser/parser.js +135 -0
  78. package/dist/parser/parser.js.map +1 -0
  79. package/dist/state/snapshot.d.ts +3 -0
  80. package/dist/state/snapshot.d.ts.map +1 -0
  81. package/dist/state/snapshot.js +22 -0
  82. package/dist/state/snapshot.js.map +1 -0
  83. package/package.json +33 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Split SQL text into statements by semicolon.
3
+ *
4
+ * Handles semicolons inside:
5
+ * - single-quoted strings
6
+ * - double-quoted identifiers
7
+ * - line/block comments
8
+ * - dollar-quoted blocks
9
+ */
10
+ export function splitSqlStatements(sql) {
11
+ const statements = [];
12
+ let current = '';
13
+ let inSingleQuote = false;
14
+ let inDoubleQuote = false;
15
+ let inLineComment = false;
16
+ let inBlockComment = false;
17
+ let dollarTag = null;
18
+ let index = 0;
19
+ while (index < sql.length) {
20
+ const char = sql[index];
21
+ const next = index + 1 < sql.length ? sql[index + 1] : '';
22
+ if (inLineComment) {
23
+ current += char;
24
+ if (char === '\n') {
25
+ inLineComment = false;
26
+ }
27
+ index++;
28
+ continue;
29
+ }
30
+ if (inBlockComment) {
31
+ current += char;
32
+ if (char === '*' && next === '/') {
33
+ current += next;
34
+ inBlockComment = false;
35
+ index += 2;
36
+ continue;
37
+ }
38
+ index++;
39
+ continue;
40
+ }
41
+ if (!inSingleQuote && !inDoubleQuote && dollarTag === null) {
42
+ if (char === '-' && next === '-') {
43
+ current += char + next;
44
+ inLineComment = true;
45
+ index += 2;
46
+ continue;
47
+ }
48
+ if (char === '/' && next === '*') {
49
+ current += char + next;
50
+ inBlockComment = true;
51
+ index += 2;
52
+ continue;
53
+ }
54
+ }
55
+ if (!inDoubleQuote && dollarTag === null && char === "'") {
56
+ current += char;
57
+ if (inSingleQuote && next === "'") {
58
+ current += next;
59
+ index += 2;
60
+ continue;
61
+ }
62
+ inSingleQuote = !inSingleQuote;
63
+ index++;
64
+ continue;
65
+ }
66
+ if (!inSingleQuote && dollarTag === null && char === '"') {
67
+ current += char;
68
+ if (inDoubleQuote && next === '"') {
69
+ current += next;
70
+ index += 2;
71
+ continue;
72
+ }
73
+ inDoubleQuote = !inDoubleQuote;
74
+ index++;
75
+ continue;
76
+ }
77
+ if (!inSingleQuote && !inDoubleQuote) {
78
+ if (dollarTag === null && char === '$') {
79
+ const remainder = sql.slice(index);
80
+ const match = remainder.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*\$|^\$\$/);
81
+ if (match) {
82
+ dollarTag = match[0];
83
+ current += match[0];
84
+ index += match[0].length;
85
+ continue;
86
+ }
87
+ }
88
+ if (dollarTag !== null && sql.startsWith(dollarTag, index)) {
89
+ current += dollarTag;
90
+ index += dollarTag.length;
91
+ dollarTag = null;
92
+ continue;
93
+ }
94
+ }
95
+ if (!inSingleQuote && !inDoubleQuote && dollarTag === null && char === ';') {
96
+ if (current.trim().length > 0) {
97
+ statements.push(current.trim());
98
+ }
99
+ current = '';
100
+ index++;
101
+ continue;
102
+ }
103
+ current += char;
104
+ index++;
105
+ }
106
+ if (current.trim().length > 0) {
107
+ statements.push(current.trim());
108
+ }
109
+ return statements;
110
+ }
111
+ //# sourceMappingURL=split-statements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-statements.js","sourceRoot":"","sources":["../../../src/core/sql/split-statements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { DatabaseSchema, StateFile } from '../types/schema';
2
+ /**
3
+ * Convert a DatabaseSchema to a StateFile
4
+ * Transforms table columns from array to indexed Record format
5
+ */
6
+ export declare function schemaToState(schema: DatabaseSchema): Promise<StateFile>;
7
+ /**
8
+ * Load state from disk
9
+ * Returns fallback { version: 1, tables: {} } if file doesn't exist
10
+ */
11
+ export declare function loadState(statePath: string): Promise<StateFile>;
12
+ /**
13
+ * Save state to disk
14
+ * Creates parent directories automatically if they don't exist
15
+ */
16
+ export declare function saveState(statePath: string, state: StateFile): Promise<void>;
17
+ //# sourceMappingURL=state-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-manager.d.ts","sourceRoot":"","sources":["../../src/core/state-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAEd,SAAS,EAEV,MAAM,iBAAiB,CAAC;AAGzB;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,CA6B9E;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAErE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlF"}
@@ -0,0 +1,48 @@
1
+ import path from 'path';
2
+ import { ensureDir, fileExists, readJsonFile, writeJsonFile } from './fs';
3
+ /**
4
+ * Convert a DatabaseSchema to a StateFile
5
+ * Transforms table columns from array to indexed Record format
6
+ */
7
+ export async function schemaToState(schema) {
8
+ const tables = {};
9
+ for (const [tableName, table] of Object.entries(schema.tables)) {
10
+ const columns = {};
11
+ const primaryKeyColumn = table.primaryKey ?? table.columns.find(column => column.primaryKey)?.name ?? null;
12
+ for (const column of table.columns) {
13
+ columns[column.name] = {
14
+ type: column.type,
15
+ ...(column.primaryKey !== undefined && { primaryKey: column.primaryKey }),
16
+ ...(column.unique !== undefined && { unique: column.unique }),
17
+ nullable: column.nullable ?? true,
18
+ ...(column.default !== undefined && { default: column.default }),
19
+ ...(column.foreignKey !== undefined && { foreignKey: column.foreignKey }),
20
+ };
21
+ }
22
+ tables[tableName] = {
23
+ columns,
24
+ ...(primaryKeyColumn !== null && { primaryKey: primaryKeyColumn }),
25
+ };
26
+ }
27
+ return {
28
+ version: 1,
29
+ tables,
30
+ };
31
+ }
32
+ /**
33
+ * Load state from disk
34
+ * Returns fallback { version: 1, tables: {} } if file doesn't exist
35
+ */
36
+ export async function loadState(statePath) {
37
+ return await readJsonFile(statePath, { version: 1, tables: {} });
38
+ }
39
+ /**
40
+ * Save state to disk
41
+ * Creates parent directories automatically if they don't exist
42
+ */
43
+ export async function saveState(statePath, state) {
44
+ const dirPath = path.dirname(statePath);
45
+ await ensureDir(dirPath);
46
+ await writeJsonFile(statePath, state);
47
+ }
48
+ //# sourceMappingURL=state-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/core/state-manager.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE1E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAsB;IACxD,MAAM,MAAM,GAA+B,EAAE,CAAC;IAE9C,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAgC,EAAE,CAAC;QAChD,MAAM,gBAAgB,GACpB,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;QAEpF,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG;gBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;gBACzE,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC7D,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;gBACjC,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;gBAChE,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;aAC1E,CAAC;QACJ,CAAC;QAED,MAAM,CAAC,SAAS,CAAC,GAAG;YAClB,OAAO;YACP,GAAG,CAAC,gBAAgB,KAAK,IAAI,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC;SACnE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC;QACV,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB;IAC/C,OAAO,MAAM,YAAY,CAAY,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,KAAgB;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Generates a timestamp string in the format YYYYMMDDHHmmss
3
+ * using the local time zone.
4
+ *
5
+ * @returns A 14-character timestamp string (e.g., "20260222143045")
6
+ * @example
7
+ * ```typescript
8
+ * const timestamp = nowTimestamp();
9
+ * // Returns: "20260222143045"
10
+ * ```
11
+ */
12
+ export declare function nowTimestamp(): string;
13
+ /**
14
+ * Converts a string to a safe kebab-case format suitable for file names.
15
+ * Trims whitespace, converts to lowercase, replaces non-alphanumeric characters
16
+ * with hyphens, and removes leading/trailing hyphens.
17
+ *
18
+ * @param name - The string to convert to kebab-case
19
+ * @returns A kebab-case string, or 'migration' if the result is empty
20
+ * @example
21
+ * ```typescript
22
+ * slugifyName("MyFileName"); // Returns: "my-file-name"
23
+ * slugifyName("My File Name"); // Returns: "my-file-name"
24
+ * slugifyName("My_File@#$Name"); // Returns: "my-file-name"
25
+ * slugifyName(""); // Returns: "migration"
26
+ * slugifyName(" "); // Returns: "migration"
27
+ * ```
28
+ */
29
+ export declare function slugifyName(name: string): string;
30
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/core/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAYrC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQhD"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Generates a timestamp string in the format YYYYMMDDHHmmss
3
+ * using the local time zone.
4
+ *
5
+ * @returns A 14-character timestamp string (e.g., "20260222143045")
6
+ * @example
7
+ * ```typescript
8
+ * const timestamp = nowTimestamp();
9
+ * // Returns: "20260222143045"
10
+ * ```
11
+ */
12
+ export function nowTimestamp() {
13
+ const date = new Date();
14
+ const pad = (value) => String(value).padStart(2, '0');
15
+ return (String(date.getFullYear()) +
16
+ pad(date.getMonth() + 1) +
17
+ pad(date.getDate()) +
18
+ pad(date.getHours()) +
19
+ pad(date.getMinutes()) +
20
+ pad(date.getSeconds()));
21
+ }
22
+ /**
23
+ * Converts a string to a safe kebab-case format suitable for file names.
24
+ * Trims whitespace, converts to lowercase, replaces non-alphanumeric characters
25
+ * with hyphens, and removes leading/trailing hyphens.
26
+ *
27
+ * @param name - The string to convert to kebab-case
28
+ * @returns A kebab-case string, or 'migration' if the result is empty
29
+ * @example
30
+ * ```typescript
31
+ * slugifyName("MyFileName"); // Returns: "my-file-name"
32
+ * slugifyName("My File Name"); // Returns: "my-file-name"
33
+ * slugifyName("My_File@#$Name"); // Returns: "my-file-name"
34
+ * slugifyName(""); // Returns: "migration"
35
+ * slugifyName(" "); // Returns: "migration"
36
+ * ```
37
+ */
38
+ export function slugifyName(name) {
39
+ return (name
40
+ .trim()
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9]+/g, '-')
43
+ .replace(/^-+|-+$/g, '')) || 'migration';
44
+ }
45
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/core/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IACxB,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEtE,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CACvB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,CACL,IAAI;SACD,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAC3B,IAAI,WAAW,CAAC;AACnB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { DatabaseSchema, StateFile } from '../types/schema';
2
+ export type Severity = 'error' | 'warning';
3
+ export interface Finding {
4
+ severity: Severity;
5
+ code: 'DROP_TABLE' | 'DROP_COLUMN' | 'ALTER_COLUMN_TYPE' | 'SET_NOT_NULL';
6
+ table: string;
7
+ column?: string;
8
+ from?: string;
9
+ to?: string;
10
+ message: string;
11
+ }
12
+ export interface ValidationReport {
13
+ hasErrors: boolean;
14
+ hasWarnings: boolean;
15
+ errors: Array<Omit<Finding, 'severity'>>;
16
+ warnings: Array<Omit<Finding, 'severity'>>;
17
+ }
18
+ export declare function validateSchemaChanges(previousState: StateFile, currentSchema: DatabaseSchema): Finding[];
19
+ export declare function toValidationReport(findings: Finding[]): ValidationReport;
20
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjE,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,mBAAmB,GAAG,cAAc,CAAC;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACzC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;CAC5C;AAgHD,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,GAAG,OAAO,EAAE,CAyDxG;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAUxE"}
@@ -0,0 +1,154 @@
1
+ import { diffSchemas } from './diff';
2
+ function normalizeColumnType(type) {
3
+ return type
4
+ .toLowerCase()
5
+ .trim()
6
+ .replace(/\s+/g, ' ')
7
+ .replace(/\s*\(\s*/g, '(')
8
+ .replace(/\s*,\s*/g, ',')
9
+ .replace(/\s*\)\s*/g, ')');
10
+ }
11
+ function parseVarcharLength(type) {
12
+ const match = normalizeColumnType(type).match(/^varchar\((\d+)\)$/);
13
+ return match ? Number(match[1]) : null;
14
+ }
15
+ function parseNumericType(type) {
16
+ const match = normalizeColumnType(type).match(/^numeric\((\d+),(\d+)\)$/);
17
+ if (!match) {
18
+ return null;
19
+ }
20
+ return {
21
+ precision: Number(match[1]),
22
+ scale: Number(match[2]),
23
+ };
24
+ }
25
+ function classifyTypeChange(from, to) {
26
+ const fromType = normalizeColumnType(from);
27
+ const toType = normalizeColumnType(to);
28
+ const uuidInvolved = fromType === 'uuid' || toType === 'uuid';
29
+ if (uuidInvolved && fromType !== toType) {
30
+ return {
31
+ severity: 'error',
32
+ message: `Type changed from ${fromType} to ${toType} (likely incompatible cast)`,
33
+ };
34
+ }
35
+ if (fromType === 'int' && toType === 'bigint') {
36
+ return {
37
+ severity: 'warning',
38
+ message: 'Type widened from int to bigint',
39
+ };
40
+ }
41
+ if (fromType === 'bigint' && toType === 'int') {
42
+ return {
43
+ severity: 'error',
44
+ message: 'Type narrowed from bigint to int (likely incompatible cast)',
45
+ };
46
+ }
47
+ if (fromType === 'text' && parseVarcharLength(toType) !== null) {
48
+ return {
49
+ severity: 'error',
50
+ message: `Type changed from text to ${toType} (may truncate existing values)`,
51
+ };
52
+ }
53
+ if (parseVarcharLength(fromType) !== null && toType === 'text') {
54
+ return {
55
+ severity: 'warning',
56
+ message: 'Type widened from varchar(n) to text',
57
+ };
58
+ }
59
+ const fromVarcharLength = parseVarcharLength(fromType);
60
+ const toVarcharLength = parseVarcharLength(toType);
61
+ if (fromVarcharLength !== null && toVarcharLength !== null) {
62
+ if (toVarcharLength >= fromVarcharLength) {
63
+ return {
64
+ severity: 'warning',
65
+ message: `Type widened from varchar(${fromVarcharLength}) to varchar(${toVarcharLength})`,
66
+ };
67
+ }
68
+ return {
69
+ severity: 'error',
70
+ message: `Type narrowed from varchar(${fromVarcharLength}) to varchar(${toVarcharLength})`,
71
+ };
72
+ }
73
+ const fromNumeric = parseNumericType(fromType);
74
+ const toNumeric = parseNumericType(toType);
75
+ if (fromNumeric && toNumeric && fromNumeric.scale === toNumeric.scale) {
76
+ if (toNumeric.precision >= fromNumeric.precision) {
77
+ return {
78
+ severity: 'warning',
79
+ message: `Type widened from numeric(${fromNumeric.precision},${fromNumeric.scale}) to numeric(${toNumeric.precision},${toNumeric.scale})`,
80
+ };
81
+ }
82
+ return {
83
+ severity: 'error',
84
+ message: `Type narrowed from numeric(${fromNumeric.precision},${fromNumeric.scale}) to numeric(${toNumeric.precision},${toNumeric.scale})`,
85
+ };
86
+ }
87
+ return {
88
+ severity: 'warning',
89
+ message: `Type changed from ${fromType} to ${toType} (compatibility unknown)`,
90
+ };
91
+ }
92
+ export function validateSchemaChanges(previousState, currentSchema) {
93
+ const findings = [];
94
+ const diff = diffSchemas(previousState, currentSchema);
95
+ for (const operation of diff.operations) {
96
+ switch (operation.kind) {
97
+ case 'drop_table':
98
+ findings.push({
99
+ severity: 'error',
100
+ code: 'DROP_TABLE',
101
+ table: operation.tableName,
102
+ message: 'Table removed',
103
+ });
104
+ break;
105
+ case 'drop_column':
106
+ findings.push({
107
+ severity: 'error',
108
+ code: 'DROP_COLUMN',
109
+ table: operation.tableName,
110
+ column: operation.columnName,
111
+ message: 'Column removed',
112
+ });
113
+ break;
114
+ case 'column_type_changed': {
115
+ const classification = classifyTypeChange(operation.fromType, operation.toType);
116
+ findings.push({
117
+ severity: classification.severity,
118
+ code: 'ALTER_COLUMN_TYPE',
119
+ table: operation.tableName,
120
+ column: operation.columnName,
121
+ from: normalizeColumnType(operation.fromType),
122
+ to: normalizeColumnType(operation.toType),
123
+ message: classification.message,
124
+ });
125
+ break;
126
+ }
127
+ case 'column_nullability_changed':
128
+ if (operation.from && !operation.to) {
129
+ findings.push({
130
+ severity: 'warning',
131
+ code: 'SET_NOT_NULL',
132
+ table: operation.tableName,
133
+ column: operation.columnName,
134
+ message: 'Column changed to NOT NULL (may fail if data contains NULLs)',
135
+ });
136
+ }
137
+ break;
138
+ default:
139
+ break;
140
+ }
141
+ }
142
+ return findings;
143
+ }
144
+ export function toValidationReport(findings) {
145
+ const errors = findings.filter(finding => finding.severity === 'error');
146
+ const warnings = findings.filter(finding => finding.severity === 'warning');
147
+ return {
148
+ hasErrors: errors.length > 0,
149
+ hasWarnings: warnings.length > 0,
150
+ errors: errors.map(({ severity, ...finding }) => finding),
151
+ warnings: warnings.map(({ severity, ...finding }) => finding),
152
+ };
153
+ }
154
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { DatabaseSchema } from '../types/schema';
2
+ /**
3
+ * Validates a DatabaseSchema structure and throws an Error when validations fail
4
+ *
5
+ * Validations:
6
+ * - Detects duplicate tables
7
+ * - Detects duplicate columns within each table
8
+ * - Detects multiple primary keys in a table
9
+ * - Validates that column types are valid
10
+ * - Validates that foreign keys reference existing tables and columns
11
+ *
12
+ * @param schema - The DatabaseSchema to validate
13
+ * @throws Error with a descriptive message if validation rules are violated
14
+ */
15
+ export declare function validateSchema(schema: DatabaseSchema): void;
16
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,iBAAiB,CAAC;AAgCjF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAQ3D"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Valid column types for the database
3
+ */
4
+ const VALID_BASE_COLUMN_TYPES = [
5
+ 'uuid',
6
+ 'varchar',
7
+ 'text',
8
+ 'int',
9
+ 'bigint',
10
+ 'boolean',
11
+ 'timestamptz',
12
+ 'date',
13
+ ];
14
+ function isValidColumnType(type) {
15
+ const normalizedType = type
16
+ .toLowerCase()
17
+ .trim()
18
+ .replace(/\s+/g, ' ')
19
+ .replace(/\s*\(\s*/g, '(')
20
+ .replace(/\s*,\s*/g, ',')
21
+ .replace(/\s*\)\s*/g, ')');
22
+ if (VALID_BASE_COLUMN_TYPES.includes(normalizedType)) {
23
+ return true;
24
+ }
25
+ return /^varchar\(\d+\)$/.test(normalizedType) || /^numeric\(\d+,\d+\)$/.test(normalizedType);
26
+ }
27
+ /**
28
+ * Validates a DatabaseSchema structure and throws an Error when validations fail
29
+ *
30
+ * Validations:
31
+ * - Detects duplicate tables
32
+ * - Detects duplicate columns within each table
33
+ * - Detects multiple primary keys in a table
34
+ * - Validates that column types are valid
35
+ * - Validates that foreign keys reference existing tables and columns
36
+ *
37
+ * @param schema - The DatabaseSchema to validate
38
+ * @throws Error with a descriptive message if validation rules are violated
39
+ */
40
+ export function validateSchema(schema) {
41
+ validateDuplicateTables(schema);
42
+ // Validate each table
43
+ for (const tableName in schema.tables) {
44
+ const table = schema.tables[tableName];
45
+ validateTableColumns(tableName, table, schema.tables);
46
+ }
47
+ }
48
+ /**
49
+ * Validates that there are no duplicate tables in the schema
50
+ *
51
+ * @param schema - The DatabaseSchema to validate
52
+ * @throws Error if duplicate tables are detected
53
+ */
54
+ function validateDuplicateTables(schema) {
55
+ const tableNames = Object.keys(schema.tables);
56
+ const seen = new Set();
57
+ for (const tableName of tableNames) {
58
+ if (seen.has(tableName)) {
59
+ throw new Error(`Duplicate table: '${tableName}'`);
60
+ }
61
+ seen.add(tableName);
62
+ }
63
+ }
64
+ /**
65
+ * Validates columns, primary keys, types, and foreign keys within a table
66
+ *
67
+ * @param tableName - Table name
68
+ * @param table - The table to validate
69
+ * @param allTables - All schema tables (used to validate foreign keys)
70
+ * @throws Error if validation violations are detected
71
+ */
72
+ function validateTableColumns(tableName, table, allTables) {
73
+ // Validate duplicate columns
74
+ const columnNames = new Set();
75
+ const primaryKeyColumns = [];
76
+ for (const column of table.columns) {
77
+ // Check for duplicate columns
78
+ if (columnNames.has(column.name)) {
79
+ throw new Error(`Table '${tableName}': duplicate column '${column.name}'`);
80
+ }
81
+ columnNames.add(column.name);
82
+ // Count primary keys
83
+ if (column.primaryKey) {
84
+ primaryKeyColumns.push(column.name);
85
+ }
86
+ // Validate column type
87
+ if (!isValidColumnType(column.type)) {
88
+ throw new Error(`Table '${tableName}', column '${column.name}': type '${column.type}' is not valid. Supported types: ${VALID_BASE_COLUMN_TYPES.join(', ')}, varchar(n), numeric(p,s)`);
89
+ }
90
+ // Validate foreign key
91
+ if (column.foreignKey) {
92
+ const fkTable = column.foreignKey.table;
93
+ const fkColumn = column.foreignKey.column;
94
+ // Check that the referenced table exists
95
+ if (!allTables[fkTable]) {
96
+ throw new Error(`Table '${tableName}', column '${column.name}': referenced table '${fkTable}' does not exist`);
97
+ }
98
+ // Check that the column exists in the referenced table
99
+ const referencedTable = allTables[fkTable];
100
+ const columnExists = referencedTable.columns.some(col => col.name === fkColumn);
101
+ if (!columnExists) {
102
+ throw new Error(`Table '${tableName}', column '${column.name}': table '${fkTable}' does not have column '${fkColumn}'`);
103
+ }
104
+ }
105
+ }
106
+ // Validate multiple primary keys
107
+ if (primaryKeyColumns.length > 1) {
108
+ throw new Error(`Table '${tableName}': can only have one primary key (found ${primaryKeyColumns.length})`);
109
+ }
110
+ const normalizedPrimaryKey = table.primaryKey ?? primaryKeyColumns[0] ?? null;
111
+ if (table.primaryKey && !columnNames.has(table.primaryKey)) {
112
+ throw new Error(`Table '${tableName}': primary key column '${table.primaryKey}' does not exist`);
113
+ }
114
+ if (table.primaryKey &&
115
+ primaryKeyColumns.length === 1 &&
116
+ primaryKeyColumns[0] !== table.primaryKey) {
117
+ throw new Error(`Table '${tableName}': column-level primary key '${primaryKeyColumns[0]}' does not match table primary key '${table.primaryKey}'`);
118
+ }
119
+ if (normalizedPrimaryKey) {
120
+ const pkMatches = table.columns.filter(column => column.name === normalizedPrimaryKey);
121
+ if (pkMatches.length !== 1) {
122
+ throw new Error(`Table '${tableName}': primary key column '${normalizedPrimaryKey}' is invalid`);
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Column, DatabaseSchema, DiffResult, StateColumn, StateFile } from '../types/schema';
2
+ export declare function getTableNamesFromState(state: StateFile): Set<string>;
3
+ export declare function getTableNamesFromSchema(schema: DatabaseSchema): Set<string>;
4
+ export declare function getColumnNamesFromState(stateColumns: Record<string, StateColumn>): Set<string>;
5
+ export declare function getColumnNamesFromSchema(dbColumns: Column[]): Set<string>;
6
+ export declare function diffSchemas(oldSnapshot: StateFile, newSchema: DatabaseSchema): DiffResult;
7
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,cAAc,EACd,UAAU,EAEV,WAAW,EACX,SAAS,EACV,MAAM,iBAAiB,CAAC;AAEzB,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAEpE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,CAE3E;AAED,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACxC,GAAG,CAAC,MAAM,CAAC,CAEb;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAEzE;AAMD,wBAAgB,WAAW,CAAC,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,GAAG,UAAU,CA0EzF"}