import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { type Kysely, sql } from "kysely"; import { FileMigrationProvider, type MigrationResultSet, Migrator } from "kysely/migration"; /** 迁移目录:packages/db/migrations,文件名 NNNN_name.ts,导出 up/down */ export const MIGRATIONS_DIR = fileURLToPath(new URL("../migrations", import.meta.url)); export interface MigratorOptions { /** * kysely_migration 表所在 schema。默认不指定(走 search_path)。 * 在非 public schema 里迁移(如测试隔离 schema)时必须显式指定, * 否则 Kysely 会把 public 下已存在的迁移表当成当前库的迁移记录。 */ migrationTableSchema?: string; } // biome-ignore lint/suspicious/noExplicitAny: 迁移器不关心具体表类型 export function createMigrator(db: Kysely, opts: MigratorOptions = {}): Migrator { return new Migrator({ db, provider: new FileMigrationProvider({ fs, path, migrationFolder: MIGRATIONS_DIR }), ...(opts.migrationTableSchema ? { migrationTableSchema: opts.migrationTableSchema } : {}), }); } /** 迁移到最新;失败时抛出 */ // biome-ignore lint/suspicious/noExplicitAny: 同上 export async function migrateToLatest(db: Kysely, opts: MigratorOptions = {}): Promise { const result = await createMigrator(db, opts).migrateToLatest(); if (result.error) throw result.error; return result; } /** 清空 public schema(仅开发/测试用) */ // biome-ignore lint/suspicious/noExplicitAny: 同上 export async function resetSchema(db: Kysely): Promise { await sql`drop schema if exists public cascade`.execute(db); await sql`create schema public`.execute(db); }