save-migrations
Client-side save schema versioning. Bumps CURRENT_SAVE_VERSION whenever the shape of BootstrapPayload['save'] changes in a way old payloads can’t satisfy, and runs registered per-version migration functions to bring stale saves up to the current shape before any store hydration.
Styx
- Entry point:
runSaveMigrations(save)invoked fromplayerBootstrapimmediately after thebootstrap_playerRPC returns, before any store hydration (seesrc/metagame/services/playerBootstrap.ts:104). - Input:
SaveBlobfrom./save-schema— fieldsselected_ship_id,fleet_json(deprecated, ignored),settings_json,save_version,updated_at. - Loop: while
current.save_version < CURRENT_SAVE_VERSION, look upMIGRATIONS[current.save_version]and apply it; the migration must return aSaveBlobwithsave_versionadvanced. - Missing migration: a missing entry for a version below
CURRENT_SAVE_VERSIONthrowsError("[save-migrations] No migration registered for save_version <n> (CURRENT=<m>)"). No silent fallback. - Output:
SaveBlobwhosesave_version === CURRENT_SAVE_VERSION.
Constants
| Name | Value | Notes |
|---|---|---|
CURRENT_SAVE_VERSION | 1 | Bump when SaveBlob shape changes incompatibly. |
Types
Migration = (save: SaveBlob) => SaveBlob— pure function from one version’s shape to the next.MIGRATIONS: Record<number, Migration>— sparse map keyed by source version. Currently empty (commented stub atsave-migrations.ts:19shows the canonical form:1: (save) => ({ ...save, newField: defaultValue, save_version: 2 })).
Authoring a new migration
- Update
SaveBlobin./save-schemato the new shape. - Increment
CURRENT_SAVE_VERSIONto N+1. - Register
MIGRATIONS[N] = (save) => ({ ...save, /* new/changed fields */, save_version: N + 1 }). - The migration must set
save_versionon its return value so the loop terminates.
EXTRACT-CANDIDATE
- None. File is 34 lines, single responsibility, single call site. The
MIGRATIONSmap is the natural extension point; no abstraction to extract.