@xubylele/schema-forge 1.5.0 → 1.5.2
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 +2 -0
- package/dist/cli.js +2189 -51
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,13 +30,2144 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
25
32
|
|
|
33
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/parser.js
|
|
34
|
+
function parseSchema(source) {
|
|
35
|
+
const lines = source.split("\n");
|
|
36
|
+
const tables = {};
|
|
37
|
+
let currentLine = 0;
|
|
38
|
+
const validBaseColumnTypes = /* @__PURE__ */ new Set([
|
|
39
|
+
"uuid",
|
|
40
|
+
"varchar",
|
|
41
|
+
"text",
|
|
42
|
+
"int",
|
|
43
|
+
"bigint",
|
|
44
|
+
"boolean",
|
|
45
|
+
"timestamptz",
|
|
46
|
+
"date"
|
|
47
|
+
]);
|
|
48
|
+
const validIdentifierPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
49
|
+
function normalizeColumnType3(type) {
|
|
50
|
+
return type.toLowerCase().trim().replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*,\s*/g, ",").replace(/\s*\)\s*/g, ")");
|
|
51
|
+
}
|
|
52
|
+
function isValidColumnType2(type) {
|
|
53
|
+
const normalizedType = normalizeColumnType3(type);
|
|
54
|
+
if (validBaseColumnTypes.has(normalizedType)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
const varcharMatch = normalizedType.match(/^varchar\((\d+)\)$/);
|
|
58
|
+
if (varcharMatch) {
|
|
59
|
+
const length = Number(varcharMatch[1]);
|
|
60
|
+
return Number.isInteger(length) && length > 0;
|
|
61
|
+
}
|
|
62
|
+
const numericMatch = normalizedType.match(/^numeric\((\d+),(\d+)\)$/);
|
|
63
|
+
if (numericMatch) {
|
|
64
|
+
const precision = Number(numericMatch[1]);
|
|
65
|
+
const scale = Number(numericMatch[2]);
|
|
66
|
+
return Number.isInteger(precision) && Number.isInteger(scale) && precision > 0 && scale >= 0 && scale <= precision;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
function validateIdentifier(identifier, lineNum, context) {
|
|
71
|
+
if (!validIdentifierPattern.test(identifier)) {
|
|
72
|
+
throw new Error(`Line ${lineNum}: Invalid ${context} name '${identifier}'. Use letters, numbers, and underscores, and do not start with a number.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function validateDefaultValue(value, lineNum) {
|
|
76
|
+
let parenBalance = 0;
|
|
77
|
+
let inSingleQuote = false;
|
|
78
|
+
let inDoubleQuote = false;
|
|
79
|
+
for (let index = 0; index < value.length; index++) {
|
|
80
|
+
const char = value[index];
|
|
81
|
+
if (char === "'" && !inDoubleQuote) {
|
|
82
|
+
if (inSingleQuote && value[index + 1] === "'") {
|
|
83
|
+
index++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
inSingleQuote = !inSingleQuote;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (char === '"' && !inSingleQuote) {
|
|
90
|
+
if (inDoubleQuote && value[index + 1] === '"') {
|
|
91
|
+
index++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
inDoubleQuote = !inDoubleQuote;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (inSingleQuote || inDoubleQuote) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (char === "(") {
|
|
101
|
+
parenBalance++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (char === ")") {
|
|
105
|
+
parenBalance--;
|
|
106
|
+
if (parenBalance < 0) {
|
|
107
|
+
throw new Error(`Line ${lineNum}: Invalid default value '${value}'. Unmatched parentheses in function call.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (inSingleQuote || inDoubleQuote) {
|
|
112
|
+
throw new Error(`Line ${lineNum}: Invalid default value '${value}'. Unterminated quoted string.`);
|
|
113
|
+
}
|
|
114
|
+
if (parenBalance > 0) {
|
|
115
|
+
if (parenBalance === 1) {
|
|
116
|
+
throw new Error(`Line ${lineNum}: Invalid default value '${value}'. Function call is missing closing parenthesis.`);
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`Line ${lineNum}: Invalid default value '${value}'. Unmatched parentheses in function call.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function cleanLine(line) {
|
|
122
|
+
let inSingleQuote = false;
|
|
123
|
+
let inDoubleQuote = false;
|
|
124
|
+
for (let index = 0; index < line.length; index++) {
|
|
125
|
+
const char = line[index];
|
|
126
|
+
const nextChar = line[index + 1];
|
|
127
|
+
if (char === "'" && !inDoubleQuote) {
|
|
128
|
+
if (inSingleQuote && nextChar === "'") {
|
|
129
|
+
index++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
inSingleQuote = !inSingleQuote;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (char === '"' && !inSingleQuote) {
|
|
136
|
+
if (inDoubleQuote && nextChar === '"') {
|
|
137
|
+
index++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
inDoubleQuote = !inDoubleQuote;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (inSingleQuote || inDoubleQuote) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (char === "#") {
|
|
147
|
+
line = line.substring(0, index);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (char === "/" && nextChar === "/") {
|
|
151
|
+
line = line.substring(0, index);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return line.trim();
|
|
156
|
+
}
|
|
157
|
+
function parseForeignKey(fkRef, lineNum) {
|
|
158
|
+
const parts = fkRef.split(".");
|
|
159
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
160
|
+
throw new Error(`Line ${lineNum}: Invalid foreign key format '${fkRef}'. Expected format: table.column`);
|
|
161
|
+
}
|
|
162
|
+
validateIdentifier(parts[0], lineNum, "foreign key table");
|
|
163
|
+
validateIdentifier(parts[1], lineNum, "foreign key column");
|
|
164
|
+
return {
|
|
165
|
+
table: parts[0],
|
|
166
|
+
column: parts[1]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function parseColumn(line, lineNum) {
|
|
170
|
+
const tokens = line.split(/\s+/).filter((t) => t.length > 0);
|
|
171
|
+
const modifiers = /* @__PURE__ */ new Set(["pk", "unique", "nullable", "default", "fk"]);
|
|
172
|
+
const appliedModifiers = /* @__PURE__ */ new Set();
|
|
173
|
+
if (tokens.length < 2) {
|
|
174
|
+
throw new Error(`Line ${lineNum}: Invalid column definition. Expected: <name> <type> [modifiers...]`);
|
|
175
|
+
}
|
|
176
|
+
const colName = tokens[0];
|
|
177
|
+
const colType = normalizeColumnType3(tokens[1]);
|
|
178
|
+
validateIdentifier(colName, lineNum, "column");
|
|
179
|
+
if (!isValidColumnType2(colType)) {
|
|
180
|
+
throw new Error(`Line ${lineNum}: Invalid column type '${tokens[1]}'. Valid types: ${Array.from(validBaseColumnTypes).join(", ")}, varchar(n), numeric(p,s)`);
|
|
181
|
+
}
|
|
182
|
+
const column = {
|
|
183
|
+
name: colName,
|
|
184
|
+
type: colType,
|
|
185
|
+
nullable: true
|
|
186
|
+
};
|
|
187
|
+
let i = 2;
|
|
188
|
+
while (i < tokens.length) {
|
|
189
|
+
const modifier = tokens[i];
|
|
190
|
+
const markModifierApplied = (name) => {
|
|
191
|
+
if (appliedModifiers.has(name)) {
|
|
192
|
+
throw new Error(`Line ${lineNum}: Duplicate modifier '${name}'`);
|
|
193
|
+
}
|
|
194
|
+
appliedModifiers.add(name);
|
|
195
|
+
};
|
|
196
|
+
switch (modifier) {
|
|
197
|
+
case "pk":
|
|
198
|
+
markModifierApplied("pk");
|
|
199
|
+
column.primaryKey = true;
|
|
200
|
+
i++;
|
|
201
|
+
break;
|
|
202
|
+
case "unique":
|
|
203
|
+
markModifierApplied("unique");
|
|
204
|
+
column.unique = true;
|
|
205
|
+
i++;
|
|
206
|
+
break;
|
|
207
|
+
case "nullable":
|
|
208
|
+
if (appliedModifiers.has("not null")) {
|
|
209
|
+
throw new Error(`Line ${lineNum}: Cannot combine 'nullable' with 'not null'`);
|
|
210
|
+
}
|
|
211
|
+
markModifierApplied("nullable");
|
|
212
|
+
column.nullable = true;
|
|
213
|
+
i++;
|
|
214
|
+
break;
|
|
215
|
+
case "not":
|
|
216
|
+
if (tokens[i + 1] !== "null") {
|
|
217
|
+
throw new Error(`Line ${lineNum}: Unknown modifier 'not'`);
|
|
218
|
+
}
|
|
219
|
+
if (appliedModifiers.has("nullable")) {
|
|
220
|
+
throw new Error(`Line ${lineNum}: Cannot combine 'not null' with 'nullable'`);
|
|
221
|
+
}
|
|
222
|
+
markModifierApplied("not null");
|
|
223
|
+
column.nullable = false;
|
|
224
|
+
i += 2;
|
|
225
|
+
break;
|
|
226
|
+
case "default":
|
|
227
|
+
markModifierApplied("default");
|
|
228
|
+
i++;
|
|
229
|
+
if (i >= tokens.length) {
|
|
230
|
+
throw new Error(`Line ${lineNum}: 'default' modifier requires a value`);
|
|
231
|
+
}
|
|
232
|
+
{
|
|
233
|
+
const defaultTokens = [];
|
|
234
|
+
while (i < tokens.length && !modifiers.has(tokens[i]) && !(tokens[i] === "not" && tokens[i + 1] === "null")) {
|
|
235
|
+
defaultTokens.push(tokens[i]);
|
|
236
|
+
i++;
|
|
237
|
+
}
|
|
238
|
+
if (defaultTokens.length === 0) {
|
|
239
|
+
throw new Error(`Line ${lineNum}: 'default' modifier requires a value`);
|
|
240
|
+
}
|
|
241
|
+
const defaultValue = defaultTokens.join(" ");
|
|
242
|
+
validateDefaultValue(defaultValue, lineNum);
|
|
243
|
+
column.default = defaultValue;
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
case "fk":
|
|
247
|
+
markModifierApplied("fk");
|
|
248
|
+
i++;
|
|
249
|
+
if (i >= tokens.length) {
|
|
250
|
+
throw new Error(`Line ${lineNum}: 'fk' modifier requires a table.column reference`);
|
|
251
|
+
}
|
|
252
|
+
column.foreignKey = parseForeignKey(tokens[i], lineNum);
|
|
253
|
+
i++;
|
|
254
|
+
break;
|
|
255
|
+
default:
|
|
256
|
+
throw new Error(`Line ${lineNum}: Unknown modifier '${modifier}'`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return column;
|
|
260
|
+
}
|
|
261
|
+
function parseTableBlock(startLine) {
|
|
262
|
+
const firstLine = cleanLine(lines[startLine]);
|
|
263
|
+
const match = firstLine.match(/^table\s+(\w+)\s*\{\s*$/);
|
|
264
|
+
if (!match) {
|
|
265
|
+
throw new Error(`Line ${startLine + 1}: Invalid table definition. Expected: table <name> {`);
|
|
266
|
+
}
|
|
267
|
+
const tableName = match[1];
|
|
268
|
+
validateIdentifier(tableName, startLine + 1, "table");
|
|
269
|
+
if (tables[tableName]) {
|
|
270
|
+
throw new Error(`Line ${startLine + 1}: Duplicate table definition '${tableName}'`);
|
|
271
|
+
}
|
|
272
|
+
const columns = [];
|
|
273
|
+
let lineIdx = startLine + 1;
|
|
274
|
+
let foundClosingBrace = false;
|
|
275
|
+
while (lineIdx < lines.length) {
|
|
276
|
+
const cleaned = cleanLine(lines[lineIdx]);
|
|
277
|
+
if (!cleaned) {
|
|
278
|
+
lineIdx++;
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (cleaned === "}") {
|
|
282
|
+
foundClosingBrace = true;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const column = parseColumn(cleaned, lineIdx + 1);
|
|
287
|
+
columns.push(column);
|
|
288
|
+
} catch (error2) {
|
|
289
|
+
throw error2;
|
|
290
|
+
}
|
|
291
|
+
lineIdx++;
|
|
292
|
+
}
|
|
293
|
+
if (!foundClosingBrace) {
|
|
294
|
+
throw new Error(`Line ${startLine + 1}: Table '${tableName}' block not closed (missing '}')`);
|
|
295
|
+
}
|
|
296
|
+
const primaryKeyColumn = columns.find((column) => column.primaryKey)?.name ?? null;
|
|
297
|
+
tables[tableName] = {
|
|
298
|
+
name: tableName,
|
|
299
|
+
columns,
|
|
300
|
+
...primaryKeyColumn !== null && { primaryKey: primaryKeyColumn }
|
|
301
|
+
};
|
|
302
|
+
return lineIdx;
|
|
303
|
+
}
|
|
304
|
+
while (currentLine < lines.length) {
|
|
305
|
+
const cleaned = cleanLine(lines[currentLine]);
|
|
306
|
+
if (!cleaned) {
|
|
307
|
+
currentLine++;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (cleaned.startsWith("table ")) {
|
|
311
|
+
currentLine = parseTableBlock(currentLine);
|
|
312
|
+
} else {
|
|
313
|
+
throw new Error(`Line ${currentLine + 1}: Unexpected content '${cleaned}'. Expected table definition.`);
|
|
314
|
+
}
|
|
315
|
+
currentLine++;
|
|
316
|
+
}
|
|
317
|
+
return { tables };
|
|
318
|
+
}
|
|
319
|
+
var init_parser = __esm({
|
|
320
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/parser.js"() {
|
|
321
|
+
"use strict";
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/normalize.js
|
|
326
|
+
function normalizeIdent(input) {
|
|
327
|
+
return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
328
|
+
}
|
|
329
|
+
function pkName(table) {
|
|
330
|
+
return `pk_${normalizeIdent(table)}`;
|
|
331
|
+
}
|
|
332
|
+
function uqName(table, column) {
|
|
333
|
+
return `uq_${normalizeIdent(table)}_${normalizeIdent(column)}`;
|
|
334
|
+
}
|
|
335
|
+
function legacyPkName(table) {
|
|
336
|
+
return `${normalizeIdent(table)}_pkey`;
|
|
337
|
+
}
|
|
338
|
+
function legacyUqName(table, column) {
|
|
339
|
+
return `${normalizeIdent(table)}_${normalizeIdent(column)}_key`;
|
|
340
|
+
}
|
|
341
|
+
function normalizeSpacesOutsideQuotes(value) {
|
|
342
|
+
let result = "";
|
|
343
|
+
let inSingleQuote = false;
|
|
344
|
+
let inDoubleQuote = false;
|
|
345
|
+
let pendingSpace = false;
|
|
346
|
+
for (const char of value) {
|
|
347
|
+
if (char === "'" && !inDoubleQuote) {
|
|
348
|
+
if (pendingSpace && result.length > 0 && result[result.length - 1] !== " ") {
|
|
349
|
+
result += " ";
|
|
350
|
+
}
|
|
351
|
+
pendingSpace = false;
|
|
352
|
+
inSingleQuote = !inSingleQuote;
|
|
353
|
+
result += char;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (char === '"' && !inSingleQuote) {
|
|
357
|
+
if (pendingSpace && result.length > 0 && result[result.length - 1] !== " ") {
|
|
358
|
+
result += " ";
|
|
359
|
+
}
|
|
360
|
+
pendingSpace = false;
|
|
361
|
+
inDoubleQuote = !inDoubleQuote;
|
|
362
|
+
result += char;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (!inSingleQuote && !inDoubleQuote && /\s/.test(char)) {
|
|
366
|
+
pendingSpace = true;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (pendingSpace && result.length > 0 && result[result.length - 1] !== " ") {
|
|
370
|
+
result += " ";
|
|
371
|
+
}
|
|
372
|
+
pendingSpace = false;
|
|
373
|
+
result += char;
|
|
374
|
+
}
|
|
375
|
+
return result.trim();
|
|
376
|
+
}
|
|
377
|
+
function normalizeKnownFunctionsOutsideQuotes(value) {
|
|
378
|
+
let result = "";
|
|
379
|
+
let inSingleQuote = false;
|
|
380
|
+
let inDoubleQuote = false;
|
|
381
|
+
let buffer = "";
|
|
382
|
+
function flushBuffer() {
|
|
383
|
+
if (!buffer) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
result += buffer.replace(/\bnow\s*\(\s*\)/gi, "now()").replace(/\bgen_random_uuid\s*\(\s*\)/gi, "gen_random_uuid()");
|
|
387
|
+
buffer = "";
|
|
388
|
+
}
|
|
389
|
+
for (const char of value) {
|
|
390
|
+
if (char === "'" && !inDoubleQuote) {
|
|
391
|
+
flushBuffer();
|
|
392
|
+
inSingleQuote = !inSingleQuote;
|
|
393
|
+
result += char;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (char === '"' && !inSingleQuote) {
|
|
397
|
+
flushBuffer();
|
|
398
|
+
inDoubleQuote = !inDoubleQuote;
|
|
399
|
+
result += char;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (inSingleQuote || inDoubleQuote) {
|
|
403
|
+
result += char;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
buffer += char;
|
|
407
|
+
}
|
|
408
|
+
flushBuffer();
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
function normalizePunctuationOutsideQuotes(value) {
|
|
412
|
+
let result = "";
|
|
413
|
+
let inSingleQuote = false;
|
|
414
|
+
let inDoubleQuote = false;
|
|
415
|
+
for (let index = 0; index < value.length; index++) {
|
|
416
|
+
const char = value[index];
|
|
417
|
+
if (char === "'" && !inDoubleQuote) {
|
|
418
|
+
inSingleQuote = !inSingleQuote;
|
|
419
|
+
result += char;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (char === '"' && !inSingleQuote) {
|
|
423
|
+
inDoubleQuote = !inDoubleQuote;
|
|
424
|
+
result += char;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
if (!inSingleQuote && !inDoubleQuote && (char === "(" || char === ")")) {
|
|
428
|
+
while (result.endsWith(" ")) {
|
|
429
|
+
result = result.slice(0, -1);
|
|
430
|
+
}
|
|
431
|
+
result += char;
|
|
432
|
+
let lookahead = index + 1;
|
|
433
|
+
while (lookahead < value.length && value[lookahead] === " ") {
|
|
434
|
+
lookahead++;
|
|
435
|
+
}
|
|
436
|
+
index = lookahead - 1;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (!inSingleQuote && !inDoubleQuote && char === ",") {
|
|
440
|
+
while (result.endsWith(" ")) {
|
|
441
|
+
result = result.slice(0, -1);
|
|
442
|
+
}
|
|
443
|
+
result += ", ";
|
|
444
|
+
let lookahead = index + 1;
|
|
445
|
+
while (lookahead < value.length && value[lookahead] === " ") {
|
|
446
|
+
lookahead++;
|
|
447
|
+
}
|
|
448
|
+
index = lookahead - 1;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
result += char;
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
function normalizeDefault(expr) {
|
|
456
|
+
if (expr === void 0 || expr === null) {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
const trimmed = expr.trim();
|
|
460
|
+
if (trimmed.length === 0) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
const normalizedSpacing = normalizeSpacesOutsideQuotes(trimmed);
|
|
464
|
+
const normalizedPunctuation = normalizePunctuationOutsideQuotes(normalizedSpacing);
|
|
465
|
+
return normalizeKnownFunctionsOutsideQuotes(normalizedPunctuation);
|
|
466
|
+
}
|
|
467
|
+
var init_normalize = __esm({
|
|
468
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/normalize.js"() {
|
|
469
|
+
"use strict";
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/diff.js
|
|
474
|
+
function getTableNamesFromState(state) {
|
|
475
|
+
return new Set(Object.keys(state.tables));
|
|
476
|
+
}
|
|
477
|
+
function getTableNamesFromSchema(schema) {
|
|
478
|
+
return new Set(Object.keys(schema.tables));
|
|
479
|
+
}
|
|
480
|
+
function getColumnNamesFromState(stateColumns) {
|
|
481
|
+
return new Set(Object.keys(stateColumns));
|
|
482
|
+
}
|
|
483
|
+
function getColumnNamesFromSchema(dbColumns) {
|
|
484
|
+
return new Set(dbColumns.map((column) => column.name));
|
|
485
|
+
}
|
|
486
|
+
function getSortedNames(names) {
|
|
487
|
+
return Array.from(names).sort((a, b) => a.localeCompare(b));
|
|
488
|
+
}
|
|
489
|
+
function normalizeColumnType(type) {
|
|
490
|
+
return type.toLowerCase().trim().replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*,\s*/g, ",").replace(/\s*\)\s*/g, ")");
|
|
491
|
+
}
|
|
492
|
+
function resolveStatePrimaryKey(table) {
|
|
493
|
+
return table.primaryKey ?? Object.entries(table.columns).find(([, column]) => column.primaryKey)?.[0] ?? null;
|
|
494
|
+
}
|
|
495
|
+
function resolveSchemaPrimaryKey(table) {
|
|
496
|
+
return table.primaryKey ?? table.columns.find((column) => column.primaryKey)?.name ?? null;
|
|
497
|
+
}
|
|
498
|
+
function normalizeNullable(nullable) {
|
|
499
|
+
return nullable ?? true;
|
|
500
|
+
}
|
|
501
|
+
function diffSchemas(oldState, newSchema) {
|
|
502
|
+
const operations = [];
|
|
503
|
+
const oldTableNames = getTableNamesFromState(oldState);
|
|
504
|
+
const newTableNames = getTableNamesFromSchema(newSchema);
|
|
505
|
+
const sortedNewTableNames = getSortedNames(newTableNames);
|
|
506
|
+
const sortedOldTableNames = getSortedNames(oldTableNames);
|
|
507
|
+
for (const tableName of sortedNewTableNames) {
|
|
508
|
+
if (!oldTableNames.has(tableName)) {
|
|
509
|
+
operations.push({
|
|
510
|
+
kind: "create_table",
|
|
511
|
+
table: newSchema.tables[tableName]
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const commonTableNames = sortedNewTableNames.filter((tableName) => oldTableNames.has(tableName));
|
|
516
|
+
for (const tableName of commonTableNames) {
|
|
517
|
+
const newTable = newSchema.tables[tableName];
|
|
518
|
+
const oldTable = oldState.tables[tableName];
|
|
519
|
+
if (!newTable || !oldTable) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
for (const column of newTable.columns) {
|
|
523
|
+
const previousColumn = oldTable.columns[column.name];
|
|
524
|
+
if (!previousColumn) {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const previousType = normalizeColumnType(previousColumn.type);
|
|
528
|
+
const currentType = normalizeColumnType(column.type);
|
|
529
|
+
if (previousType !== currentType) {
|
|
530
|
+
operations.push({
|
|
531
|
+
kind: "column_type_changed",
|
|
532
|
+
tableName,
|
|
533
|
+
columnName: column.name,
|
|
534
|
+
fromType: previousColumn.type,
|
|
535
|
+
toType: column.type
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
for (const tableName of commonTableNames) {
|
|
541
|
+
const newTable = newSchema.tables[tableName];
|
|
542
|
+
const oldTable = oldState.tables[tableName];
|
|
543
|
+
if (!newTable || !oldTable) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const previousPrimaryKey = resolveStatePrimaryKey(oldTable);
|
|
547
|
+
const currentPrimaryKey = resolveSchemaPrimaryKey(newTable);
|
|
548
|
+
if (previousPrimaryKey !== null && previousPrimaryKey !== currentPrimaryKey) {
|
|
549
|
+
operations.push({
|
|
550
|
+
kind: "drop_primary_key_constraint",
|
|
551
|
+
tableName
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
for (const tableName of commonTableNames) {
|
|
556
|
+
const newTable = newSchema.tables[tableName];
|
|
557
|
+
const oldTable = oldState.tables[tableName];
|
|
558
|
+
if (!newTable || !oldTable) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
for (const column of newTable.columns) {
|
|
562
|
+
const previousColumn = oldTable.columns[column.name];
|
|
563
|
+
if (!previousColumn) {
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
const previousUnique = previousColumn.unique ?? false;
|
|
567
|
+
const currentUnique = column.unique ?? false;
|
|
568
|
+
if (previousUnique !== currentUnique) {
|
|
569
|
+
operations.push({
|
|
570
|
+
kind: "column_unique_changed",
|
|
571
|
+
tableName,
|
|
572
|
+
columnName: column.name,
|
|
573
|
+
from: previousUnique,
|
|
574
|
+
to: currentUnique
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
for (const tableName of commonTableNames) {
|
|
580
|
+
const newTable = newSchema.tables[tableName];
|
|
581
|
+
const oldTable = oldState.tables[tableName];
|
|
582
|
+
if (!newTable || !oldTable) {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
for (const column of newTable.columns) {
|
|
586
|
+
const previousColumn = oldTable.columns[column.name];
|
|
587
|
+
if (!previousColumn) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const previousNullable = normalizeNullable(previousColumn.nullable);
|
|
591
|
+
const currentNullable = normalizeNullable(column.nullable);
|
|
592
|
+
if (previousNullable !== currentNullable) {
|
|
593
|
+
operations.push({
|
|
594
|
+
kind: "column_nullability_changed",
|
|
595
|
+
tableName,
|
|
596
|
+
columnName: column.name,
|
|
597
|
+
from: previousNullable,
|
|
598
|
+
to: currentNullable
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
for (const tableName of commonTableNames) {
|
|
604
|
+
const newTable = newSchema.tables[tableName];
|
|
605
|
+
const oldTable = oldState.tables[tableName];
|
|
606
|
+
if (!newTable || !oldTable) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
for (const column of newTable.columns) {
|
|
610
|
+
const previousColumn = oldTable.columns[column.name];
|
|
611
|
+
if (!previousColumn) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const previousDefault = normalizeDefault(previousColumn.default);
|
|
615
|
+
const currentDefault = normalizeDefault(column.default);
|
|
616
|
+
if (previousDefault !== currentDefault) {
|
|
617
|
+
operations.push({
|
|
618
|
+
kind: "column_default_changed",
|
|
619
|
+
tableName,
|
|
620
|
+
columnName: column.name,
|
|
621
|
+
fromDefault: previousDefault,
|
|
622
|
+
toDefault: currentDefault
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
for (const tableName of commonTableNames) {
|
|
628
|
+
const newTable = newSchema.tables[tableName];
|
|
629
|
+
const oldTable = oldState.tables[tableName];
|
|
630
|
+
if (!newTable || !oldTable) {
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
const oldColumnNames = getColumnNamesFromState(oldTable.columns);
|
|
634
|
+
for (const column of newTable.columns) {
|
|
635
|
+
if (!oldColumnNames.has(column.name)) {
|
|
636
|
+
operations.push({
|
|
637
|
+
kind: "add_column",
|
|
638
|
+
tableName,
|
|
639
|
+
column
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
for (const tableName of commonTableNames) {
|
|
645
|
+
const newTable = newSchema.tables[tableName];
|
|
646
|
+
const oldTable = oldState.tables[tableName];
|
|
647
|
+
if (!newTable || !oldTable) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
const previousPrimaryKey = resolveStatePrimaryKey(oldTable);
|
|
651
|
+
const currentPrimaryKey = resolveSchemaPrimaryKey(newTable);
|
|
652
|
+
if (currentPrimaryKey !== null && previousPrimaryKey !== currentPrimaryKey) {
|
|
653
|
+
operations.push({
|
|
654
|
+
kind: "add_primary_key_constraint",
|
|
655
|
+
tableName,
|
|
656
|
+
columnName: currentPrimaryKey
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
for (const tableName of commonTableNames) {
|
|
661
|
+
const newTable = newSchema.tables[tableName];
|
|
662
|
+
const oldTable = oldState.tables[tableName];
|
|
663
|
+
if (!newTable || !oldTable) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
const newColumnNames = getColumnNamesFromSchema(newTable.columns);
|
|
667
|
+
for (const columnName of Object.keys(oldTable.columns)) {
|
|
668
|
+
if (!newColumnNames.has(columnName)) {
|
|
669
|
+
operations.push({
|
|
670
|
+
kind: "drop_column",
|
|
671
|
+
tableName,
|
|
672
|
+
columnName
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
for (const tableName of sortedOldTableNames) {
|
|
678
|
+
if (!newTableNames.has(tableName)) {
|
|
679
|
+
operations.push({
|
|
680
|
+
kind: "drop_table",
|
|
681
|
+
tableName
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return { operations };
|
|
686
|
+
}
|
|
687
|
+
var init_diff = __esm({
|
|
688
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/diff.js"() {
|
|
689
|
+
"use strict";
|
|
690
|
+
init_normalize();
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/validator.js
|
|
695
|
+
function isValidColumnType(type) {
|
|
696
|
+
const normalizedType = type.toLowerCase().trim().replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*,\s*/g, ",").replace(/\s*\)\s*/g, ")");
|
|
697
|
+
if (VALID_BASE_COLUMN_TYPES.includes(normalizedType)) {
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
return /^varchar\(\d+\)$/.test(normalizedType) || /^numeric\(\d+,\d+\)$/.test(normalizedType);
|
|
701
|
+
}
|
|
702
|
+
function validateSchema(schema) {
|
|
703
|
+
validateDuplicateTables(schema);
|
|
704
|
+
for (const tableName in schema.tables) {
|
|
705
|
+
const table = schema.tables[tableName];
|
|
706
|
+
validateTableColumns(tableName, table, schema.tables);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function validateDuplicateTables(schema) {
|
|
710
|
+
const tableNames = Object.keys(schema.tables);
|
|
711
|
+
const seen = /* @__PURE__ */ new Set();
|
|
712
|
+
for (const tableName of tableNames) {
|
|
713
|
+
if (seen.has(tableName)) {
|
|
714
|
+
throw new Error(`Duplicate table: '${tableName}'`);
|
|
715
|
+
}
|
|
716
|
+
seen.add(tableName);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function validateTableColumns(tableName, table, allTables) {
|
|
720
|
+
const columnNames = /* @__PURE__ */ new Set();
|
|
721
|
+
const primaryKeyColumns = [];
|
|
722
|
+
for (const column of table.columns) {
|
|
723
|
+
if (columnNames.has(column.name)) {
|
|
724
|
+
throw new Error(`Table '${tableName}': duplicate column '${column.name}'`);
|
|
725
|
+
}
|
|
726
|
+
columnNames.add(column.name);
|
|
727
|
+
if (column.primaryKey) {
|
|
728
|
+
primaryKeyColumns.push(column.name);
|
|
729
|
+
}
|
|
730
|
+
if (!isValidColumnType(column.type)) {
|
|
731
|
+
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)`);
|
|
732
|
+
}
|
|
733
|
+
if (column.foreignKey) {
|
|
734
|
+
const fkTable = column.foreignKey.table;
|
|
735
|
+
const fkColumn = column.foreignKey.column;
|
|
736
|
+
if (!allTables[fkTable]) {
|
|
737
|
+
throw new Error(`Table '${tableName}', column '${column.name}': referenced table '${fkTable}' does not exist`);
|
|
738
|
+
}
|
|
739
|
+
const referencedTable = allTables[fkTable];
|
|
740
|
+
const columnExists = referencedTable.columns.some((col) => col.name === fkColumn);
|
|
741
|
+
if (!columnExists) {
|
|
742
|
+
throw new Error(`Table '${tableName}', column '${column.name}': table '${fkTable}' does not have column '${fkColumn}'`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (primaryKeyColumns.length > 1) {
|
|
747
|
+
throw new Error(`Table '${tableName}': can only have one primary key (found ${primaryKeyColumns.length})`);
|
|
748
|
+
}
|
|
749
|
+
const normalizedPrimaryKey = table.primaryKey ?? primaryKeyColumns[0] ?? null;
|
|
750
|
+
if (table.primaryKey && !columnNames.has(table.primaryKey)) {
|
|
751
|
+
throw new Error(`Table '${tableName}': primary key column '${table.primaryKey}' does not exist`);
|
|
752
|
+
}
|
|
753
|
+
if (table.primaryKey && primaryKeyColumns.length === 1 && primaryKeyColumns[0] !== table.primaryKey) {
|
|
754
|
+
throw new Error(`Table '${tableName}': column-level primary key '${primaryKeyColumns[0]}' does not match table primary key '${table.primaryKey}'`);
|
|
755
|
+
}
|
|
756
|
+
if (normalizedPrimaryKey) {
|
|
757
|
+
const pkMatches = table.columns.filter((column) => column.name === normalizedPrimaryKey);
|
|
758
|
+
if (pkMatches.length !== 1) {
|
|
759
|
+
throw new Error(`Table '${tableName}': primary key column '${normalizedPrimaryKey}' is invalid`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
var VALID_BASE_COLUMN_TYPES;
|
|
764
|
+
var init_validator = __esm({
|
|
765
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/validator.js"() {
|
|
766
|
+
"use strict";
|
|
767
|
+
VALID_BASE_COLUMN_TYPES = [
|
|
768
|
+
"uuid",
|
|
769
|
+
"varchar",
|
|
770
|
+
"text",
|
|
771
|
+
"int",
|
|
772
|
+
"bigint",
|
|
773
|
+
"boolean",
|
|
774
|
+
"timestamptz",
|
|
775
|
+
"date"
|
|
776
|
+
];
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/validate.js
|
|
781
|
+
function normalizeColumnType2(type) {
|
|
782
|
+
return type.toLowerCase().trim().replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*,\s*/g, ",").replace(/\s*\)\s*/g, ")");
|
|
783
|
+
}
|
|
784
|
+
function parseVarcharLength(type) {
|
|
785
|
+
const match = normalizeColumnType2(type).match(/^varchar\((\d+)\)$/);
|
|
786
|
+
return match ? Number(match[1]) : null;
|
|
787
|
+
}
|
|
788
|
+
function parseNumericType(type) {
|
|
789
|
+
const match = normalizeColumnType2(type).match(/^numeric\((\d+),(\d+)\)$/);
|
|
790
|
+
if (!match) {
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
precision: Number(match[1]),
|
|
795
|
+
scale: Number(match[2])
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function classifyTypeChange(from, to) {
|
|
799
|
+
const fromType = normalizeColumnType2(from);
|
|
800
|
+
const toType = normalizeColumnType2(to);
|
|
801
|
+
const uuidInvolved = fromType === "uuid" || toType === "uuid";
|
|
802
|
+
if (uuidInvolved && fromType !== toType) {
|
|
803
|
+
return {
|
|
804
|
+
severity: "error",
|
|
805
|
+
message: `Type changed from ${fromType} to ${toType} (likely incompatible cast)`
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
if (fromType === "int" && toType === "bigint") {
|
|
809
|
+
return {
|
|
810
|
+
severity: "warning",
|
|
811
|
+
message: "Type widened from int to bigint"
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
if (fromType === "bigint" && toType === "int") {
|
|
815
|
+
return {
|
|
816
|
+
severity: "error",
|
|
817
|
+
message: "Type narrowed from bigint to int (likely incompatible cast)"
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
if (fromType === "text" && parseVarcharLength(toType) !== null) {
|
|
821
|
+
return {
|
|
822
|
+
severity: "error",
|
|
823
|
+
message: `Type changed from text to ${toType} (may truncate existing values)`
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
if (parseVarcharLength(fromType) !== null && toType === "text") {
|
|
827
|
+
return {
|
|
828
|
+
severity: "warning",
|
|
829
|
+
message: "Type widened from varchar(n) to text"
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
const fromVarcharLength = parseVarcharLength(fromType);
|
|
833
|
+
const toVarcharLength = parseVarcharLength(toType);
|
|
834
|
+
if (fromVarcharLength !== null && toVarcharLength !== null) {
|
|
835
|
+
if (toVarcharLength >= fromVarcharLength) {
|
|
836
|
+
return {
|
|
837
|
+
severity: "warning",
|
|
838
|
+
message: `Type widened from varchar(${fromVarcharLength}) to varchar(${toVarcharLength})`
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
severity: "error",
|
|
843
|
+
message: `Type narrowed from varchar(${fromVarcharLength}) to varchar(${toVarcharLength})`
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
const fromNumeric = parseNumericType(fromType);
|
|
847
|
+
const toNumeric = parseNumericType(toType);
|
|
848
|
+
if (fromNumeric && toNumeric && fromNumeric.scale === toNumeric.scale) {
|
|
849
|
+
if (toNumeric.precision >= fromNumeric.precision) {
|
|
850
|
+
return {
|
|
851
|
+
severity: "warning",
|
|
852
|
+
message: `Type widened from numeric(${fromNumeric.precision},${fromNumeric.scale}) to numeric(${toNumeric.precision},${toNumeric.scale})`
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
severity: "error",
|
|
857
|
+
message: `Type narrowed from numeric(${fromNumeric.precision},${fromNumeric.scale}) to numeric(${toNumeric.precision},${toNumeric.scale})`
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
severity: "warning",
|
|
862
|
+
message: `Type changed from ${fromType} to ${toType} (compatibility unknown)`
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
function validateSchemaChanges(previousState, currentSchema) {
|
|
866
|
+
const findings = [];
|
|
867
|
+
const diff = diffSchemas(previousState, currentSchema);
|
|
868
|
+
for (const operation of diff.operations) {
|
|
869
|
+
switch (operation.kind) {
|
|
870
|
+
case "drop_table":
|
|
871
|
+
findings.push({
|
|
872
|
+
severity: "error",
|
|
873
|
+
code: "DROP_TABLE",
|
|
874
|
+
table: operation.tableName,
|
|
875
|
+
message: "Table removed"
|
|
876
|
+
});
|
|
877
|
+
break;
|
|
878
|
+
case "drop_column":
|
|
879
|
+
findings.push({
|
|
880
|
+
severity: "error",
|
|
881
|
+
code: "DROP_COLUMN",
|
|
882
|
+
table: operation.tableName,
|
|
883
|
+
column: operation.columnName,
|
|
884
|
+
message: "Column removed"
|
|
885
|
+
});
|
|
886
|
+
break;
|
|
887
|
+
case "column_type_changed": {
|
|
888
|
+
const classification = classifyTypeChange(operation.fromType, operation.toType);
|
|
889
|
+
findings.push({
|
|
890
|
+
severity: classification.severity,
|
|
891
|
+
code: "ALTER_COLUMN_TYPE",
|
|
892
|
+
table: operation.tableName,
|
|
893
|
+
column: operation.columnName,
|
|
894
|
+
from: normalizeColumnType2(operation.fromType),
|
|
895
|
+
to: normalizeColumnType2(operation.toType),
|
|
896
|
+
message: classification.message
|
|
897
|
+
});
|
|
898
|
+
break;
|
|
899
|
+
}
|
|
900
|
+
case "column_nullability_changed":
|
|
901
|
+
if (operation.from && !operation.to) {
|
|
902
|
+
findings.push({
|
|
903
|
+
severity: "warning",
|
|
904
|
+
code: "SET_NOT_NULL",
|
|
905
|
+
table: operation.tableName,
|
|
906
|
+
column: operation.columnName,
|
|
907
|
+
message: "Column changed to NOT NULL (may fail if data contains NULLs)"
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
break;
|
|
911
|
+
default:
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return findings;
|
|
916
|
+
}
|
|
917
|
+
function toValidationReport(findings) {
|
|
918
|
+
const errors = findings.filter((finding) => finding.severity === "error");
|
|
919
|
+
const warnings = findings.filter((finding) => finding.severity === "warning");
|
|
920
|
+
return {
|
|
921
|
+
hasErrors: errors.length > 0,
|
|
922
|
+
hasWarnings: warnings.length > 0,
|
|
923
|
+
errors: errors.map(({ severity, ...finding }) => finding),
|
|
924
|
+
warnings: warnings.map(({ severity, ...finding }) => finding)
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
var init_validate = __esm({
|
|
928
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/validate.js"() {
|
|
929
|
+
"use strict";
|
|
930
|
+
init_diff();
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/fs.js
|
|
935
|
+
async function ensureDir2(dirPath) {
|
|
936
|
+
try {
|
|
937
|
+
await import_fs2.promises.mkdir(dirPath, { recursive: true });
|
|
938
|
+
} catch (error2) {
|
|
939
|
+
throw new Error(`Failed to create directory ${dirPath}: ${error2}`);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
async function fileExists2(filePath) {
|
|
943
|
+
try {
|
|
944
|
+
await import_fs2.promises.access(filePath);
|
|
945
|
+
return true;
|
|
946
|
+
} catch {
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
async function readTextFile2(filePath) {
|
|
951
|
+
try {
|
|
952
|
+
return await import_fs2.promises.readFile(filePath, "utf-8");
|
|
953
|
+
} catch (error2) {
|
|
954
|
+
throw new Error(`Failed to read file ${filePath}: ${error2}`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
async function writeTextFile2(filePath, content) {
|
|
958
|
+
try {
|
|
959
|
+
const dir = import_path3.default.dirname(filePath);
|
|
960
|
+
await ensureDir2(dir);
|
|
961
|
+
await import_fs2.promises.writeFile(filePath, content, "utf-8");
|
|
962
|
+
} catch (error2) {
|
|
963
|
+
throw new Error(`Failed to write file ${filePath}: ${error2}`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
async function readJsonFile2(filePath, fallback) {
|
|
967
|
+
try {
|
|
968
|
+
const exists = await fileExists2(filePath);
|
|
969
|
+
if (!exists) {
|
|
970
|
+
return fallback;
|
|
971
|
+
}
|
|
972
|
+
const content = await readTextFile2(filePath);
|
|
973
|
+
return JSON.parse(content);
|
|
974
|
+
} catch (error2) {
|
|
975
|
+
throw new Error(`Failed to read JSON file ${filePath}: ${error2}`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
async function writeJsonFile2(filePath, data) {
|
|
979
|
+
try {
|
|
980
|
+
const content = JSON.stringify(data, null, 2);
|
|
981
|
+
await writeTextFile2(filePath, content);
|
|
982
|
+
} catch (error2) {
|
|
983
|
+
throw new Error(`Failed to write JSON file ${filePath}: ${error2}`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
async function findFiles(dirPath, pattern) {
|
|
987
|
+
const results = [];
|
|
988
|
+
try {
|
|
989
|
+
const items = await import_fs2.promises.readdir(dirPath, { withFileTypes: true });
|
|
990
|
+
for (const item of items) {
|
|
991
|
+
const fullPath = import_path3.default.join(dirPath, item.name);
|
|
992
|
+
if (item.isDirectory()) {
|
|
993
|
+
const subResults = await findFiles(fullPath, pattern);
|
|
994
|
+
results.push(...subResults);
|
|
995
|
+
} else if (item.isFile() && pattern.test(item.name)) {
|
|
996
|
+
results.push(fullPath);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
} catch (error2) {
|
|
1000
|
+
throw new Error(`Failed to find files in ${dirPath}: ${error2}`);
|
|
1001
|
+
}
|
|
1002
|
+
return results;
|
|
1003
|
+
}
|
|
1004
|
+
var import_fs2, import_path3;
|
|
1005
|
+
var init_fs = __esm({
|
|
1006
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/fs.js"() {
|
|
1007
|
+
"use strict";
|
|
1008
|
+
import_fs2 = require("fs");
|
|
1009
|
+
import_path3 = __toESM(require("path"), 1);
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/state-manager.js
|
|
1014
|
+
async function schemaToState(schema) {
|
|
1015
|
+
const tables = {};
|
|
1016
|
+
for (const [tableName, table] of Object.entries(schema.tables)) {
|
|
1017
|
+
const columns = {};
|
|
1018
|
+
const primaryKeyColumn = table.primaryKey ?? table.columns.find((column) => column.primaryKey)?.name ?? null;
|
|
1019
|
+
for (const column of table.columns) {
|
|
1020
|
+
columns[column.name] = {
|
|
1021
|
+
type: column.type,
|
|
1022
|
+
...column.primaryKey !== void 0 && { primaryKey: column.primaryKey },
|
|
1023
|
+
...column.unique !== void 0 && { unique: column.unique },
|
|
1024
|
+
nullable: column.nullable ?? true,
|
|
1025
|
+
...column.default !== void 0 && { default: column.default },
|
|
1026
|
+
...column.foreignKey !== void 0 && { foreignKey: column.foreignKey }
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
tables[tableName] = {
|
|
1030
|
+
columns,
|
|
1031
|
+
...primaryKeyColumn !== null && { primaryKey: primaryKeyColumn }
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
version: 1,
|
|
1036
|
+
tables
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
async function loadState(statePath) {
|
|
1040
|
+
return await readJsonFile2(statePath, { version: 1, tables: {} });
|
|
1041
|
+
}
|
|
1042
|
+
async function saveState(statePath, state) {
|
|
1043
|
+
const dirPath = import_path4.default.dirname(statePath);
|
|
1044
|
+
await ensureDir2(dirPath);
|
|
1045
|
+
await writeJsonFile2(statePath, state);
|
|
1046
|
+
}
|
|
1047
|
+
var import_path4;
|
|
1048
|
+
var init_state_manager = __esm({
|
|
1049
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/state-manager.js"() {
|
|
1050
|
+
"use strict";
|
|
1051
|
+
import_path4 = __toESM(require("path"), 1);
|
|
1052
|
+
init_fs();
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
// node_modules/@xubylele/schema-forge-core/dist/generator/sql-generator.js
|
|
1057
|
+
function generateSql(diff, provider, sqlConfig) {
|
|
1058
|
+
const statements = [];
|
|
1059
|
+
for (const operation of diff.operations) {
|
|
1060
|
+
const sql = generateOperation(operation, provider, sqlConfig);
|
|
1061
|
+
if (sql) {
|
|
1062
|
+
statements.push(sql);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return statements.join("\n\n");
|
|
1066
|
+
}
|
|
1067
|
+
function generateOperation(operation, provider, sqlConfig) {
|
|
1068
|
+
switch (operation.kind) {
|
|
1069
|
+
case "create_table":
|
|
1070
|
+
return generateCreateTable(operation.table, provider, sqlConfig);
|
|
1071
|
+
case "drop_table":
|
|
1072
|
+
return generateDropTable(operation.tableName);
|
|
1073
|
+
case "column_type_changed":
|
|
1074
|
+
return generateAlterColumnType(operation.tableName, operation.columnName, operation.toType);
|
|
1075
|
+
case "column_nullability_changed":
|
|
1076
|
+
return generateAlterColumnNullability(operation.tableName, operation.columnName, operation.to);
|
|
1077
|
+
case "add_column":
|
|
1078
|
+
return generateAddColumn(operation.tableName, operation.column, provider, sqlConfig);
|
|
1079
|
+
case "column_default_changed":
|
|
1080
|
+
return generateAlterColumnDefault(operation.tableName, operation.columnName, operation.toDefault);
|
|
1081
|
+
case "drop_column":
|
|
1082
|
+
return generateDropColumn(operation.tableName, operation.columnName);
|
|
1083
|
+
case "column_unique_changed":
|
|
1084
|
+
return operation.to ? generateAddUniqueConstraint(operation.tableName, operation.columnName) : generateDropUniqueConstraint(operation.tableName, operation.columnName);
|
|
1085
|
+
case "drop_primary_key_constraint":
|
|
1086
|
+
return generateDropPrimaryKeyConstraint(operation.tableName);
|
|
1087
|
+
case "add_primary_key_constraint":
|
|
1088
|
+
return generateAddPrimaryKeyConstraint(operation.tableName, operation.columnName);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function generateCreateTable(table, provider, sqlConfig) {
|
|
1092
|
+
const columnDefs = table.columns.map((col) => generateColumnDefinition(col, provider, sqlConfig));
|
|
1093
|
+
const lines = ["CREATE TABLE " + table.name + " ("];
|
|
1094
|
+
columnDefs.forEach((colDef, index) => {
|
|
1095
|
+
const isLast = index === columnDefs.length - 1;
|
|
1096
|
+
lines.push(" " + colDef + (isLast ? "" : ","));
|
|
1097
|
+
});
|
|
1098
|
+
lines.push(");");
|
|
1099
|
+
return lines.join("\n");
|
|
1100
|
+
}
|
|
1101
|
+
function generateColumnDefinition(column, provider, sqlConfig) {
|
|
1102
|
+
const parts = [column.name, column.type];
|
|
1103
|
+
if (column.foreignKey) {
|
|
1104
|
+
parts.push(`references ${column.foreignKey.table}(${column.foreignKey.column})`);
|
|
1105
|
+
}
|
|
1106
|
+
if (column.primaryKey) {
|
|
1107
|
+
parts.push("primary key");
|
|
1108
|
+
}
|
|
1109
|
+
if (column.unique) {
|
|
1110
|
+
parts.push("unique");
|
|
1111
|
+
}
|
|
1112
|
+
if (column.nullable === false) {
|
|
1113
|
+
parts.push("not null");
|
|
1114
|
+
}
|
|
1115
|
+
if (column.default !== void 0) {
|
|
1116
|
+
parts.push("default " + column.default);
|
|
1117
|
+
} else if (column.type === "uuid" && column.primaryKey && provider === "supabase") {
|
|
1118
|
+
parts.push("default gen_random_uuid()");
|
|
1119
|
+
}
|
|
1120
|
+
return parts.join(" ");
|
|
1121
|
+
}
|
|
1122
|
+
function generateDropTable(tableName) {
|
|
1123
|
+
return `DROP TABLE ${tableName};`;
|
|
1124
|
+
}
|
|
1125
|
+
function generateAddColumn(tableName, column, provider, sqlConfig) {
|
|
1126
|
+
const colDef = generateColumnDefinition(column, provider, sqlConfig);
|
|
1127
|
+
return `ALTER TABLE ${tableName} ADD COLUMN ${colDef};`;
|
|
1128
|
+
}
|
|
1129
|
+
function generateDropColumn(tableName, columnName) {
|
|
1130
|
+
return `ALTER TABLE ${tableName} DROP COLUMN ${columnName};`;
|
|
1131
|
+
}
|
|
1132
|
+
function generateAlterColumnType(tableName, columnName, newType) {
|
|
1133
|
+
return `ALTER TABLE ${tableName} ALTER COLUMN ${columnName} TYPE ${newType} USING ${columnName}::${newType};`;
|
|
1134
|
+
}
|
|
1135
|
+
function generateAddUniqueConstraint(tableName, columnName) {
|
|
1136
|
+
const deterministicConstraintName = uqName(tableName, columnName);
|
|
1137
|
+
return `ALTER TABLE ${tableName} ADD CONSTRAINT ${deterministicConstraintName} UNIQUE (${columnName});`;
|
|
1138
|
+
}
|
|
1139
|
+
function generateDropUniqueConstraint(tableName, columnName) {
|
|
1140
|
+
const deterministicConstraintName = uqName(tableName, columnName);
|
|
1141
|
+
const fallbackConstraintName = legacyUqName(tableName, columnName);
|
|
1142
|
+
return generateDropConstraintStatements(tableName, [deterministicConstraintName, fallbackConstraintName]);
|
|
1143
|
+
}
|
|
1144
|
+
function generateDropPrimaryKeyConstraint(tableName) {
|
|
1145
|
+
const deterministicConstraintName = pkName(tableName);
|
|
1146
|
+
const fallbackConstraintName = legacyPkName(tableName);
|
|
1147
|
+
return generateDropConstraintStatements(tableName, [deterministicConstraintName, fallbackConstraintName]);
|
|
1148
|
+
}
|
|
1149
|
+
function generateAddPrimaryKeyConstraint(tableName, columnName) {
|
|
1150
|
+
const deterministicConstraintName = pkName(tableName);
|
|
1151
|
+
return `ALTER TABLE ${tableName} ADD CONSTRAINT ${deterministicConstraintName} PRIMARY KEY (${columnName});`;
|
|
1152
|
+
}
|
|
1153
|
+
function generateDropConstraintStatements(tableName, constraintNames) {
|
|
1154
|
+
const uniqueConstraintNames = Array.from(new Set(constraintNames));
|
|
1155
|
+
return uniqueConstraintNames.map((constraintName) => `ALTER TABLE ${tableName} DROP CONSTRAINT IF EXISTS ${constraintName};`).join("\n");
|
|
1156
|
+
}
|
|
1157
|
+
function generateAlterColumnDefault(tableName, columnName, newDefault) {
|
|
1158
|
+
if (newDefault === null) {
|
|
1159
|
+
return `ALTER TABLE ${tableName} ALTER COLUMN ${columnName} DROP DEFAULT;`;
|
|
1160
|
+
}
|
|
1161
|
+
return `ALTER TABLE ${tableName} ALTER COLUMN ${columnName} SET DEFAULT ${newDefault};`;
|
|
1162
|
+
}
|
|
1163
|
+
function generateAlterColumnNullability(tableName, columnName, toNullable) {
|
|
1164
|
+
if (toNullable) {
|
|
1165
|
+
return `ALTER TABLE ${tableName} ALTER COLUMN ${columnName} DROP NOT NULL;`;
|
|
1166
|
+
}
|
|
1167
|
+
return `ALTER TABLE ${tableName} ALTER COLUMN ${columnName} SET NOT NULL;`;
|
|
1168
|
+
}
|
|
1169
|
+
var init_sql_generator = __esm({
|
|
1170
|
+
"node_modules/@xubylele/schema-forge-core/dist/generator/sql-generator.js"() {
|
|
1171
|
+
"use strict";
|
|
1172
|
+
init_normalize();
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/sql/split-statements.js
|
|
1177
|
+
function splitSqlStatements(sql) {
|
|
1178
|
+
const statements = [];
|
|
1179
|
+
let current = "";
|
|
1180
|
+
let inSingleQuote = false;
|
|
1181
|
+
let inDoubleQuote = false;
|
|
1182
|
+
let inLineComment = false;
|
|
1183
|
+
let inBlockComment = false;
|
|
1184
|
+
let dollarTag = null;
|
|
1185
|
+
let index = 0;
|
|
1186
|
+
while (index < sql.length) {
|
|
1187
|
+
const char = sql[index];
|
|
1188
|
+
const next = index + 1 < sql.length ? sql[index + 1] : "";
|
|
1189
|
+
if (inLineComment) {
|
|
1190
|
+
current += char;
|
|
1191
|
+
if (char === "\n") {
|
|
1192
|
+
inLineComment = false;
|
|
1193
|
+
}
|
|
1194
|
+
index++;
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
if (inBlockComment) {
|
|
1198
|
+
current += char;
|
|
1199
|
+
if (char === "*" && next === "/") {
|
|
1200
|
+
current += next;
|
|
1201
|
+
inBlockComment = false;
|
|
1202
|
+
index += 2;
|
|
1203
|
+
continue;
|
|
1204
|
+
}
|
|
1205
|
+
index++;
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
if (!inSingleQuote && !inDoubleQuote && dollarTag === null) {
|
|
1209
|
+
if (char === "-" && next === "-") {
|
|
1210
|
+
current += char + next;
|
|
1211
|
+
inLineComment = true;
|
|
1212
|
+
index += 2;
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
if (char === "/" && next === "*") {
|
|
1216
|
+
current += char + next;
|
|
1217
|
+
inBlockComment = true;
|
|
1218
|
+
index += 2;
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (!inDoubleQuote && dollarTag === null && char === "'") {
|
|
1223
|
+
current += char;
|
|
1224
|
+
if (inSingleQuote && next === "'") {
|
|
1225
|
+
current += next;
|
|
1226
|
+
index += 2;
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
inSingleQuote = !inSingleQuote;
|
|
1230
|
+
index++;
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (!inSingleQuote && dollarTag === null && char === '"') {
|
|
1234
|
+
current += char;
|
|
1235
|
+
if (inDoubleQuote && next === '"') {
|
|
1236
|
+
current += next;
|
|
1237
|
+
index += 2;
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1241
|
+
index++;
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
1245
|
+
if (dollarTag === null && char === "$") {
|
|
1246
|
+
const remainder = sql.slice(index);
|
|
1247
|
+
const match = remainder.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*\$|^\$\$/);
|
|
1248
|
+
if (match) {
|
|
1249
|
+
dollarTag = match[0];
|
|
1250
|
+
current += match[0];
|
|
1251
|
+
index += match[0].length;
|
|
1252
|
+
continue;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
if (dollarTag !== null && sql.startsWith(dollarTag, index)) {
|
|
1256
|
+
current += dollarTag;
|
|
1257
|
+
index += dollarTag.length;
|
|
1258
|
+
dollarTag = null;
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
if (!inSingleQuote && !inDoubleQuote && dollarTag === null && char === ";") {
|
|
1263
|
+
if (current.trim().length > 0) {
|
|
1264
|
+
statements.push(current.trim());
|
|
1265
|
+
}
|
|
1266
|
+
current = "";
|
|
1267
|
+
index++;
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
current += char;
|
|
1271
|
+
index++;
|
|
1272
|
+
}
|
|
1273
|
+
if (current.trim().length > 0) {
|
|
1274
|
+
statements.push(current.trim());
|
|
1275
|
+
}
|
|
1276
|
+
return statements;
|
|
1277
|
+
}
|
|
1278
|
+
var init_split_statements = __esm({
|
|
1279
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/sql/split-statements.js"() {
|
|
1280
|
+
"use strict";
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/sql/parse-migration.js
|
|
1285
|
+
function normalizeSqlType(type) {
|
|
1286
|
+
return type.trim().toLowerCase().replace(/\s+/g, " ").replace(/\s*\(\s*/g, "(").replace(/\s*,\s*/g, ",").replace(/\s*\)\s*/g, ")");
|
|
1287
|
+
}
|
|
1288
|
+
function unquoteIdentifier(value) {
|
|
1289
|
+
const trimmed = value.trim();
|
|
1290
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
|
|
1291
|
+
return trimmed.slice(1, -1).replace(/""/g, '"');
|
|
1292
|
+
}
|
|
1293
|
+
return trimmed;
|
|
1294
|
+
}
|
|
1295
|
+
function normalizeIdentifier(identifier) {
|
|
1296
|
+
const parts = identifier.trim().split(".").map((part) => unquoteIdentifier(part)).filter((part) => part.length > 0);
|
|
1297
|
+
const leaf = parts.length > 0 ? parts[parts.length - 1] : identifier.trim();
|
|
1298
|
+
return leaf.toLowerCase();
|
|
1299
|
+
}
|
|
1300
|
+
function removeSqlComments(statement) {
|
|
1301
|
+
let result = "";
|
|
1302
|
+
let inSingleQuote = false;
|
|
1303
|
+
let inDoubleQuote = false;
|
|
1304
|
+
let inLineComment = false;
|
|
1305
|
+
let inBlockComment = false;
|
|
1306
|
+
for (let index = 0; index < statement.length; index++) {
|
|
1307
|
+
const char = statement[index];
|
|
1308
|
+
const next = index + 1 < statement.length ? statement[index + 1] : "";
|
|
1309
|
+
if (inLineComment) {
|
|
1310
|
+
if (char === "\n") {
|
|
1311
|
+
inLineComment = false;
|
|
1312
|
+
result += char;
|
|
1313
|
+
}
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
if (inBlockComment) {
|
|
1317
|
+
if (char === "*" && next === "/") {
|
|
1318
|
+
inBlockComment = false;
|
|
1319
|
+
index++;
|
|
1320
|
+
}
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
1324
|
+
if (char === "-" && next === "-") {
|
|
1325
|
+
inLineComment = true;
|
|
1326
|
+
index++;
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
if (char === "/" && next === "*") {
|
|
1330
|
+
inBlockComment = true;
|
|
1331
|
+
index++;
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
if (char === "'" && !inDoubleQuote) {
|
|
1336
|
+
if (inSingleQuote && next === "'") {
|
|
1337
|
+
result += "''";
|
|
1338
|
+
index++;
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
inSingleQuote = !inSingleQuote;
|
|
1342
|
+
result += char;
|
|
1343
|
+
continue;
|
|
1344
|
+
}
|
|
1345
|
+
if (char === '"' && !inSingleQuote) {
|
|
1346
|
+
if (inDoubleQuote && next === '"') {
|
|
1347
|
+
result += '""';
|
|
1348
|
+
index++;
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1352
|
+
result += char;
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
result += char;
|
|
1356
|
+
}
|
|
1357
|
+
return result.trim();
|
|
1358
|
+
}
|
|
1359
|
+
function splitTopLevelComma(input) {
|
|
1360
|
+
const parts = [];
|
|
1361
|
+
let current = "";
|
|
1362
|
+
let depth = 0;
|
|
1363
|
+
let inSingleQuote = false;
|
|
1364
|
+
let inDoubleQuote = false;
|
|
1365
|
+
for (let index = 0; index < input.length; index++) {
|
|
1366
|
+
const char = input[index];
|
|
1367
|
+
const next = index + 1 < input.length ? input[index + 1] : "";
|
|
1368
|
+
if (char === "'" && !inDoubleQuote) {
|
|
1369
|
+
current += char;
|
|
1370
|
+
if (inSingleQuote && next === "'") {
|
|
1371
|
+
current += next;
|
|
1372
|
+
index++;
|
|
1373
|
+
continue;
|
|
1374
|
+
}
|
|
1375
|
+
inSingleQuote = !inSingleQuote;
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (char === '"' && !inSingleQuote) {
|
|
1379
|
+
current += char;
|
|
1380
|
+
if (inDoubleQuote && next === '"') {
|
|
1381
|
+
current += next;
|
|
1382
|
+
index++;
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
1389
|
+
if (char === "(") {
|
|
1390
|
+
depth++;
|
|
1391
|
+
} else if (char === ")") {
|
|
1392
|
+
depth = Math.max(0, depth - 1);
|
|
1393
|
+
} else if (char === "," && depth === 0) {
|
|
1394
|
+
const segment = current.trim();
|
|
1395
|
+
if (segment.length > 0) {
|
|
1396
|
+
parts.push(segment);
|
|
1397
|
+
}
|
|
1398
|
+
current = "";
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
current += char;
|
|
1403
|
+
}
|
|
1404
|
+
const tail = current.trim();
|
|
1405
|
+
if (tail.length > 0) {
|
|
1406
|
+
parts.push(tail);
|
|
1407
|
+
}
|
|
1408
|
+
return parts;
|
|
1409
|
+
}
|
|
1410
|
+
function tokenize(segment) {
|
|
1411
|
+
const tokens = [];
|
|
1412
|
+
let current = "";
|
|
1413
|
+
let depth = 0;
|
|
1414
|
+
let inSingleQuote = false;
|
|
1415
|
+
let inDoubleQuote = false;
|
|
1416
|
+
for (let index = 0; index < segment.length; index++) {
|
|
1417
|
+
const char = segment[index];
|
|
1418
|
+
const next = index + 1 < segment.length ? segment[index + 1] : "";
|
|
1419
|
+
if (char === "'" && !inDoubleQuote) {
|
|
1420
|
+
current += char;
|
|
1421
|
+
if (inSingleQuote && next === "'") {
|
|
1422
|
+
current += next;
|
|
1423
|
+
index++;
|
|
1424
|
+
continue;
|
|
1425
|
+
}
|
|
1426
|
+
inSingleQuote = !inSingleQuote;
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
if (char === '"' && !inSingleQuote) {
|
|
1430
|
+
current += char;
|
|
1431
|
+
if (inDoubleQuote && next === '"') {
|
|
1432
|
+
current += next;
|
|
1433
|
+
index++;
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
1440
|
+
if (char === "(") {
|
|
1441
|
+
depth++;
|
|
1442
|
+
} else if (char === ")") {
|
|
1443
|
+
depth = Math.max(0, depth - 1);
|
|
1444
|
+
}
|
|
1445
|
+
if (/\s/.test(char) && depth === 0) {
|
|
1446
|
+
if (current.length > 0) {
|
|
1447
|
+
tokens.push(current);
|
|
1448
|
+
current = "";
|
|
1449
|
+
}
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
current += char;
|
|
1454
|
+
}
|
|
1455
|
+
if (current.length > 0) {
|
|
1456
|
+
tokens.push(current);
|
|
1457
|
+
}
|
|
1458
|
+
return tokens;
|
|
1459
|
+
}
|
|
1460
|
+
function parseColumnDefinition(segment) {
|
|
1461
|
+
const tokens = tokenize(segment);
|
|
1462
|
+
if (tokens.length < 2) {
|
|
1463
|
+
return null;
|
|
1464
|
+
}
|
|
1465
|
+
const name = normalizeIdentifier(tokens[0]);
|
|
1466
|
+
let cursor = 1;
|
|
1467
|
+
const typeTokens = [];
|
|
1468
|
+
while (cursor < tokens.length) {
|
|
1469
|
+
const lower = tokens[cursor].toLowerCase();
|
|
1470
|
+
if (COLUMN_CONSTRAINT_KEYWORDS.has(lower)) {
|
|
1471
|
+
break;
|
|
1472
|
+
}
|
|
1473
|
+
typeTokens.push(tokens[cursor]);
|
|
1474
|
+
cursor++;
|
|
1475
|
+
}
|
|
1476
|
+
if (typeTokens.length === 0) {
|
|
1477
|
+
return null;
|
|
1478
|
+
}
|
|
1479
|
+
const parsed = {
|
|
1480
|
+
name,
|
|
1481
|
+
type: normalizeSqlType(typeTokens.join(" ")),
|
|
1482
|
+
nullable: true
|
|
1483
|
+
};
|
|
1484
|
+
while (cursor < tokens.length) {
|
|
1485
|
+
const lower = tokens[cursor].toLowerCase();
|
|
1486
|
+
if (lower === "primary" && tokens[cursor + 1]?.toLowerCase() === "key") {
|
|
1487
|
+
parsed.primaryKey = true;
|
|
1488
|
+
parsed.nullable = false;
|
|
1489
|
+
cursor += 2;
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
if (lower === "unique") {
|
|
1493
|
+
parsed.unique = true;
|
|
1494
|
+
cursor++;
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1497
|
+
if (lower === "not" && tokens[cursor + 1]?.toLowerCase() === "null") {
|
|
1498
|
+
parsed.nullable = false;
|
|
1499
|
+
cursor += 2;
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
if (lower === "null") {
|
|
1503
|
+
parsed.nullable = true;
|
|
1504
|
+
cursor++;
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
if (lower === "default") {
|
|
1508
|
+
cursor++;
|
|
1509
|
+
const defaultTokens = [];
|
|
1510
|
+
while (cursor < tokens.length) {
|
|
1511
|
+
const probe = tokens[cursor].toLowerCase();
|
|
1512
|
+
if (probe === "constraint" || probe === "references" || probe === "check" || probe === "not" && tokens[cursor + 1]?.toLowerCase() === "null" || probe === "null" || probe === "unique" || probe === "primary" && tokens[cursor + 1]?.toLowerCase() === "key") {
|
|
1513
|
+
break;
|
|
1514
|
+
}
|
|
1515
|
+
defaultTokens.push(tokens[cursor]);
|
|
1516
|
+
cursor++;
|
|
1517
|
+
}
|
|
1518
|
+
parsed.default = normalizeDefault(defaultTokens.join(" "));
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
cursor++;
|
|
1522
|
+
}
|
|
1523
|
+
return parsed;
|
|
1524
|
+
}
|
|
1525
|
+
function parseCreateTableConstraint(segment) {
|
|
1526
|
+
const normalized = segment.trim().replace(/\s+/g, " ");
|
|
1527
|
+
const constraintMatch = normalized.match(/^constraint\s+([^\s]+)\s+(primary\s+key|unique)\s*\((.+)\)$/i);
|
|
1528
|
+
if (constraintMatch) {
|
|
1529
|
+
const [, rawName, kind, rawColumns] = constraintMatch;
|
|
1530
|
+
const columns = splitTopLevelComma(rawColumns).map((item) => normalizeIdentifier(item));
|
|
1531
|
+
if (kind.toLowerCase().includes("primary")) {
|
|
1532
|
+
return { type: "PRIMARY_KEY", name: normalizeIdentifier(rawName), columns };
|
|
1533
|
+
}
|
|
1534
|
+
return { type: "UNIQUE", name: normalizeIdentifier(rawName), columns };
|
|
1535
|
+
}
|
|
1536
|
+
const barePk = normalized.match(/^primary\s+key\s*\((.+)\)$/i);
|
|
1537
|
+
if (barePk) {
|
|
1538
|
+
const columns = splitTopLevelComma(barePk[1]).map((item) => normalizeIdentifier(item));
|
|
1539
|
+
return { type: "PRIMARY_KEY", columns };
|
|
1540
|
+
}
|
|
1541
|
+
const bareUnique = normalized.match(/^unique\s*\((.+)\)$/i);
|
|
1542
|
+
if (bareUnique) {
|
|
1543
|
+
const columns = splitTopLevelComma(bareUnique[1]).map((item) => normalizeIdentifier(item));
|
|
1544
|
+
return { type: "UNIQUE", columns };
|
|
1545
|
+
}
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
function parseAlterTablePrefix(stmt) {
|
|
1549
|
+
const match = stmt.match(/^alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?(.+)$/i);
|
|
1550
|
+
if (!match) {
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
const remainder = match[1].trim();
|
|
1554
|
+
const tokens = tokenize(remainder);
|
|
1555
|
+
if (tokens.length < 2) {
|
|
1556
|
+
return null;
|
|
1557
|
+
}
|
|
1558
|
+
const tableToken = tokens[0];
|
|
1559
|
+
const table = normalizeIdentifier(tableToken);
|
|
1560
|
+
const rest = remainder.slice(tableToken.length).trim();
|
|
1561
|
+
return { table, rest };
|
|
1562
|
+
}
|
|
1563
|
+
function parseCreateTable(stmt) {
|
|
1564
|
+
const match = stmt.match(/^create\s+table\s+(?:if\s+not\s+exists\s+)?(.+?)\s*\((.*)\)$/is);
|
|
1565
|
+
if (!match) {
|
|
1566
|
+
return null;
|
|
1567
|
+
}
|
|
1568
|
+
const table = normalizeIdentifier(match[1]);
|
|
1569
|
+
const body = match[2];
|
|
1570
|
+
const segments = splitTopLevelComma(body);
|
|
1571
|
+
const columns = [];
|
|
1572
|
+
const constraints = [];
|
|
1573
|
+
for (const segment of segments) {
|
|
1574
|
+
const constraint = parseCreateTableConstraint(segment);
|
|
1575
|
+
if (constraint) {
|
|
1576
|
+
constraints.push(constraint);
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
const column = parseColumnDefinition(segment);
|
|
1580
|
+
if (column) {
|
|
1581
|
+
columns.push(column);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return {
|
|
1585
|
+
kind: "CREATE_TABLE",
|
|
1586
|
+
table,
|
|
1587
|
+
columns,
|
|
1588
|
+
constraints
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function parseAlterTableAddColumn(stmt) {
|
|
1592
|
+
const prefix = parseAlterTablePrefix(stmt);
|
|
1593
|
+
if (!prefix) {
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1596
|
+
const match = prefix.rest.match(/^add\s+column\s+(?:if\s+not\s+exists\s+)?(.+)$/i);
|
|
1597
|
+
if (!match) {
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
const column = parseColumnDefinition(match[1]);
|
|
1601
|
+
if (!column) {
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
return { kind: "ADD_COLUMN", table: prefix.table, column };
|
|
1605
|
+
}
|
|
1606
|
+
function parseAlterColumnType(stmt) {
|
|
1607
|
+
const prefix = parseAlterTablePrefix(stmt);
|
|
1608
|
+
if (!prefix) {
|
|
1609
|
+
return null;
|
|
1610
|
+
}
|
|
1611
|
+
const match = prefix.rest.match(/^alter\s+column\s+([^\s]+)\s+type\s+(.+)$/i);
|
|
1612
|
+
if (!match) {
|
|
1613
|
+
return null;
|
|
1614
|
+
}
|
|
1615
|
+
const column = normalizeIdentifier(match[1]);
|
|
1616
|
+
const toType = normalizeSqlType(match[2].replace(/\s+using\s+[\s\S]*$/i, "").trim());
|
|
1617
|
+
return {
|
|
1618
|
+
kind: "ALTER_COLUMN_TYPE",
|
|
1619
|
+
table: prefix.table,
|
|
1620
|
+
column,
|
|
1621
|
+
toType
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
function parseSetDropNotNull(stmt) {
|
|
1625
|
+
const prefix = parseAlterTablePrefix(stmt);
|
|
1626
|
+
if (!prefix) {
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
const setMatch = prefix.rest.match(/^alter\s+column\s+([^\s]+)\s+set\s+not\s+null$/i);
|
|
1630
|
+
if (setMatch) {
|
|
1631
|
+
return {
|
|
1632
|
+
kind: "SET_NOT_NULL",
|
|
1633
|
+
table: prefix.table,
|
|
1634
|
+
column: normalizeIdentifier(setMatch[1])
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
const dropMatch = prefix.rest.match(/^alter\s+column\s+([^\s]+)\s+drop\s+not\s+null$/i);
|
|
1638
|
+
if (dropMatch) {
|
|
1639
|
+
return {
|
|
1640
|
+
kind: "DROP_NOT_NULL",
|
|
1641
|
+
table: prefix.table,
|
|
1642
|
+
column: normalizeIdentifier(dropMatch[1])
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
return null;
|
|
1646
|
+
}
|
|
1647
|
+
function parseSetDropDefault(stmt) {
|
|
1648
|
+
const prefix = parseAlterTablePrefix(stmt);
|
|
1649
|
+
if (!prefix) {
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
const setMatch = prefix.rest.match(/^alter\s+column\s+([^\s]+)\s+set\s+default\s+(.+)$/i);
|
|
1653
|
+
if (setMatch) {
|
|
1654
|
+
return {
|
|
1655
|
+
kind: "SET_DEFAULT",
|
|
1656
|
+
table: prefix.table,
|
|
1657
|
+
column: normalizeIdentifier(setMatch[1]),
|
|
1658
|
+
expr: normalizeDefault(setMatch[2].trim()) ?? setMatch[2].trim()
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
const dropMatch = prefix.rest.match(/^alter\s+column\s+([^\s]+)\s+drop\s+default$/i);
|
|
1662
|
+
if (dropMatch) {
|
|
1663
|
+
return {
|
|
1664
|
+
kind: "DROP_DEFAULT",
|
|
1665
|
+
table: prefix.table,
|
|
1666
|
+
column: normalizeIdentifier(dropMatch[1])
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
function parseAddDropConstraint(stmt) {
|
|
1672
|
+
const prefix = parseAlterTablePrefix(stmt);
|
|
1673
|
+
if (!prefix) {
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
const addMatch = prefix.rest.match(/^add\s+constraint\s+([^\s]+)\s+(primary\s+key|unique)\s*\((.+)\)$/i);
|
|
1677
|
+
if (addMatch) {
|
|
1678
|
+
const [, rawName, kind, rawColumns] = addMatch;
|
|
1679
|
+
const columns = splitTopLevelComma(rawColumns).map((item) => normalizeIdentifier(item));
|
|
1680
|
+
const constraint = kind.toLowerCase().includes("primary") ? { type: "PRIMARY_KEY", name: normalizeIdentifier(rawName), columns } : { type: "UNIQUE", name: normalizeIdentifier(rawName), columns };
|
|
1681
|
+
return {
|
|
1682
|
+
kind: "ADD_CONSTRAINT",
|
|
1683
|
+
table: prefix.table,
|
|
1684
|
+
constraint
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
const dropMatch = prefix.rest.match(/^drop\s+constraint\s+(?:if\s+exists\s+)?([^\s]+)(?:\s+cascade)?$/i);
|
|
1688
|
+
if (dropMatch) {
|
|
1689
|
+
return {
|
|
1690
|
+
kind: "DROP_CONSTRAINT",
|
|
1691
|
+
table: prefix.table,
|
|
1692
|
+
name: normalizeIdentifier(dropMatch[1])
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
return null;
|
|
1696
|
+
}
|
|
1697
|
+
function parseDropColumn(stmt) {
|
|
1698
|
+
const prefix = parseAlterTablePrefix(stmt);
|
|
1699
|
+
if (!prefix) {
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1702
|
+
const match = prefix.rest.match(/^drop\s+column\s+(?:if\s+exists\s+)?([^\s]+)(?:\s+cascade)?$/i);
|
|
1703
|
+
if (!match) {
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
return {
|
|
1707
|
+
kind: "DROP_COLUMN",
|
|
1708
|
+
table: prefix.table,
|
|
1709
|
+
column: normalizeIdentifier(match[1])
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
function parseDropTable(stmt) {
|
|
1713
|
+
const match = stmt.match(/^drop\s+table\s+(?:if\s+exists\s+)?([^\s]+)(?:\s+cascade)?$/i);
|
|
1714
|
+
if (!match) {
|
|
1715
|
+
return null;
|
|
1716
|
+
}
|
|
1717
|
+
return {
|
|
1718
|
+
kind: "DROP_TABLE",
|
|
1719
|
+
table: normalizeIdentifier(match[1])
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
function parseMigrationSql(sql) {
|
|
1723
|
+
const statements = splitSqlStatements(sql);
|
|
1724
|
+
const ops = [];
|
|
1725
|
+
const warnings = [];
|
|
1726
|
+
for (const raw of statements) {
|
|
1727
|
+
const stmt = removeSqlComments(raw).trim();
|
|
1728
|
+
if (!stmt) {
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
let parsed = null;
|
|
1732
|
+
for (const parseFn of PARSERS) {
|
|
1733
|
+
parsed = parseFn(stmt);
|
|
1734
|
+
if (parsed) {
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
if (parsed) {
|
|
1739
|
+
ops.push(parsed);
|
|
1740
|
+
} else {
|
|
1741
|
+
warnings.push({
|
|
1742
|
+
statement: stmt,
|
|
1743
|
+
reason: "Unsupported or unrecognized statement"
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
return { ops, warnings };
|
|
1748
|
+
}
|
|
1749
|
+
var COLUMN_CONSTRAINT_KEYWORDS, PARSERS;
|
|
1750
|
+
var init_parse_migration = __esm({
|
|
1751
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/sql/parse-migration.js"() {
|
|
1752
|
+
"use strict";
|
|
1753
|
+
init_normalize();
|
|
1754
|
+
init_split_statements();
|
|
1755
|
+
COLUMN_CONSTRAINT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1756
|
+
"primary",
|
|
1757
|
+
"unique",
|
|
1758
|
+
"not",
|
|
1759
|
+
"null",
|
|
1760
|
+
"default",
|
|
1761
|
+
"constraint",
|
|
1762
|
+
"references",
|
|
1763
|
+
"check"
|
|
1764
|
+
]);
|
|
1765
|
+
PARSERS = [
|
|
1766
|
+
parseCreateTable,
|
|
1767
|
+
parseAlterTableAddColumn,
|
|
1768
|
+
parseAlterColumnType,
|
|
1769
|
+
parseSetDropNotNull,
|
|
1770
|
+
parseSetDropDefault,
|
|
1771
|
+
parseAddDropConstraint,
|
|
1772
|
+
parseDropColumn,
|
|
1773
|
+
parseDropTable
|
|
1774
|
+
];
|
|
1775
|
+
}
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/sql/apply-ops.js
|
|
1779
|
+
function toSchemaColumn(column) {
|
|
1780
|
+
return {
|
|
1781
|
+
name: column.name,
|
|
1782
|
+
type: column.type,
|
|
1783
|
+
nullable: column.nullable,
|
|
1784
|
+
...column.default !== void 0 ? { default: column.default } : {},
|
|
1785
|
+
...column.unique !== void 0 ? { unique: column.unique } : {},
|
|
1786
|
+
...column.primaryKey !== void 0 ? { primaryKey: column.primaryKey } : {}
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
function applySingleColumnConstraint(table, constraint) {
|
|
1790
|
+
if (constraint.columns.length !== 1) {
|
|
1791
|
+
return false;
|
|
1792
|
+
}
|
|
1793
|
+
const targetColumn = table.columns.find((column) => column.name === constraint.columns[0]);
|
|
1794
|
+
if (!targetColumn) {
|
|
1795
|
+
return false;
|
|
1796
|
+
}
|
|
1797
|
+
if (constraint.type === "PRIMARY_KEY") {
|
|
1798
|
+
table.primaryKey = targetColumn.name;
|
|
1799
|
+
targetColumn.primaryKey = true;
|
|
1800
|
+
targetColumn.nullable = false;
|
|
1801
|
+
return true;
|
|
1802
|
+
}
|
|
1803
|
+
targetColumn.unique = true;
|
|
1804
|
+
return true;
|
|
1805
|
+
}
|
|
1806
|
+
function clearConstraintByName(table, name) {
|
|
1807
|
+
if (name.endsWith("_pkey") || name.startsWith("pk_")) {
|
|
1808
|
+
if (table.primaryKey) {
|
|
1809
|
+
const pkColumn = table.columns.find((column) => column.name === table.primaryKey);
|
|
1810
|
+
if (pkColumn) {
|
|
1811
|
+
pkColumn.primaryKey = false;
|
|
1812
|
+
}
|
|
1813
|
+
table.primaryKey = null;
|
|
1814
|
+
}
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
if (name.endsWith("_key") || name.startsWith("uq_")) {
|
|
1818
|
+
for (const column of table.columns) {
|
|
1819
|
+
if (column.unique) {
|
|
1820
|
+
column.unique = false;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
function getOrCreateTable(tables, name) {
|
|
1826
|
+
if (!tables[name]) {
|
|
1827
|
+
tables[name] = { name, columns: [] };
|
|
1828
|
+
}
|
|
1829
|
+
return tables[name];
|
|
1830
|
+
}
|
|
1831
|
+
function applySqlOps(ops) {
|
|
1832
|
+
const tables = {};
|
|
1833
|
+
const warnings = [];
|
|
1834
|
+
for (const op of ops) {
|
|
1835
|
+
switch (op.kind) {
|
|
1836
|
+
case "CREATE_TABLE": {
|
|
1837
|
+
const table = {
|
|
1838
|
+
name: op.table,
|
|
1839
|
+
columns: op.columns.map(toSchemaColumn)
|
|
1840
|
+
};
|
|
1841
|
+
for (const column of table.columns) {
|
|
1842
|
+
if (column.primaryKey) {
|
|
1843
|
+
table.primaryKey = column.name;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
for (const constraint of op.constraints) {
|
|
1847
|
+
const applied = applySingleColumnConstraint(table, constraint);
|
|
1848
|
+
if (!applied) {
|
|
1849
|
+
warnings.push({
|
|
1850
|
+
statement: `CREATE TABLE ${op.table}`,
|
|
1851
|
+
reason: `Constraint ${constraint.type}${constraint.name ? ` (${constraint.name})` : ""} is unsupported for schema reconstruction`
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
tables[op.table] = table;
|
|
1856
|
+
break;
|
|
1857
|
+
}
|
|
1858
|
+
case "ADD_COLUMN": {
|
|
1859
|
+
const table = getOrCreateTable(tables, op.table);
|
|
1860
|
+
table.columns = table.columns.filter((column) => column.name !== op.column.name);
|
|
1861
|
+
table.columns.push(toSchemaColumn(op.column));
|
|
1862
|
+
if (op.column.primaryKey) {
|
|
1863
|
+
table.primaryKey = op.column.name;
|
|
1864
|
+
}
|
|
1865
|
+
break;
|
|
1866
|
+
}
|
|
1867
|
+
case "ALTER_COLUMN_TYPE": {
|
|
1868
|
+
const table = tables[op.table];
|
|
1869
|
+
if (!table) {
|
|
1870
|
+
break;
|
|
1871
|
+
}
|
|
1872
|
+
const column = table.columns.find((item) => item.name === op.column);
|
|
1873
|
+
if (column) {
|
|
1874
|
+
column.type = op.toType;
|
|
1875
|
+
}
|
|
1876
|
+
break;
|
|
1877
|
+
}
|
|
1878
|
+
case "SET_NOT_NULL": {
|
|
1879
|
+
const table = tables[op.table];
|
|
1880
|
+
const column = table?.columns.find((item) => item.name === op.column);
|
|
1881
|
+
if (column) {
|
|
1882
|
+
column.nullable = false;
|
|
1883
|
+
}
|
|
1884
|
+
break;
|
|
1885
|
+
}
|
|
1886
|
+
case "DROP_NOT_NULL": {
|
|
1887
|
+
const table = tables[op.table];
|
|
1888
|
+
const column = table?.columns.find((item) => item.name === op.column);
|
|
1889
|
+
if (column) {
|
|
1890
|
+
column.nullable = true;
|
|
1891
|
+
}
|
|
1892
|
+
break;
|
|
1893
|
+
}
|
|
1894
|
+
case "SET_DEFAULT": {
|
|
1895
|
+
const table = tables[op.table];
|
|
1896
|
+
const column = table?.columns.find((item) => item.name === op.column);
|
|
1897
|
+
if (column) {
|
|
1898
|
+
column.default = op.expr;
|
|
1899
|
+
}
|
|
1900
|
+
break;
|
|
1901
|
+
}
|
|
1902
|
+
case "DROP_DEFAULT": {
|
|
1903
|
+
const table = tables[op.table];
|
|
1904
|
+
const column = table?.columns.find((item) => item.name === op.column);
|
|
1905
|
+
if (column) {
|
|
1906
|
+
column.default = null;
|
|
1907
|
+
}
|
|
1908
|
+
break;
|
|
1909
|
+
}
|
|
1910
|
+
case "ADD_CONSTRAINT": {
|
|
1911
|
+
const table = tables[op.table];
|
|
1912
|
+
if (!table) {
|
|
1913
|
+
break;
|
|
1914
|
+
}
|
|
1915
|
+
const applied = applySingleColumnConstraint(table, op.constraint);
|
|
1916
|
+
if (!applied) {
|
|
1917
|
+
warnings.push({
|
|
1918
|
+
statement: `ALTER TABLE ${op.table} ADD CONSTRAINT ${op.constraint.name ?? "<unnamed>"}`,
|
|
1919
|
+
reason: `Constraint ${op.constraint.type} is unsupported for schema reconstruction`
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
break;
|
|
1923
|
+
}
|
|
1924
|
+
case "DROP_CONSTRAINT": {
|
|
1925
|
+
const table = tables[op.table];
|
|
1926
|
+
if (!table) {
|
|
1927
|
+
break;
|
|
1928
|
+
}
|
|
1929
|
+
clearConstraintByName(table, op.name);
|
|
1930
|
+
break;
|
|
1931
|
+
}
|
|
1932
|
+
case "DROP_COLUMN": {
|
|
1933
|
+
const table = tables[op.table];
|
|
1934
|
+
if (!table) {
|
|
1935
|
+
break;
|
|
1936
|
+
}
|
|
1937
|
+
table.columns = table.columns.filter((column) => column.name !== op.column);
|
|
1938
|
+
if (table.primaryKey === op.column) {
|
|
1939
|
+
table.primaryKey = null;
|
|
1940
|
+
}
|
|
1941
|
+
break;
|
|
1942
|
+
}
|
|
1943
|
+
case "DROP_TABLE": {
|
|
1944
|
+
delete tables[op.table];
|
|
1945
|
+
break;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
const schema = { tables };
|
|
1950
|
+
return { schema, warnings };
|
|
1951
|
+
}
|
|
1952
|
+
var init_apply_ops = __esm({
|
|
1953
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/sql/apply-ops.js"() {
|
|
1954
|
+
"use strict";
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
|
|
1958
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/sql/schema-to-dsl.js
|
|
1959
|
+
function renderColumn(column) {
|
|
1960
|
+
const parts = [column.name, column.type];
|
|
1961
|
+
if (column.primaryKey) {
|
|
1962
|
+
parts.push("pk");
|
|
1963
|
+
}
|
|
1964
|
+
if (column.unique) {
|
|
1965
|
+
parts.push("unique");
|
|
1966
|
+
}
|
|
1967
|
+
if (column.nullable === false && !column.primaryKey) {
|
|
1968
|
+
parts.push("not null");
|
|
1969
|
+
}
|
|
1970
|
+
if (column.default !== void 0 && column.default !== null) {
|
|
1971
|
+
parts.push(`default ${column.default}`);
|
|
1972
|
+
}
|
|
1973
|
+
return ` ${parts.join(" ")}`;
|
|
1974
|
+
}
|
|
1975
|
+
function schemaToDsl(schema) {
|
|
1976
|
+
const tableNames = Object.keys(schema.tables).sort((left, right) => left.localeCompare(right));
|
|
1977
|
+
const blocks = tableNames.map((tableName) => {
|
|
1978
|
+
const table = schema.tables[tableName];
|
|
1979
|
+
const lines = [`table ${table.name} {`];
|
|
1980
|
+
for (const column of table.columns) {
|
|
1981
|
+
lines.push(renderColumn(column));
|
|
1982
|
+
}
|
|
1983
|
+
lines.push("}");
|
|
1984
|
+
return lines.join("\n");
|
|
1985
|
+
});
|
|
1986
|
+
if (blocks.length === 0) {
|
|
1987
|
+
return "# SchemaForge schema definition\n";
|
|
1988
|
+
}
|
|
1989
|
+
return `# SchemaForge schema definition
|
|
1990
|
+
|
|
1991
|
+
${blocks.join("\n\n")}
|
|
1992
|
+
`;
|
|
1993
|
+
}
|
|
1994
|
+
var init_schema_to_dsl = __esm({
|
|
1995
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/sql/schema-to-dsl.js"() {
|
|
1996
|
+
"use strict";
|
|
1997
|
+
}
|
|
1998
|
+
});
|
|
1999
|
+
|
|
2000
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/sql/load-migrations.js
|
|
2001
|
+
async function loadMigrationSqlInput(inputPath) {
|
|
2002
|
+
const stats = await import_fs4.promises.stat(inputPath);
|
|
2003
|
+
if (stats.isFile()) {
|
|
2004
|
+
if (!inputPath.toLowerCase().endsWith(".sql")) {
|
|
2005
|
+
throw new Error(`Input file must be a .sql file: ${inputPath}`);
|
|
2006
|
+
}
|
|
2007
|
+
return [{ filePath: inputPath, sql: await readTextFile2(inputPath) }];
|
|
2008
|
+
}
|
|
2009
|
+
if (!stats.isDirectory()) {
|
|
2010
|
+
throw new Error(`Input path must be a .sql file or directory: ${inputPath}`);
|
|
2011
|
+
}
|
|
2012
|
+
const sqlFiles = await findFiles(inputPath, /\.sql$/i);
|
|
2013
|
+
sqlFiles.sort((left, right) => import_path5.default.basename(left).localeCompare(import_path5.default.basename(right)));
|
|
2014
|
+
const result = [];
|
|
2015
|
+
for (const filePath of sqlFiles) {
|
|
2016
|
+
result.push({
|
|
2017
|
+
filePath,
|
|
2018
|
+
sql: await readTextFile2(filePath)
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
return result;
|
|
2022
|
+
}
|
|
2023
|
+
var import_fs4, import_path5;
|
|
2024
|
+
var init_load_migrations = __esm({
|
|
2025
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/sql/load-migrations.js"() {
|
|
2026
|
+
"use strict";
|
|
2027
|
+
import_fs4 = require("fs");
|
|
2028
|
+
import_path5 = __toESM(require("path"), 1);
|
|
2029
|
+
init_fs();
|
|
2030
|
+
}
|
|
2031
|
+
});
|
|
2032
|
+
|
|
2033
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/paths.js
|
|
2034
|
+
function getProjectRoot2(cwd = process.cwd()) {
|
|
2035
|
+
return cwd;
|
|
2036
|
+
}
|
|
2037
|
+
function getSchemaForgeDir2(root) {
|
|
2038
|
+
return import_path6.default.join(root, "schemaforge");
|
|
2039
|
+
}
|
|
2040
|
+
function getSchemaFilePath2(root, config) {
|
|
2041
|
+
const schemaForgeDir = getSchemaForgeDir2(root);
|
|
2042
|
+
const fileName = config?.schemaFile || "schema.sf";
|
|
2043
|
+
return import_path6.default.join(schemaForgeDir, fileName);
|
|
2044
|
+
}
|
|
2045
|
+
function getConfigPath2(root) {
|
|
2046
|
+
const schemaForgeDir = getSchemaForgeDir2(root);
|
|
2047
|
+
return import_path6.default.join(schemaForgeDir, "config.json");
|
|
2048
|
+
}
|
|
2049
|
+
function getStatePath2(root, config) {
|
|
2050
|
+
const schemaForgeDir = getSchemaForgeDir2(root);
|
|
2051
|
+
const fileName = config?.stateFile || "state.json";
|
|
2052
|
+
return import_path6.default.join(schemaForgeDir, fileName);
|
|
2053
|
+
}
|
|
2054
|
+
var import_path6;
|
|
2055
|
+
var init_paths = __esm({
|
|
2056
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/paths.js"() {
|
|
2057
|
+
"use strict";
|
|
2058
|
+
import_path6 = __toESM(require("path"), 1);
|
|
2059
|
+
}
|
|
2060
|
+
});
|
|
2061
|
+
|
|
2062
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/utils.js
|
|
2063
|
+
function nowTimestamp() {
|
|
2064
|
+
const date = /* @__PURE__ */ new Date();
|
|
2065
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
2066
|
+
return String(date.getFullYear()) + pad(date.getMonth() + 1) + pad(date.getDate()) + pad(date.getHours()) + pad(date.getMinutes()) + pad(date.getSeconds());
|
|
2067
|
+
}
|
|
2068
|
+
function slugifyName(name) {
|
|
2069
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "migration";
|
|
2070
|
+
}
|
|
2071
|
+
var init_utils = __esm({
|
|
2072
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/utils.js"() {
|
|
2073
|
+
"use strict";
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
|
|
2077
|
+
// node_modules/@xubylele/schema-forge-core/dist/core/errors.js
|
|
2078
|
+
var SchemaValidationError;
|
|
2079
|
+
var init_errors = __esm({
|
|
2080
|
+
"node_modules/@xubylele/schema-forge-core/dist/core/errors.js"() {
|
|
2081
|
+
"use strict";
|
|
2082
|
+
SchemaValidationError = class extends Error {
|
|
2083
|
+
constructor(message) {
|
|
2084
|
+
super(message);
|
|
2085
|
+
this.name = "SchemaValidationError";
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
|
|
2091
|
+
// node_modules/@xubylele/schema-forge-core/dist/index.js
|
|
2092
|
+
var dist_exports = {};
|
|
2093
|
+
__export(dist_exports, {
|
|
2094
|
+
SchemaValidationError: () => SchemaValidationError,
|
|
2095
|
+
applySqlOps: () => applySqlOps,
|
|
2096
|
+
diffSchemas: () => diffSchemas,
|
|
2097
|
+
ensureDir: () => ensureDir2,
|
|
2098
|
+
fileExists: () => fileExists2,
|
|
2099
|
+
findFiles: () => findFiles,
|
|
2100
|
+
generateSql: () => generateSql,
|
|
2101
|
+
getColumnNamesFromSchema: () => getColumnNamesFromSchema,
|
|
2102
|
+
getColumnNamesFromState: () => getColumnNamesFromState,
|
|
2103
|
+
getConfigPath: () => getConfigPath2,
|
|
2104
|
+
getProjectRoot: () => getProjectRoot2,
|
|
2105
|
+
getSchemaFilePath: () => getSchemaFilePath2,
|
|
2106
|
+
getSchemaForgeDir: () => getSchemaForgeDir2,
|
|
2107
|
+
getStatePath: () => getStatePath2,
|
|
2108
|
+
getTableNamesFromSchema: () => getTableNamesFromSchema,
|
|
2109
|
+
getTableNamesFromState: () => getTableNamesFromState,
|
|
2110
|
+
legacyPkName: () => legacyPkName,
|
|
2111
|
+
legacyUqName: () => legacyUqName,
|
|
2112
|
+
loadMigrationSqlInput: () => loadMigrationSqlInput,
|
|
2113
|
+
loadState: () => loadState,
|
|
2114
|
+
normalizeDefault: () => normalizeDefault,
|
|
2115
|
+
normalizeIdent: () => normalizeIdent,
|
|
2116
|
+
nowTimestamp: () => nowTimestamp,
|
|
2117
|
+
parseAddDropConstraint: () => parseAddDropConstraint,
|
|
2118
|
+
parseAlterColumnType: () => parseAlterColumnType,
|
|
2119
|
+
parseAlterTableAddColumn: () => parseAlterTableAddColumn,
|
|
2120
|
+
parseCreateTable: () => parseCreateTable,
|
|
2121
|
+
parseDropColumn: () => parseDropColumn,
|
|
2122
|
+
parseDropTable: () => parseDropTable,
|
|
2123
|
+
parseMigrationSql: () => parseMigrationSql,
|
|
2124
|
+
parseSchema: () => parseSchema,
|
|
2125
|
+
parseSetDropDefault: () => parseSetDropDefault,
|
|
2126
|
+
parseSetDropNotNull: () => parseSetDropNotNull,
|
|
2127
|
+
pkName: () => pkName,
|
|
2128
|
+
readJsonFile: () => readJsonFile2,
|
|
2129
|
+
readTextFile: () => readTextFile2,
|
|
2130
|
+
saveState: () => saveState,
|
|
2131
|
+
schemaToDsl: () => schemaToDsl,
|
|
2132
|
+
schemaToState: () => schemaToState,
|
|
2133
|
+
slugifyName: () => slugifyName,
|
|
2134
|
+
splitSqlStatements: () => splitSqlStatements,
|
|
2135
|
+
toValidationReport: () => toValidationReport,
|
|
2136
|
+
uqName: () => uqName,
|
|
2137
|
+
validateSchema: () => validateSchema,
|
|
2138
|
+
validateSchemaChanges: () => validateSchemaChanges,
|
|
2139
|
+
writeJsonFile: () => writeJsonFile2,
|
|
2140
|
+
writeTextFile: () => writeTextFile2
|
|
2141
|
+
});
|
|
2142
|
+
var init_dist = __esm({
|
|
2143
|
+
"node_modules/@xubylele/schema-forge-core/dist/index.js"() {
|
|
2144
|
+
"use strict";
|
|
2145
|
+
init_parser();
|
|
2146
|
+
init_diff();
|
|
2147
|
+
init_validator();
|
|
2148
|
+
init_validate();
|
|
2149
|
+
init_state_manager();
|
|
2150
|
+
init_sql_generator();
|
|
2151
|
+
init_parse_migration();
|
|
2152
|
+
init_apply_ops();
|
|
2153
|
+
init_schema_to_dsl();
|
|
2154
|
+
init_load_migrations();
|
|
2155
|
+
init_split_statements();
|
|
2156
|
+
init_fs();
|
|
2157
|
+
init_normalize();
|
|
2158
|
+
init_paths();
|
|
2159
|
+
init_utils();
|
|
2160
|
+
init_errors();
|
|
2161
|
+
}
|
|
2162
|
+
});
|
|
2163
|
+
|
|
26
2164
|
// src/cli.ts
|
|
27
2165
|
var import_commander6 = require("commander");
|
|
28
2166
|
|
|
29
2167
|
// package.json
|
|
30
2168
|
var package_default = {
|
|
31
2169
|
name: "@xubylele/schema-forge",
|
|
32
|
-
version: "1.5.
|
|
2170
|
+
version: "1.5.2",
|
|
33
2171
|
description: "Universal migration generator from schema DSL",
|
|
34
2172
|
main: "dist/cli.js",
|
|
35
2173
|
type: "commonjs",
|
|
@@ -69,7 +2207,6 @@ var package_default = {
|
|
|
69
2207
|
node: ">=18.0.0"
|
|
70
2208
|
},
|
|
71
2209
|
dependencies: {
|
|
72
|
-
"@xubylele/schema-forge-core": "^1.0.4",
|
|
73
2210
|
boxen: "^8.0.1",
|
|
74
2211
|
chalk: "^5.6.2",
|
|
75
2212
|
commander: "^14.0.3"
|
|
@@ -77,6 +2214,7 @@ var package_default = {
|
|
|
77
2214
|
devDependencies: {
|
|
78
2215
|
"@changesets/cli": "^2.29.8",
|
|
79
2216
|
"@types/node": "^25.2.3",
|
|
2217
|
+
"@xubylele/schema-forge-core": "^1.1.0",
|
|
80
2218
|
"ts-node": "^10.9.2",
|
|
81
2219
|
tsup: "^8.5.1",
|
|
82
2220
|
typescript: "^5.9.3",
|
|
@@ -86,7 +2224,7 @@ var package_default = {
|
|
|
86
2224
|
|
|
87
2225
|
// src/commands/diff.ts
|
|
88
2226
|
var import_commander = require("commander");
|
|
89
|
-
var
|
|
2227
|
+
var import_path7 = __toESM(require("path"));
|
|
90
2228
|
|
|
91
2229
|
// src/core/fs.ts
|
|
92
2230
|
var import_fs = require("fs");
|
|
@@ -179,59 +2317,59 @@ function resolveProvider(provider) {
|
|
|
179
2317
|
var corePromise;
|
|
180
2318
|
async function loadCore() {
|
|
181
2319
|
if (!corePromise) {
|
|
182
|
-
corePromise =
|
|
2320
|
+
corePromise = Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
183
2321
|
}
|
|
184
2322
|
return corePromise;
|
|
185
2323
|
}
|
|
186
|
-
async function
|
|
2324
|
+
async function parseSchema2(source) {
|
|
187
2325
|
const core = await loadCore();
|
|
188
2326
|
return core.parseSchema(source);
|
|
189
2327
|
}
|
|
190
|
-
async function
|
|
2328
|
+
async function validateSchema2(schema) {
|
|
191
2329
|
const core = await loadCore();
|
|
192
2330
|
core.validateSchema(schema);
|
|
193
2331
|
}
|
|
194
|
-
async function
|
|
2332
|
+
async function diffSchemas2(previousState, currentSchema) {
|
|
195
2333
|
const core = await loadCore();
|
|
196
2334
|
return core.diffSchemas(previousState, currentSchema);
|
|
197
2335
|
}
|
|
198
|
-
async function
|
|
2336
|
+
async function generateSql2(diff, provider, config) {
|
|
199
2337
|
const core = await loadCore();
|
|
200
2338
|
return core.generateSql(diff, provider, config);
|
|
201
2339
|
}
|
|
202
|
-
async function
|
|
2340
|
+
async function schemaToState2(schema) {
|
|
203
2341
|
const core = await loadCore();
|
|
204
2342
|
return core.schemaToState(schema);
|
|
205
2343
|
}
|
|
206
|
-
async function
|
|
2344
|
+
async function loadState2(statePath) {
|
|
207
2345
|
const core = await loadCore();
|
|
208
2346
|
return core.loadState(statePath);
|
|
209
2347
|
}
|
|
210
|
-
async function
|
|
2348
|
+
async function saveState2(statePath, state) {
|
|
211
2349
|
const core = await loadCore();
|
|
212
2350
|
return core.saveState(statePath, state);
|
|
213
2351
|
}
|
|
214
|
-
async function
|
|
2352
|
+
async function validateSchemaChanges2(previousState, currentSchema) {
|
|
215
2353
|
const core = await loadCore();
|
|
216
2354
|
return core.validateSchemaChanges(previousState, currentSchema);
|
|
217
2355
|
}
|
|
218
|
-
async function
|
|
2356
|
+
async function toValidationReport2(findings) {
|
|
219
2357
|
const core = await loadCore();
|
|
220
2358
|
return core.toValidationReport(findings);
|
|
221
2359
|
}
|
|
222
|
-
async function
|
|
2360
|
+
async function parseMigrationSql2(sql) {
|
|
223
2361
|
const core = await loadCore();
|
|
224
2362
|
return core.parseMigrationSql(sql);
|
|
225
2363
|
}
|
|
226
|
-
async function
|
|
2364
|
+
async function applySqlOps2(ops) {
|
|
227
2365
|
const core = await loadCore();
|
|
228
2366
|
return core.applySqlOps(ops);
|
|
229
2367
|
}
|
|
230
|
-
async function
|
|
2368
|
+
async function schemaToDsl2(schema) {
|
|
231
2369
|
const core = await loadCore();
|
|
232
2370
|
return core.schemaToDsl(schema);
|
|
233
2371
|
}
|
|
234
|
-
async function
|
|
2372
|
+
async function loadMigrationSqlInput2(inputPath) {
|
|
235
2373
|
const core = await loadCore();
|
|
236
2374
|
return core.loadMigrationSqlInput(inputPath);
|
|
237
2375
|
}
|
|
@@ -288,7 +2426,7 @@ function error(message) {
|
|
|
288
2426
|
// src/commands/diff.ts
|
|
289
2427
|
var REQUIRED_CONFIG_FIELDS = ["schemaFile", "stateFile"];
|
|
290
2428
|
function resolveConfigPath(root, targetPath) {
|
|
291
|
-
return
|
|
2429
|
+
return import_path7.default.isAbsolute(targetPath) ? targetPath : import_path7.default.join(root, targetPath);
|
|
292
2430
|
}
|
|
293
2431
|
async function runDiff() {
|
|
294
2432
|
const root = getProjectRoot();
|
|
@@ -307,36 +2445,36 @@ async function runDiff() {
|
|
|
307
2445
|
const statePath = resolveConfigPath(root, config.stateFile);
|
|
308
2446
|
const { provider } = resolveProvider(config.provider);
|
|
309
2447
|
const schemaSource = await readTextFile(schemaPath);
|
|
310
|
-
const schema = await
|
|
2448
|
+
const schema = await parseSchema2(schemaSource);
|
|
311
2449
|
try {
|
|
312
|
-
await
|
|
2450
|
+
await validateSchema2(schema);
|
|
313
2451
|
} catch (error2) {
|
|
314
2452
|
if (error2 instanceof Error) {
|
|
315
2453
|
throw await createSchemaValidationError(error2.message);
|
|
316
2454
|
}
|
|
317
2455
|
throw error2;
|
|
318
2456
|
}
|
|
319
|
-
const previousState = await
|
|
320
|
-
const diff = await
|
|
2457
|
+
const previousState = await loadState2(statePath);
|
|
2458
|
+
const diff = await diffSchemas2(previousState, schema);
|
|
321
2459
|
if (diff.operations.length === 0) {
|
|
322
2460
|
success("No changes detected");
|
|
323
2461
|
return;
|
|
324
2462
|
}
|
|
325
|
-
const sql = await
|
|
2463
|
+
const sql = await generateSql2(diff, provider, config.sql);
|
|
326
2464
|
console.log(sql);
|
|
327
2465
|
}
|
|
328
2466
|
|
|
329
2467
|
// src/commands/generate.ts
|
|
330
2468
|
var import_commander2 = require("commander");
|
|
331
|
-
var
|
|
2469
|
+
var import_path8 = __toESM(require("path"));
|
|
332
2470
|
|
|
333
2471
|
// src/core/utils.ts
|
|
334
|
-
function
|
|
2472
|
+
function nowTimestamp2() {
|
|
335
2473
|
const date = /* @__PURE__ */ new Date();
|
|
336
2474
|
const pad = (value) => String(value).padStart(2, "0");
|
|
337
2475
|
return String(date.getFullYear()) + pad(date.getMonth() + 1) + pad(date.getDate()) + pad(date.getHours()) + pad(date.getMinutes()) + pad(date.getSeconds());
|
|
338
2476
|
}
|
|
339
|
-
function
|
|
2477
|
+
function slugifyName2(name) {
|
|
340
2478
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "migration";
|
|
341
2479
|
}
|
|
342
2480
|
|
|
@@ -347,7 +2485,7 @@ var REQUIRED_CONFIG_FIELDS2 = [
|
|
|
347
2485
|
"outputDir"
|
|
348
2486
|
];
|
|
349
2487
|
function resolveConfigPath2(root, targetPath) {
|
|
350
|
-
return
|
|
2488
|
+
return import_path8.default.isAbsolute(targetPath) ? targetPath : import_path8.default.join(root, targetPath);
|
|
351
2489
|
}
|
|
352
2490
|
async function runGenerate(options) {
|
|
353
2491
|
const root = getProjectRoot();
|
|
@@ -371,58 +2509,58 @@ async function runGenerate(options) {
|
|
|
371
2509
|
}
|
|
372
2510
|
info("Generating SQL...");
|
|
373
2511
|
const schemaSource = await readTextFile(schemaPath);
|
|
374
|
-
const schema = await
|
|
2512
|
+
const schema = await parseSchema2(schemaSource);
|
|
375
2513
|
try {
|
|
376
|
-
await
|
|
2514
|
+
await validateSchema2(schema);
|
|
377
2515
|
} catch (error2) {
|
|
378
2516
|
if (error2 instanceof Error) {
|
|
379
2517
|
throw await createSchemaValidationError(error2.message);
|
|
380
2518
|
}
|
|
381
2519
|
throw error2;
|
|
382
2520
|
}
|
|
383
|
-
const previousState = await
|
|
384
|
-
const diff = await
|
|
2521
|
+
const previousState = await loadState2(statePath);
|
|
2522
|
+
const diff = await diffSchemas2(previousState, schema);
|
|
385
2523
|
if (diff.operations.length === 0) {
|
|
386
2524
|
info("No changes detected");
|
|
387
2525
|
return;
|
|
388
2526
|
}
|
|
389
|
-
const sql = await
|
|
390
|
-
const timestamp =
|
|
391
|
-
const slug =
|
|
2527
|
+
const sql = await generateSql2(diff, provider, config.sql);
|
|
2528
|
+
const timestamp = nowTimestamp2();
|
|
2529
|
+
const slug = slugifyName2(options.name ?? "migration");
|
|
392
2530
|
const fileName = `${timestamp}-${slug}.sql`;
|
|
393
2531
|
await ensureDir(outputDir);
|
|
394
|
-
const migrationPath =
|
|
2532
|
+
const migrationPath = import_path8.default.join(outputDir, fileName);
|
|
395
2533
|
await writeTextFile(migrationPath, sql + "\n");
|
|
396
|
-
const nextState = await
|
|
397
|
-
await
|
|
2534
|
+
const nextState = await schemaToState2(schema);
|
|
2535
|
+
await saveState2(statePath, nextState);
|
|
398
2536
|
success(`SQL generated successfully: ${migrationPath}`);
|
|
399
2537
|
}
|
|
400
2538
|
|
|
401
2539
|
// src/commands/import.ts
|
|
402
2540
|
var import_commander3 = require("commander");
|
|
403
|
-
var
|
|
2541
|
+
var import_path9 = __toESM(require("path"));
|
|
404
2542
|
function resolveConfigPath3(root, targetPath) {
|
|
405
|
-
return
|
|
2543
|
+
return import_path9.default.isAbsolute(targetPath) ? targetPath : import_path9.default.join(root, targetPath);
|
|
406
2544
|
}
|
|
407
2545
|
async function runImport(inputPath, options = {}) {
|
|
408
2546
|
const root = getProjectRoot();
|
|
409
2547
|
const absoluteInputPath = resolveConfigPath3(root, inputPath);
|
|
410
|
-
const inputs = await
|
|
2548
|
+
const inputs = await loadMigrationSqlInput2(absoluteInputPath);
|
|
411
2549
|
if (inputs.length === 0) {
|
|
412
2550
|
throw new Error(`No .sql migration files found in: ${absoluteInputPath}`);
|
|
413
2551
|
}
|
|
414
2552
|
const allOps = [];
|
|
415
2553
|
const parseWarnings = [];
|
|
416
2554
|
for (const input of inputs) {
|
|
417
|
-
const result = await
|
|
2555
|
+
const result = await parseMigrationSql2(input.sql);
|
|
418
2556
|
allOps.push(...result.ops);
|
|
419
2557
|
parseWarnings.push(...result.warnings.map((item) => ({
|
|
420
|
-
statement: `[${
|
|
2558
|
+
statement: `[${import_path9.default.basename(input.filePath)}] ${item.statement}`,
|
|
421
2559
|
reason: item.reason
|
|
422
2560
|
})));
|
|
423
2561
|
}
|
|
424
|
-
const applied = await
|
|
425
|
-
const dsl = await
|
|
2562
|
+
const applied = await applySqlOps2(allOps);
|
|
2563
|
+
const dsl = await schemaToDsl2(applied.schema);
|
|
426
2564
|
let targetPath = options.out;
|
|
427
2565
|
if (!targetPath) {
|
|
428
2566
|
const configPath = getConfigPath(root);
|
|
@@ -515,10 +2653,10 @@ table users {
|
|
|
515
2653
|
|
|
516
2654
|
// src/commands/validate.ts
|
|
517
2655
|
var import_commander5 = require("commander");
|
|
518
|
-
var
|
|
2656
|
+
var import_path10 = __toESM(require("path"));
|
|
519
2657
|
var REQUIRED_CONFIG_FIELDS3 = ["schemaFile", "stateFile"];
|
|
520
2658
|
function resolveConfigPath4(root, targetPath) {
|
|
521
|
-
return
|
|
2659
|
+
return import_path10.default.isAbsolute(targetPath) ? targetPath : import_path10.default.join(root, targetPath);
|
|
522
2660
|
}
|
|
523
2661
|
async function runValidate(options = {}) {
|
|
524
2662
|
const root = getProjectRoot();
|
|
@@ -536,18 +2674,18 @@ async function runValidate(options = {}) {
|
|
|
536
2674
|
const schemaPath = resolveConfigPath4(root, config.schemaFile);
|
|
537
2675
|
const statePath = resolveConfigPath4(root, config.stateFile);
|
|
538
2676
|
const schemaSource = await readTextFile(schemaPath);
|
|
539
|
-
const schema = await
|
|
2677
|
+
const schema = await parseSchema2(schemaSource);
|
|
540
2678
|
try {
|
|
541
|
-
await
|
|
2679
|
+
await validateSchema2(schema);
|
|
542
2680
|
} catch (error2) {
|
|
543
2681
|
if (error2 instanceof Error) {
|
|
544
2682
|
throw await createSchemaValidationError(error2.message);
|
|
545
2683
|
}
|
|
546
2684
|
throw error2;
|
|
547
2685
|
}
|
|
548
|
-
const previousState = await
|
|
549
|
-
const findings = await
|
|
550
|
-
const report = await
|
|
2686
|
+
const previousState = await loadState2(statePath);
|
|
2687
|
+
const findings = await validateSchemaChanges2(previousState, schema);
|
|
2688
|
+
const report = await toValidationReport2(findings);
|
|
551
2689
|
if (options.json) {
|
|
552
2690
|
console.log(JSON.stringify(report, null, 2));
|
|
553
2691
|
process.exitCode = report.hasErrors ? 1 : 0;
|