From 7e232a900041d57126719bc6a4195efe040858e7 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Mon, 21 Sep 2026 23:39:02 +0300 Subject: [PATCH] feat!: balances and items replace the wallet in the player profile --- README.md | 14 +++++------ skills/rudder-web-sdk/SKILL.md | 8 +++--- skills/rudder-web-sdk/reference/player.md | 30 +++++++++++++---------- src/domains/PlayerDomain.ts | 2 +- src/generated/player.ts | 19 ++++++++------ src/index.ts | 2 +- 6 files changed, 42 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 464900a..61f2e69 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # @rudder/sdk Rudder client SDK for web games: device and social auth, player state -(profile, wallets, counters), catalog currencies, player storage, and calls to +(profile, balances, items), catalog currencies, player storage, and calls to installed modules through generated typed clients. ## Install @@ -34,8 +34,8 @@ const client = createClient({ await client.auth.loginWithDevice(); const profile = client.player.value; -const gold = profile?.wallets?.find((w) => w.currency === 'gold')?.balance; -const wins = profile?.counters?.wins ?? 0; +const gold = profile?.balances?.find((b) => b.currency === 'gold')?.balance; +const swords = profile?.items?.find((i) => i.item === 'sword')?.quantity ?? 0; await client.storage.save('progress', JSON.stringify({ level: 7 })); @@ -90,13 +90,13 @@ can also call directly: | Area | Access | |---|---| | Auth | `client.auth.loginWithDevice()`, `loginWithGoogle({ idToken })`, `loginWithApple({ idToken })`, `loginWithCustom({ customData })`, `linkWithGoogle(idToken)`, `linkWithApple(idToken)`, `linkWithCustom({ customData })`, `unlinkIdentity(provider)`, `logout()`, `isAuthenticated`, `onAuthStateChange(cb)` | -| Player | `client.player` state: `player`, `identities`, `wallets`, `counters` | +| Player | `client.player` state: `player`, `identities`, `balances`, `items` | | Catalog currencies | `client.catalog` state (`Map`) | | Storage | `client.storage` state + `.save(type, data)` / `.delete(type)` | | Modules | `client.modules.call(module, fn, args?, { moduleVersion? })`, usually through a generated client | -`counters` holds the values of the project's client-visible number counters -by slug; currency balances are in `wallets`. +`balances` carries one entry per released currency and `items` one per +inventory stack. ## Error handling @@ -172,7 +172,7 @@ function useSynced(state: { onChange(cb: () => void): () => void; value: T | function Wallet() { const profile = useSynced(client.player); - return
{profile?.wallets?.map((w) => `${w.currency}: ${w.balance}`).join(', ')}
; + return
{profile?.balances?.map((b) => `${b.currency}: ${b.balance}`).join(', ')}
; } ``` diff --git a/skills/rudder-web-sdk/SKILL.md b/skills/rudder-web-sdk/SKILL.md index b088cb5..5130430 100644 --- a/skills/rudder-web-sdk/SKILL.md +++ b/skills/rudder-web-sdk/SKILL.md @@ -1,6 +1,6 @@ --- name: rudder-web-sdk -description: Use when working with the Rudder TypeScript/JavaScript client SDK (@rudder/sdk) for web games. Covers createClient, device/google/apple/custom auth, identity linking, state objects (player with wallets and counters, catalog, storage), module calls through clients generated by `rudder client generate`, idempotent retries, and error handling. Load this whenever code imports from @rudder/sdk or a generated rudder.modules.ts. +description: Use when working with the Rudder TypeScript/JavaScript client SDK (@rudder/sdk) for web games. Covers createClient, device/google/apple/custom auth, identity linking, state objects (player with balances and items, catalog, storage), module calls through clients generated by `rudder client generate`, idempotent retries, and error handling. Load this whenever code imports from @rudder/sdk or a generated rudder.modules.ts. --- # Rudder Web SDK (@rudder/sdk) @@ -75,7 +75,7 @@ refresh clears tokens and emits `signed-out`. Details: | Area | Access | Kind | |---|---|---| | Auth | `client.auth` | service | -| Player | `client.player` (`player`, `identities`, `wallets`, `counters`) | `SyncedState` | +| Player | `client.player` (`player`, `identities`, `balances`, `items`) | `SyncedState` | | Catalog currencies | `client.catalog` | `SyncedState>` | | Player storage | `client.storage` + `.save(type, data)` / `.delete(type)` | `SyncedState` + mutations | | Modules | `client.modules.call(module, fn, args?, { moduleVersion? })` | resolves to `result` as `T` | @@ -116,7 +116,7 @@ and commit it when installed modules change. Details: revision poll (`GET /sdk/v1/sync`, keys `profile`, `catalog`, `storage`) runs every 30 s (±20% jitter), pauses while the tab is hidden, and reloads the states whose revision grew. Storage mutations invalidate immediately. Call -`player.reload()` after a module call that changes wallets or counters when +`player.reload()` after a module call that changes balances or items when you need fresh values right away. ## Errors @@ -137,6 +137,6 @@ timeout a 504, both without details. ## Reference files - [reference/auth.md](reference/auth.md): login flows, options, token store, session lifecycle -- [reference/player.md](reference/player.md): profile, wallets, counters +- [reference/player.md](reference/player.md): profile, balances, items - [reference/storage.md](reference/storage.md): player storage, limits - [reference/modules.md](reference/modules.md): generated clients, `modules.call`, idempotency, retries, errors diff --git a/skills/rudder-web-sdk/reference/player.md b/skills/rudder-web-sdk/reference/player.md index d6eeeaa..5ab0d4f 100644 --- a/skills/rudder-web-sdk/reference/player.md +++ b/skills/rudder-web-sdk/reference/player.md @@ -1,12 +1,11 @@ # Player — `client.player` `PlayerDomain` (source: `src/domains/PlayerDomain.ts`) — the observable player -profile: player, linked identities, currency wallets and -number counters. Synced under revision key `profile`. +profile: player, linked identities, currency balances and inventory stacks. +Synced under revision key `profile`. -There is no separate wallet service in this SDK. Wallet balances are read from -the profile and are credited/debited server-side (admin wallet adjust, module -functions). +There is no separate ledger service in this SDK. Balances and items are read +from the profile and are written server-side (admin adjust, module functions). ## Surface @@ -24,10 +23,10 @@ await client.player.reload(); // force refetch ```ts interface PlayerProfile { - counters?: { [slug: string]: number }; // client-visible number counters + balances?: Balance[]; identities?: PlayerProfileIdentity[]; + items?: InventoryStack[]; player?: Player; - wallets?: Wallet[]; } interface PlayerProfileIdentity { @@ -45,20 +44,25 @@ interface Player { region?: string; } -interface Wallet { +interface Balance { balance?: number; - currency?: string; // currency code configured in the dashboard + currency?: string; // currency slug configured in the dashboard +} + +interface InventoryStack { + item?: string; // item slug + quantity?: number; } ``` ## Behavior notes -- `counters` holds the values of `number` counters whose definition is - `client_visible`; hidden counters are left out. Currency counters are in - `wallets`, not in `counters`. Counter changes bump the `profile` revision. +- `balances` carries one entry per currency in the latest release, zero-filled + for a player who never received it; `items` carries one entry per non-empty + inventory stack. Both bump the `profile` revision when they change. - Warmed automatically at login (with catalog). - Kept fresh by the revision poll; call `reload()` after a module function - that changes wallets or counters. + that changes balances or items. - All fields are optional on the wire (`?`); code defensively. - React: `useSyncExternalStore` maps directly onto `onChange` (see the SDK README "React recipe"). diff --git a/src/domains/PlayerDomain.ts b/src/domains/PlayerDomain.ts index d78d77e..c1afc43 100644 --- a/src/domains/PlayerDomain.ts +++ b/src/domains/PlayerDomain.ts @@ -5,7 +5,7 @@ import type { PlayerProfile } from '../generated/player.js'; /** * PlayerDomain — the observable player profile (player, identities, - * wallets, counters). + * balances, items). * Synced under revision key `profile`. */ export class PlayerDomain extends SyncedState { diff --git a/src/generated/player.ts b/src/generated/player.ts index 22fcab3..1342367 100644 --- a/src/generated/player.ts +++ b/src/generated/player.ts @@ -1,5 +1,15 @@ // Code generated by apigen. DO NOT EDIT. +export interface Balance { + "balance"?: number; + "currency"?: string; +} + +export interface InventoryStack { + "item"?: string; + "quantity"?: number; +} + export interface Player { "avatarUrl"?: string; "createdAt"?: string; @@ -11,10 +21,10 @@ export interface Player { } export interface PlayerProfile { - "counters"?: { [key: string]: number }; + "balances"?: Balance[]; "identities"?: PlayerProfileIdentity[]; + "items"?: InventoryStack[]; "player"?: Player; - "wallets"?: Wallet[]; } export interface PlayerProfileIdentity { @@ -23,8 +33,3 @@ export interface PlayerProfileIdentity { "subject": string; } -export interface Wallet { - "balance"?: number; - "currency"?: string; -} - diff --git a/src/index.ts b/src/index.ts index 4937995..80a656f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,7 +46,7 @@ export type { StorageDomain } from './domains/StorageDomain.js'; // Generated wire types — the shapes returned by the observable state and // handles, so consumers can name them instead of re-declaring mirrors. -export type { Player, PlayerProfile, PlayerProfileIdentity, Wallet } from './generated/player.js'; +export type { Balance, InventoryStack, Player, PlayerProfile, PlayerProfileIdentity } from './generated/player.js'; export type { CatalogCurrency } from './generated/catalog.js'; export type { StorageItem, GetStorageResponse } from './generated/storage.js'; export type { ModuleErrorResponse } from './generated/modules.js';