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/shipsHULL_CLASSES (hull-class id list), getShipDef(hull, 1) (rarity lookup at 1★).
  • ../data/modsMOD_TEMPLATES (every mod template that needs a Legendary instance).
  • ../data/mods/grid-unlockallCellKeys() (every purchasable grid-cell key).
  • ../stores/modGridStoreMOD_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 to player_saves.mod_grid_json.
  • invokeRpc('dev_reset_account') — Supabase RPC. Server wipes inventory, wallet, save back to defaults.
  • bootstrapPlayer() from ../../metagame/services/playerBootstrap — re-hydrates inventoryStore + modGridStore from 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_account leaves it intact).
  • Does not iterate the 300-entry SHIPS roster — it sends one entry per HULL_CLASS and 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; invokeRpc errors propagate as rejections.
  • Side effects observable through the rehydrated inventoryStore and modGridStore after bootstrapPlayer() 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_CLASSES vs. SHIPS: the server stores hull_class as template_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).rarity is used because rarity is constant across stars for a hull, so the 1★ row is a safe lookup.