feat!: balances and items replace the wallet in the player profile
CI / check (push) Failing after 10s
CI / publish (push) Has been skipped

This commit is contained in:
edmand46
2026-09-21 23:39:02 +03:00
parent 83e3f78823
commit 7e232a9000
6 changed files with 42 additions and 33 deletions
+7 -7
View File
@@ -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<slug, CatalogCurrency>`) |
| Storage | `client.storage` state + `.save(type, data)` / `.delete(type)` |
| Modules | `client.modules.call<T>(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<T>(state: { onChange(cb: () => void): () => void; value: T |
function Wallet() {
const profile = useSynced(client.player);
return <div>{profile?.wallets?.map((w) => `${w.currency}: ${w.balance}`).join(', ')}</div>;
return <div>{profile?.balances?.map((b) => `${b.currency}: ${b.balance}`).join(', ')}</div>;
}
```
+4 -4
View File
@@ -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<PlayerProfile>` |
| Player | `client.player` (`player`, `identities`, `balances`, `items`) | `SyncedState<PlayerProfile>` |
| Catalog currencies | `client.catalog` | `SyncedState<Map<string, CatalogCurrency>>` |
| Player storage | `client.storage` + `.save(type, data)` / `.delete(type)` | `SyncedState<GetStorageResponse>` + mutations |
| Modules | `client.modules.call<T>(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
+17 -13
View File
@@ -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").
+1 -1
View File
@@ -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<PlayerProfile> {
+12 -7
View File
@@ -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;
}
+1 -1
View File
@@ -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';