fix(forms): surface validation and recover schema drift
Quality / quality (ubuntu-latest) (push) Failing after 10m23s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-15 13:01:10 +05:30
parent 1d16ef1e82
commit d78707be9f
10 changed files with 128 additions and 19 deletions
+40 -1
View File
@@ -86,6 +86,40 @@ function hasExecutableSql(sql: string): boolean {
return false;
}
function additiveColumnTarget(sql: string): { table: string; column: string } | undefined {
const executable = sql
.replace(/\/\*[\s\S]*?\*\//g, " ")
.replace(/--[^\r\n]*/g, " ")
.trim();
const match = /^ALTER\s+TABLE\s+([A-Za-z_][A-Za-z0-9_]*)\s+ADD\s+COLUMN\s+([A-Za-z_][A-Za-z0-9_]*)\b[\s\S]*;?\s*$/i.exec(
executable,
);
return match ? { table: match[1]!, column: match[2]! } : undefined;
}
async function additiveColumnAlreadyExists(db: Db, sql: string): Promise<boolean> {
const target = additiveColumnTarget(sql);
if (!target) return false;
if (db.driver.dialect === "sqlite") {
const columns = await db.all<{ name: string }>(`PRAGMA table_info(${target.table})`);
return columns.some(({ name }) => name.toLowerCase() === target.column.toLowerCase());
}
if (db.driver.dialect === "postgres") {
return Boolean(
await db.one(
"SELECT 1 AS present FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = ? AND column_name = ?",
[target.table, target.column],
),
);
}
return Boolean(
await db.one(
"SELECT 1 AS present FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?",
[target.table, target.column],
),
);
}
/** Load and parse all migration files in a directory, sorted by filename. */
export function loadMigrations(dir: string): Migration[] {
if (!existsSync(dir)) return [];
@@ -171,7 +205,12 @@ export async function applyMigrations(
for (const migration of pending.filter(({ name }) => !current.has(name))) {
throwIfAborted(options.signal);
await db.tx(async (tx) => {
if (hasExecutableSql(migration.up)) await tx.exec(migration.up);
if (
hasExecutableSql(migration.up) &&
!(await additiveColumnAlreadyExists(tx, migration.up))
) {
await tx.exec(migration.up);
}
await tx.exec(`INSERT INTO ${MIGRATIONS_TABLE} (name) VALUES (?)`, [migration.name]);
});
done.push(migration.name);