@remix-gg/sdk 0.4.1 → 0.4.3

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/README.md CHANGED
@@ -21,11 +21,10 @@ When you upload game code to Remix, a script tag to reference the SDK will autom
21
21
  ## Example Implementation
22
22
 
23
23
  ```typescript
24
- import { sdk } from '@remix-gg/sdk'
25
- import type { GameState, Player } from '@remix-gg/sdk'
24
+ import type { RemixSDK, GameState, Player } from '@remix-gg/sdk'
26
25
 
27
26
  class MyGame {
28
- sdk = sdk
27
+ sdk: RemixSDK
29
28
  player: Player
30
29
  gameState: GameState | null = null
31
30
  isMuted: boolean = true
@@ -143,6 +142,11 @@ Get the current game state. This will be `null` when there is no existing GameSt
143
142
  #### `sdk.purchasedItems: string[]`
144
143
 
145
144
  Get the list of item IDs that the current player has purchased. This is a read-only property that reflects the `purchasedItems` array from the current player's `GameInfo`.
145
+ When an item is purchased multiple times, that slug appears multiple times in the array.
146
+
147
+ #### `sdk.inventory: { slug: string, quantity: number }[]`
148
+
149
+ Get the player's inventory as slug + quantity entries, derived from `purchasedItems`.
146
150
 
147
151
  #### `sdk.storeItems: StoreItem[]`
148
152
 
@@ -158,6 +162,16 @@ Parameters:
158
162
  Returns:
159
163
  - `true` if the player has purchased the item, `false` otherwise
160
164
 
165
+ #### `sdk.getItemPurchaseCount(item: string): number`
166
+
167
+ Get how many times the current player has purchased a specific item.
168
+
169
+ Parameters:
170
+ - `item`: The item identifier to check (string)
171
+
172
+ Returns:
173
+ - The purchase count for that item (0 when never purchased)
174
+
161
175
  #### `sdk.getStoreItem(slug: string): StoreItem | undefined`
162
176
 
163
177
  Get a specific store item by slug.
@@ -203,7 +217,7 @@ The purchase flow works as follows:
203
217
  3. The promise resolves with `{ success: true }` if the purchase succeeds, or `{ success: false }` if it fails
204
218
  4. You can also listen for `purchase_complete` events using `sdk.onPurchaseComplete()` for additional handling
205
219
 
206
- **Important**: The `purchasedItems` array in `GameInfo` will be automatically updated after a successful purchase, and you can check item ownership using `sdk.hasItem(item)` or `sdk.purchasedItems`.
220
+ **Important**: Purchase data is automatically updated after a successful purchase. Use `sdk.getItemPurchaseCount(item)` (or `sdk.inventory`) when your game needs quantity, and `sdk.hasItem(item)` for simple ownership checks.
207
221
 
208
222
 
209
223
  ### Single Player Actions
@@ -314,12 +328,12 @@ Register a callback function that is called when game information is received. T
314
328
  Parameters:
315
329
  - `callback`: Function that receives the `GameInfo` object
316
330
 
317
- ##### `sdk.onPurchaseComplete(callback: (data: { success: boolean }) => void)`
331
+ ##### `sdk.onPurchaseComplete(callback: (data: { success: boolean, item?: string }) => void)`
318
332
 
319
333
  Register a callback function that is called when a purchase completes. This can be used for additional handling beyond the promise returned by `purchase()`. It returns the same value as awaiting the call to `purchase()`, but sometimes users may boost your game without your game triggering the `purchase` function.
320
334
 
321
335
  Parameters:
322
- - `callback`: Function that receives `{ success: boolean }` indicating whether the purchase succeeded
336
+ - `callback`: Function that receives purchase result data, including optional item and quantity info when available
323
337
 
324
338
  ## TypeScript Support
325
339
 
@@ -332,11 +346,10 @@ Multiplayer game development is not yet available to all users, but the SDK supp
332
346
  multiplayer game support.
333
347
 
334
348
  ```typescript
335
- import { sdk } from '@remix-gg/sdk'
336
- import type { GameState, Player } from '@remix-gg/sdk'
349
+ import type { RemixSDK, GameState, Player } from '@remix-gg/sdk'
337
350
 
