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:
| Type | Identifies |
|---|---|
UserId | A player |
LobbyId | A lobby |
LobbyMessageId | A lobby chat message |
LobbyInviteId | A lobby invite |
LeaderboardId | A leaderboard |
LeaderboardEntryId | A leaderboard entry |
UGCId | A user-generated content item |
PurchaseId | A 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.FULFILLEDResponse 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 whyChecking 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;Paid content types
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.