Search documentation

Find pages, sections, and content across all docs.

WavedashDocs

Types reference

TypeScript type definitions for the Wavedash SDK

This page documents the shared TypeScript types used throughout the Wavedash SDK. For method signatures and usage examples, see the Functions reference or the individual feature pages under the SDK and Multiplayer sections.

ID types

All IDs are strings. The SDK gives each kind its own name so the type system catches accidental mix-ups, like passing a LobbyId where a UserId is expected:

TypeIdentifies
UserIdA player
LobbyIdA lobby
LobbyMessageIdA lobby chat message
LobbyInviteIdA lobby invite
LeaderboardIdA leaderboard
LeaderboardEntryIdA leaderboard entry
UGCIdA user-generated content item
PurchaseIdA purchase
import type { UserId, LobbyId, LeaderboardId, UGCId, PurchaseId } from "@wvdsh/sdk-js";

Under the hood, each is a Convex-style branded Id<"tableName">, an opaque string tagged with its table. UserId is the same type as Id<"users">, so code written against the Id<...> form keeps compiling.

IDs are opaque branded strings. Don't parse or construct them manually — always use the IDs returned from SDK methods.

Constants

Types on this page that take a fixed set of values, like UGCType, LobbyVisibility, and PurchaseType, have a named constant for each value in every engine. Use the constant instead of the raw number or string.

# WavedashSDK.Constants.<TYPE>_<VALUE>
WavedashSDK.Constants.UGC_TYPE_SCREENSHOT
WavedashSDK.Constants.LOBBY_VISIBILITY_PUBLIC
WavedashSDK.Constants.PURCHASE_TYPE_DURABLE
WavedashSDK.Constants.FULFILL_PURCHASE_STATUS_FULFILLED
// WavedashConstants.<Type>.<VALUE>
WavedashConstants.UGCItemType.SCREENSHOT
WavedashConstants.LobbyVisibility.PUBLIC
WavedashConstants.PurchaseType.DURABLE
WavedashConstants.FulfillPurchaseStatus.FULFILLED
-- wavedash.<TYPE>_<VALUE>
wavedash.UGC_TYPE_SCREENSHOT
wavedash.LOBBY_VISIBILITY_PUBLIC
wavedash.PURCHASE_TYPE_DURABLE
wavedash.FULFILL_PURCHASE_STATUS_FULFILLED
// Wavedash.<Type>.<VALUE>
Wavedash.UGCType.SCREENSHOT
Wavedash.LobbyVisibility.PUBLIC
Wavedash.PurchaseType.DURABLE
Wavedash.FulfillPurchaseStatus.FULFILLED

Response types

WavedashResponse

Most async service calls return a WavedashResponse wrapper. Synchronous getters such as getUser() do not.

type WavedashResponse<T> =
  | { success: true; data: T }                       // Success: data holds the result
  | { success: false; data: null; message: string }; // Failure: message explains why

Checking success narrows the type, so data is non-null inside the success branch.

Example usage:

import type { Leaderboard } from "@wvdsh/sdk-js";

const user = Wavedash.getUser(); // sync getter

const response = await Wavedash.getLeaderboard("high-scores");

if (response.success) {
  const leaderboard: Leaderboard = response.data;
  // Use leaderboard...
} else {
  console.error(response.message);
}

Player types

User