338
351
  class Game {
339
- sdk = sdk
352
+ sdk: RemixSDK
340
353
  gameState: GameState
341
354
  player: Player
342
355
  players: Player[]
@@ -370,12 +383,12 @@ class Game {
370
383
  if (newGameStateIsValid) {
371
384
  this.gameState = gameState
372
385
  } else {
373
- sdk.multiplayer.actions.refuteGameState({ gameStateId: id });
386
+ this.sdk.multiplayer.actions.refuteGameState({ gameStateId: id });
374
387
  }
375
388
  })
376
389
 
377
390
  // Listen for purchase completions (optional)
378
- sdk.onPurchaseComplete((data) => {
391
+ this.sdk.onPurchaseComplete((data) => {
379
392
  if (data.success) {
380
393
  this.handlePurchaseSuccess()
381
394
  }
@@ -391,23 +404,23 @@ class Game {
391
404
  }
392
405
 
393
406
  private saveGameState(gameState: Record<string, unknown>, nextTurnPlayerId: string) {
394
- sdk.multiplayer.actions.saveGameState({
407
+ this.sdk.multiplayer.actions.saveGameState({
395
408
  gameState,
396
409
  alertUserIds: [nextTurnPlayerId],
397
410
  })
398
411
  }
399
412
 
400
413
  private gameOver(scores: { playerId: string, score: number }[]) {
401
- sdk.multiplayer.actions.gameOver({ scores })
414
+ this.sdk.multiplayer.actions.gameOver({ scores })
402
415
  }
403
416
 
404
417
  private async purchaseItem(itemId: string) {
405
418
  try {
406
- const result = await sdk.multiplayer.actions.purchase({ item: itemId })
419
+ const result = await this.sdk.multiplayer.actions.purchase({ item: itemId })
407
420
  if (result.success) {
408
421
  console.log(`Successfully purchased ${itemId}`)
409
- // Item is now available via sdk.hasItem(itemId) or sdk.purchasedItems
410
- // The purchasedItems array is automatically updated for all players
422
+ // Item quantity is available via sdk.getItemPurchaseCount(itemId)
423
+ // Purchase data is automatically updated for all players
411
424
  } else {
412
425
  console.log(`Purchase failed for ${itemId}`)
413
426
  }
package/dist/index.d.ts CHANGED
@@ -12,6 +12,10 @@ type Player = {
12
12
  purchasedItems: string[];
13
13
  imageUrl?: string;
14
14
  };
15
+ type InventoryItem = {
16
+ slug: string;
17
+ quantity: number;
18
+ };
15
19
  type StoreItem = {
16
20
  slug: string;
17
21
  name: string;
@@ -148,6 +152,7 @@ declare class RemixSDK {
148
152
  onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): void;
149
153
  setTarget(target: Window): void;
150
154
  get purchasedItems(): string[];
155
+ get inventory(): InventoryItem[];
151
156
  get storeItems(): StoreItem[];
152
157
  get gameInfo(): GameInfo | undefined;
153
158
  get gameState(): GameState | null | undefined;
@@ -159,6 +164,7 @@ declare class RemixSDK {
159
164
  reportError: (data: GameErrorEvent["data"]) => void;
160
165
  hapticFeedback: () => void;
161
166
  hasItem: (item: string) => boolean;
167
+ getItemPurchaseCount: (item: string) => number;
162
168
  getStoreItem: (slug: string) => StoreItem | undefined;
163
169
  singlePlayer: {
164
170
  actions: {
@@ -186,7 +192,8 @@ declare class RemixSDK {
186
192
  private sendMessage;
187
193
  private handleGlobalError;
188
194
  private handleUnhandledRejection;
195
+ private applyPurchaseToCurrentPlayer;
189
196
  }
190
197
  declare const sdk: RemixSDK;
191
198
 
192
- export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type IncomingGameEvent, type MultiplayerGameOverEvent, type MultiplayerSaveGameStateEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RefuteGameStateEvent, RemixSDK, type SaveGameStateEvent, type SinglePlayerGameOverEvent, type StoreItem, type ToggleMuteEvent, type ViewContext, sdk };
199
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type IncomingGameEvent, type InventoryItem, type MultiplayerGameOverEvent, type MultiplayerSaveGameStateEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RefuteGameStateEvent, RemixSDK, type SaveGameStateEvent, type SinglePlayerGameOverEvent, type StoreItem, type ToggleMuteEvent, type ViewContext, sdk };
package/dist/index.js CHANGED
@@ -82,6 +82,13 @@
82
82
  get purchasedItems() {
83
83
  return this._gameInfo?.player.purchasedItems || [];
84
84
  }
85
+ get inventory() {
86
+ const counts = new Map;
87
+ for (const item of this.purchasedItems) {
88
+ counts.set(item, (counts.get(item) || 0) + 1);
89
+ }
90
+ return [...counts.entries()].map(([slug, quantity]) => ({ slug, quantity }));
91
+ }
85
92
  get storeItems() {
86
93
  return this._gameInfo?.storeItems || [];
87
94
  }
@@ -121,7 +128,11 @@
121
128
  this.sendMessage("haptic_feedback", undefined);
122
129
  };
123
130
  hasItem = (item) => {
124
- return this._gameInfo?.player.purchasedItems.includes(item) || false;
131
+ return this.getItemPurchaseCount(item) > 0;
132
+ };
133
+ getItemPurchaseCount = (item) => {
134
+ const inventoryItem = this.inventory.find((entry) => entry.slug === item);
135
+ return inventoryItem?.quantity || 0;
125
136
  };
126
137
  getStoreItem = (slug) => {
127
138
  return this.storeItems.find((item) => item.slug === slug);
@@ -166,9 +177,15 @@
166
177
  this.readyPromiseResolve = null;
167
178
  }
168
179
  }
169
- if (eventType === "purchase_complete" && this.purchasePromiseResolve) {
170
- this.purchasePromiseResolve(data);
171
- this.purchasePromiseResolve = null;
180
+ if (eventType === "purchase_complete") {
181
+ const eventData = data;
182
+ if (eventData.success && eventData.item && this._gameInfo?.player) {
183
+ this.applyPurchaseToCurrentPlayer(eventData.item);
184
+ }
185
+ if (this.purchasePromiseResolve) {
186
+ this.purchasePromiseResolve(eventData);
187
+ this.purchasePromiseResolve = null;
188
+ }
172
189
  }
173
190
  if (eventType === "game_state_updated") {
174
191
  const eventData = data;
@@ -209,6 +226,20 @@
209
226
  error
210
227
  });
211
228
  };
229
+ applyPurchaseToCurrentPlayer(item) {
230
+ if (!this._gameInfo)
231
+ return;
232
+ const currentPlayer = this._gameInfo.player;
233
+ currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
234
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
235
+ if (player.id !== currentPlayer.id)
236
+ return player;
237
+ return {
238
+ ...player,
239
+ purchasedItems: currentPlayer.purchasedItems
240
+ };
241
+ });
242
+ }
212
243
  }
213
244
  var sdk = new RemixSDK;
214
245
  if (typeof window !== "undefined") {
@@ -217,4 +248,4 @@
217
248
  }
218
249
  })();
219
250
 
220
- //# debugId=80A549C4A21F32CD64756E2164756E21
251
+ //# debugId=C6C853AC9CA67D5364756E2164756E21
package/dist/index.js.map CHANGED
@@ -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 GameState = Record<string, unknown>\n\nexport type Player = {\n id: string\n name: string\n purchasedItems: string[]\n imageUrl?: string\n}\n\nexport type StoreItem = {\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 storeItems?: StoreItem[]\n viewContext: ViewContext\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 storeItems(): StoreItem[] {\n return this._gameInfo?.storeItems || []\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._gameInfo?.player.purchasedItems.includes(item) || false\n }\n\n getStoreItem = (slug: string): StoreItem | undefined => {\n return this.storeItems.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' && this.purchasePromiseResolve) {\n this.purchasePromiseResolve(data as PurchaseCompleteEvent['data'])\n this.purchasePromiseResolve = null\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\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 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 StoreItem = {\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 storeItems?: StoreItem[]\n viewContext: ViewContext\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 storeItems(): StoreItem[] {\n return this._gameInfo?.storeItems || []\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 getStoreItem = (slug: string): StoreItem | undefined => {\n return this.storeItems.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": "80A549C4A21F32CD64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "C6C853AC9CA67D5364756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/index.min.js CHANGED
@@ -82,6 +82,13 @@
82
82
  get purchasedItems() {
83
83
  return this._gameInfo?.player.purchasedItems || [];
84
84
  }
85
+ get inventory() {
86
+ const counts = new Map;
87
+ for (const item of this.purchasedItems) {
88
+ counts.set(item, (counts.get(item) || 0) + 1);
89
+ }
90
+ return [...counts.entries()].map(([slug, quantity]) => ({ slug, quantity }));
91
+ }
85
92
  get storeItems() {
86
93
  return this._gameInfo?.storeItems || [];
87
94
  }
@@ -121,7 +128,11 @@
121
128
  this.sendMessage("haptic_feedback", undefined);
122
129
  };
123
130
  hasItem = (item) => {
124
- return this._gameInfo?.player.purchasedItems.includes(item) || false;
131
+ return this.getItemPurchaseCount(item) > 0;
132
+ };
133
+ getItemPurchaseCount = (item) => {
134
+ const inventoryItem = this.inventory.find((entry) => entry.slug === item);
135
+ return inventoryItem?.quantity || 0;
125
136
  };
126
137
  getStoreItem = (slug) => {
127
138
  return this.storeItems.find((item) => item.slug === slug);
@@ -166,9 +177,15 @@
166
177
  this.readyPromiseResolve = null;
167
178
  }
168
179
  }
169
- if (eventType === "purchase_complete" && this.purchasePromiseResolve) {
170
- this.purchasePromiseResolve(data);
171
- this.purchasePromiseResolve = null;
180
+ if (eventType === "purchase_complete") {
181
+ const eventData = data;
182
+ if (eventData.success && eventData.item && this._gameInfo?.player) {
183
+ this.applyPurchaseToCurrentPlayer(eventData.item);
184
+ }
185
+ if (this.purchasePromiseResolve) {
186
+ this.purchasePromiseResolve(eventData);
187
+ this.purchasePromiseResolve = null;
188
+ }
172
189
  }
173
190
  if (eventType === "game_state_updated") {
174
191
  const eventData = data;
@@ -209,6 +226,20 @@
209
226
  error
210
227
  });
211
228
  };
229
+ applyPurchaseToCurrentPlayer(item) {
230
+ if (!this._gameInfo)
231
+ return;
232
+ const currentPlayer = this._gameInfo.player;
233
+ currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
234
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
235
+ if (player.id !== currentPlayer.id)
236
+ return player;
237
+ return {
238
+ ...player,
239
+ purchasedItems: currentPlayer.purchasedItems
240
+ };
241
+ });
242
+ }
212
243
  }
