# How Read Frog Database Migration Scripts Handle Schema Evolution and Version Updates

> Explore Read Frog database migration scripts for automated, versioned schema evolution and data updates, ensuring your configurations survive extension changes without manual intervention.

- Repository: [MengXi/read-frog](https://github.com/mengxi-ream/read-frog)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Read Frog’s database migration scripts form an automated, versioned pipeline that transforms persisted user configurations from legacy schemas to the current version, ensuring data survives extension updates without manual intervention.**

Read Frog is a browser extension that persists user preferences in the browser's extension storage. As the codebase evolves, configuration schemas change—fields are added, renamed, or restructured. The **Read Frog database migration scripts**, implemented in [`src/utils/config/migration.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/config/migration.ts), automatically bridge these schema gaps by sequentially applying version-specific transformation functions, preventing data loss while maintaining backward compatibility.

## Migration Pipeline Architecture

The migration workflow orchestrates upgrades through a deterministic, step-wise process defined in [`src/utils/config/migration.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/config/migration.ts). When the extension initializes, it retrieves the stored configuration and initiates the migration sequence.

### Version Detection and Comparison

The system first extracts the stored schema version from the `originalConfigSchemaVersion` property. It compares this value against `CONFIG_SCHEMA_VERSION` (currently set to `58` in [`src/utils/constants/config.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/constants/config.ts)).

If the stored version exceeds the current supported version, the pipeline throws a `ConfigVersionTooNewError`, forcing the user to update the extension rather than risk data corruption.

### Sequential Migration Execution

For outdated configurations, the code enters a controlled loop that increments the version number one step at a time:

1. Calculates the next target version (`currentVersion + 1`)
2. Retrieves the corresponding function from the static `migrationScripts` map
3. Executes `runMigration(nextVersion, config)` to apply the transformation
4. Replaces the configuration object with the migrated result
5. Repeats until reaching `CONFIG_SCHEMA_VERSION`

This approach ensures each intermediate schema transition occurs explicitly, making debugging straightforward and state changes predictable.

### Schema Validation

After completing the migration chain, the final configuration object undergoes validation via `configSchema.safeParse`. This Zod validation step catches malformed data or failed transformations before the extension attempts to use the configuration.

## Migration Script Structure

Individual migration scripts reside in `src/utils/config/migration-scripts/` and export pure functions following the signature `(oldConfig: any) => any`.

### Incremental Transformation Functions

Each script handles exactly one version bump. For example, a migration from version 1 to 2 might add default values for new fields:

```typescript
import { deepmerge } from 'deepmerge-ts'

export function migrate(oldConfig: any): any {
  // Adds a default page translation range that did not exist in v001
  return deepmerge(oldConfig, {
    pageTranslate: { range: 'mainContent' },
  })
}

```

### Static Import Map

Because the extension runs as an ES-module Service Worker, migration functions are statically imported and registered in a `Record<number, MigrationFunction>` map within [`migration.ts`](https://github.com/mengxi-ream/read-frog/blob/main/migration.ts):

```typescript
export const migrationScripts: Record<number, MigrationFunction> = {
  2: migrateV001ToV002,
  3: migrateV002ToV003,
  // … continues up to version 58
}

```

## Practical Implementation Example

To load and migrate configuration in the extension context:

```typescript
import { migrateConfig } from '@/utils/config/migration'
import { CONFIG_STORAGE_KEY } from '@/utils/constants/config'

async function loadConfig() {
  const raw = await chrome.storage.sync.get(CONFIG_STORAGE_KEY)
  const storedConfig = raw[CONFIG_STORAGE_KEY] ?? {}
  const storedVersion = storedConfig.schemaVersion ?? 1 // fallback for legacy installs

  // Automatically migrates to schema version 58
  const config = await migrateConfig(storedConfig, storedVersion)
  return config
}

```

## Testing Strategy

Read Frog maintains rigorous test coverage for migrations. The [`src/utils/config/__tests__/migration.test.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/config/__tests__/migration.test.ts) file verifies that newer-than-current versions trigger the appropriate error, while [`src/utils/config/__tests__/migration-scripts/all-migrations.test.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/config/__tests__/migration-scripts/all-migrations.test.ts) executes every migration script in sequence to ensure deterministic upgrades across the entire version chain.

## Summary

- **Read Frog database migration scripts** reside in [`src/utils/config/migration.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/config/migration.ts) and provide automated schema evolution for browser extension storage.
- The system compares stored versions against `CONFIG_SCHEMA_VERSION` (currently 58) and rejects incompatible future versions with `ConfigVersionTooNewError`.
- Migration functions are chained sequentially in the `migrationScripts` map, with each handling exactly one version increment.
- Final Zod validation via `configSchema.safeParse` ensures data integrity after transformations complete.
- Individual scripts live in `src/utils/config/migration-scripts/` and follow a simple `(oldConfig: any) => any` signature.

## Frequently Asked Questions

### What happens if a user has a newer config version than the installed extension supports?

The migration pipeline throws a `ConfigVersionTooNewError` when `originalConfigSchemaVersion` exceeds `CONFIG_SCHEMA_VERSION`. This prevents data corruption by forcing users to update their extension to a version capable of understanding the newer schema.

### How are migration scripts organized in the Read Frog codebase?

Individual scripts are stored in `src/utils/config/migration-scripts/` as separate TypeScript files. The orchestrator in [`src/utils/config/migration.ts`](https://github.com/mengxi-ream/read-frog/blob/main/src/utils/config/migration.ts) imports these into a static `migrationScripts` map keyed by target version numbers, which ensures sequential execution from the stored version up to the current schema version.

### Why does Read Frog use Zod validation after running migrations?

The final `configSchema.safeParse` call serves as a safety net that catches malformed configurations or migration failures. This validation ensures only type-safe, schema-compliant data reaches the extension's runtime, preventing runtime errors caused by incomplete or buggy transformations.

### Can migration scripts be tested individually?

Yes. The codebase includes [`__tests__/migration-scripts/all-migrations.test.ts`](https://github.com/mengxi-ream/read-frog/blob/main/__tests__/migration-scripts/all-migrations.test.ts), which runs every migration function in sequence to verify deterministic upgrades across the entire version history. Additionally, [`migration.test.ts`](https://github.com/mengxi-ream/read-frog/blob/main/migration.test.ts) validates error handling for version mismatches, ensuring the pipeline behaves correctly under edge cases.