Returned by getUser(). This is the signed-in player's own profile. @wvdsh/sdk-js doesn't export this type (it's SDKUser from @wvdsh/api) — use ReturnType<typeof Wavedash.getUser>.

interface User {
  id: UserId;                  // The player's user ID
  username: string;                 // Display name
  avatarUrl: string | undefined;    // Avatar URL (if set)
}

Unlike the other player-shaped types (Friend, LobbyUser, LobbyMessage, LeaderboardEntry, P2PPeer), which expose the ID as userId, User exposes it as id. Reading .userId on a getUser() result returns undefined.

Configuration types

WavedashConfig

SDK configuration options passed to Wavedash.init:

interface WavedashConfig {
  debug?: boolean;                       // Useful for logging and testing — enables verbose output in the browser console
  deferEvents?: boolean;                 // Queues all SDK events until readyForEvents() is called
  p2p?: Partial<P2PConfig>;             // P2P networking configuration (see P2PConfig below)
}

All config options are optional.

P2PConfig

Peer-to-peer networking configuration:

interface P2PConfig {
  enableReliableChannel: boolean;  // TCP-like channel
  enableUnreliableChannel: boolean; // UDP-like channel
  messageSize?: number;            // Max bytes per message slot (default: 2048, max: 65536)
  maxIncomingMessages?: number;    // Incoming message queue depth (default: 1024)
}

Tweak these values in your call to Wavedash.init to fine tune your P2P networking memory footprint

Launch param types

GameLaunchParams

Key-value mapping of URL query parameters that were present when the game launched. See SDK setup for details.

interface GameLaunchParams {
  lobby?: LobbyId;       // Lobby ID if the player followed an invite link
  [key: string]: string | undefined; // Any other wvdsh_-prefixed params
}

Leaderboard types

Leaderboard

interface Leaderboard {
  id: LeaderboardId;              // Leaderboard ID
  name: string;                        // The identifier you pass to getLeaderboard / getOrCreateLeaderboard
  totalEntries: number;                // Total number of entries
}

LeaderboardSortOrder

// 0 = ascending  — Lower scores rank higher (time trials, golf)
// 1 = descending — Higher scores rank higher (points, high scores)
type LeaderboardSortOrder = 0 | 1;

LeaderboardDisplayType

// 0 = numeric           — Display as a number (e.g., "1,000")
// 1 = time_seconds      — Display as time in seconds (e.g., "1:23")
// 2 = time_milliseconds — Display as time with milliseconds (e.g., "1:23.456")
// 3 = time_game_ticks   — Display as game ticks (assumes 60fps)
type LeaderboardDisplayType = 0 | 1 | 2 | 3;

LeaderboardEntry

The package exports the array type LeaderboardEntries; one entry is LeaderboardEntries[number].

interface LeaderboardEntry {
  userId: UserId;                  // Player's user ID
  username: string;                     // Player's display name
  userAvatarUrl?: string;               // Player's avatar URL (if set)
  score: number;                        // Score value
  globalRank: number;                   // Current rank
  ugcId?: UGCId;   // Attached UGC (optional)
  timestamp: number;                    // Submission timestamp
  metadata?: LeaderboardEntryMetadata;  // Data stored with the score (optional)
}

LeaderboardEntryMetadata

Small key/value data attached to a leaderboard entry by uploadLeaderboardScore. Values are strings, numbers, or booleans — no nested objects, arrays, or null. Capped at 16 keys, 64-character keys, 256-character string values, and 2048 bytes total. See Leaderboards for the storage rules.

type LeaderboardEntryMetadata = Record<string, string | number | boolean>;

UpsertedLeaderboardEntry

Returned from uploadLeaderboardScore. A different shape from LeaderboardEntry — it reports what changed on upload. It carries your saved standing (score / globalRank) alongside the submitted attempt (submittedScore / submittedRank), which differ when keepBest kept a better existing score. See Leaderboards for details.

interface UpsertedLeaderboardEntry {
  entryId: LeaderboardEntryId;
  score: number;                        // Your saved standing's score
  scoreChanged: boolean;                // false if keepBest kept a better score
  globalRank: number;                   // Your saved standing's rank
  submittedScore: number;               // The score you just submitted
  submittedRank: number;                // Where that submitted run would rank
  timestamp: number;                    // When the saved entry was recorded
  ugcId?: UGCId;   // UGC attached to the saved entry
  metadata?: LeaderboardEntryMetadata;  // The metadata now stored on the entry
  userId: UserId;
  username: string;
  userAvatarUrl?: string;
}

Lobby types

Lobby

interface Lobby {
  lobbyId: LobbyId;            // Lobby ID
  visibility: LobbyVisibility;       // Access level
  maxPlayers: number;                // Player limit
  playerCount: number;               // Current count
  metadata: Record<string, LobbyDataValue>; // Host-set key/value data
}

LobbyDataValue

A value stored in lobby metadata: string, number, or boolean.

type LobbyDataValue = string | number | boolean;

getLobbyData returns LobbyDataValue | null; null means the key isn't set.

LobbyDataUpdate

A value accepted by setLobbyData: a LobbyDataValue, or null to delete the key. deleteLobbyData is shorthand for setting null.

type LobbyDataUpdate = LobbyDataValue | null;

LobbyVisibility

// 0 = public       — Anyone can find and join
// 1 = friends_only — Listed only to friends; anyone with the ID can join
// 2 = private      — Only joinable with the lobby ID
type LobbyVisibility = 0 | 1 | 2;

LobbyUser

interface LobbyUser {
  lobbyId: LobbyId;       // Lobby ID
  userId: UserId;          // User ID
  username: string;             // Display name
  userAvatarUrl?: string;       // Avatar URL
  isHost: boolean;              // Whether this user is the host
}

LobbyMessage

interface LobbyMessage {
  messageId: LobbyMessageId; // Message ID
  lobbyId: LobbyId;       // Lobby ID
  userId: UserId;          // Sender's ID
  username: string;             // Sender's name
  message: string;              // Message text
  timestamp: number;            // Timestamp
}

LobbyInvite

interface LobbyInvite {
  notificationId: LobbyInviteId; // Notification ID
  lobbyId: LobbyId;       // ID of the lobby you're invited to
  sender: {
    _id: UserId;           // User ID of who sent the invite
    username: string;           // Username of who sent the invite
    avatarUrl?: string;         // Avatar URL of the sender
  };
  _creationTime: number;        // When the invite was sent
}

Friend types

Friend

interface Friend {
  userId: UserId;   // Friend's user ID
  username: string;      // Friend's display name
  avatarUrl?: string;    // Avatar URL (if set)
  isOnline: boolean;     // Whether the friend is currently online
}

P2P types

P2PConnection

interface P2PConnection {
  lobbyId: LobbyId;
  peers: Record<UserId, P2PPeer>;
}

P2PPeer

interface P2PPeer {
  userId: UserId;   // Peer's user ID
  username: string;      // Peer's display name
}

P2PMessage

interface P2PMessage {
  fromUserId: UserId;   // Sender's user ID
  channel: number;           // Channel number (0-7)
  payload: Uint8Array;  // Binary data
}

UGC types

UGCType

// UGC types are integer values:
// 0 = SCREENSHOT    - Player-captured screenshot
// 1 = VIDEO         - Player-captured video
// 2 = COMMUNITY     - Community-created content (levels, mods, etc.)
// 3 = GAME_MANAGED  - Game-managed content (replays, saves)
// 4 = OTHER         - Other UGC types
type UGCType = 0 | 1 | 2 | 3 | 4;

UGCVisibility

// UGC visibility is an integer value:
// 0 = PUBLIC  - Anyone can view
// 2 = PRIVATE - Only the creator can view
type UGCVisibility = 0 | 2;

Purchase

One purchase, as delivered by the PURCHASE_COMPLETED event and returned by getUnfulfilledPurchases(). See Consumables.

interface Purchase {
  purchaseId: PurchaseId;       // Dedupe on this; pass it to fulfillPurchase
  contentIdentifier: string;    // The offer's content identifier
  type: PurchaseType;
  fulfilled: boolean;           // false for a consumable awaiting fulfillPurchase
  purchasedAt: number;          // Milliseconds since epoch
  receiptJwt: string;           // Signed receipt to verify on your backend
}

type PurchaseCompletedPayload = Purchase;

PurchaseType

type PurchaseType = "DURABLE" | "CONSUMABLE";

FulfillPurchaseResult

interface FulfillPurchaseResult {
  status: "FULFILLED" | "ALREADY_FULFILLED" | "NOT_FOUND";
}

Storage types

RemoteFileMetadata

interface RemoteFileMetadata {
  exists: boolean;     // Whether file exists
  key: string;         // Full storage key
  name: string;        // Filename
  lastModified: number; // Unix timestamp in seconds
  size: number;        // Size in bytes
  etag: string;        // Version identifier
}

Engine types

EngineInstance

Interface for game engine instances (set internally by the engine bindings and Wavedash's entrypoints — games don't normally touch it):

interface EngineInstance {
  type: "GODOT" | "UNITY" | "UNREAL" | "JSDOS" | "RUFFLE" | "RENPY" | "CUSTOM";

  // Send message to engine
  SendMessage(
    objectName: string,
    methodName: WavedashEvent,
    value?: string | number
  ): void;

  // Emscripten filesystem API
  FS: {
    readFile(path: string, opts?: Record<string, unknown>): string | Uint8Array;
    writeFile(path: string, data: string | ArrayBufferView, opts?: Record<string, unknown>): void;
    mkdirTree(path: string, mode?: number): void;
    syncfs(populate: boolean, callback?: (err: unknown) => void): void;
    analyzePath(path: string): { exists: boolean };
  };

  // Unity only: Application.persistentDataPath
  unityPersistentDataPath?: string;
}

JWT types

Both tokens are signed with RS256 by Wavedash's keys, published at https://auth.wavedash.com/.well-known/jwks.json. Verify them on your backend before trusting any claim. The SDK doesn't export these interfaces; they describe what a JWT library returns once verification passes.

UserJwtPayload

The payload of the token getUserJwt() returns. Verify it with issuer https://auth.wavedash.com and audience gameplay.wavedash.com. See Verifying the JWT on your backend.

// Header: { alg: "RS256", kid: string, typ: "JWT" }
interface UserJwtPayload {
  iss: "https://auth.wavedash.com";
  aud: "gameplay.wavedash.com";
  sub: string;      // Wavedash user ID: the player's stable identity, same as getUserId()
  scope: "gameplay";
  gbid: string;     // Game build ID the player is running
  r2key?: string;   // Storage key of the uploaded build (uploaded builds only)
  gcid: string;     // Game cloud ID: your game in one environment (playtest or production)
  ents: string[];   // Durable content identifiers the player owns (what isEntitled reads)
  iat: number;      // Issued at (seconds since epoch)
  nbf: number;      // Not before (seconds since epoch), 60 seconds before iat for clock skew
  exp: number;      // Expiry (seconds since epoch), 1 hour after iat
}

PurchaseReceiptPayload

The payload of a receiptJwt, from the PURCHASE_COMPLETED event or a purchase webhook. Verify it with issuer https://auth.wavedash.com, your game's ID as the audience, and header typ wavedash-purchase+jwt. See Verifying a receipt.

// Header: { alg: "RS256", kid: string, typ: "wavedash-purchase+jwt" }
interface PurchaseReceiptPayload {
  iss: "https://auth.wavedash.com";
  aud: string;               // Your game's ID (game_id in wavedash.toml)
  sub: string;               // Wavedash user ID of the buyer, same as the user JWT's sub
  env: "PRODUCTION" | "PLAYTEST";
  gcid: string;              // Game cloud ID, same as the user JWT's gcid in that environment
  event: "purchase.completed" | "purchase.refunded";
  purchaseId: PurchaseId;    // Same as Purchase.purchaseId; dedupe grants on it
  ptype: PurchaseType;    // "DURABLE" or "CONSUMABLE"
  contentIdentifier: string; // The offer that was bought
  created: number;           // When the event happened: the purchase, or the refund (seconds since epoch)
  iat: number;               // When this receipt was signed (seconds since epoch)
  jti?: string;              // Webhook deliveries only: the delivery ID, stable across retries and resends
}

Receipts have no exp, because a purchase stays valid proof of itself. Replaying one is harmless as long as you grant each purchaseId only once. A receipt stops verifying once Wavedash rotates out the key that signed it.