@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/README.md CHANGED
@@ -45,8 +45,8 @@ class MyGame {
45
45
  }
46
46
 
47
47
  private async setupEventListeners() {
48
- // Listen for play again events
49
- this.sdk.onPlayAgain(() => {
48
+ // Listen for play (replay or host-directed level start)
49
+ this.sdk.onPlay(() => {
50
50
  this.resetGame()
51
51
  })
52
52
 
@@ -123,6 +123,8 @@ class MyGame {
123
123
 
124
124
  This function will return when the SDK has recieved all the game information from Remix. You should await the call to this function before getting info from the SDK (sdk.player, sdk.gameState, etc)
125
125
 
126
+ The SDK announces itself to the host automatically when it loads — games never send the `ready` handshake themselves. `sdk.ready()` only waits for host data (`gameInfo`, `gameState`, level progression) to arrive before you read it; a game that reads nothing from the host does not need to call it.
127
+
126
128
  #### `sdk.isReady: boolean`
127
129
 
128
130
  Check if the SDK has received game info and is ready. More often, you will just await the call to `sdk.ready()`, but you may use this value if you prefer.
@@ -222,13 +224,34 @@ The purchase flow works as follows:
222
224
 
223
225
  ### Single Player Actions
224
226
 
225
- #### `sdk.singlePlayer.actions.gameOver({ score: number })`
227
+ #### `sdk.singlePlayer.actions.gameOver({ score: number, levelAttempt?: LevelAttempt })`
226
228
 
227
229
  Call this method when the game is over to report the final score.
228
230
 
229
231
  Parameters:
230
232
 
231
233
  - `score`: The player's final score (number)
234
+ - `levelAttempt`: For level-based games only — the result of the attempt that just ended (see Level-Based Games below). `score` is ignored for level-based games; pass `0`.
235
+
236
+ ### Level-Based Games
237
+
238
+ When the host configures a fixed level count, `gameInfo.levelBased` is set and the game runs as a level progression game:
239
+
240
+ - `gameInfo.levelBased.levelCount` is the total number of levels.
241
+ - `gameInfo.levelBased.progress` is the platform-owned, score-derived progression (`LevelProgressState`). Read it through the getters `sdk.isLevelBased`, `sdk.levelCount`, `sdk.currentLevelIndex`, `sdk.levelStars`, `sdk.highestUnlockedLevel`, and `sdk.totalStars`. Never store progression in `gameState`.
242
+ - When an attempt ends, report only what happened:
243
+
244
+ ```js
245
+ sdk.singlePlayer.actions.gameOver({
246
+ score: 0, // ignored for level-based games
247
+ levelAttempt: {
248
+ levelIndex, // 1-based level that was just played
249
+ stars, // 0 = failed, 1 | 2 | 3 earned on completion
250
+ },
251
+ })
252
+ ```
253
+
254
+ The platform merges attempts into cumulative progression — games never compute or report star maps, unlock state, or "all levels complete" (the host derives world completion from `levelIndex` and its own `levelCount`). Listen for host-directed level starts with `sdk.onPlay((data) => ...)`; the host passes the target level as `data.levelIndex`.
232
255
 
233
256
  #### `sdk.singlePlayer.actions.saveGameState({ gameState: Record<string, unknown> })`
234
257
 
@@ -303,9 +326,15 @@ The SDK provides both generic event listeners and specific typed listener method
303
326
 
304
327
  #### Event Listeners
305
328
 
306
- ##### `sdk.onPlayAgain(callback: () => void)`
329
+ ##### `sdk.onPlay(callback: (data?: { levelIndex?: number }) => void)`
330
+
331
+ Register a callback that is called when the host starts play — a replay after
332
+ game over, or (for level-based games) a host-directed level via `data.levelIndex`.
333
+ `data` may be omitted for a plain restart.
334
+
335
+ ##### `sdk.onPlayAgain(callback: (data?: { levelIndex?: number }) => void)`
307
336
 
308
- Register a callback function that is called when the player wants to play again.
337
+ Alias of `sdk.onPlay`.
309
338
 
310
339
  ##### `sdk.onToggleMute(callback: (data: { isMuted: boolean }) => void)`
311
340
 
@@ -365,8 +394,8 @@ class Game {
365
394
  }
366
395
 
367
396
  private setupEventListeners() {
368
- // Listen for play again events
369
- this.sdk.onPlayAgain(() => {
397
+ // Listen for play (replay or host-directed level start)
398
+ this.sdk.onPlay(() => {
370
399
  this.resetGame()
371
400
  })
372
401
 
@@ -0,0 +1,284 @@
1
+ declare global {
2
+ interface Window {
3
+ FarcadeSDK: typeof sdk;
4
+ RemixSDK: typeof sdk;
5
+ }
6
+ }
7
+ type ViewContext = 'feed' | 'full_screen' | 'challenge' | 'tournament';
8
+ type SafeAreaInset = {
9
+ top: number;
10
+ right: number;
11
+ bottom: number;
12
+ left: number;
13
+ };
14
+ declare const ZERO_SAFE_AREA_INSET: SafeAreaInset;
15
+ type GameState = Record<string, unknown>;
16
+ type Player = {
17
+ id: string;
18
+ name: string;
19
+ purchasedItems: string[];
20
+ imageUrl?: string;
21
+ };
22
+ type InventoryItem = {
23
+ slug: string;
24
+ quantity: number;
25
+ };
26
+ type ShopItem = {
27
+ slug: string;
28
+ name: string;
29
+ itemType?: string;
30
+ bitsCost?: number | null;
31
+ description?: string | null;
32
+ iconUrl?: string | null;
33
+ tier?: number | null;
34
+ };
35
+ type GameInfo = {
36
+ players: Player[];
37
+ player: Player;
38
+ shopItems?: ShopItem[];
39
+ viewContext: ViewContext;
40
+ contentSafeAreaInset: SafeAreaInset;
41
+ initialGameState: {
42
+ id: string;
43
+ gameState: GameState;
44
+ } | null;
45
+ levelBased?: {
46
+ levelCount: number;
47
+ /**
48
+ * Score-derived level progression for this player. Platform-owned and kept
49
+ * fully separate from `gameState` — never read progression out of gameState.
50
+ */
51
+ progress: LevelProgressState;
52
+ };
53
+ };
54
+ /** Score-derived, platform-owned progression for a level-based game. */
55
+ type LevelProgressState = {
56
+ currentLevelIndex: number;
57
+ highestUnlockedLevel: number;
58
+ levelStars: Record<string, LevelStars>;
59
+ };
60
+ type ReadyEvent = {
61
+ type: 'ready';
62
+ data: {
63
+ instanceId: string;
64
+ } | undefined;
65
+ };
66
+ type PlayAgainEvent = {
67
+ type: 'play_again';
68
+ data?: {
69
+ levelIndex?: number;
70
+ };
71
+ };
72
+ type LevelStars = 1 | 2 | 3;
73
+ type LevelOutcome = 'level_complete' | 'level_failed' | 'all_complete';
74
+ /**
75
+ * One level attempt reported with game_over. Games report only what happened
76
+ * in the attempt; the host owns and derives all cumulative progression
77
+ * (star maps, unlock state) — see `LevelProgressState` for the host→game snapshot.
78
+ */
79
+ type LevelAttempt = {
80
+ /** 1-based index of the level that was just played. */
81
+ levelIndex: number;
82
+ /** Stars earned this attempt. Omit for star-less games; a completion counts as 1. */
83
+ stars?: LevelStars;
84
+ outcome: LevelOutcome;
85
+ };
86
+ type SinglePlayerGameOverEvent = {
87
+ type: 'game_over';
88
+ data: {
89
+ score: number;
90
+ levelAttempt?: LevelAttempt;
91
+ };
92
+ };
93
+ type MultiplayerGameOverEvent = {
94
+ type: 'multiplayer_game_over';
95
+ data: {
96
+ scores: {
97
+ playerId: string;
98
+ score: number;
99
+ }[];
100
+ };
101
+ };
102
+ type HapticFeedbackType = 'light' | 'medium' | 'hard' | 'success' | 'error';
103
+ type HapticFeedbackEvent = {
104
+ type: 'haptic_feedback';
105
+ data?: {
106
+ type?: HapticFeedbackType;
107
+ };
108
+ };
109
+ type ToggleMuteEvent = {
110
+ type: 'toggle_mute';
111
+ data: {
112
+ isMuted: boolean;
113
+ };
114
+ };
115
+ type GameErrorEvent = {
116
+ type: 'error';
117
+ data: {
118
+ message: string;
119
+ source?: string;
120
+ lineno?: number;
121
+ colno?: number;
122
+ error?: Error;
123
+ stack?: string;
124
+ };
125
+ };
126
+ type GameInfoEvent = {
127
+ type: 'game_info';
128
+ data: GameInfo;
129
+ };
130
+ type MultiplayerSaveGameStateEvent = {
131
+ type: 'multiplayer_save_game_state';
132
+ data: {
133
+ gameState: GameState;
134
+ alertUserIds?: string[];
135
+ };
136
+ };
137
+ type GameStateUpdatedEvent = {
138
+ type: 'game_state_updated';
139
+ data: {
140
+ id: string;
141
+ gameState: GameState;
142
+ } | null;
143
+ };
144
+ type RefuteGameStateEvent = {
145
+ type: 'refute_game_state';
146
+ data: {
147
+ gameStateId: string;
148
+ };
149
+ };
150
+ type SaveGameStateEvent = {
151
+ type: 'save_game_state';
152
+ data: {
153
+ gameState: GameState;
154
+ };
155
+ };
156
+ type PurchaseEvent = {
157
+ type: 'purchase';
158
+ data: {
159
+ item: string;
160
+ };
161
+ };
162
+ type PurchaseCompleteEvent = {
163
+ type: 'purchase_complete';
164
+ data: {
165
+ success: boolean;
166
+ item?: string;
167
+ };
168
+ };
169
+ type GameEvent = PlayAgainEvent | SinglePlayerGameOverEvent | ReadyEvent | HapticFeedbackEvent | ToggleMuteEvent | GameErrorEvent | SaveGameStateEvent | RefuteGameStateEvent | GameInfoEvent | GameStateUpdatedEvent | MultiplayerGameOverEvent | MultiplayerSaveGameStateEvent | PurchaseEvent | PurchaseCompleteEvent;
170
+ type GameEventMessage<T extends GameEvent['type']> = {
171
+ type: 'game_event';
172
+ event: Extract<GameEvent, {
173
+ type: T;
174
+ }>;
175
+ };
176
+ /**
177
+ * Build a game_event envelope for sending over postMessage. Hosts and the SDK
178
+ * share this so the wire format is defined in one place.
179
+ */
180
+ declare const createGameEventMessage: <T extends GameEvent["type"]>(type: T, data: Extract<GameEvent, {
181
+ type: T;
182
+ }>["data"]) => GameEventMessage<T>;
183
+ /**
184
+ * Parse an unknown postMessage payload into a GameEvent, or null when the
185
+ * payload is not a well-formed game_event envelope.
186
+ */
187
+ declare const parseGameEventMessage: (value: unknown) => GameEvent | null;
188
+ /**
189
+ * Messages from the game host to the game client
190
+ */
191
+ type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'>;
192
+ type OutgoingGameEvent = GameEventMessage<'game_over' | 'ready' | 'haptic_feedback' | 'error' | 'save_game_state' | 'refute_game_state' | 'multiplayer_game_over' | 'multiplayer_save_game_state' | 'purchase'>;
193
+ type IncomingGameEventType = IncomingGameEvent['event']['type'];
194
+ type IncomingGameEventData<T extends IncomingGameEventType> = Extract<GameEvent, {
195
+ type: T;
196
+ }>['data'];
197
+ declare class RemixSDK {
198
+ private isClient;
199
+ private target;
200
+ private eventListeners;
201
+ private readyPromise;
202
+ private readyPromiseResolve;
203
+ private purchasePromiseResolvers;
204
+ private readyResendTimer;
205
+ private readonly instanceId;
206
+ private _gameInfo?;
207
+ private _gameState?;
208
+ private _levelStars;
209
+ constructor();
210
+ private scheduleReadyResend;
211
+ private cancelReadyResend;
212
+ on<T extends IncomingGameEventType>(eventType: T, callback: (data: IncomingGameEventData<T>) => void): () => void;
213
+ off<T extends IncomingGameEventType>(eventType: T, callback: (data: IncomingGameEventData<T>) => void): void;
214
+ /**
215
+ * Listen for the host starting play: a replay after game over, or — for level
216
+ * games — a host-directed level via `data.levelIndex` (Studio continue/jump or
217
+ * the production next-level button). `data` may be omitted for a plain restart.
218
+ */
219
+ onPlay(callback: (data?: PlayAgainEvent['data']) => void): () => void;
220
+ /** @deprecated Use {@link onPlay}. Retained for backward compatibility. */
221
+ onPlayAgain(callback: (data?: PlayAgainEvent['data']) => void): () => void;
222
+ onToggleMute(callback: (data: ToggleMuteEvent['data']) => void): () => void;
223
+ onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void): () => void;
224
+ onGameInfo(callback: (data: GameInfoEvent['data']) => void): () => void;
225
+ onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): () => void;
226
+ setTarget(target: Window): void;
227
+ get purchasedItems(): string[];
228
+ get inventory(): InventoryItem[];
229
+ get shopItems(): ShopItem[];
230
+ get gameInfo(): GameInfo | undefined;
231
+ get gameState(): GameState | null | undefined;
232
+ get players(): Player[] | undefined;
233
+ get player(): Player | undefined;
234
+ get isReady(): boolean;
235
+ /** True when the host configured this game with a fixed level count. */
236
+ get isLevelBased(): boolean;
237
+ /** Total levels for level-based games (from host metadata). */
238
+ get levelCount(): number | undefined;
239
+ /** 1-based level index from score-derived progression, defaulting to 1. */
240
+ get currentLevelIndex(): number;
241
+ /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
242
+ get levelStars(): Record<string, LevelStars>;
243
+ /** Highest unlocked level from score-derived progression, defaulting to 1. */
244
+ get highestUnlockedLevel(): number;
245
+ /** Sum of best stars across all completed levels. */
246
+ get totalStars(): number;
247
+ ready: () => Promise<GameInfo>;
248
+ purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
249
+ reportError: (data: GameErrorEvent["data"]) => void;
250
+ hapticFeedback: (type?: HapticFeedbackType) => void;
251
+ hasItem: (item: string) => boolean;
252
+ getItemPurchaseCount: (item: string) => number;
253
+ getShopItem: (slug: string) => ShopItem | undefined;
254
+ singlePlayer: {
255
+ actions: {
256
+ ready: () => Promise<GameInfo>;
257
+ hapticFeedback: (type?: HapticFeedbackType) => void;
258
+ reportError: (data: GameErrorEvent["data"]) => void;
259
+ purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
260
+ gameOver: (data: SinglePlayerGameOverEvent["data"]) => void;
261
+ saveGameState: (data: SaveGameStateEvent["data"]) => void;
262
+ };
263
+ };
264
+ multiplayer: {
265
+ actions: {
266
+ gameOver: (data: MultiplayerGameOverEvent["data"]) => void;
267
+ refuteGameState: (data: RefuteGameStateEvent["data"]) => void;
268
+ saveGameState: (data: MultiplayerSaveGameStateEvent["data"]) => void;
269
+ ready: () => Promise<GameInfo>;
270
+ hapticFeedback: (type?: HapticFeedbackType) => void;
271
+ reportError: (data: GameErrorEvent["data"]) => void;
272
+ purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
273
+ };
274
+ };
275
+ private emit;
276
+ private handleMessage;
277
+ private sendMessage;
278
+ private handleGlobalError;
279
+ private handleUnhandledRejection;
280
+ private applyPurchaseToCurrentPlayer;
281
+ }
282
+ declare const sdk: RemixSDK;
283
+
284
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type IncomingGameEventData, type IncomingGameEventType, type InventoryItem, type LevelAttempt, type LevelOutcome, type LevelProgressState, type LevelStars, type MultiplayerGameOverEvent, type MultiplayerSaveGameStateEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RefuteGameStateEvent, RemixSDK, type SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, createGameEventMessage, parseGameEventMessage, sdk };
package/dist/index.d.ts CHANGED
@@ -42,19 +42,52 @@ type GameInfo = {
42
42
  id: string;
43
43
  gameState: GameState;
44
44
  } | null;
45
+ levelBased?: {
46
+ levelCount: number;
47
+ /**
48
+ * Score-derived level progression for this player. Platform-owned and kept
49
+ * fully separate from `gameState` — never read progression out of gameState.
50
+ */
51
+ progress: LevelProgressState;
52
+ };
53
+ };
54
+ /** Score-derived, platform-owned progression for a level-based game. */
55
+ type LevelProgressState = {
56
+ currentLevelIndex: number;
57
+ highestUnlockedLevel: number;
58
+ levelStars: Record<string, LevelStars>;
45
59
  };
46
60
  type ReadyEvent = {
47
61
  type: 'ready';
48
- data: undefined;
62
+ data: {
63
+ instanceId: string;
64
+ } | undefined;
49
65
  };
50
66
  type PlayAgainEvent = {
51
67
  type: 'play_again';
52
- data: undefined;
68
+ data?: {
69
+ levelIndex?: number;
70
+ };
71
+ };
72
+ type LevelStars = 1 | 2 | 3;
73
+ type LevelOutcome = 'level_complete' | 'level_failed' | 'all_complete';
74
+ /**
75
+ * One level attempt reported with game_over. Games report only what happened
76
+ * in the attempt; the host owns and derives all cumulative progression
77
+ * (star maps, unlock state) — see `LevelProgressState` for the host→game snapshot.
78
+ */
79
+ type LevelAttempt = {
80
+ /** 1-based index of the level that was just played. */
81
+ levelIndex: number;
82
+ /** Stars earned this attempt. Omit for star-less games; a completion counts as 1. */
83
+ stars?: LevelStars;
84
+ outcome: LevelOutcome;
53
85
  };
54
86
  type SinglePlayerGameOverEvent = {
55
87
  type: 'game_over';
56
88
  data: {
57
89
  score: number;
90
+ levelAttempt?: LevelAttempt;
58
91
  };
59
92
  };
60
93
  type MultiplayerGameOverEvent = {
@@ -66,9 +99,12 @@ type MultiplayerGameOverEvent = {
66
99
  }[];
67
100
  };
68
101
  };
102
+ type HapticFeedbackType = 'light' | 'medium' | 'hard' | 'success' | 'error';
69
103
  type HapticFeedbackEvent = {
70
104
  type: 'haptic_feedback';
71
- data: undefined;
105
+ data?: {
106
+ type?: HapticFeedbackType;
107
+ };
72
108
  };
73
109
  type ToggleMuteEvent = {
74
110
  type: 'toggle_mute';
@@ -84,6 +120,7 @@ type GameErrorEvent = {
84
120
  lineno?: number;
85
121
  colno?: number;
86
122
  error?: Error;
123
+ stack?: string;
87
124
  };
88
125
  };
89
126
  type GameInfoEvent = {
@@ -136,28 +173,56 @@ type GameEventMessage<T extends GameEvent['type']> = {
136
173
  type: T;
137
174
  }>;
138
175
  };
176
+ /**
177
+ * Build a game_event envelope for sending over postMessage. Hosts and the SDK
178
+ * share this so the wire format is defined in one place.
179
+ */
180
+ declare const createGameEventMessage: <T extends GameEvent["type"]>(type: T, data: Extract<GameEvent, {
181
+ type: T;
182
+ }>["data"]) => GameEventMessage<T>;
183
+ /**
184
+ * Parse an unknown postMessage payload into a GameEvent, or null when the
185
+ * payload is not a well-formed game_event envelope.
186
+ */
187
+ declare const parseGameEventMessage: (value: unknown) => GameEvent | null;
139
188
  /**
140
189
  * Messages from the game host to the game client
141
190
  */
142
191
  type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'>;
143
192
  type OutgoingGameEvent = GameEventMessage<'game_over' | 'ready' | 'haptic_feedback' | 'error' | 'save_game_state' | 'refute_game_state' | 'multiplayer_game_over' | 'multiplayer_save_game_state' | 'purchase'>;
144
- type EventCallback = (data: unknown) => void;
193
+ type IncomingGameEventType = IncomingGameEvent['event']['type'];
194
+ type IncomingGameEventData<T extends IncomingGameEventType> = Extract<GameEvent, {
195
+ type: T;
196
+ }>['data'];
145
197
  declare class RemixSDK {
146
198
  private isClient;
147
199
  private target;
148
200
  private eventListeners;
201
+ private readyPromise;
149
202
  private readyPromiseResolve;
150
- private purchasePromiseResolve;
203
+ private purchasePromiseResolvers;
204
+ private readyResendTimer;
205
+ private readonly instanceId;
151
206
  private _gameInfo?;
152
207
  private _gameState?;
208
+ private _levelStars;
153
209
  constructor();
154
- on(eventType: IncomingGameEvent['event']['type'], callback: EventCallback): void;
155
- off(eventType: IncomingGameEvent['event']['type'], callback: EventCallback): void;
156
- onPlayAgain(callback: () => void): void;
157
- onToggleMute(callback: (data: ToggleMuteEvent['data']) => void): void;
158
- onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void): void;
159
- onGameInfo(callback: (data: GameInfoEvent['data']) => void): void;
160
- onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): void;
210
+ private scheduleReadyResend;
211
+ private cancelReadyResend;
212
+ on<T extends IncomingGameEventType>(eventType: T, callback: (data: IncomingGameEventData<T>) => void): () => void;
213
+ off<T extends IncomingGameEventType>(eventType: T, callback: (data: IncomingGameEventData<T>) => void): void;
214
+ /**
215
+ * Listen for the host starting play: a replay after game over, or — for level
216
+ * games — a host-directed level via `data.levelIndex` (Studio continue/jump or
217
+ * the production next-level button). `data` may be omitted for a plain restart.
218
+ */
219
+ onPlay(callback: (data?: PlayAgainEvent['data']) => void): () => void;
220
+ /** @deprecated Use {@link onPlay}. Retained for backward compatibility. */
221
+ onPlayAgain(callback: (data?: PlayAgainEvent['data']) => void): () => void;
222
+ onToggleMute(callback: (data: ToggleMuteEvent['data']) => void): () => void;
223
+ onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void): () => void;
224
+ onGameInfo(callback: (data: GameInfoEvent['data']) => void): () => void;
225
+ onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): () => void;
161
226
  setTarget(target: Window): void;
