@remix-gg/sdk 0.7.0 → 0.8.1

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/dist/index.min.js CHANGED
@@ -31,75 +31,34 @@
31
31
  var exports_src = {};
32
32
  __export(exports_src, {
33
33
  sdk: () => sdk,
34
- parseGameEventMessage: () => parseGameEventMessage,
35
- createGameEventMessage: () => createGameEventMessage,
36
34
  ZERO_SAFE_AREA_INSET: () => ZERO_SAFE_AREA_INSET,
37
35
  RemixSDK: () => RemixSDK
38
36
  });
39
- var ZERO_SAFE_AREA_INSET = Object.freeze({
37
+ var ZERO_SAFE_AREA_INSET = {
40
38
  top: 0,
41
39
  right: 0,
42
40
  bottom: 0,
43
41
  left: 0
44
- });
45
- var createGameEventMessage = (type, data) => {
46
- return { type: "game_event", event: { type, data } };
47
- };
48
- var parseGameEventMessage = (value) => {
49
- if (typeof value !== "object" || value === null)
50
- return null;
51
- const message = value;
52
- if (message.type !== "game_event")
53
- return null;
54
- const event = message.event;
55
- if (typeof event !== "object" || event === null)
56
- return null;
57
- if (typeof event.type !== "string")
58
- return null;
59
- return event;
60
42
  };
61
- var READY_RESEND_INITIAL_DELAY_MS = 250;
62
- var READY_RESEND_MAX_ATTEMPTS = 5;
63
- var generateInstanceId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
64
- var isBrowserGameClient = () => typeof window !== "undefined" && typeof window.addEventListener === "function" && typeof window.removeEventListener === "function";
43
+ var SDK_VERSION = "0.8.1";
65
44
 
66
45
  class RemixSDK {
46
+ version = SDK_VERSION;
67
47
  isClient;
68
48
  target = null;
69
49
  eventListeners = new Map;
70
- readyPromise = null;
71
50
  readyPromiseResolve = null;
72
- purchasePromiseResolvers = [];
73
- readyResendTimer = null;
74
- instanceId = generateInstanceId();
51
+ purchasePromiseResolve = null;
75
52
  _gameInfo;
76
53
  _gameState;
77
- _levelStars = Object.freeze({});
78
54
  constructor() {
79
- this.isClient = isBrowserGameClient();
55
+ this.isClient = typeof window !== "undefined";
80
56
  this.target = this.isClient ? window.parent : null;
81
57
  if (this.isClient) {
82
58
  window.addEventListener("message", this.handleMessage);
83
59
  window.addEventListener("error", this.handleGlobalError);
84
60
  window.addEventListener("unhandledrejection", this.handleUnhandledRejection);
85
- this.sendMessage("ready", { instanceId: this.instanceId });
86
- this.scheduleReadyResend(1);
87
- }
88
- }
89
- scheduleReadyResend(attempt) {
90
- if (attempt > READY_RESEND_MAX_ATTEMPTS)
91
- return;
92
- this.readyResendTimer = setTimeout(() => {
93
- if (this._gameInfo)
94
- return;
95
- this.sendMessage("ready", { instanceId: this.instanceId });
96
- this.scheduleReadyResend(attempt + 1);
97
- }, READY_RESEND_INITIAL_DELAY_MS * 2 ** (attempt - 1));
98
- }
99
- cancelReadyResend() {
100
- if (this.readyResendTimer !== null) {
101
- clearTimeout(this.readyResendTimer);
102
- this.readyResendTimer = null;
61
+ this.sendMessage("ready", undefined);
103
62
  }
104
63
  }
105
64
  on(eventType, callback) {
@@ -107,28 +66,27 @@
107
66
  this.eventListeners.set(eventType, new Set);
108
67
  }
109
68
  this.eventListeners.get(eventType)?.add(callback);
110
- return () => this.off(eventType, callback);
111
69
  }
112
70
  off(eventType, callback) {
113
71
  this.eventListeners.get(eventType)?.delete(callback);
114
72
  }
115
73
  onPlay(callback) {
116
- return this.on("play_again", (data) => callback(data));
74
+ this.on("play_again", (data) => callback(data));
117
75
  }
118
76
  onPlayAgain(callback) {
119
- return this.onPlay(callback);
77
+ this.onPlay(callback);
120
78
  }
121
79
  onToggleMute(callback) {
122
- return this.on("toggle_mute", callback);
80
+ this.on("toggle_mute", (data) => callback(data));
123
81
  }
124
82
  onGameStateUpdated(callback) {
125
- return this.on("game_state_updated", callback);
83
+ this.on("game_state_updated", (data) => callback(data));
126
84
  }
127
85
  onGameInfo(callback) {
128
- return this.on("game_info", callback);
86
+ this.on("game_info", (data) => callback(data));
129
87
  }
130
88
  onPurchaseComplete(callback) {
131
- return this.on("purchase_complete", callback);
89
+ this.on("purchase_complete", (data) => callback(data));
132
90
  }
133
91
  setTarget(target) {
134
92
  this.target = target;
@@ -174,7 +132,15 @@
174
132
  return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
175
133
  }
176
134
  get levelStars() {
177
- return this._levelStars;
135
+ const raw = this._gameInfo?.levelBased?.progress?.levelStars;
136
+ const levelStars = {};
137
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
138
+ for (const [key, starValue] of Object.entries(raw)) {
139
+ if (starValue === 1 || starValue === 2 || starValue === 3)
140
+ levelStars[key] = starValue;
141
+ }
142
+ }
143
+ return levelStars;
178
144
  }
179
145
  get highestUnlockedLevel() {
180
146
  const value = this._gameInfo?.levelBased?.progress?.highestUnlockedLevel;
@@ -187,17 +153,14 @@
187
153
  if (this._gameInfo) {
188
154
  return Promise.resolve(this._gameInfo);
189
155
  }
190
- if (!this.readyPromise) {
191
- this.readyPromise = new Promise((resolve) => {
192
- this.readyPromiseResolve = resolve;
193
- });
194
- }
195
- return this.readyPromise;
156
+ return new Promise((resolve) => {
157
+ this.readyPromiseResolve = resolve;
158
+ });
196
159
  };
197
160
  purchase = (data) => {
198
161
  this.sendMessage("purchase", data);
199
162
  return new Promise((resolve) => {
200
- this.purchasePromiseResolvers.push(resolve);
163
+ this.purchasePromiseResolve = resolve;
201
164
  });
202
165
  };
