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

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, 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. 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).

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:

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:

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:

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 file verifies that newer-than-current versions trigger the appropriate error, while 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 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 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, which runs every migration function in sequence to verify deterministic upgrades across the entire version history. Additionally, migration.test.ts validates error handling for version mismatches, ensuring the pipeline behaves correctly under edge cases.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →