# Inventory + catalog — `client.inventory`, `client.catalog` ## Inventory `InventoryDomain` (source: `src/domains/InventoryDomain.ts`) — owned items merged with their catalog entries. Synced under revision key `inventory`; also refreshed whenever the catalog changes. ```ts client.inventory.data // InventoryItem[] | undefined client.inventory.onChange(cb); await client.inventory.load() / .reload(); ``` ### `InventoryItem` (merged view, source: `src/state/inventory.ts`) ```ts interface InventoryItem { slug: string; // '' when the wire item has no slug amount: number; // 0 when absent on the wire name: string; // nameOverride || catalog name || slug properties: Record; // catalog properties + propertiesOverride (override wins) tags: string[]; // from the catalog entry } ``` The raw wire shape is `PlayerInventoryItem` (`slug?, amount?, nameOverride?, propertiesOverride?, updatedAt?`) — you rarely need it; the domain hands out the merged `InventoryItem`. ## Catalog `CatalogDomain` (source: `src/domains/CatalogDomain.ts`) — the item catalog keyed by slug. Synced under revision key `catalog`. ```ts client.catalog.data // Map | undefined interface CatalogItem { name?: string; properties?: { [key: string]: unknown }; slug?: string; tags?: string[]; } ``` Items without a `slug` are skipped when the map is built. ## Behavior notes - The catalog is warmed at login; inventory loads on first use, waits on the catalog load, and merges, so `client.inventory.data` always has catalog fields filled in. - Invalidated after store purchases and scenario callbacks. - Inventory has no client-side mutations — items change via purchases, quest / battle pass rewards, and scenario nodes (all server-side).