203
166
  reportError = (data) => {
@@ -248,16 +211,6 @@
248
211
  if (eventType === "game_info") {
249
212
  const eventData = data;
250
213
  this._gameInfo = eventData;
251
- const rawLevelStars = eventData.levelBased?.progress?.levelStars;
252
- const levelStars = {};
253
- if (rawLevelStars && typeof rawLevelStars === "object" && !Array.isArray(rawLevelStars)) {
254
- for (const [key, starValue] of Object.entries(rawLevelStars)) {
255
- if (starValue === 1 || starValue === 2 || starValue === 3)
256
- levelStars[key] = starValue;
257
- }
258
- }
259
- this._levelStars = Object.freeze(levelStars);
260
- this.cancelReadyResend();
261
214
  if (!this._gameState && eventData.initialGameState) {
262
215
  this._gameState = eventData.initialGameState.gameState;
263
216
  }
@@ -271,9 +224,9 @@
271
224
  if (eventData.success && eventData.item && this._gameInfo?.player) {
272
225
  this.applyPurchaseToCurrentPlayer(eventData.item);
273
226
  }
274
- const resolvePurchase = this.purchasePromiseResolvers.shift();
275
- if (resolvePurchase) {
276
- resolvePurchase(eventData);
227
+ if (this.purchasePromiseResolve) {
228
+ this.purchasePromiseResolve(eventData);
229
+ this.purchasePromiseResolve = null;
277
230
  }
278
231
  }
279
232
  if (eventType === "game_state_updated") {
@@ -289,12 +242,9 @@
289
242
  }
290
243
  }
291
244
  handleMessage = (event) => {
292
- if (event.source && event.source !== this.target)
245
+ if (event.data?.type !== "game_event")
293
246
  return;
294
- const gameEvent = parseGameEventMessage(event.data);
295
- if (!gameEvent)
296
- return;
297
- this.emit(gameEvent.type, gameEvent.data);
247
+ this.emit(event.data.event.type, event.data.event.data);
298
248
  };
299
249
  sendMessage = (type, data) => {
300
250
  if (!this.isClient || !this.target)
@@ -308,16 +258,14 @@
308
258
  source: event.filename,
309
259
  lineno: event.lineno,
310
260
  colno: event.colno,
311
- error: event.error,
312
- stack: event.error instanceof Error ? event.error.stack : undefined
261
+ error: event.error
313
262
  });
314
263
  };
315
264
  handleUnhandledRejection = (event) => {
316
265
  const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
317
266
  this.sendMessage("error", {
318
267
  message: error.message,
319
- error,
320
- stack: error.stack
268
+ error
321
269
  });
322
270
  };
323
271
  applyPurchaseToCurrentPlayer(item) {
@@ -325,18 +273,21 @@
325
273
  return;
326
274
  const currentPlayer = this._gameInfo.player;
327
275
  currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
328
- for (const player of this._gameInfo.players) {
329
- if (player !== currentPlayer && player.id === currentPlayer.id) {
330
- player.purchasedItems = currentPlayer.purchasedItems;
331
- }
332
- }
276
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
277
+ if (player.id !== currentPlayer.id)
278
+ return player;
279
+ return {
280
+ ...player,
281
+ purchasedItems: currentPlayer.purchasedItems
282
+ };
283
+ });
333
284
  }
334
285
  }
335
286
  var sdk = new RemixSDK;
336
- if (isBrowserGameClient()) {
287
+ if (typeof window !== "undefined") {
337
288
  window.FarcadeSDK = sdk;
338
289
  window.RemixSDK = sdk;
339
290
  }
340
291
  })();
341
292
 
