/
githubmirror
/
strapi
Обзор
Документация
Войти
/
githubmirror
/
strapi
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
packages/core/database/src/migrations/runner.ts
116 строк
3 KB
Ben Irvin
chore(database): replace umzug with internal migration runner (#26824)
05 авг 2026, 12:57
Не верифицирован
05 авг 2026, 12:57
93875d3
Код
Авторство
О чём код?
import type { MigrationRunnerFn } from './common'; import type { createStorage } from './storage'; export interface MigrationMeta { name: string; path?: string; } export interface RunnableMigration extends MigrationMeta { up: MigrationRunnerFn; down: MigrationRunnerFn; } export interface MigrationRunnerLogger { info(message: unknown): void; } export interface MigrationRunnerOptions { storage: ReturnType<typeof createStorage>; logger: MigrationRunnerLogger; getMigrations: () => Promise<RunnableMigration[]>; } const logEvent = ( logger: MigrationRunnerLogger, event: string, name: string, extra?: Record<string, unknown> ) => { logger.info({ event, name, ...extra }); }; const wrapMigrationError = (name: string, direction: 'up' | 'down', cause: unknown): Error => { const causeMessage = cause instanceof Error ? `Original error: ${cause.message}` : `Non-error value thrown. See cause for full value: ${String(cause)}`; return new Error(`Migration ${name} (${direction}) failed: ${causeMessage}`, { cause }); }; export const createMigrationRunner = (opts: MigrationRunnerOptions) => { const getPendingMigrations = async (): Promise<RunnableMigration[]> => { const [migrations, executedNames] = await Promise.all([ opts.getMigrations(), opts.storage.executed(), ]); const executedSet = new Set(executedNames); return migrations.filter((migration) => !executedSet.has(migration.name)); }; const getExecutedMigrations = async (): Promise<RunnableMigration[]> => { const [migrations, executedNames] = await Promise.all([ opts.getMigrations(), opts.storage.executed(), ]); const executedSet = new Set(executedNames); return migrations.filter((migration) => executedSet.has(migration.name)); }; return { async pending(): Promise<MigrationMeta[]> { const pending = await getPendingMigrations(); return pending.map(({ name, path }) => ({ name, path })); }, async up(): Promise<MigrationMeta[]> { const toBeApplied = await getPendingMigrations(); for (const migration of toBeApplied) { const start = Date.now(); logEvent(opts.logger, 'migrating', migration.name); try { await migration.up(); } catch (error) { throw wrapMigrationError(migration.name, 'up', error); } await opts.storage.logMigration({ name: migration.name }); const durationSeconds = (Date.now() - start) / 1000; logEvent(opts.logger, 'migrated', migration.name, { durationSeconds }); } return toBeApplied.map(({ name, path }) => ({ name, path })); }, async down(): Promise<MigrationMeta[]> { const executedReversed = (await getExecutedMigrations()).slice().reverse(); const toBeReverted = executedReversed.slice(0, 1); for (const migration of toBeReverted) { const start = Date.now(); logEvent(opts.logger, 'reverting', migration.name); try { await migration.down(); } catch (error) { throw wrapMigrationError(migration.name, 'down', error); } await opts.storage.unlogMigration({ name: migration.name }); const durationSeconds = Number.parseFloat(((Date.now() - start) / 1000).toFixed(3)); logEvent(opts.logger, 'reverted', migration.name, { durationSeconds }); } return toBeReverted.map(({ name, path }) => ({ name, path })); }, }; };