213
244
  var sdk = new RemixSDK;
214
245
  if (typeof window !== "undefined") {
@@ -217,4 +248,4 @@
217
248
  }
218
249
  })();
219
250
 
220
- //# debugId=FD72E584CDBAFAC864756E2164756E21
251
+ //# debugId=A861C36BE6ABDE1864756E2164756E21
@@ -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 GameState = Record<string, unknown>\n\nexport type Player = {\n id: string\n name: string\n purchasedItems: string[]\n imageUrl?: string\n}\n\nexport type StoreItem = {\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 storeItems?: StoreItem[]\n viewContext: ViewContext\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 storeItems(): StoreItem[] {\n return this._gameInfo?.storeItems || []\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._gameInfo?.player.purchasedItems.includes(item) || false\n }\n\n getStoreItem = (slug: string): StoreItem | undefined => {\n return this.storeItems.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' && this.purchasePromiseResolve) {\n this.purchasePromiseResolve(data as PurchaseCompleteEvent['data'])\n this.purchasePromiseResolve = null\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\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 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 StoreItem = {\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 storeItems?: StoreItem[]\n viewContext: ViewContext\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 storeItems(): StoreItem[] {\n return this._gameInfo?.storeItems || []\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 getStoreItem = (slug: string): StoreItem | undefined => {\n return this.storeItems.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": "FD72E584CDBAFAC864756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "A861C36BE6ABDE1864756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/index.mjs CHANGED
@@ -47,6 +47,13 @@ class RemixSDK {
47
47
  get purchasedItems() {
48
48
  return this._gameInfo?.player.purchasedItems || [];
49
49
  }
50
+ get inventory() {
51
+ const counts = new Map;
52
+ for (const item of this.purchasedItems) {
53
+ counts.set(item, (counts.get(item) || 0) + 1);
54
+ }
55
+ return [...counts.entries()].map(([slug, quantity]) => ({ slug, quantity }));
56
+ }
50
57
  get storeItems() {
51
58
  return this._gameInfo?.storeItems || [];
52
59
  }
@@ -86,7 +93,11 @@ class RemixSDK {
86
93
  this.sendMessage("haptic_feedback", undefined);
87
94
  };
88
95
  hasItem = (item) => {
89
- return this._gameInfo?.player.purchasedItems.includes(item) || false;
96
+ return this.getItemPurchaseCount(item) > 0;
97
+ };
98
+ getItemPurchaseCount = (item) => {
99
+ const inventoryItem = this.inventory.find((entry) => entry.slug === item);
100
+ return inventoryItem?.quantity || 0;
90
101
  };
91
102
  getStoreItem = (slug) => {
92
103
  return this.storeItems.find((item) => item.slug === slug);
@@ -131,9 +142,15 @@ class RemixSDK {
131
142
  this.readyPromiseResolve = null;
132
143
  }
133
144
  }
134
- if (eventType === "purchase_complete" && this.purchasePromiseResolve) {
135
- this.purchasePromiseResolve(data);
136
- this.purchasePromiseResolve = null;
145
+ if (eventType === "purchase_complete") {
146
+ const eventData = data;
147
+ if (eventData.success && eventData.item && this._gameInfo?.player) {
148
+ this.applyPurchaseToCurrentPlayer(eventData.item);
149
+ }
150
+ if (this.purchasePromiseResolve) {
151
+ this.purchasePromiseResolve(eventData);
152
+ this.purchasePromiseResolve = null;
153
+ }
137
154
  }
138
155
  if (eventType === "game_state_updated") {
139
156
  const eventData = data;
@@ -174,6 +191,20 @@ class RemixSDK {
174
191
  error
175
192
  });
176
193
  };
194
+ applyPurchaseToCurrentPlayer(item) {
195
+ if (!this._gameInfo)
196
+ return;
197
+ const currentPlayer = this._gameInfo.player;
198
+ currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
199
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
200
+ if (player.id !== currentPlayer.id)
201
+ return player;
202
+ return {
203
+ ...player,
204
+ purchasedItems: currentPlayer.purchasedItems
205
+ };
206
+ });
207
+ }
177
208
  }
