@remix-gg/sdk 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -42,15 +42,31 @@ type GameInfo = {
42
42
  id: string;
43
43
  gameState: GameState;
44
44
  } | null;
45
+ /**
46
+ * Present only when the host has level progression enabled for this session.
47
+ * Games must key level mode off this block's presence, not their own level
48
+ * content — the platform can disable it (e.g. feature gating) at any time.
49
+ */
45
50
  levelBased?: {
51
+ /** Canonical level total configured on the platform at upload. */
46
52
  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
- */
53
+ /** Score-derived, platform-owned progression — never read from gameState. */
51
54
  progress: LevelProgressState;
52
55
  };
53
56
  };
57
+ type LevelStars = 1 | 2 | 3;
58
+ /** Stars for one level attempt: 0 = failed, 1–3 = completed with that rating. */
59
+ type LevelAttemptStars = 0 | LevelStars;
60
+ /**
61
+ * One level attempt reported with game_over. Games report only what happened
62
+ * in the attempt; the host owns and derives all cumulative progression.
63
+ */
64
+ type LevelAttempt = {
65
+ /** 1-based index of the level that was just played. */
66
+ levelIndex: number;
67
+ /** Stars earned this attempt; 0 means the level was failed. */
68
+ stars: LevelAttemptStars;
69
+ };
54
70
  /** Score-derived, platform-owned progression for a level-based game. */
55
71
  type LevelProgressState = {
56
72
  currentLevelIndex: number;
@@ -59,9 +75,7 @@ type LevelProgressState = {
59
75
  };
60
76
  type ReadyEvent = {
61
77
  type: 'ready';
62
- data: {
63
- instanceId: string;
64
- } | undefined;
78
+ data: undefined;
65
79
  };
66
80
  type PlayAgainEvent = {
67
81
  type: 'play_again';
@@ -69,20 +83,6 @@ type PlayAgainEvent = {
69
83
  levelIndex?: number;
70
84
  };
71
85
  };
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
86
  type SinglePlayerGameOverEvent = {
87
87
  type: 'game_over';
88
88
  data: {
@@ -120,7 +120,6 @@ type GameErrorEvent = {
120
120
  lineno?: number;
121
121
  colno?: number;
122
122
  error?: Error;
123
- stack?: string;
124
123
  };
125
124
  };
126
125
  type GameInfoEvent = {
@@ -173,56 +172,41 @@ type GameEventMessage<T extends GameEvent['type']> = {
173
172
  type: T;
174
173
  }>;
175
174
  };
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
175
  /**
189
176
  * Messages from the game host to the game client
190
177
  */
191
178
  type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'>;
192
179
  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'];
180
+ type EventCallback = (data: unknown) => void;
197
181
  declare class RemixSDK {
182
+ /**
183
+ * The published version of this bundle. The host compares it against the
184
+ * version it asked the browser to load, so a cached bundle from a previous
185
+ * release can be detected before any game code runs.
186
+ */
187
+ readonly version: string;
198
188
  private isClient;
199
189
  private target;
200
190
  private eventListeners;
201
- private readyPromise;
202
191
  private readyPromiseResolve;
203
- private purchasePromiseResolvers;
204
- private readyResendTimer;
205
- private readonly instanceId;
192
+ private purchasePromiseResolve;
206
193
  private _gameInfo?;
207
194
  private _gameState?;
208
- private _levelStars;
209
195
  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;
196
+ on(eventType: IncomingGameEvent['event']['type'], callback: EventCallback): void;
197
+ off(eventType: IncomingGameEvent['event']['type'], callback: EventCallback): void;
214
198
  /**
215
199
  * 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.
200
+ * games — a host-directed level via `data.levelIndex`. `data` may be omitted
201
+ * for a plain restart.
218
202
  */
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;
203
+ onPlay(callback: (data?: PlayAgainEvent['data']) => void): void;
204
+ /** Alias of {@link onPlay}. */
205
+ onPlayAgain(callback: (data?: PlayAgainEvent['data']) => void): void;
206
+ onToggleMute(callback: (data: ToggleMuteEvent['data']) => void): void;
207
+ onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void): void;
208
+ onGameInfo(callback: (data: GameInfoEvent['data']) => void): void;
209
+ onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): void;
226
210
  setTarget(target: Window): void;
227
211
  get purchasedItems(): string[];
228
212
  get inventory(): InventoryItem[];
@@ -238,7 +222,7 @@ declare class RemixSDK {
238
222
  get levelCount(): number | undefined;
239
223
  /** 1-based level index from score-derived progression, defaulting to 1. */
240
224
  get currentLevelIndex(): number;
241
- /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
225
+ /** Best stars per level from score-derived progression. Keys are 1-based level indices. */
242
226
  get levelStars(): Record<string, LevelStars>;
243
227
  /** Highest unlocked level from score-derived progression, defaulting to 1. */
244
228
  get highestUnlockedLevel(): number;
@@ -281,4 +265,4 @@ declare class RemixSDK {
281
265
  }
282
266
  declare const sdk: RemixSDK;
283
267
 
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 };
268
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type InventoryItem, type LevelAttempt, type LevelAttemptStars, 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, sdk };
package/dist/index.d.ts CHANGED
@@ -42,15 +42,31 @@ type GameInfo = {
42
42
  id: string;
43
43
  gameState: GameState;
44
44
  } | null;
