migrate method

Future<void> migrate({
  1. bool dryRun = false,
})

Applies all pending migrations.

Creates the _dartapi_migrations tracking table if it does not exist. Runs each unapplied .sql file inside a transaction and records it.

When dryRun is true, pending migrations are printed but not applied — useful for CI checks or previewing what migrate() would do:

await runner.migrate(dryRun: true);
// → [DRY RUN] Would apply: 0003_add_index.sql

Implementation

Future<void> migrate({bool dryRun = false}) async {
  if (!dryRun) await _ensureMigrationsTable();

  final applied = dryRun ? <String>{} : await _appliedMigrations();
  final pending = await _pendingMigrations(applied);

  if (pending.isEmpty) {
    print(dryRun
        ? '[DRY RUN] No pending migrations.'
        : '✅ No pending migrations.');
    return;
  }

  if (dryRun) {
    print('[DRY RUN] ${pending.length} pending migration(s):');
    for (final file in pending) {
      print('  • ${_fileName(file)}');
    }
    return;
  }

  for (final file in pending) {
    final name = _fileName(file);
    print('⏳ Applying migration: $name');

    final sql = file.readAsStringSync();

    await db.transaction((tx) async {
      await tx.rawQuery(sql);
      await tx.rawQuery(
        'INSERT INTO _dartapi_migrations (name) VALUES (:name);',
        values: {'name': name},
      );
    });

    print('✅ Applied: $name');
  }

  print('🎉 All migrations applied (${pending.length} total).');
}