178
209
  var sdk = new RemixSDK;
179
210
  if (typeof window !== "undefined") {
@@ -185,4 +216,4 @@ export {
185
216
  RemixSDK
186
217
  };
187
218
 
188
- //# debugId=B7999179C96847D364756E2164756E21
219
+ //# debugId=5A1A1DD44E6CD23664756E2164756E21
@@ -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 GameState = Record<string, unknown>\n\nexport type Player = {\n id: string\n name: string\n purchasedItems: string[]\n imageUrl?: string\n}\n\nexport type StoreItem = {\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 storeItems?: StoreItem[]\n viewContext: ViewContext\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 storeItems(): StoreItem[] {\n return this._gameInfo?.storeItems || []\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._gameInfo?.player.purchasedItems.includes(item) || false\n }\n\n getStoreItem = (slug: string): StoreItem | undefined => {\n return this.storeItems.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' && this.purchasePromiseResolve) {\n this.purchasePromiseResolve(data as PurchaseCompleteEvent['data'])\n this.purchasePromiseResolve = null\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\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 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 StoreItem = {\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 storeItems?: StoreItem[]\n viewContext: ViewContext\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 storeItems(): StoreItem[] {\n return this._gameInfo?.storeItems || []\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 getStoreItem = (slug: string): StoreItem | undefined => {\n return this.storeItems.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": "B7999179C96847D364756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "5A1A1DD44E6CD23664756E2164756E21",
9
9
  "names": []
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-gg/sdk",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "main": "./src/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",