feat: modules and resources domains, drop removed domains, regenerated models
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
# @rudder/js-sdk
|
||||
|
||||
LiveOps Web SDK for browser games — device auth, observable player state,
|
||||
stores, remote config, leaderboards, quests, battle pass, and a scenario runtime
|
||||
surfaced as typed effects.
|
||||
catalog currencies, storage, generic resources, and module function calls.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -29,22 +28,18 @@ const client = new RudderClient({
|
||||
projectKey: 'your-project-key',
|
||||
// tokenStore is optional: defaults to localStorage with an in-memory
|
||||
// fallback where localStorage is unavailable (SSR, private mode).
|
||||
appVersion: '1.4.2', // optional; sent with platform 'web' on login and refresh
|
||||
});
|
||||
|
||||
await client.auth.loginWithDevice(); // region 'global', language 'en'
|
||||
// or: await client.auth.loginWithDevice({ region: 'eu', language: 'de', nickname: 'Bob' });
|
||||
|
||||
// Observable domains — read `.data`, subscribe with `onChange`, force `reload()`.
|
||||
const profile = client.player.data;
|
||||
const speed = client.remoteConfig.get('player_speed', 200);
|
||||
const gold = profile?.wallets?.find((w) => w.currency === 'gold')?.balance;
|
||||
|
||||
client.stores.onChange(({ data: stores }) => renderStores(stores ?? []));
|
||||
const items = await client.resources('items').list();
|
||||
const result = await client.modules.call('quests', 'list');
|
||||
|
||||
// Scenario-driven UI arrives through effects.
|
||||
client.effects.onStoreOffer((offer) => showOffer(offer));
|
||||
client.effects.onScenarioFailed(({ error }) => console.error('scenario failed', error));
|
||||
|
||||
// Tear down background work (sync poll, wait timers) on unmount / HMR.
|
||||
client.dispose();
|
||||
```
|
||||
|
||||
@@ -54,75 +49,15 @@ client.dispose();
|
||||
|---|---|
|
||||
| Auth | `client.auth.loginWithDevice()`, `client.auth.loginWithCustom({ customData })`, `client.auth.logout()`, `client.auth.isAuthenticated`, `client.auth.onAuthStateChange(cb)` |
|
||||
| Player / wallets | `client.player` (observable) |
|
||||
| Inventory | `client.inventory` (observable, catalog-merged) |
|
||||
| Catalog | `client.catalog` (observable) |
|
||||
| Stores | `client.stores` (observable) + `client.stores.purchase(slug, offerId)` |
|
||||
| Remote config | `client.remoteConfig.get(key, default)` (observable, typed) |
|
||||
| Catalog currencies | `client.catalog` (observable) |
|
||||
| Storage | `client.storage` (observable) + `.save(items)` / `.delete(type)` |
|
||||
| Leaderboards | `client.leaderboards.findBySlug(slug)` → `handle.submit(score)` / `handle.list(limit?)` |
|
||||
| Battle pass | `client.battlePass` (getProgress / addXp / claimReward / purchasePremium) |
|
||||
| Quests | `client.quests.list()` / `client.quests.claim(id)` / `client.quests.reportProgress(metric, amount)` |
|
||||
| Scenario effects | `client.effects.on*` |
|
||||
| Project storage | `client.projectStorage` (observable) + `.save(items)` |
|
||||
| Modules | `client.modules.call(module, fn, args?)` |
|
||||
| Resources | `client.resources(slug)` → `list` / `get` / `create` / `update` / `delete` |
|
||||
|
||||
Type the remote config for `client.remoteConfig`:
|
||||
|
||||
```ts
|
||||
interface GameConfig extends Record<string, unknown> {
|
||||
player_speed: number;
|
||||
feature_x: boolean;
|
||||
}
|
||||
const client = new RudderClient<GameConfig>({ /* … */ });
|
||||
client.remoteConfig.get('player_speed', 200); // number
|
||||
```
|
||||
|
||||
## Quests
|
||||
|
||||
`client.quests` covers the player's global quests — list with per-objective
|
||||
progress, claim, and metric reports. These are distinct from scenario quest
|
||||
nodes, which advance through the `onQuest` effect's `QuestSession`. Global
|
||||
quests have no live sync: re-list after a claim or report.
|
||||
|
||||
```ts
|
||||
const quests = await client.quests.list();
|
||||
for (const quest of quests) {
|
||||
if (quest.status === 'completed' && quest.id) {
|
||||
await client.quests.claim(quest.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Custom metrics advance matching objectives server-side; the call returns
|
||||
// the ids of quests completed by this report.
|
||||
const completedIds = await client.quests.reportProgress('kills', 1);
|
||||
```
|
||||
|
||||
Purchase metrics are reported automatically when a purchase goes through
|
||||
`client.stores`; the `QuestMetrics` helpers name the format so quest configs
|
||||
and client code agree on it:
|
||||
|
||||
```ts
|
||||
import { QuestMetrics } from '@rudder/js-sdk';
|
||||
|
||||
QuestMetrics.purchaseOffer('starter-pack'); // "purchase.offer:starter-pack"
|
||||
QuestMetrics.purchaseItem('moonberry'); // "purchase.item:moonberry"
|
||||
```
|
||||
|
||||
## Scenario effects
|
||||
|
||||
The scenario runtime is not exposed directly; scenario nodes surface through
|
||||
`client.effects`:
|
||||
|
||||
- `onNotification`, `onStoreOffer`, `onLeaderboard`, `onConfigChanged`
|
||||
- `onWait`, `onQuest`, `onBattlePass`, `onBattlePassLevel`
|
||||
- `onScenarioCompleted`, `onScenarioFailed`
|
||||
|
||||
The server executes the scenario graph. The SDK is a thin effects client
|
||||
(trigger, pending poll, callbacks) loaded lazily on first login — a client
|
||||
that only reads domains never pulls it into the page. `onConfigChanged`
|
||||
stays on the surface, but scenario `remote_config_override` nodes are
|
||||
applied server-side and no longer emit that effect.
|
||||
|
||||
Errors thrown inside effect handlers are reported via the `onEffectError`
|
||||
client option (default: `console.error`).
|
||||
Game-design features (quests, stores, inventory, leaderboards, battle pass,
|
||||
remote config) live in installed modules. Call them through
|
||||
`client.modules.call` and read their rows through `client.resources(slug)`.
|
||||
|
||||
## Error handling
|
||||
|
||||
@@ -135,7 +70,6 @@ import {
|
||||
RudderNetworkError,
|
||||
RudderHttpError,
|
||||
RudderAuthError,
|
||||
RudderErrorCodes,
|
||||
} from '@rudder/js-sdk';
|
||||
|
||||
try {
|
||||
@@ -146,9 +80,6 @@ try {
|
||||
// cleared tokens and emitted 'signed-out' via onAuthStateChange.
|
||||
showLogin();
|
||||
} else if (error instanceof RudderHttpError) {
|
||||
if (error.code === RudderErrorCodes.runExpired) {
|
||||
// typed server error code
|
||||
}
|
||||
console.error(error.status, error.code, error.body);
|
||||
} else if (error instanceof RudderNetworkError) {
|
||||
// fetch failed / timeout — the underlying error is in error.cause
|
||||
@@ -171,15 +102,14 @@ client.auth.onAuthStateChange((state) => {
|
||||
|
||||
## Data liveness model
|
||||
|
||||
Observable domains (`player`, `inventory`, `catalog`, `stores`, `remoteConfig`,
|
||||
`storage`) are warmed once at login and then kept fresh by a revision poll:
|
||||
the client asks the gateway for entity revisions every **30 s (±20% jitter)**
|
||||
and reloads only the entities whose revision grew. Polling **pauses while the
|
||||
tab is hidden** and resumes on `visibilitychange`. Mutations (purchases,
|
||||
storage writes, scenario callbacks) invalidate the affected domains
|
||||
immediately, so subscribers see fresh data without waiting for the next poll.
|
||||
Expect data to be eventually consistent within one poll interval; call
|
||||
`reload()` when you need a guarantee.
|
||||
Observable domains (`player`, `catalog`, `storage`, `projectStorage`) are
|
||||
warmed at login (`player` and `catalog`) or on first use, then kept fresh by
|
||||
a revision poll: the client asks the gateway for entity revisions every
|
||||
**30 s (±20% jitter)** and reloads only the entities whose revision grew.
|
||||
Polling **pauses while the tab is hidden** and resumes on `visibilitychange`.
|
||||
Storage writes invalidate the affected domain immediately. Expect data to be
|
||||
eventually consistent within one poll interval; call `reload()` when you need
|
||||
a guarantee.
|
||||
|
||||
## React recipe
|
||||
|
||||
@@ -202,9 +132,6 @@ function Wallet() {
|
||||
}
|
||||
```
|
||||
|
||||
The same pattern works for `client.remoteConfig`, `client.stores`, and
|
||||
`client.auth.onAuthStateChange`.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user