162
227
  get purchasedItems(): string[];
163
228
  get inventory(): InventoryItem[];
@@ -167,17 +232,29 @@ declare class RemixSDK {
167
232
  get players(): Player[] | undefined;
168
233
  get player(): Player | undefined;
169
234
  get isReady(): boolean;
235
+ /** True when the host configured this game with a fixed level count. */
236
+ get isLevelBased(): boolean;
237
+ /** Total levels for level-based games (from host metadata). */
238
+ get levelCount(): number | undefined;
239
+ /** 1-based level index from score-derived progression, defaulting to 1. */
240
+ get currentLevelIndex(): number;
241
+ /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
242
+ get levelStars(): Record<string, LevelStars>;
243
+ /** Highest unlocked level from score-derived progression, defaulting to 1. */
244
+ get highestUnlockedLevel(): number;
245
+ /** Sum of best stars across all completed levels. */
246
+ get totalStars(): number;
170
247
  ready: () => Promise<GameInfo>;
171
248
  purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
172
249
  reportError: (data: GameErrorEvent["data"]) => void;
173
- hapticFeedback: () => void;
250
+ hapticFeedback: (type?: HapticFeedbackType) => void;
174
251
  hasItem: (item: string) => boolean;
175
252
  getItemPurchaseCount: (item: string) => number;
176
253
  getShopItem: (slug: string) => ShopItem | undefined;
177
254
  singlePlayer: {
178
255
  actions: {
179
256
  ready: () => Promise<GameInfo>;
180
- hapticFeedback: () => void;
257
+ hapticFeedback: (type?: HapticFeedbackType) => void;
181
258
  reportError: (data: GameErrorEvent["data"]) => void;
182
259
  purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
183
260
  gameOver: (data: SinglePlayerGameOverEvent["data"]) => void;
@@ -190,7 +267,7 @@ declare class RemixSDK {
190
267
  refuteGameState: (data: RefuteGameStateEvent["data"]) => void;
191
268
  saveGameState: (data: MultiplayerSaveGameStateEvent["data"]) => void;
192
269
  ready: () => Promise<GameInfo>;
193
- hapticFeedback: () => void;
270
+ hapticFeedback: (type?: HapticFeedbackType) => void;
194
271
  reportError: (data: GameErrorEvent["data"]) => void;
195
272
  purchase: (data: PurchaseEvent["data"]) => Promise<PurchaseCompleteEvent["data"]>;
196
273
  };
@@ -204,4 +281,4 @@ declare class RemixSDK {
204
281
  }
205
282
  declare const sdk: RemixSDK;
206
283
 
207
- 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 SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, sdk };
284
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type IncomingGameEventData, type IncomingGameEventType, type InventoryItem, type LevelAttempt, type LevelOutcome, type LevelProgressState, type LevelStars, type MultiplayerGameOverEvent, type MultiplayerSaveGameStateEvent, type OutgoingGameEvent, type PlayAgainEvent, type Player, type PurchaseCompleteEvent, type PurchaseEvent, type ReadyEvent, type RefuteGameStateEvent, RemixSDK, type SafeAreaInset, type SaveGameStateEvent, type ShopItem, type SinglePlayerGameOverEvent, type ToggleMuteEvent, type ViewContext, ZERO_SAFE_AREA_INSET, createGameEventMessage, parseGameEventMessage, sdk };