342
- //# debugId=7A69B8A8B828F7ED64756E2164756E21
293
+ //# debugId=2E2F1C2C1ACE218064756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
4
  "sourcesContent": [
5
- "declare global {\n interface Window {\n FarcadeSDK: typeof sdk\n RemixSDK: typeof sdk\n }\n}\n\nexport type ViewContext = 'feed' | 'full_screen' | 'challenge' | 'tournament'\n\nexport type SafeAreaInset = {\n top: number\n right: number\n bottom: number\n left: number\n}\n\nexport const ZERO_SAFE_AREA_INSET: SafeAreaInset = Object.freeze({\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n})\n\nexport type GameState = Record<string, unknown>\n\nexport type Player = {\n id: string\n name: string\n purchasedItems: string[]\n imageUrl?: string\n}\n\nexport type InventoryItem = {\n slug: string\n quantity: number\n}\n\nexport type ShopItem = {\n slug: string\n name: string\n itemType?: string\n bitsCost?: number | null\n description?: string | null\n iconUrl?: string | null\n tier?: number | null\n}\n\nexport type GameInfo = {\n players: Player[]\n player: Player\n shopItems?: ShopItem[]\n viewContext: ViewContext\n contentSafeAreaInset: SafeAreaInset\n initialGameState: {\n id: string\n gameState: GameState\n } | null\n levelBased?: {\n levelCount: number\n /**\n * Score-derived level progression for this player. Platform-owned and kept\n * fully separate from `gameState` — never read progression out of gameState.\n */\n progress: LevelProgressState\n }\n}\n\n/** Score-derived, platform-owned progression for a level-based game. */\nexport type LevelProgressState = {\n currentLevelIndex: number\n highestUnlockedLevel: number\n levelStars: Record<string, LevelStars>\n}\n\nexport type ReadyEvent = {\n type: 'ready'\n // instanceId identifies one SDK page load so hosts can dedupe re-sent ready\n // messages; legacy SDK versions send undefined\n data: { instanceId: string } | undefined\n}\n\nexport type PlayAgainEvent = {\n type: 'play_again'\n data?: {\n levelIndex?: number\n }\n}\n\nexport type LevelStars = 1 | 2 | 3\n\nexport type LevelOutcome = 'level_complete' | 'level_failed' | 'all_complete'\n\n/**\n * One level attempt reported with game_over. Games report only what happened\n * in the attempt; the host owns and derives all cumulative progression\n * (star maps, unlock state) — see `LevelProgressState` for the host→game snapshot.\n */\nexport type LevelAttempt = {\n /** 1-based index of the level that was just played. */\n levelIndex: number\n /** Stars earned this attempt. Omit for star-less games; a completion counts as 1. */\n stars?: LevelStars\n outcome: LevelOutcome\n}\n\nexport type SinglePlayerGameOverEvent = {\n type: 'game_over'\n data: {\n score: number\n levelAttempt?: LevelAttempt\n }\n}\n\nexport type MultiplayerGameOverEvent = {\n type: 'multiplayer_game_over'\n data: {\n scores: {\n playerId: string\n score: number\n }[]\n }\n}\n\nexport type HapticFeedbackType = 'light' | 'medium' | 'hard' | 'success' | 'error'\n\nexport type HapticFeedbackEvent = {\n type: 'haptic_feedback'\n data?: { type?: HapticFeedbackType }\n}\n\nexport type ToggleMuteEvent = {\n type: 'toggle_mute'\n data: {\n isMuted: boolean\n }\n}\n\nexport type GameErrorEvent = {\n type: 'error'\n data: {\n message: string\n source?: string\n lineno?: number\n colno?: number\n error?: Error\n // Error objects do not survive JSON serialization (React Native WebView\n // hosts), so the stack is also sent as a plain string\n stack?: string\n }\n}\n\nexport type GameInfoEvent = {\n type: 'game_info'\n data: GameInfo\n}\n\nexport type MultiplayerSaveGameStateEvent = {\n type: 'multiplayer_save_game_state'\n data: {\n gameState: GameState\n alertUserIds?: string[]\n }\n}\n\nexport type GameStateUpdatedEvent = {\n type: 'game_state_updated'\n data: {\n id: string\n gameState: GameState\n } | null\n}\n\nexport type RefuteGameStateEvent = {\n type: 'refute_game_state'\n data: {\n gameStateId: string\n }\n}\n\nexport type SaveGameStateEvent = {\n type: 'save_game_state'\n data: {\n gameState: GameState\n }\n}\n\nexport type PurchaseEvent = {\n type: 'purchase'\n data: {\n item: string\n }\n}\n\nexport type PurchaseCompleteEvent = {\n type: 'purchase_complete'\n data: {\n success: boolean\n item?: string\n }\n}\n\nexport type GameEvent =\n | PlayAgainEvent\n | SinglePlayerGameOverEvent\n | ReadyEvent\n | HapticFeedbackEvent\n | ToggleMuteEvent\n | GameErrorEvent\n | SaveGameStateEvent\n | RefuteGameStateEvent\n | GameInfoEvent\n | GameStateUpdatedEvent\n | MultiplayerGameOverEvent\n | MultiplayerSaveGameStateEvent\n | PurchaseEvent\n | PurchaseCompleteEvent\n\nexport type GameEventMessage<T extends GameEvent['type']> = {\n type: 'game_event'\n event: Extract<GameEvent, { type: T }>\n}\n\n/**\n * Build a game_event envelope for sending over postMessage. Hosts and the SDK\n * share this so the wire format is defined in one place.\n */\nexport const createGameEventMessage = <T extends GameEvent['type']>(\n type: T,\n data: Extract<GameEvent, { type: T }>['data'],\n): GameEventMessage<T> => {\n return { type: 'game_event', event: { type, data } } as GameEventMessage<T>\n}\n\n/**\n * Parse an unknown postMessage payload into a GameEvent, or null when the\n * payload is not a well-formed game_event envelope.\n */\nexport const parseGameEventMessage = (value: unknown): GameEvent | null => {\n if (typeof value !== 'object' || value === null) return null\n const message = value as { type?: unknown; event?: unknown }\n if (message.type !== 'game_event') return null\n const event = message.event\n if (typeof event !== 'object' || event === null) return null\n if (typeof (event as { type?: unknown }).type !== 'string') return null\n return event as GameEvent\n}\n\n/**\n * Messages from the game host to the game client\n */\nexport type IncomingGameEvent = GameEventMessage<\n 'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'\n>\n\nexport type OutgoingGameEvent = GameEventMessage<\n | 'game_over'\n | 'ready'\n | 'haptic_feedback'\n | 'error'\n | 'save_game_state'\n | 'refute_game_state'\n | 'multiplayer_game_over'\n | 'multiplayer_save_game_state'\n | 'purchase'\n>\n\ntype EventCallback = (data: unknown) => void\n\nexport type IncomingGameEventType = IncomingGameEvent['event']['type']\n\nexport type IncomingGameEventData<T extends IncomingGameEventType> = Extract<\n GameEvent,\n { type: T }\n>['data']\n\nconst READY_RESEND_INITIAL_DELAY_MS = 250\nconst READY_RESEND_MAX_ATTEMPTS = 5\n\nconst generateInstanceId = (): string =>\n typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2)\n\n/** True only in a real browser iframe client (not React Native / SSR shims). */\nconst isBrowserGameClient = (): boolean =>\n typeof window !== 'undefined' &&\n typeof window.addEventListener === 'function' &&\n typeof window.removeEventListener === 'function'\n\nexport class RemixSDK {\n private isClient: boolean\n private target: Window | null = null\n private eventListeners: Map<string, Set<EventCallback>> = new Map()\n private readyPromise: Promise<GameInfo> | null = null\n private readyPromiseResolve: ((gameInfo: GameInfo) => void) | null = null\n private purchasePromiseResolvers: Array<(data: PurchaseCompleteEvent['data']) => void> = []\n private readyResendTimer: ReturnType<typeof setTimeout> | null = null\n private readonly instanceId = generateInstanceId()\n private _gameInfo?: GameInfo\n private _gameState?: GameState | null\n private _levelStars: Record<string, LevelStars> = Object.freeze({})\n\n constructor() {\n this.isClient = isBrowserGameClient()\n this.target = this.isClient ? window.parent : null\n\n if (this.isClient) {\n window.addEventListener('message', this.handleMessage)\n\n // Set up global error handler\n window.addEventListener('error', this.handleGlobalError)\n window.addEventListener('unhandledrejection', this.handleUnhandledRejection)\n\n // send the ready message to the game host\n this.sendMessage('ready', { instanceId: this.instanceId })\n\n // The host may not have registered its message listener for this game\n // yet (the SDK runs at import time), so re-send ready with backoff\n // until game_info arrives. Hosts dedupe repeats via instanceId.\n this.scheduleReadyResend(1)\n }\n }\n\n private scheduleReadyResend(attempt: number) {\n if (attempt > READY_RESEND_MAX_ATTEMPTS) return\n\n this.readyResendTimer = setTimeout(\n () => {\n if (this._gameInfo) return\n this.sendMessage('ready', { instanceId: this.instanceId })\n this.scheduleReadyResend(attempt + 1)\n },\n READY_RESEND_INITIAL_DELAY_MS * 2 ** (attempt - 1),\n )\n }\n\n private cancelReadyResend() {\n if (this.readyResendTimer !== null) {\n clearTimeout(this.readyResendTimer)\n this.readyResendTimer = null\n }\n }\n\n on<T extends IncomingGameEventType>(\n eventType: T,\n callback: (data: IncomingGameEventData<T>) => void,\n ): () => void {\n if (!this.eventListeners.has(eventType)) {\n this.eventListeners.set(eventType, new Set())\n }\n this.eventListeners.get(eventType)?.add(callback as EventCallback)\n return () => this.off(eventType, callback)\n }\n\n off<T extends IncomingGameEventType>(\n eventType: T,\n callback: (data: IncomingGameEventData<T>) => void,\n ) {\n this.eventListeners.get(eventType)?.delete(callback as EventCallback)\n }\n\n // explicitly named event listeners for better understanding and type safety;\n // each returns an unsubscribe function\n /**\n * Listen for the host starting play: a replay after game over, or — for level\n * games — a host-directed level via `data.levelIndex` (Studio continue/jump or\n * the production next-level button). `data` may be omitted for a plain restart.\n */\n onPlay(callback: (data?: PlayAgainEvent['data']) => void): () => void {\n return this.on('play_again', (data) => callback(data as PlayAgainEvent['data']))\n }\n\n /** @deprecated Use {@link onPlay}. Retained for backward compatibility. */\n onPlayAgain(callback: (data?: PlayAgainEvent['data']) => void): () => void {\n return this.onPlay(callback)\n }\n\n onToggleMute(callback: (data: ToggleMuteEvent['data']) => void): () => void {\n return this.on('toggle_mute', callback)\n }\n\n onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void): () => void {\n return this.on('game_state_updated', callback)\n }\n\n onGameInfo(callback: (data: GameInfoEvent['data']) => void): () => void {\n return this.on('game_info', callback)\n }\n\n onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): () => void {\n return this.on('purchase_complete', callback)\n }\n // end of explicitly named event listeners\n\n setTarget(target: Window) {\n this.target = target\n }\n\n get purchasedItems(): string[] {\n return this._gameInfo?.player.purchasedItems || []\n }\n\n get inventory(): InventoryItem[] {\n const counts = new Map<string, number>()\n for (const item of this.purchasedItems) {\n counts.set(item, (counts.get(item) || 0) + 1)\n }\n return [...counts.entries()].map(([slug, quantity]) => ({ slug, quantity }))\n }\n\n get shopItems(): ShopItem[] {\n return this._gameInfo?.shopItems || []\n }\n\n get gameInfo(): GameInfo | undefined {\n return this._gameInfo\n }\n\n get gameState(): GameState | null | undefined {\n return this._gameState\n }\n\n get players(): Player[] | undefined {\n return this._gameInfo?.players\n }\n\n get player(): Player | undefined {\n return this._gameInfo?.player\n }\n\n get isReady(): boolean {\n return !!this._gameInfo\n }\n\n /** True when the host configured this game with a fixed level count. */\n get isLevelBased(): boolean {\n const levelCount = this._gameInfo?.levelBased?.levelCount\n return levelCount != null && levelCount > 0\n }\n\n /** Total levels for level-based games (from host metadata). */\n get levelCount(): number | undefined {\n const levelCount = this._gameInfo?.levelBased?.levelCount\n return levelCount != null && levelCount > 0 ? levelCount : undefined\n }\n\n /** 1-based level index from score-derived progression, defaulting to 1. */\n get currentLevelIndex(): number {\n const value = this._gameInfo?.levelBased?.progress?.currentLevelIndex\n return typeof value === 'number' && Number.isFinite(value) && value >= 1 ? value : 1\n }\n\n /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */\n get levelStars(): Record<string, LevelStars> {\n return this._levelStars\n }\n\n /** Highest unlocked level from score-derived progression, defaulting to 1. */\n get highestUnlockedLevel(): number {\n const value = this._gameInfo?.levelBased?.progress?.highestUnlockedLevel\n return typeof value === 'number' && Number.isFinite(value) && value >= 1 ? value : 1\n }\n\n /** Sum of best stars across all completed levels. */\n get totalStars(): number {\n return Object.values(this.levelStars).reduce((sum, stars) => sum + stars, 0)\n }\n\n ready = (): Promise<GameInfo> => {\n // if the game info is already set, return it immediately\n if (this._gameInfo) {\n return Promise.resolve(this._gameInfo)\n }\n\n // internal shared promise allows Remix to return GameInfo as a response to\n // the ready message; every ready() caller gets the same promise so\n // concurrent calls all resolve\n if (!this.readyPromise) {\n this.readyPromise = new Promise((resolve) => {\n this.readyPromiseResolve = resolve\n })\n }\n return this.readyPromise\n }\n\n purchase = (data: PurchaseEvent['data']): Promise<PurchaseCompleteEvent['data']> => {\n this.sendMessage('purchase', data)\n\n // internal resolver queue allows Remix to return PurchaseCompleteEvent data\n // as a response to the purchase message; purchase_complete events resolve\n // pending purchases in request order\n return new Promise((resolve) => {\n this.purchasePromiseResolvers.push(resolve)\n })\n }\n\n reportError = (data: GameErrorEvent['data']) => {\n this.sendMessage('error', data)\n }\n\n hapticFeedback = (type?: HapticFeedbackType) => {\n this.sendMessage('haptic_feedback', type ? { type } : undefined)\n }\n\n hasItem = (item: string): boolean => {\n return this.getItemPurchaseCount(item) > 0\n }\n\n getItemPurchaseCount = (item: string): number => {\n const inventoryItem = this.inventory.find((entry) => entry.slug === item)\n return inventoryItem?.quantity || 0\n }\n\n getShopItem = (slug: string): ShopItem | undefined => {\n return this.shopItems.find((item) => item.slug === slug)\n }\n\n singlePlayer = {\n actions: {\n ready: this.ready,\n hapticFeedback: this.hapticFeedback,\n reportError: this.reportError,\n purchase: this.purchase,\n gameOver: (data: SinglePlayerGameOverEvent['data']) => {\n this.sendMessage('game_over', data)\n },\n saveGameState: (data: SaveGameStateEvent['data']) => {\n this.sendMessage('save_game_state', data)\n },\n },\n }\n\n multiplayer = {\n actions: {\n ...this.singlePlayer.actions,\n gameOver: (data: MultiplayerGameOverEvent['data']) => {\n this.sendMessage('multiplayer_game_over', data)\n },\n refuteGameState: (data: RefuteGameStateEvent['data']) => {\n this.sendMessage('refute_game_state', data)\n },\n saveGameState: (data: MultiplayerSaveGameStateEvent['data']) => {\n this.sendMessage('multiplayer_save_game_state', data)\n },\n },\n }\n\n private emit(eventType: IncomingGameEventType, data: unknown) {\n // Handle game_info specially to resolve the ready promise\n if (eventType === 'game_info') {\n const eventData = data as GameInfoEvent['data']\n this._gameInfo = eventData\n const rawLevelStars = eventData.levelBased?.progress?.levelStars\n const levelStars: Record<string, LevelStars> = {}\n if (rawLevelStars && typeof rawLevelStars === 'object' && !Array.isArray(rawLevelStars)) {\n for (const [key, starValue] of Object.entries(rawLevelStars)) {\n if (starValue === 1 || starValue === 2 || starValue === 3) levelStars[key] = starValue\n }\n }\n this._levelStars = Object.freeze(levelStars)\n this.cancelReadyResend()\n\n // Set the game state if it exists and is not already set\n if (!this._gameState && eventData.initialGameState) {\n this._gameState = eventData.initialGameState.gameState\n }\n\n if (this.readyPromiseResolve) {\n this.readyPromiseResolve(this._gameInfo)\n this.readyPromiseResolve = null\n }\n }\n\n // Handle purchase_complete specially to resolve the purchase promise\n if (eventType === 'purchase_complete') {\n const eventData = data as PurchaseCompleteEvent['data']\n\n if (eventData.success && eventData.item && this._gameInfo?.player) {\n this.applyPurchaseToCurrentPlayer(eventData.item)\n }\n\n const resolvePurchase = this.purchasePromiseResolvers.shift()\n if (resolvePurchase) {\n resolvePurchase(eventData)\n }\n }\n\n if (eventType === 'game_state_updated') {\n const eventData = data as GameStateUpdatedEvent['data']\n if (eventData) {\n this._gameState = eventData.gameState\n } else {\n this._gameState = null\n }\n }\n\n for (const callback of this.eventListeners.get(eventType) || []) {\n callback(data)\n }\n }\n\n private handleMessage = (event: MessageEvent<IncomingGameEvent>) => {\n // Only accept messages from the host target. Synthetic events without a\n // source window are allowed because React Native WebView hosts deliver\n // messages via dispatched MessageEvents that have no source.\n if (event.source && event.source !== this.target) return\n\n const gameEvent = parseGameEventMessage(event.data)\n if (!gameEvent) return\n\n this.emit(gameEvent.type as IncomingGameEventType, gameEvent.data)\n }\n\n private sendMessage = <T extends GameEvent['type']>(\n type: T,\n data: Extract<GameEvent, { type: T }>['data'],\n ) => {\n if (!this.isClient || !this.target) return\n // built inline rather than via createGameEventMessage because TS cannot\n // relate the generic indexed-access parameter types across the call\n const gameEvent = { type: 'game_event', event: { type, data } } as GameEventMessage<T>\n this.target.postMessage(gameEvent, '*')\n }\n\n private handleGlobalError = (event: globalThis.ErrorEvent) => {\n // Send both formats for backward compatibility\n this.sendMessage('error', {\n message: event.message || 'Unknown error',\n source: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n error: event.error,\n stack: event.error instanceof Error ? event.error.stack : undefined,\n })\n }\n\n private handleUnhandledRejection = (event: PromiseRejectionEvent) => {\n const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason))\n\n // Send both formats for backward compatibility\n this.sendMessage('error', {\n message: error.message,\n error,\n stack: error.stack,\n })\n }\n\n private applyPurchaseToCurrentPlayer(item: string) {\n if (!this._gameInfo) return\n\n const currentPlayer = this._gameInfo.player\n\n // Update in place so references games already hold (sdk.player, entries of\n // sdk.players) observe the new purchase\n currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item]\n\n for (const player of this._gameInfo.players) {\n if (player !== currentPlayer && player.id === currentPlayer.id) {\n player.purchasedItems = currentPlayer.purchasedItems\n }\n }\n }\n}\n\n// Initialize and add to window\nexport const sdk = new RemixSDK()\n\n// Add SDK to window object for easier access in HTML games\nif (isBrowserGameClient()) {\n window.FarcadeSDK = sdk\n window.RemixSDK = sdk\n}\n"
5
+ "declare global {\n interface Window {\n FarcadeSDK: typeof sdk\n RemixSDK: typeof sdk\n }\n}\n\nexport type ViewContext = 'feed' | 'full_screen' | 'challenge' | 'tournament'\n\nexport type SafeAreaInset = {\n top: number\n right: number\n bottom: number\n left: number\n}\n\nexport const ZERO_SAFE_AREA_INSET: SafeAreaInset = {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n}\n\nexport type GameState = Record<string, unknown>\n\nexport type Player = {\n id: string\n name: string\n purchasedItems: string[]\n imageUrl?: string\n}\n\nexport type InventoryItem = {\n slug: string\n quantity: number\n}\n\nexport type ShopItem = {\n slug: string\n name: string\n itemType?: string\n bitsCost?: number | null\n description?: string | null\n iconUrl?: string | null\n tier?: number | null\n}\n\nexport type GameInfo = {\n players: Player[]\n player: Player\n shopItems?: ShopItem[]\n viewContext: ViewContext\n contentSafeAreaInset: SafeAreaInset\n initialGameState: {\n id: string\n gameState: GameState\n } | null\n /**\n * Present only when the host has level progression enabled for this session.\n * Games must key level mode off this block's presence, not their own level\n * content — the platform can disable it (e.g. feature gating) at any time.\n */\n levelBased?: {\n /** Canonical level total configured on the platform at upload. */\n levelCount: number\n /** Score-derived, platform-owned progression — never read from gameState. */\n progress: LevelProgressState\n }\n}\n\nexport type LevelStars = 1 | 2 | 3\n\n/** Stars for one level attempt: 0 = failed, 1–3 = completed with that rating. */\nexport type LevelAttemptStars = 0 | LevelStars\n\n/**\n * One level attempt reported with game_over. Games report only what happened\n * in the attempt; the host owns and derives all cumulative progression.\n */\nexport type LevelAttempt = {\n /** 1-based index of the level that was just played. */\n levelIndex: number\n /** Stars earned this attempt; 0 means the level was failed. */\n stars: LevelAttemptStars\n}\n\n/** Score-derived, platform-owned progression for a level-based game. */\nexport type LevelProgressState = {\n currentLevelIndex: number\n highestUnlockedLevel: number\n levelStars: Record<string, LevelStars>\n}\n\nexport type ReadyEvent = {\n type: 'ready'\n data: undefined\n}\n\nexport type PlayAgainEvent = {\n type: 'play_again'\n data?: {\n levelIndex?: number\n }\n}\n\nexport type SinglePlayerGameOverEvent = {\n type: 'game_over'\n data: {\n score: number\n levelAttempt?: LevelAttempt\n }\n}\n\nexport type MultiplayerGameOverEvent = {\n type: 'multiplayer_game_over'\n data: {\n scores: {\n playerId: string\n score: number\n }[]\n }\n}\n\nexport type HapticFeedbackType = 'light' | 'medium' | 'hard' | 'success' | 'error'\n\nexport type HapticFeedbackEvent = {\n type: 'haptic_feedback'\n data?: { type?: HapticFeedbackType }\n}\n\nexport type ToggleMuteEvent = {\n type: 'toggle_mute'\n data: {\n isMuted: boolean\n }\n}\n\nexport type GameErrorEvent = {\n type: 'error'\n data: {\n message: string\n source?: string\n lineno?: number\n colno?: number\n error?: Error\n }\n}\n\nexport type GameInfoEvent = {\n type: 'game_info'\n data: GameInfo\n}\n\nexport type MultiplayerSaveGameStateEvent = {\n type: 'multiplayer_save_game_state'\n data: {\n gameState: GameState\n alertUserIds?: string[]\n }\n}\n\nexport type GameStateUpdatedEvent = {\n type: 'game_state_updated'\n data: {\n id: string\n gameState: GameState\n } | null\n}\n\nexport type RefuteGameStateEvent = {\n type: 'refute_game_state'\n data: {\n gameStateId: string\n }\n}\n\nexport type SaveGameStateEvent = {\n type: 'save_game_state'\n data: {\n gameState: GameState\n }\n}\n\nexport type PurchaseEvent = {\n type: 'purchase'\n data: {\n item: string\n }\n}\n\nexport type PurchaseCompleteEvent = {\n type: 'purchase_complete'\n data: {\n success: boolean\n item?: string\n }\n}\n\nexport type GameEvent =\n | PlayAgainEvent\n | SinglePlayerGameOverEvent\n | ReadyEvent\n | HapticFeedbackEvent\n | ToggleMuteEvent\n | GameErrorEvent\n | SaveGameStateEvent\n | RefuteGameStateEvent\n | GameInfoEvent\n | GameStateUpdatedEvent\n | MultiplayerGameOverEvent\n | MultiplayerSaveGameStateEvent\n | PurchaseEvent\n | PurchaseCompleteEvent\n\nexport type GameEventMessage<T extends GameEvent['type']> = {\n type: 'game_event'\n event: Extract<GameEvent, { type: T }>\n}\n\n/**\n * Messages from the game host to the game client\n */\nexport type IncomingGameEvent = GameEventMessage<\n 'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'\n>\n\nexport type OutgoingGameEvent = GameEventMessage<\n | 'game_over'\n | 'ready'\n | 'haptic_feedback'\n | 'error'\n | 'save_game_state'\n | 'refute_game_state'\n | 'multiplayer_game_over'\n | 'multiplayer_save_game_state'\n | 'purchase'\n>\n\ntype EventCallback = (data: unknown) => void\n\n/**\n * Replaced with the published package version at build time by both builders\n * (`scripts/build.ts` for the IIFE bundle, `tsup.config.ts` for ESM/CJS).\n *\n * Read through `typeof` so an un-substituted identifier cannot throw a\n * ReferenceError at runtime — a broken build degrades to `UNKNOWN_SDK_VERSION`\n * instead of taking every game down on the first line it evaluates.\n */\ndeclare const __REMIX_SDK_VERSION__: string\n\nconst UNKNOWN_SDK_VERSION = '0.0.0-unknown'\n\nconst SDK_VERSION: string =\n typeof __REMIX_SDK_VERSION__ === 'string' ? __REMIX_SDK_VERSION__ : UNKNOWN_SDK_VERSION\n\nexport class RemixSDK {\n /**\n * The published version of this bundle. The host compares it against the\n * version it asked the browser to load, so a cached bundle from a previous\n * release can be detected before any game code runs.\n */\n readonly version: string = SDK_VERSION\n\n private isClient: boolean\n private target: Window | null = null\n private eventListeners: Map<string, Set<EventCallback>> = new Map()\n private readyPromiseResolve: ((gameInfo: GameInfo) => void) | null = null\n private purchasePromiseResolve: ((data: PurchaseCompleteEvent['data']) => void) | null = null\n private _gameInfo?: GameInfo\n private _gameState?: GameState | null\n\n constructor() {\n this.isClient = typeof window !== 'undefined'\n this.target = this.isClient ? window.parent : null\n\n if (this.isClient) {\n window.addEventListener('message', this.handleMessage)\n\n // Set up global error handler\n window.addEventListener('error', this.handleGlobalError)\n window.addEventListener('unhandledrejection', this.handleUnhandledRejection)\n\n // send the ready message to the game host\n this.sendMessage('ready', undefined)\n }\n }\n\n on(eventType: IncomingGameEvent['event']['type'], callback: EventCallback) {\n if (!this.eventListeners.has(eventType)) {\n this.eventListeners.set(eventType, new Set())\n }\n this.eventListeners.get(eventType)?.add(callback)\n }\n\n off(eventType: IncomingGameEvent['event']['type'], callback: EventCallback) {\n this.eventListeners.get(eventType)?.delete(callback)\n }\n\n // explicitly named event listeners for better understanding and type safety\n /**\n * Listen for the host starting play: a replay after game over, or — for level\n * games — a host-directed level via `data.levelIndex`. `data` may be omitted\n * for a plain restart.\n */\n onPlay(callback: (data?: PlayAgainEvent['data']) => void) {\n this.on('play_again', (data) => callback(data as PlayAgainEvent['data']))\n }\n\n /** Alias of {@link onPlay}. */\n onPlayAgain(callback: (data?: PlayAgainEvent['data']) => void) {\n this.onPlay(callback)\n }\n\n onToggleMute(callback: (data: ToggleMuteEvent['data']) => void) {\n this.on('toggle_mute', (data) => callback(data as ToggleMuteEvent['data']))\n }\n\n onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void) {\n this.on('game_state_updated', (data) => callback(data as GameStateUpdatedEvent['data']))\n }\n\n onGameInfo(callback: (data: GameInfoEvent['data']) => void) {\n this.on('game_info', (data) => callback(data as GameInfoEvent['data']))\n }\n\n onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void) {\n this.on('purchase_complete', (data) => callback(data as PurchaseCompleteEvent['data']))\n }\n // end of explicitly named event listeners\n\n setTarget(target: Window) {\n this.target = target\n }\n\n get purchasedItems(): string[] {\n return this._gameInfo?.player.purchasedItems || []\n }\n\n get inventory(): InventoryItem[] {\n const counts = new Map<string, number>()\n for (const item of this.purchasedItems) {\n counts.set(item, (counts.get(item) || 0) + 1)\n }\n return [...counts.entries()].map(([slug, quantity]) => ({ slug, quantity }))\n }\n\n get shopItems(): ShopItem[] {\n return this._gameInfo?.shopItems || []\n }\n\n get gameInfo(): GameInfo | undefined {\n return this._gameInfo\n }\n\n get gameState(): GameState | null | undefined {\n return this._gameState\n }\n\n get players(): Player[] | undefined {\n return this._gameInfo?.players\n }\n\n get player(): Player | undefined {\n return this._gameInfo?.player\n }\n\n get isReady(): boolean {\n return !!this._gameInfo\n }\n\n /** True when the host configured this game with a fixed level count. */\n get isLevelBased(): boolean {\n const levelCount = this._gameInfo?.levelBased?.levelCount\n return levelCount != null && levelCount > 0\n }\n\n /** Total levels for level-based games (from host metadata). */\n get levelCount(): number | undefined {\n const levelCount = this._gameInfo?.levelBased?.levelCount\n return levelCount != null && levelCount > 0 ? levelCount : undefined\n }\n\n /** 1-based level index from score-derived progression, defaulting to 1. */\n get currentLevelIndex(): number {\n const value = this._gameInfo?.levelBased?.progress?.currentLevelIndex\n return typeof value === 'number' && Number.isFinite(value) && value >= 1 ? value : 1\n }\n\n /** Best stars per level from score-derived progression. Keys are 1-based level indices. */\n get levelStars(): Record<string, LevelStars> {\n const raw = this._gameInfo?.levelBased?.progress?.levelStars\n const levelStars: Record<string, LevelStars> = {}\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n for (const [key, starValue] of Object.entries(raw)) {\n if (starValue === 1 || starValue === 2 || starValue === 3) levelStars[key] = starValue\n }\n }\n return levelStars\n }\n\n /** Highest unlocked level from score-derived progression, defaulting to 1. */\n get highestUnlockedLevel(): number {\n const value = this._gameInfo?.levelBased?.progress?.highestUnlockedLevel\n return typeof value === 'number' && Number.isFinite(value) && value >= 1 ? value : 1\n }\n\n /** Sum of best stars across all completed levels. */\n get totalStars(): number {\n return Object.values(this.levelStars).reduce((sum, stars) => sum + stars, 0)\n }\n\n ready = (): Promise<GameInfo> => {\n // if the game info is already set, return it immediately\n if (this._gameInfo) {\n return Promise.resolve(this._gameInfo)\n }\n\n // internal promise allows Remix to return GameInfo as a response to the ready message\n return new Promise((resolve) => {\n this.readyPromiseResolve = resolve\n })\n }\n\n purchase = (data: PurchaseEvent['data']): Promise<PurchaseCompleteEvent['data']> => {\n this.sendMessage('purchase', data)\n\n // internal promise allows Remix to return PurchaseCompleteEvent data as a response to the purchase message\n return new Promise((resolve) => {\n this.purchasePromiseResolve = resolve\n })\n }\n\n reportError = (data: GameErrorEvent['data']) => {\n this.sendMessage('error', data)\n }\n\n hapticFeedback = (type?: HapticFeedbackType) => {\n this.sendMessage('haptic_feedback', type ? { type } : undefined)\n }\n\n hasItem = (item: string): boolean => {\n return this.getItemPurchaseCount(item) > 0\n }\n\n getItemPurchaseCount = (item: string): number => {\n const inventoryItem = this.inventory.find((entry) => entry.slug === item)\n return inventoryItem?.quantity || 0\n }\n\n getShopItem = (slug: string): ShopItem | undefined => {\n return this.shopItems.find((item) => item.slug === slug)\n }\n\n singlePlayer = {\n actions: {\n ready: this.ready,\n hapticFeedback: this.hapticFeedback,\n reportError: this.reportError,\n purchase: this.purchase,\n gameOver: (data: SinglePlayerGameOverEvent['data']) => {\n this.sendMessage('game_over', data)\n },\n saveGameState: (data: SaveGameStateEvent['data']) => {\n this.sendMessage('save_game_state', data)\n },\n },\n }\n\n multiplayer = {\n actions: {\n ...this.singlePlayer.actions,\n gameOver: (data: MultiplayerGameOverEvent['data']) => {\n this.sendMessage('multiplayer_game_over', data)\n },\n refuteGameState: (data: RefuteGameStateEvent['data']) => {\n this.sendMessage('refute_game_state', data)\n },\n saveGameState: (data: MultiplayerSaveGameStateEvent['data']) => {\n this.sendMessage('multiplayer_save_game_state', data)\n },\n },\n }\n\n private emit(eventType: IncomingGameEvent['event']['type'], data: unknown) {\n // Handle game_info specially to resolve the ready promise\n if (eventType === 'game_info') {\n const eventData = data as GameInfoEvent['data']\n this._gameInfo = eventData\n\n // Set the game state if it exists and is not already set\n if (!this._gameState && eventData.initialGameState) {\n this._gameState = eventData.initialGameState.gameState\n }\n\n if (this.readyPromiseResolve) {\n this.readyPromiseResolve(this._gameInfo)\n this.readyPromiseResolve = null\n }\n }\n\n // Handle purchase_complete specially to resolve the purchase promise\n if (eventType === 'purchase_complete') {\n const eventData = data as PurchaseCompleteEvent['data']\n\n if (eventData.success && eventData.item && this._gameInfo?.player) {\n this.applyPurchaseToCurrentPlayer(eventData.item)\n }\n\n if (this.purchasePromiseResolve) {\n this.purchasePromiseResolve(eventData)\n this.purchasePromiseResolve = null\n }\n }\n\n if (eventType === 'game_state_updated') {\n const eventData = data as GameStateUpdatedEvent['data']\n if (eventData) {\n this._gameState = eventData.gameState\n } else {\n this._gameState = null\n }\n }\n\n for (const callback of this.eventListeners.get(eventType) || []) {\n callback(data)\n }\n }\n\n private handleMessage = (event: MessageEvent<IncomingGameEvent>) => {\n if (event.data?.type !== 'game_event') return\n\n this.emit(event.data.event.type, event.data.event.data)\n }\n\n private sendMessage = <T extends GameEvent['type']>(\n type: T,\n data: Extract<GameEvent, { type: T }>['data'],\n ) => {\n if (!this.isClient || !this.target) return\n const gameEvent = { type: 'game_event', event: { type, data } } as GameEventMessage<T>\n this.target.postMessage(gameEvent, '*')\n }\n\n private handleGlobalError = (event: globalThis.ErrorEvent) => {\n // Send both formats for backward compatibility\n this.sendMessage('error', {\n message: event.message || 'Unknown error',\n source: event.filename,\n lineno: event.lineno,\n colno: event.colno,\n error: event.error,\n })\n }\n\n private handleUnhandledRejection = (event: PromiseRejectionEvent) => {\n const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason))\n\n // Send both formats for backward compatibility\n this.sendMessage('error', {\n message: error.message,\n error,\n })\n }\n\n private applyPurchaseToCurrentPlayer(item: string) {\n if (!this._gameInfo) return\n\n const currentPlayer = this._gameInfo.player\n\n currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item]\n\n this._gameInfo.players = this._gameInfo.players.map((player) => {\n if (player.id !== currentPlayer.id) return player\n return {\n ...player,\n purchasedItems: currentPlayer.purchasedItems,\n }\n })\n }\n}\n\n// Initialize and add to window\nexport const sdk = new RemixSDK()\n\n// Add SDK to window object for easier access in HTML games\nif (typeof window !== 'undefined') {\n window.FarcadeSDK = sdk\n window.RemixSDK = sdk\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "7A69B8A8B828F7ED64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "2E2F1C2C1ACE218064756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/index.mjs CHANGED
@@ -19,57 +19,37 @@ var __spreadValues = (a, b) => {
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
20
 
21
21
  // src/index.ts
22
- var ZERO_SAFE_AREA_INSET = Object.freeze({
22
+ var ZERO_SAFE_AREA_INSET = {
23
23
  top: 0,
24
24
  right: 0,
25
25
  bottom: 0,
26
26
  left: 0
27
- });
28
- var createGameEventMessage = (type, data) => {
29
- return { type: "game_event", event: { type, data } };
30
27
  };
31
- var parseGameEventMessage = (value) => {
32
- if (typeof value !== "object" || value === null)
33
- return null;
34
- const message = value;
35
- if (message.type !== "game_event")
36
- return null;
37
- const event = message.event;
38
- if (typeof event !== "object" || event === null)
39
- return null;
40
- if (typeof event.type !== "string")
41
- return null;
42
- return event;
43
- };
44
- var READY_RESEND_INITIAL_DELAY_MS = 250;
45
- var READY_RESEND_MAX_ATTEMPTS = 5;
46
- var generateInstanceId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
47
- var isBrowserGameClient = () => typeof window !== "undefined" && typeof window.addEventListener === "function" && typeof window.removeEventListener === "function";
28
+ var SDK_VERSION = true ? "0.8.1" : UNKNOWN_SDK_VERSION;
48
29
  var RemixSDK = class {
49
30
  constructor() {
31
+ /**
32
+ * The published version of this bundle. The host compares it against the
33
+ * version it asked the browser to load, so a cached bundle from a previous
34
+ * release can be detected before any game code runs.
35
+ */
36
+ this.version = SDK_VERSION;
50
37
  this.target = null;
51
38
  this.eventListeners = /* @__PURE__ */ new Map();
52
- this.readyPromise = null;
53
39
  this.readyPromiseResolve = null;
54
- this.purchasePromiseResolvers = [];
55
- this.readyResendTimer = null;
56
- this.instanceId = generateInstanceId();
57
- this._levelStars = Object.freeze({});
40
+ this.purchasePromiseResolve = null;
58
41
  this.ready = () => {
59
42
  if (this._gameInfo) {
60
43
  return Promise.resolve(this._gameInfo);
61
44
  }
62
- if (!this.readyPromise) {
63
- this.readyPromise = new Promise((resolve) => {
64
- this.readyPromiseResolve = resolve;
65
- });
66
- }
67
- return this.readyPromise;
45
+ return new Promise((resolve) => {
46
+ this.readyPromiseResolve = resolve;
47
+ });
68
48
  };
69
49
  this.purchase = (data) => {
70
50
  this.sendMessage("purchase", data);
71
51
  return new Promise((resolve) => {
72
- this.purchasePromiseResolvers.push(resolve);
52
+ this.purchasePromiseResolve = resolve;
73
53
  });
74
54
  };
75
55
  this.reportError = (data) => {
@@ -116,12 +96,10 @@ var RemixSDK = class {
116
96
  })
117
97
  };
118
98
  this.handleMessage = (event) => {
119
- if (event.source && event.source !== this.target)
120
- return;
121
- const gameEvent = parseGameEventMessage(event.data);
122
- if (!gameEvent)
99
+ var _a;
100
+ if (((_a = event.data) == null ? void 0 : _a.type) !== "game_event")
123
101
  return;
124
- this.emit(gameEvent.type, gameEvent.data);
102
+ this.emit(event.data.event.type, event.data.event.data);
125
103
  };
126
104
  this.sendMessage = (type, data) => {
127
105
  if (!this.isClient || !this.target)
@@ -135,45 +113,23 @@ var RemixSDK = class {
135
113
  source: event.filename,
136
114
  lineno: event.lineno,
137
115
  colno: event.colno,
138
- error: event.error,
139
- stack: event.error instanceof Error ? event.error.stack : void 0
116
+ error: event.error
140
117
  });
141
118
  };
142
119
  this.handleUnhandledRejection = (event) => {
143
120
  const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
144
121
  this.sendMessage("error", {
145
122
  message: error.message,
146
- error,
147
- stack: error.stack
123
+ error
148
124
  });
149
125
  };
150
- this.isClient = isBrowserGameClient();
126
+ this.isClient = typeof window !== "undefined";
151
127
  this.target = this.isClient ? window.parent : null;
152
128
  if (this.isClient) {
153
129
  window.addEventListener("message", this.handleMessage);
154
130
  window.addEventListener("error", this.handleGlobalError);
155
131
  window.addEventListener("unhandledrejection", this.handleUnhandledRejection);
156
- this.sendMessage("ready", { instanceId: this.instanceId });
157
- this.scheduleReadyResend(1);
158
- }
159
- }
160
- scheduleReadyResend(attempt) {
161
- if (attempt > READY_RESEND_MAX_ATTEMPTS)
162
- return;
163
- this.readyResendTimer = setTimeout(
164
- () => {
165
- if (this._gameInfo)
166
- return;
167
- this.sendMessage("ready", { instanceId: this.instanceId });
168
- this.scheduleReadyResend(attempt + 1);
169
- },
170
- READY_RESEND_INITIAL_DELAY_MS * 2 ** (attempt - 1)
171
- );
172
- }
173
- cancelReadyResend() {
174
- if (this.readyResendTimer !== null) {
175
- clearTimeout(this.readyResendTimer);
176
- this.readyResendTimer = null;
132
+ this.sendMessage("ready", void 0);
177
133
  }
178
134
  }
179
135
  on(eventType, callback) {
@@ -182,37 +138,35 @@ var RemixSDK = class {
182
138
  this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
183
139
  }
184
140
  (_a = this.eventListeners.get(eventType)) == null ? void 0 : _a.add(callback);
185
- return () => this.off(eventType, callback);
186
141
  }
187
142
  off(eventType, callback) {
188
143
  var _a;
189
144
  (_a = this.eventListeners.get(eventType)) == null ? void 0 : _a.delete(callback);
190
145
  }
191
- // explicitly named event listeners for better understanding and type safety;
192
- // each returns an unsubscribe function
146
+ // explicitly named event listeners for better understanding and type safety
193
147
  /**
194
148
  * Listen for the host starting play: a replay after game over, or — for level
195
- * games — a host-directed level via `data.levelIndex` (Studio continue/jump or
196
- * the production next-level button). `data` may be omitted for a plain restart.
149
+ * games — a host-directed level via `data.levelIndex`. `data` may be omitted
150
+ * for a plain restart.
197
151
  */
198
152
  onPlay(callback) {
199
- return this.on("play_again", (data) => callback(data));
153
+ this.on("play_again", (data) => callback(data));
200
154
  }
201
- /** @deprecated Use {@link onPlay}. Retained for backward compatibility. */
155
+ /** Alias of {@link onPlay}. */
202
156
  onPlayAgain(callback) {
203
- return this.onPlay(callback);
157
+ this.onPlay(callback);
204
158
  }
205
159
  onToggleMute(callback) {
206
- return this.on("toggle_mute", callback);
160
+ this.on("toggle_mute", (data) => callback(data));
207
161
  }
208
162
  onGameStateUpdated(callback) {
209
- return this.on("game_state_updated", callback);
163
+ this.on("game_state_updated", (data) => callback(data));
210
164
  }
211
165
  onGameInfo(callback) {
212
- return this.on("game_info", callback);
166
+ this.on("game_info", (data) => callback(data));
213
167
  }
214
168
  onPurchaseComplete(callback) {
215
- return this.on("purchase_complete", callback);
169
+ this.on("purchase_complete", (data) => callback(data));
216
170
  }
217
171
  // end of explicitly named event listeners
218
172
  setTarget(target) {
@@ -268,9 +222,18 @@ var RemixSDK = class {
268
222
  const value = (_c = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.progress) == null ? void 0 : _c.currentLevelIndex;
269
223
  return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
270
224
  }
271
- /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
225
+ /** Best stars per level from score-derived progression. Keys are 1-based level indices. */
272
226
  get levelStars() {
273
- return this._levelStars;
227
+ var _a, _b, _c;
228
+ const raw = (_c = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.progress) == null ? void 0 : _c.levelStars;
229
+ const levelStars = {};
230
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
231
+ for (const [key, starValue] of Object.entries(raw)) {
232
+ if (starValue === 1 || starValue === 2 || starValue === 3)
233
+ levelStars[key] = starValue;
234
+ }
235
+ }
236
+ return levelStars;
274
237
  }
275
238
  /** Highest unlocked level from score-derived progression, defaulting to 1. */
276
239
  get highestUnlockedLevel() {
@@ -283,20 +246,10 @@ var RemixSDK = class {
283
246
  return Object.values(this.levelStars).reduce((sum, stars) => sum + stars, 0);
284
247
  }
285
248
  emit(eventType, data) {
286
- var _a, _b, _c;
249
+ var _a;
287
250
  if (eventType === "game_info") {
288
251
  const eventData = data;
289
252
  this._gameInfo = eventData;
290
- const rawLevelStars = (_b = (_a = eventData.levelBased) == null ? void 0 : _a.progress) == null ? void 0 : _b.levelStars;
291
- const levelStars = {};
292
- if (rawLevelStars && typeof rawLevelStars === "object" && !Array.isArray(rawLevelStars)) {
293
- for (const [key, starValue] of Object.entries(rawLevelStars)) {
294
- if (starValue === 1 || starValue === 2 || starValue === 3)
295
- levelStars[key] = starValue;
296
- }
297
- }
298
- this._levelStars = Object.freeze(levelStars);
299
- this.cancelReadyResend();
300
253
  if (!this._gameState && eventData.initialGameState) {
301
254
  this._gameState = eventData.initialGameState.gameState;
302
255
  }
@@ -307,12 +260,12 @@ var RemixSDK = class {
307
260
  }
308
261
  if (eventType === "purchase_complete") {
309
262
  const eventData = data;
310
- if (eventData.success && eventData.item && ((_c = this._gameInfo) == null ? void 0 : _c.player)) {
263
+ if (eventData.success && eventData.item && ((_a = this._gameInfo) == null ? void 0 : _a.player)) {
311
264
  this.applyPurchaseToCurrentPlayer(eventData.item);
312
265
  }
313
- const resolvePurchase = this.purchasePromiseResolvers.shift();
314
- if (resolvePurchase) {
315
- resolvePurchase(eventData);
266
+ if (this.purchasePromiseResolve) {
267
+ this.purchasePromiseResolve(eventData);
268
+ this.purchasePromiseResolve = null;
316
269
  }
317
270
  }
318
271
  if (eventType === "game_state_updated") {
@@ -332,22 +285,22 @@ var RemixSDK = class {
332
285
  return;
333
286
  const currentPlayer = this._gameInfo.player;
334
287
  currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
335
- for (const player of this._gameInfo.players) {
336
- if (player !== currentPlayer && player.id === currentPlayer.id) {
337
- player.purchasedItems = currentPlayer.purchasedItems;
338
- }
339
- }
288
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
289
+ if (player.id !== currentPlayer.id)
290
+ return player;
291
+ return __spreadProps(__spreadValues({}, player), {
292
+ purchasedItems: currentPlayer.purchasedItems
293
+ });
294
+ });
340
295
  }
341
296
  };
342
297
  var sdk = new RemixSDK();
343
- if (isBrowserGameClient()) {
298
+ if (typeof window !== "undefined") {
344
299
  window.FarcadeSDK = sdk;
345
300
  window.RemixSDK = sdk;
346
301
  }
347
302
  export {
348
303
  RemixSDK,
349
304
  ZERO_SAFE_AREA_INSET,
350
- createGameEventMessage,
351
- parseGameEventMessage,
352
305
  sdk
353
306
  };