import { SYNC_QUEUE_SCHEMA_VERSION } from "../constants/index"; import type { SyncQueueEntry } from "../types/common"; /** A single forward migrator from `fromVersion` to `fromVersion + 1`. */ export interface VersionMigrator { readonly fromVersion: number; upgrade(entry: Record): Record; } /** v1 → v2: legacy entries lacked `schemaVersion`; assume retryCount/maxRetries defaults if absent. */ const V1_TO_V2: VersionMigrator = { fromVersion: 1, upgrade(entry) { const rc = entry["retryCount"]; const mr = entry["maxRetries"]; return { ...entry, retryCount: typeof rc === "number" ? rc : 0, maxRetries: typeof mr === "number" ? mr : 3, schemaVersion: 2, }; }, }; /** Default migrator registry shipped with the package. */ export const DEFAULT_MIGRATORS: readonly VersionMigrator[] = Object.freeze([ V1_TO_V2, ]); /** * Forward-migrate a single raw entry record to the current schema version. * Idempotent: entries already at the current version pass through unchanged. */ export function migrateEntry( raw: Record, migrators: readonly VersionMigrator[] = DEFAULT_MIGRATORS ): Record { const startVersion = typeof raw["schemaVersion"] === "number" ? raw["schemaVersion"] : 1; let current: Record = { ...raw, schemaVersion: startVersion, }; while (true) { const v = current["schemaVersion"]; if (typeof v !== "number" || v >= SYNC_QUEUE_SCHEMA_VERSION) break; const migrator = migrators.find((m) => m.fromVersion === v); if (migrator === undefined) { throw new Error( `No sync-queue migrator registered for schemaVersion=${String(v)}` ); } current = migrator.upgrade(current); } return current; } /** Migrate a batch of raw entries. */ export function migrateBatch( raws: readonly Record[], migrators: readonly VersionMigrator[] = DEFAULT_MIGRATORS ): Record[] { return raws.map((r) => migrateEntry(r, migrators)); } /** Type-narrowing helper to cast a migrated record into a {@link SyncQueueEntry}. */ export function asSyncQueueEntry( record: Record ): SyncQueueEntry { return record as unknown as SyncQueueEntry; }