PURPOSE
Dev-only account utility. Wraps two Supabase RPCs (dev_unlock_everything, dev_reset_account) and re-hydrates all client stores from the resulting canonical server state. Used to grant a fully-maxed account or wipe back to starter state for testing.
OWNS
unlockEverything()async function — grants all ships at 5★, max wallet, max pull tickets, one Legendary of every mod template, every grid cell purchased.resetEverything()async function — wipes account back to day-1 defaults (match history preserved).- Construction of the pre-built mod-grid snapshot (inventory + empty equipped + purchased cells) sent to the unlock RPC.
- Generation of stable per-row uids via
unlock_<base36-timestamp>_<counter>pattern.
READS FROM
../data/ships—HULL_CLASSES(hull-class id list),getShipDef(hull, 1)(rarity lookup at 1★).../data/mods—MOD_TEMPLATES(every mod template that needs a Legendary instance).../data/mods/grid-unlock—allCellKeys()(every purchasable grid-cell key).../stores/modGridStore—MOD_GRID_SCHEMA_VERSION(schema-version stamp written into the grid blob).
PUSHES TO
invokeRpc('dev_unlock_everything', { p_ship_ids, p_ship_rarities, p_mod_grid })— Supabase RPC (migration 042). Server maxes wallet/tickets, inserts 18 duplicate entity rows per ship to push xp to 17 / 5★, and writes the supplied mod-grid JSON toplayer_saves.mod_grid_json.invokeRpc('dev_reset_account')— Supabase RPC. Server wipes inventory, wallet, save back to defaults.bootstrapPlayer()from../../metagame/services/playerBootstrap— re-hydratesinventoryStore+modGridStorefrom the now-canonical server state. Called after both RPCs.
DOES NOT
- Does not mutate any Zustand store directly. State changes only flow through the RPC +
bootstrapPlayer()re-hydrate cycle, so the server is always canonical. - Does not gate by environment or user role — caller is expected to gate the dev surface that invokes these functions.
- Does not preserve any prior client state across the call; the rebuild from the server response replaces everything.
- Does not touch match history (server
dev_reset_accountleaves it intact). - Does not iterate the 300-entry SHIPS roster — it sends one entry per
HULL_CLASSand lets the server expand to per-star rows.
Signals
- Returns
Promise<void>from both functions. No success / failure signal is returned to the caller beyond the promise resolution;invokeRpcerrors propagate as rejections. - Side effects observable through the rehydrated
inventoryStoreandmodGridStoreafterbootstrapPlayer()completes.
Entry points
unlockEverything()— exported async function, no arguments.resetEverything()— exported async function, no arguments.
Pattern notes
- Server-canonical state pattern: every mutation is an RPC; the client never patches local stores then writes through. After the RPC,
bootstrapPlayer()is the single source of truth re-load. HULL_CLASSESvs.SHIPS: the server storeshull_classastemplate_id, so the unlock RPC takes one entry per hull (not per hull×star combination). Per-star rows are an expansion the server performs.- Mod-grid uid scheme:
unlock_<Date.now().toString(36)>_<++counter>— collision-safe for a one-shot dev action because the counter increments within a single call, and the base36 timestamp distinguishes separate calls. - Mod-grid snapshot shape:
{ schemaVersion, inventory: [{ uid, templateId, rarity: 'legendary' }], equipped: {}, purchasedCells: allCellKeys() }. Equipped map intentionally empty — the user equips after unlock. getShipDef(hull, 1).rarityis used because rarity is constant across stars for a hull, so the 1★ row is a safe lookup.