You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

63 lines
1.8 KiB

type SqlValue = string | number | null | Uint8Array;
export interface QueryExecResult {
columns: Array<string>;
values: Array<Array<SqlValue>>;
}
interface Migration {
name: string;
sql: string;
}
export class MigrationService {
private static instance: MigrationService;
private migrations: Migration[] = [];
private constructor() {}
static getInstance(): MigrationService {
if (!MigrationService.instance) {
MigrationService.instance = new MigrationService();
}
return MigrationService.instance;
}
async registerMigration(migration: Migration): Promise<void> {
this.migrations.push(migration);
}
async runMigrations(
sqlExec: (sql: string, params?: any[]) => Promise<Array<QueryExecResult>>
): Promise<void> {
// Create migrations table if it doesn't exist
await sqlExec(`
CREATE TABLE IF NOT EXISTS migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
// Get list of executed migrations
const result = await sqlExec('SELECT name FROM migrations');
const singleResult = result[0];
const executedMigrations = new Set(singleResult.values.map(row => row[0]));
// Run pending migrations in order
for (const migration of this.migrations) {
if (!executedMigrations.has(migration.name)) {
try {
await sqlExec(migration.sql);
await sqlExec('INSERT INTO migrations (name) VALUES (?)', [migration.name]);
console.log(`Migration ${migration.name} executed successfully`);
} catch (error) {
console.error(`Error executing migration ${migration.name}:`, error);
throw error;
}
}
}
}
}
export default MigrationService.getInstance();