@remix-gg/sdk 0.6.0 → 0.7.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/dist/index.min.js CHANGED
@@ -31,32 +31,75 @@
31
31
  var exports_src = {};
32
32
  __export(exports_src, {
33
33
  sdk: () => sdk,
34
+ parseGameEventMessage: () => parseGameEventMessage,
35
+ createGameEventMessage: () => createGameEventMessage,
34
36
  ZERO_SAFE_AREA_INSET: () => ZERO_SAFE_AREA_INSET,
35
37
  RemixSDK: () => RemixSDK
36
38
  });
37
- var ZERO_SAFE_AREA_INSET = {
39
+ var ZERO_SAFE_AREA_INSET = Object.freeze({
38
40
  top: 0,
39
41
  right: 0,
40
42
  bottom: 0,
41
43
  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;
42
60
  };
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
65
 
44
66
  class RemixSDK {
45
67
  isClient;
46
68
  target = null;
47
69
  eventListeners = new Map;
70
+ readyPromise = null;
48
71
  readyPromiseResolve = null;
49
- purchasePromiseResolve = null;
72
+ purchasePromiseResolvers = [];
73
+ readyResendTimer = null;
74
+ instanceId = generateInstanceId();
50
75
  _gameInfo;
51
76
  _gameState;
77
+ _levelStars = Object.freeze({});
52
78
  constructor() {
53
- this.isClient = typeof window !== "undefined";
79
+ this.isClient = isBrowserGameClient();
54
80
  this.target = this.isClient ? window.parent : null;
55
81
  if (this.isClient) {
56
82
  window.addEventListener("message", this.handleMessage);
57
83
  window.addEventListener("error", this.handleGlobalError);
58
84
  window.addEventListener("unhandledrejection", this.handleUnhandledRejection);
59
- this.sendMessage("ready", undefined);
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;
60
103
  }
61
104
  }
62
105
  on(eventType, callback) {
@@ -64,24 +107,28 @@
64
107
  this.eventListeners.set(eventType, new Set);
65
108
  }
66
109
  this.eventListeners.get(eventType)?.add(callback);
110
+ return () => this.off(eventType, callback);
67
111
  }
68
112
  off(eventType, callback) {
69
113
  this.eventListeners.get(eventType)?.delete(callback);
70
114
  }
115
+ onPlay(callback) {
116
+ return this.on("play_again", (data) => callback(data));
117
+ }
71
118
  onPlayAgain(callback) {
72
- this.on("play_again", () => callback());
119
+ return this.onPlay(callback);
73
120
  }
74
121
  onToggleMute(callback) {
75
- this.on("toggle_mute", (data) => callback(data));
122
+ return this.on("toggle_mute", callback);
76
123
  }
77
124
  onGameStateUpdated(callback) {
78
- this.on("game_state_updated", (data) => callback(data));
125
+ return this.on("game_state_updated", callback);
79
126
  }
80
127
  onGameInfo(callback) {
81
- this.on("game_info", (data) => callback(data));
128
+ return this.on("game_info", callback);
82
129
  }
83
130
  onPurchaseComplete(callback) {
84
- this.on("purchase_complete", (data) => callback(data));
131
+ return this.on("purchase_complete", callback);
85
132
  }
86
133
  setTarget(target) {
87
134
  this.target = target;
@@ -114,25 +161,50 @@
114
161
  get isReady() {
115
162
  return !!this._gameInfo;
116
163
  }
164
+ get isLevelBased() {
165
+ const levelCount = this._gameInfo?.levelBased?.levelCount;
166
+ return levelCount != null && levelCount > 0;
167
+ }
168
+ get levelCount() {
169
+ const levelCount = this._gameInfo?.levelBased?.levelCount;
170
+ return levelCount != null && levelCount > 0 ? levelCount : undefined;
171
+ }
172
+ get currentLevelIndex() {
173
+ const value = this._gameInfo?.levelBased?.progress?.currentLevelIndex;
174
+ return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
175
+ }
176
+ get levelStars() {
177
+ return this._levelStars;
178
+ }
179
+ get highestUnlockedLevel() {
180
+ const value = this._gameInfo?.levelBased?.progress?.highestUnlockedLevel;
181
+ return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
182
+ }
183
+ get totalStars() {
184
+ return Object.values(this.levelStars).reduce((sum, stars) => sum + stars, 0);
185
+ }
117
186
  ready = () => {
118
187
  if (this._gameInfo) {
119
188
  return Promise.resolve(this._gameInfo);
120
189
  }
121
- return new Promise((resolve) => {
122
- this.readyPromiseResolve = resolve;
123
- });
190
+ if (!this.readyPromise) {
191
+ this.readyPromise = new Promise((resolve) => {
192
+ this.readyPromiseResolve = resolve;
193
+ });
194
+ }
195
+ return this.readyPromise;
124
196
  };
125
197
  purchase = (data) => {
126
198
  this.sendMessage("purchase", data);
127
199
  return new Promise((resolve) => {
128
- this.purchasePromiseResolve = resolve;
200
+ this.purchasePromiseResolvers.push(resolve);
129
201
  });
130
202
  };
131
203
  reportError = (data) => {
132
204
  this.sendMessage("error", data);
133
205
  };
134
- hapticFeedback = () => {
135
- this.sendMessage("haptic_feedback", undefined);
206
+ hapticFeedback = (type) => {
207
+ this.sendMessage("haptic_feedback", type ? { type } : undefined);
136
208
  };
137
209
  hasItem = (item) => {
138
210
  return this.getItemPurchaseCount(item) > 0;
@@ -176,6 +248,16 @@
176
248
  if (eventType === "game_info") {
177
249
  const eventData = data;
178
250
  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();
179
261
  if (!this._gameState && eventData.initialGameState) {
180
262
  this._gameState = eventData.initialGameState.gameState;
181
263
  }
@@ -189,9 +271,9 @@
189
271
  if (eventData.success && eventData.item && this._gameInfo?.player) {
190
272
  this.applyPurchaseToCurrentPlayer(eventData.item);
191
273
  }
192
- if (this.purchasePromiseResolve) {
193
- this.purchasePromiseResolve(eventData);
194
- this.purchasePromiseResolve = null;
274
+ const resolvePurchase = this.purchasePromiseResolvers.shift();
275
+ if (resolvePurchase) {
276
+ resolvePurchase(eventData);
195
277
  }
196
278
  }
197
279
  if (eventType === "game_state_updated") {
@@ -207,9 +289,12 @@
207
289
  }
208
290
  }
209
291
  handleMessage = (event) => {
210
- if (event.data?.type !== "game_event")
292
+ if (event.source && event.source !== this.target)
293
+ return;
294
+ const gameEvent = parseGameEventMessage(event.data);
295
+ if (!gameEvent)
211
296
  return;
212
- this.emit(event.data.event.type, event.data.event.data);
297
+ this.emit(gameEvent.type, gameEvent.data);
213
298
  };
214
299
  sendMessage = (type, data) => {
215
300
  if (!this.isClient || !this.target)
@@ -223,14 +308,16 @@
223
308
  source: event.filename,
224
309
  lineno: event.lineno,
225
310
  colno: event.colno,
226
- error: event.error
311
+ error: event.error,
312
+ stack: event.error instanceof Error ? event.error.stack : undefined
227
313
  });
228
314
  };
229
315
  handleUnhandledRejection = (event) => {
230
316
  const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
231
317
  this.sendMessage("error", {
232
318
  message: error.message,
233
- error
319
+ error,
320
+ stack: error.stack
234
321
  });
235
322
  };
236
323
  applyPurchaseToCurrentPlayer(item) {
@@ -238,21 +325,18 @@
238
325
  return;
239
326
  const currentPlayer = this._gameInfo.player;
240
327
  currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
241
- this._gameInfo.players = this._gameInfo.players.map((player) => {
242
- if (player.id !== currentPlayer.id)
243
- return player;
244
- return {
245
- ...player,
246
- purchasedItems: currentPlayer.purchasedItems
247
- };
248
- });
328
+ for (const player of this._gameInfo.players) {
329
+ if (player !== currentPlayer && player.id === currentPlayer.id) {
330
+ player.purchasedItems = currentPlayer.purchasedItems;
331
+ }
332
+ }
249
333
  }
250
334
  }
251
335
  var sdk = new RemixSDK;
252
- if (typeof window !== "undefined") {
336
+ if (isBrowserGameClient()) {
253
337
  window.FarcadeSDK = sdk;
254
338
  window.RemixSDK = sdk;
255
339
  }
256
340
  })();
257
341
 
258
- //# debugId=C31B41C03D6E9E7F64756E2164756E21
342
+ //# debugId=7A69B8A8B828F7ED64756E2164756E21
@@ -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 = {\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\nexport type ReadyEvent = {\n type: 'ready'\n data: undefined\n}\n\nexport type PlayAgainEvent = {\n type: 'play_again'\n data: undefined\n}\n\nexport type SinglePlayerGameOverEvent = {\n type: 'game_over'\n data: {\n score: number\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 HapticFeedbackEvent = {\n type: 'haptic_feedback'\n data: undefined\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\nexport class RemixSDK {\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 onPlayAgain(callback: () => void) {\n this.on('play_again', () => 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 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 = () => {\n this.sendMessage('haptic_feedback', 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"
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"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "C31B41C03D6E9E7F64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "7A69B8A8B828F7ED64756E2164756E21",
9
9
  "names": []
10
10
  }