@usions/sdk 2.20.2 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/types/index.d.ts CHANGED
@@ -97,7 +97,8 @@ export interface FileStorageModule {
97
97
  // ─── Wallet ──────────────────────────────────────────────────────
98
98
 
99
99
  export interface WalletModule {
100
- getBalance(): Promise<number>;
100
+ /** Cached after the first call; pass `{ fresh: true }` to re-query the host. */
101
+ getBalance(opts?: { fresh?: boolean }): Promise<number>;
101
102
  hasCredits(amount: number): Promise<boolean>;
102
103
  requestPayment(amount: number, reason: string, data?: PaymentOptions): Promise<PaymentResponse>;
103
104
  onBalanceChange(callback: (balance: number) => void): UnsubscribeFn;
@@ -185,6 +186,9 @@ export interface PlayerJoinedData {
185
186
  export interface PlayerLeftData {
186
187
  room_id: string;
187
188
  player_id: string;
189
+ /** Authoritative roster (mirrors PlayerJoinedData) — reconcile membership from either event. */
190
+ player_ids: string[];
191
+ reason?: string;
188
192
  }
189
193
 
190
194
  export interface StateUpdateData {
@@ -274,6 +278,60 @@ export interface GameInviteResult {
274
278
  invited: string[];
275
279
  }
276
280
 
281
+ /** Unified connection-state machine, identical across all transports. */
282
+ export type GameConnectionState = 'connected' | 'disconnected' | 'rejoining' | 'reconnected';
283
+
284
+ /** Payload of game.onReconnected — fires once after a reconnect's resync. */
285
+ export interface ReconnectedInfo {
286
+ /** The server checkpoint from the resync (may be empty). */
287
+ state: Record<string, any> | null;
288
+ /** Highest action sequence after the resync. */
289
+ lastSequence: number;
290
+ /** True when recovery came through a game:sync payload. */
291
+ viaSync: boolean;
292
+ }
293
+
294
+ /** An action as seen by a syncedState reducer. */
295
+ export interface SyncedStateAction {
296
+ playerId: string;
297
+ type: string;
298
+ data: any;
299
+ /** Authoritative sequence; undefined only for direct-mode local applies. */
300
+ sequence: number | undefined;
301
+ }
302
+
303
+ /** Options for game.syncedState(). */
304
+ export interface SyncedStateOptions<S = Record<string, any>> {
305
+ /** Pure reducer applied to every action; default shallow-merges action.data. */
306
+ reduce?: (state: S, action: SyncedStateAction) => S;
307
+ /** Authority checkpoint cadence in actions (1 = every action; 0 disables). */
308
+ checkpointEvery?: number;
309
+ /** Who checkpoints: 'host' (player_ids[0], default) or 'all' participants. */
310
+ authority?: 'host' | 'all';
311
+ /** Default action type used by commit(data) (default 'update'). */
312
+ type?: string;
313
+ }
314
+
315
+ /** Reconnect-safe authoritative shared state (see game.syncedState). */
316
+ export interface SyncedState<S = Record<string, any>> {
317
+ /** Current state value. */
318
+ get(): S;
319
+ /** Commit with the default action type. Applied exactly once via the echo. */
320
+ commit(data: Record<string, any>): Promise<ActionResult>;
321
+ /** Commit a named action; opts forwarded to game.action (nextTurn, …). */
322
+ commit(type: string, data?: Record<string, any>, opts?: GameActionOptions): Promise<ActionResult>;
323
+ /** Subscribe to changes: cb(state, reason). Returns an unsubscribe fn. */
324
+ onChange(callback: (state: S, reason: 'action' | 'recover') => void): UnsubscribeFn;
325
+ /** Whether this client is the checkpointing authority. */
326
+ isAuthority(): boolean;
327
+ /** Force a server checkpoint now (no-op result in direct mode). */
328
+ checkpoint(): Promise<{ success: boolean; error?: string; code?: UsionErrorCode }>;
329
+ /** Sequence of the last action applied into this state. */
330
+ getSequence(): number;
331
+ /** Detach all listeners; the instance stops receiving updates. */
332
+ destroy(): void;
333
+ }
334
+
277
335
  export interface GameModule {
278
336
  // Connection
279
337
  connect(socketUrl?: string, token?: string): Promise<void>;
@@ -368,6 +426,36 @@ export interface GameModule {
368
426
  */
369
427
  onRoomAssigned(callback: (data: { roomId: string }) => void): UnsubscribeFn;
370
428
 
429
+ // ─── Reconnection lifecycle (SDK ≥ 2.21; documented since 2.16) ──────────
430
+ /** Highest action sequence seen (joins, syncs, live actions). */
431
+ getLastSequence(): number;
432
+ /** Highest action sequence applied locally; trails while catching up. */
433
+ getLastAppliedSequence(): number;
434
+ /** Current connection state, synchronously. */
435
+ getConnectionState(): GameConnectionState;
436
+ /**
437
+ * Observe connection-state transitions ('connected' → 'disconnected' →
438
+ * 'rejoining' → 'reconnected' → 'connected') — identical across the socket,
439
+ * embedded-proxy, and direct transports. One hook to drive a
440
+ * "Reconnecting…" overlay and gate input.
441
+ */
442
+ onConnectionState(callback: (state: GameConnectionState) => void): UnsubscribeFn;
443
+ /**
444
+ * Fires once per reconnect, after the resync completes. Restore local
445
+ * state here — it does NOT fire for a manually-requested sync.
446
+ */
447
+ onReconnected(callback: (info: ReconnectedInfo) => void): UnsubscribeFn;
448
+ /**
449
+ * Reconnect-safe authoritative shared state. Commits are sequenced actions
450
+ * applied through `reduce` on every client in the same order (deduped by
451
+ * sequence); the authority (player_ids[0] by default) auto-checkpoints via
452
+ * setState, and (re)joining clients recover automatically (load checkpoint,
453
+ * replay the un-checkpointed tail). Use for state that must survive a
454
+ * disconnect — unlike replicate() (realtime, lossy). Degrades to
455
+ * local-apply in direct mode (no platform checkpoint there).
456
+ */
457
+ syncedState<S = Record<string, any>>(initial: S, opts?: SyncedStateOptions<S>): SyncedState<S>;
458
+
371
459
  /**
372
460
  * Register an ADDITIONAL event listener. Unlike the onX methods this
373
461
  * supports multiple listeners per event, can be called before connect(),
@@ -817,7 +905,15 @@ export interface UsionSDK {
817
905
  config: UsionConfig;
818
906
 
819
907
  // Lifecycle
820
- init(callback: (config: UsionConfig) => void): void;
908
+ /**
909
+ * Initialize the SDK. The callback fires when the host's INIT config
910
+ * arrives; every call also returns a Promise of the same config, so
911
+ * `const config = await Usion.init()` works. `opts.timeout` (ms) rejects
912
+ * the promise with UsionError(INIT_TIMEOUT) when no INIT arrives in time
913
+ * (embedded but host silent). (promise/timeout: SDK ≥ 2.22)
914
+ */
915
+ init(callback?: (config: UsionConfig) => void, opts?: { timeout?: number }): Promise<UsionConfig>;
916
+ init(opts: { timeout?: number }): Promise<UsionConfig>;
821
917
  exit(options?: { backCount?: number }): void;
822
918
 
823
919
  // Theme & Locale
@@ -862,8 +958,8 @@ export interface UsionSDK {
862
958
  // Logging
863
959
  log(msg: string): void;
864
960
 
865
- // Generic event listener
866
- on(type: string, callback: (data: any) => void): void;
961
+ // Generic event listener (returns an unsubscribe function)
962
+ on(type: string, callback: (data: any) => void): UnsubscribeFn;
867
963
 
868
964
  // UI helpers
869
965
  setLoading(btn: HTMLElement | string, loading: boolean): void;
@@ -980,8 +1076,14 @@ export interface CloudModule extends CloudScope {
980
1076
  export interface Match { roomId: string; players: string[]; serviceId?: string; }
981
1077
 
982
1078
  export interface MatchmakingModule {
983
- /** Join the quick-match queue; resolves when paired with online strangers. */
984
- find(serviceId?: string, opts?: { size?: number }): Promise<Match>;
1079
+ /**
1080
+ * Join the quick-match queue; resolves when paired with online strangers.
1081
+ * `opts.timeout` (ms) bounds the wait: on expiry the queue is left and the
1082
+ * promise rejects with UsionError(MATCH_TIMEOUT). A newer find() rejects
1083
+ * the previous one with SUPERSEDED; cancel() rejects it with CANCELLED.
1084
+ * (timeout: SDK ≥ 2.22)
1085
+ */
1086
+ find(serviceId?: string, opts?: { size?: number; timeout?: number }): Promise<Match>;
985
1087
  /** Leave the queue / stop waiting. */
986
1088
  cancel(): Promise<any>;
987
1089
  /** Called whenever a match is found. */
@@ -1036,16 +1138,29 @@ export declare const ERROR_CODES: {
1036
1138
  readonly NO_ROOM: 'NO_ROOM';
1037
1139
  readonly ROOM_NOT_FOUND: 'ROOM_NOT_FOUND';
1038
1140
  readonly NOT_PARTICIPANT: 'NOT_PARTICIPANT';
1141
+ readonly NOT_IN_ROOM: 'NOT_IN_ROOM';
1039
1142
  readonly NOT_AUTHORITY: 'NOT_AUTHORITY';
1040
1143
  readonly NOT_AUTHENTICATED: 'NOT_AUTHENTICATED';
1041
1144
  readonly JOIN_TIMEOUT: 'JOIN_TIMEOUT';
1042
1145
  readonly CONNECT_TIMEOUT: 'CONNECT_TIMEOUT';
1146
+ readonly INIT_TIMEOUT: 'INIT_TIMEOUT';
1147
+ readonly MATCH_TIMEOUT: 'MATCH_TIMEOUT';
1043
1148
  readonly STATE_TOO_LARGE: 'STATE_TOO_LARGE';
1044
1149
  readonly INVALID_STATE: 'INVALID_STATE';
1150
+ readonly STALE_STATE: 'STALE_STATE';
1045
1151
  readonly INVALID_NEXT_TURN: 'INVALID_NEXT_TURN';
1152
+ readonly INVALID_INPUT: 'INVALID_INPUT';
1153
+ readonly NOT_FOUND: 'NOT_FOUND';
1154
+ readonly QUOTA_EXCEEDED: 'QUOTA_EXCEEDED';
1155
+ readonly VALUE_TOO_LARGE: 'VALUE_TOO_LARGE';
1156
+ readonly LOBBY_FULL: 'LOBBY_FULL';
1157
+ readonly LOBBY_CLOSED: 'LOBBY_CLOSED';
1158
+ readonly CONFLICT: 'CONFLICT';
1046
1159
  readonly RATE_LIMITED: 'RATE_LIMITED';
1047
1160
  readonly REQUEST_TIMEOUT: 'REQUEST_TIMEOUT';
1048
1161
  readonly QUEUE_FULL: 'QUEUE_FULL';
1162
+ readonly CANCELLED: 'CANCELLED';
1163
+ readonly SUPERSEDED: 'SUPERSEDED';
1049
1164
  readonly UNSUPPORTED: 'UNSUPPORTED';
1050
1165
  readonly UNKNOWN: 'UNKNOWN';
1051
1166
  };
@@ -1055,6 +1170,8 @@ export type UsionErrorCode = keyof typeof ERROR_CODES;
1055
1170
  export declare class UsionError extends Error {
1056
1171
  readonly name: 'UsionError';
1057
1172
  readonly code: UsionErrorCode;
1173
+ /** Seconds until a RATE_LIMITED call may be retried (when the server sent it). */
1174
+ readonly retryAfter?: number;
1058
1175
  constructor(code: UsionErrorCode | string, message?: string);
1059
1176
  }
1060
1177