45
+ /**
46
+ * Present only when the host has level progression enabled for this session.
47
+ * Games must key level mode off this block's presence, not their own level
48
+ * content — the platform can disable it (e.g. feature gating) at any time.
49
+ */
45
50
  levelBased?: {
51
+ /** Canonical level total configured on the platform at upload. */
46
52
  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
- */
53
+ /** Score-derived, platform-owned progression — never read from gameState. */
51
54
  progress: LevelProgressState;
52
55
  };
53
56
  };
57
+ type LevelStars = 1 | 2 | 3;
58
+ /** Stars for one level attempt: 0 = failed, 1–3 = completed with that rating. */
59
+ type LevelAttemptStars = 0 | LevelStars;
60
+ /**
61
+ * One level attempt reported with game_over. Games report only what happened
62
+ * in the attempt; the host owns and derives all cumulative progression.
63
+ */
64
+ type LevelAttempt = {
65
+ /** 1-based index of the level that was just played. */
66
+ levelIndex: number;
67
+ /** Stars earned this attempt; 0 means the level was failed. */
68
+ stars: LevelAttemptStars;
69
+ };
54
70
  /** Score-derived, platform-owned progression for a level-based game. */
55
71
  type LevelProgressState = {
56
72
  currentLevelIndex: number;
@@ -59,9 +75,7 @@ type LevelProgressState = {
59
75
  };
60
76
  type ReadyEvent = {
61
77
  type: 'ready';
62
- data: {
63
- instanceId: string;
64
- } | undefined;
78
+ data: undefined;
65
79
  };
66
80
  type PlayAgainEvent = {
67
81
  type: 'play_again';
@@ -69,20 +83,6 @@ type PlayAgainEvent = {
69
83
  levelIndex?: number;
70
84
  };
71
85
  };
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
86
  type SinglePlayerGameOverEvent = {
87
87
  type: 'game_over';
88
88
  data: {
@@ -120,7 +120,6 @@ type GameErrorEvent = {
120
120
  lineno?: number;
121
121
  colno?: number;
122
122
  error?: Error;
123
- stack?: string;
124
123
  };
125
124
  };
126
125
  type GameInfoEvent = {
@@ -173,56 +172,41 @@ type GameEventMessage<T extends GameEvent['type']> = {
173
172
  type: T;
174
173
  }>;
175
174
  };
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
175
  /**
189
176
  * Messages from the game host to the game client
190
177
  */
191
178
  type IncomingGameEvent = GameEventMessage<'play_again' | 'toggle_mute' | 'game_info' | 'game_state_updated' | 'purchase_complete'>;
192
179
  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'];
180
+ type EventCallback = (data: unknown) => void;
197
181
  declare class RemixSDK {
182
+ /**
183
+ * The published version of this bundle. The host compares it against the
184
+ * version it asked the browser to load, so a cached bundle from a previous
185
+ * release can be detected before any game code runs.
186
+ */
187
+ readonly version: string;
198
188
  private isClient;
199
189
  private target;
200
190
  private eventListeners;
201
- private readyPromise;
202
191
  private readyPromiseResolve;
203
- private purchasePromiseResolvers;
204
- private readyResendTimer;
205
- private readonly instanceId;
192
+ private purchasePromiseResolve;
206
193
  private _gameInfo?;
207
194
  private _gameState?;
208
- private _levelStars;
209
195
  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;
196
+ on(eventType: IncomingGameEvent['event']['type'], callback: EventCallback): void;
197
+ off(eventType: IncomingGameEvent['event']['type'], callback: EventCallback): void;
214
198
  /**
215
199
  * 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.
200
+ * games — a host-directed level via `data.levelIndex`. `data` may be omitted
201
+ * for a plain restart.
218
202
  */
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;
203
+ onPlay(callback: (data?: PlayAgainEvent['data']) => void): void;
204
+ /** Alias of {@link onPlay}. */
205
+ onPlayAgain(callback: (data?: PlayAgainEvent['data']) => void): void;
206
+ onToggleMute(callback: (data: ToggleMuteEvent['data']) => void): void;
207
+ onGameStateUpdated(callback: (data: GameStateUpdatedEvent['data']) => void): void;
208
+ onGameInfo(callback: (data: GameInfoEvent['data']) => void): void;
209
+ onPurchaseComplete(callback: (data: PurchaseCompleteEvent['data']) => void): void;
226
210
  setTarget(target: Window): void;
227
211
  get purchasedItems(): string[];
228
212
  get inventory(): InventoryItem[];
@@ -238,7 +222,7 @@ declare class RemixSDK {
238
222
  get levelCount(): number | undefined;
239
223
  /** 1-based level index from score-derived progression, defaulting to 1. */
240
224
  get currentLevelIndex(): number;
241
- /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
225
+ /** Best stars per level from score-derived progression. Keys are 1-based level indices. */
242
226
  get levelStars(): Record<string, LevelStars>;
243
227
  /** Highest unlocked level from score-derived progression, defaulting to 1. */
244
228
  get highestUnlockedLevel(): number;
@@ -281,4 +265,4 @@ declare class RemixSDK {
281
265
  }
282
266
  declare const sdk: RemixSDK;
283
267
 
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 };
268
+ export { type GameErrorEvent, type GameEvent, type GameEventMessage, type GameInfo, type GameInfoEvent, type GameState, type GameStateUpdatedEvent, type HapticFeedbackEvent, type HapticFeedbackType, type IncomingGameEvent, type InventoryItem, type LevelAttempt, type LevelAttemptStars, 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, sdk };
package/dist/index.js CHANGED
@@ -39,62 +39,40 @@ var src_exports = {};
39
39
  __export(src_exports, {
40
40
  RemixSDK: () => RemixSDK,
41
41
  ZERO_SAFE_AREA_INSET: () => ZERO_SAFE_AREA_INSET,
42
- createGameEventMessage: () => createGameEventMessage,
43
- parseGameEventMessage: () => parseGameEventMessage,
44
42
  sdk: () => sdk
45
43
  });
46
44
  module.exports = __toCommonJS(src_exports);
47
- var ZERO_SAFE_AREA_INSET = Object.freeze({
45
+ var ZERO_SAFE_AREA_INSET = {
48
46
  top: 0,
49
47
  right: 0,
50
48
  bottom: 0,
51
49
  left: 0
52
- });
53
- var createGameEventMessage = (type, data) => {
54
- return { type: "game_event", event: { type, data } };
55
- };
56
- var parseGameEventMessage = (value) => {
57
- if (typeof value !== "object" || value === null)
58
- return null;
59
- const message = value;
60
- if (message.type !== "game_event")
61
- return null;
62
- const event = message.event;
63
- if (typeof event !== "object" || event === null)
64
- return null;
65
- if (typeof event.type !== "string")
66
- return null;
67
- return event;
68
50
  };
69
- var READY_RESEND_INITIAL_DELAY_MS = 250;
70
- var READY_RESEND_MAX_ATTEMPTS = 5;
71
- var generateInstanceId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
72
- var isBrowserGameClient = () => typeof window !== "undefined" && typeof window.addEventListener === "function" && typeof window.removeEventListener === "function";
51
+ var SDK_VERSION = true ? "0.8.1" : UNKNOWN_SDK_VERSION;
73
52
  var RemixSDK = class {
74
53
  constructor() {
54
+ /**
55
+ * The published version of this bundle. The host compares it against the
56
+ * version it asked the browser to load, so a cached bundle from a previous
57
+ * release can be detected before any game code runs.
58
+ */
59
+ this.version = SDK_VERSION;
75
60
  this.target = null;
76
61
  this.eventListeners = /* @__PURE__ */ new Map();
77
- this.readyPromise = null;
78
62
  this.readyPromiseResolve = null;
79
- this.purchasePromiseResolvers = [];
80
- this.readyResendTimer = null;
81
- this.instanceId = generateInstanceId();
82
- this._levelStars = Object.freeze({});
63
+ this.purchasePromiseResolve = null;
83
64
  this.ready = () => {
84
65
  if (this._gameInfo) {
85
66
  return Promise.resolve(this._gameInfo);
86
67
  }
87
- if (!this.readyPromise) {
88
- this.readyPromise = new Promise((resolve) => {
89
- this.readyPromiseResolve = resolve;
90
- });
91
- }
92
- return this.readyPromise;
68
+ return new Promise((resolve) => {
69
+ this.readyPromiseResolve = resolve;
70
+ });
93
71
  };
94
72
  this.purchase = (data) => {
95
73
  this.sendMessage("purchase", data);
96
74
  return new Promise((resolve) => {
97
- this.purchasePromiseResolvers.push(resolve);
75
+ this.purchasePromiseResolve = resolve;
98
76
  });
99
77
  };
100
78
  this.reportError = (data) => {
@@ -141,12 +119,10 @@ var RemixSDK = class {
141
119
  })
142
120
  };
143
121
  this.handleMessage = (event) => {
144
- if (event.source && event.source !== this.target)
122
+ var _a;
123
+ if (((_a = event.data) == null ? void 0 : _a.type) !== "game_event")
145
124
  return;
146
- const gameEvent = parseGameEventMessage(event.data);
147
- if (!gameEvent)
148
- return;
149
- this.emit(gameEvent.type, gameEvent.data);
125
+ this.emit(event.data.event.type, event.data.event.data);
150
126
  };
151
127
  this.sendMessage = (type, data) => {
152
128
  if (!this.isClient || !this.target)
@@ -160,45 +136,23 @@ var RemixSDK = class {
160
136
  source: event.filename,
161
137
  lineno: event.lineno,
162
138
  colno: event.colno,
163
- error: event.error,
164
- stack: event.error instanceof Error ? event.error.stack : void 0
139
+ error: event.error
165
140
  });
166
141
  };
167
142
  this.handleUnhandledRejection = (event) => {
168
143
  const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
169
144
  this.sendMessage("error", {
170
145
  message: error.message,
171
- error,
172
- stack: error.stack
146
+ error
173
147
  });
174
148
  };
175
- this.isClient = isBrowserGameClient();
149
+ this.isClient = typeof window !== "undefined";
176
150
  this.target = this.isClient ? window.parent : null;
177
151
  if (this.isClient) {
178
152
  window.addEventListener("message", this.handleMessage);
179
153
  window.addEventListener("error", this.handleGlobalError);
180
154
  window.addEventListener("unhandledrejection", this.handleUnhandledRejection);
181
- this.sendMessage("ready", { instanceId: this.instanceId });
182
- this.scheduleReadyResend(1);
183
- }
184
- }
185
- scheduleReadyResend(attempt) {
186
- if (attempt > READY_RESEND_MAX_ATTEMPTS)
187
- return;
188
- this.readyResendTimer = setTimeout(
189
- () => {
190
- if (this._gameInfo)
191
- return;
192
- this.sendMessage("ready", { instanceId: this.instanceId });
193
- this.scheduleReadyResend(attempt + 1);
194
- },
195
- READY_RESEND_INITIAL_DELAY_MS * 2 ** (attempt - 1)
196
- );
197
- }
198
- cancelReadyResend() {
199
- if (this.readyResendTimer !== null) {
200
- clearTimeout(this.readyResendTimer);
201
- this.readyResendTimer = null;
155
+ this.sendMessage("ready", void 0);
202
156
  }
203
157
  }
204
158
  on(eventType, callback) {
@@ -207,37 +161,35 @@ var RemixSDK = class {
207
161
  this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
208
162
  }
209
163
  (_a = this.eventListeners.get(eventType)) == null ? void 0 : _a.add(callback);
210
- return () => this.off(eventType, callback);
211
164
  }
212
165
  off(eventType, callback) {
213
166
  var _a;
214
167
  (_a = this.eventListeners.get(eventType)) == null ? void 0 : _a.delete(callback);
215
168
  }
216
- // explicitly named event listeners for better understanding and type safety;
217
- // each returns an unsubscribe function
169
+ // explicitly named event listeners for better understanding and type safety
218
170
  /**
219
171
  * Listen for the host starting play: a replay after game over, or — for level
220
- * games — a host-directed level via `data.levelIndex` (Studio continue/jump or
221
- * the production next-level button). `data` may be omitted for a plain restart.
172
+ * games — a host-directed level via `data.levelIndex`. `data` may be omitted
173
+ * for a plain restart.
222
174
  */
223
175
  onPlay(callback) {
224
- return this.on("play_again", (data) => callback(data));
176
+ this.on("play_again", (data) => callback(data));
225
177
  }
226
- /** @deprecated Use {@link onPlay}. Retained for backward compatibility. */
178
+ /** Alias of {@link onPlay}. */
227
179
  onPlayAgain(callback) {
228
- return this.onPlay(callback);
180
+ this.onPlay(callback);
229
181
  }
230
182
  onToggleMute(callback) {
231
- return this.on("toggle_mute", callback);
183
+ this.on("toggle_mute", (data) => callback(data));
232
184
  }
233
185
  onGameStateUpdated(callback) {
234
- return this.on("game_state_updated", callback);
186
+ this.on("game_state_updated", (data) => callback(data));
235
187
  }
236
188
  onGameInfo(callback) {
237
- return this.on("game_info", callback);
189
+ this.on("game_info", (data) => callback(data));
238
190
  }
239
191
  onPurchaseComplete(callback) {
240
- return this.on("purchase_complete", callback);
192
+ this.on("purchase_complete", (data) => callback(data));
241
193
  }
242
194
  // end of explicitly named event listeners
243
195
  setTarget(target) {
@@ -293,9 +245,18 @@ var RemixSDK = class {
293
245
  const value = (_c = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.progress) == null ? void 0 : _c.currentLevelIndex;
294
246
  return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
295
247
  }
296
- /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
248
+ /** Best stars per level from score-derived progression. Keys are 1-based level indices. */
297
249
  get levelStars() {
298
- return this._levelStars;
250
+ var _a, _b, _c;
251
+ const raw = (_c = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.progress) == null ? void 0 : _c.levelStars;
252
+ const levelStars = {};
253
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
254
+ for (const [key, starValue] of Object.entries(raw)) {
255
+ if (starValue === 1 || starValue === 2 || starValue === 3)
256
+ levelStars[key] = starValue;
257
+ }
258
+ }
259
+ return levelStars;
299
260
  }
300
261
  /** Highest unlocked level from score-derived progression, defaulting to 1. */
301
262
  get highestUnlockedLevel() {
@@ -308,20 +269,10 @@ var RemixSDK = class {
308
269
  return Object.values(this.levelStars).reduce((sum, stars) => sum + stars, 0);
309
270
  }
310
271
  emit(eventType, data) {
311
- var _a, _b, _c;
272
+ var _a;
312
273
  if (eventType === "game_info") {
313
274
  const eventData = data;
314
275
  this._gameInfo = eventData;
315
- const rawLevelStars = (_b = (_a = eventData.levelBased) == null ? void 0 : _a.progress) == null ? void 0 : _b.levelStars;
316
- const levelStars = {};
317
- if (rawLevelStars && typeof rawLevelStars === "object" && !Array.isArray(rawLevelStars)) {
318
- for (const [key, starValue] of Object.entries(rawLevelStars)) {
319
- if (starValue === 1 || starValue === 2 || starValue === 3)
320
- levelStars[key] = starValue;
321
- }
322
- }
323
- this._levelStars = Object.freeze(levelStars);
324
- this.cancelReadyResend();
325
276
  if (!this._gameState && eventData.initialGameState) {
326
277
  this._gameState = eventData.initialGameState.gameState;
327
278
  }
@@ -332,12 +283,12 @@ var RemixSDK = class {
332
283
  }
333
284
  if (eventType === "purchase_complete") {
334
285
  const eventData = data;
335
- if (eventData.success && eventData.item && ((_c = this._gameInfo) == null ? void 0 : _c.player)) {
286
+ if (eventData.success && eventData.item && ((_a = this._gameInfo) == null ? void 0 : _a.player)) {
336
287
  this.applyPurchaseToCurrentPlayer(eventData.item);
337
288
  }
338
- const resolvePurchase = this.purchasePromiseResolvers.shift();
339
- if (resolvePurchase) {
340
- resolvePurchase(eventData);
289
+ if (this.purchasePromiseResolve) {
290
+ this.purchasePromiseResolve(eventData);
291
+ this.purchasePromiseResolve = null;
341
292
  }
342
293
  }
343
294
  if (eventType === "game_state_updated") {
@@ -357,15 +308,17 @@ var RemixSDK = class {
357
308
  return;
358
309
  const currentPlayer = this._gameInfo.player;
359
310
  currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
360
- for (const player of this._gameInfo.players) {
361
- if (player !== currentPlayer && player.id === currentPlayer.id) {
362
- player.purchasedItems = currentPlayer.purchasedItems;
363
- }
364
- }
311
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
312
+ if (player.id !== currentPlayer.id)
313
+ return player;
314
+ return __spreadProps(__spreadValues({}, player), {
315
+ purchasedItems: currentPlayer.purchasedItems
316
+ });
317
+ });
365
318
  }
366
319
  };
367
320
  var sdk = new RemixSDK();
368
- if (isBrowserGameClient()) {
321
+ if (typeof window !== "undefined") {
369
322
  window.FarcadeSDK = sdk;
370
323
  window.RemixSDK = sdk;
371
324
  }
@@ -373,7 +326,5 @@ if (isBrowserGameClient()) {
373
326
  0 && (module.exports = {
374
327
  RemixSDK,
375
328
  ZERO_SAFE_AREA_INSET,
376
- createGameEventMessage,
377
- parseGameEventMessage,
378
329
  sdk
379
330
  });