@remix-gg/sdk 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,237 +1,68 @@
1
- var __defProp = Object.defineProperty;
2
- var __defProps = Object.defineProperties;
3
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __spreadValues = (a, b) => {
9
- for (var prop in b || (b = {}))
10
- if (__hasOwnProp.call(b, prop))
11
- __defNormalProp(a, prop, b[prop]);
12
- if (__getOwnPropSymbols)
13
- for (var prop of __getOwnPropSymbols(b)) {
14
- if (__propIsEnum.call(b, prop))
15
- __defNormalProp(a, prop, b[prop]);
16
- }
17
- return a;
18
- };
19
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
-
21
1
  // src/index.ts
22
- var ZERO_SAFE_AREA_INSET = Object.freeze({
2
+ var ZERO_SAFE_AREA_INSET = {
23
3
  top: 0,
24
4
  right: 0,
25
5
  bottom: 0,
26
6
  left: 0
27
- });
28
- var createGameEventMessage = (type, data) => {
29
- return { type: "game_event", event: { type, data } };
30
7
  };
31
- var parseGameEventMessage = (value) => {
32
- if (typeof value !== "object" || value === null)
33
- return null;
34
- const message = value;
35
- if (message.type !== "game_event")
36
- return null;
37
- const event = message.event;
38
- if (typeof event !== "object" || event === null)
39
- return null;
40
- if (typeof event.type !== "string")
41
- return null;
42
- return event;
43
- };
44
- var READY_RESEND_INITIAL_DELAY_MS = 250;
45
- var READY_RESEND_MAX_ATTEMPTS = 5;
46
- var generateInstanceId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
47
- var isBrowserGameClient = () => typeof window !== "undefined" && typeof window.addEventListener === "function" && typeof window.removeEventListener === "function";
48
- var RemixSDK = class {
8
+
9
+ class RemixSDK {
10
+ isClient;
11
+ target = null;
12
+ eventListeners = new Map;
13
+ readyPromiseResolve = null;
14
+ purchasePromiseResolve = null;
15
+ _gameInfo;
16
+ _gameState;
49
17
  constructor() {
50
- this.target = null;
51
- this.eventListeners = /* @__PURE__ */ new Map();
52
- this.readyPromise = null;
53
- this.readyPromiseResolve = null;
54
- this.purchasePromiseResolvers = [];
55
- this.readyResendTimer = null;
56
- this.instanceId = generateInstanceId();
57
- this._levelStars = Object.freeze({});
58
- this.ready = () => {
59
- if (this._gameInfo) {
60
- return Promise.resolve(this._gameInfo);
61
- }
62
- if (!this.readyPromise) {
63
- this.readyPromise = new Promise((resolve) => {
64
- this.readyPromiseResolve = resolve;
65
- });
66
- }
67
- return this.readyPromise;
68
- };
69
- this.purchase = (data) => {
70
- this.sendMessage("purchase", data);
71
- return new Promise((resolve) => {
72
- this.purchasePromiseResolvers.push(resolve);
73
- });
74
- };
75
- this.reportError = (data) => {
76
- this.sendMessage("error", data);
77
- };
78
- this.hapticFeedback = (type) => {
79
- this.sendMessage("haptic_feedback", type ? { type } : void 0);
80
- };
81
- this.hasItem = (item) => {
82
- return this.getItemPurchaseCount(item) > 0;
83
- };
84
- this.getItemPurchaseCount = (item) => {
85
- const inventoryItem = this.inventory.find((entry) => entry.slug === item);
86
- return (inventoryItem == null ? void 0 : inventoryItem.quantity) || 0;
87
- };
88
- this.getShopItem = (slug) => {
89
- return this.shopItems.find((item) => item.slug === slug);
90
- };
91
- this.singlePlayer = {
92
- actions: {
93
- ready: this.ready,
94
- hapticFeedback: this.hapticFeedback,
95
- reportError: this.reportError,
96
- purchase: this.purchase,
97
- gameOver: (data) => {
98
- this.sendMessage("game_over", data);
99
- },
100
- saveGameState: (data) => {
101
- this.sendMessage("save_game_state", data);
102
- }
103
- }
104
- };
105
- this.multiplayer = {
106
- actions: __spreadProps(__spreadValues({}, this.singlePlayer.actions), {
107
- gameOver: (data) => {
108
- this.sendMessage("multiplayer_game_over", data);
109
- },
110
- refuteGameState: (data) => {
111
- this.sendMessage("refute_game_state", data);
112
- },
113
- saveGameState: (data) => {
114
- this.sendMessage("multiplayer_save_game_state", data);
115
- }
116
- })
117
- };
118
- this.handleMessage = (event) => {
119
- if (event.source && event.source !== this.target)
120
- return;
121
- const gameEvent = parseGameEventMessage(event.data);
122
- if (!gameEvent)
123
- return;
124
- this.emit(gameEvent.type, gameEvent.data);
125
- };
126
- this.sendMessage = (type, data) => {
127
- if (!this.isClient || !this.target)
128
- return;
129
- const gameEvent = { type: "game_event", event: { type, data } };
130
- this.target.postMessage(gameEvent, "*");
131
- };
132
- this.handleGlobalError = (event) => {
133
- this.sendMessage("error", {
134
- message: event.message || "Unknown error",
135
- source: event.filename,
136
- lineno: event.lineno,
137
- colno: event.colno,
138
- error: event.error,
139
- stack: event.error instanceof Error ? event.error.stack : void 0
140
- });
141
- };
142
- this.handleUnhandledRejection = (event) => {
143
- const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
144
- this.sendMessage("error", {
145
- message: error.message,
146
- error,
147
- stack: error.stack
148
- });
149
- };
150
- this.isClient = isBrowserGameClient();
18
+ this.isClient = typeof window !== "undefined";
151
19
  this.target = this.isClient ? window.parent : null;
152
20
  if (this.isClient) {
153
21
  window.addEventListener("message", this.handleMessage);
154
22
  window.addEventListener("error", this.handleGlobalError);
155
23
  window.addEventListener("unhandledrejection", this.handleUnhandledRejection);
156
- this.sendMessage("ready", { instanceId: this.instanceId });
157
- this.scheduleReadyResend(1);
158
- }
159
- }
160
- scheduleReadyResend(attempt) {
161
- if (attempt > READY_RESEND_MAX_ATTEMPTS)
162
- return;
163
- this.readyResendTimer = setTimeout(
164
- () => {
165
- if (this._gameInfo)
166
- return;
167
- this.sendMessage("ready", { instanceId: this.instanceId });
168
- this.scheduleReadyResend(attempt + 1);
169
- },
170
- READY_RESEND_INITIAL_DELAY_MS * 2 ** (attempt - 1)
171
- );
172
- }
173
- cancelReadyResend() {
174
- if (this.readyResendTimer !== null) {
175
- clearTimeout(this.readyResendTimer);
176
- this.readyResendTimer = null;
24
+ this.sendMessage("ready", undefined);
177
25
  }
178
26
  }
179
27
  on(eventType, callback) {
180
- var _a;
181
28
  if (!this.eventListeners.has(eventType)) {
182
- this.eventListeners.set(eventType, /* @__PURE__ */ new Set());
29
+ this.eventListeners.set(eventType, new Set);
183
30
  }
184
- (_a = this.eventListeners.get(eventType)) == null ? void 0 : _a.add(callback);
185
- return () => this.off(eventType, callback);
31
+ this.eventListeners.get(eventType)?.add(callback);
186
32
  }
187
33
  off(eventType, callback) {
188
- var _a;
189
- (_a = this.eventListeners.get(eventType)) == null ? void 0 : _a.delete(callback);
190
- }
191
- // explicitly named event listeners for better understanding and type safety;
192
- // each returns an unsubscribe function
193
- /**
194
- * Listen for the host starting play: a replay after game over, or — for level
195
- * games — a host-directed level via `data.levelIndex` (Studio continue/jump or
196
- * the production next-level button). `data` may be omitted for a plain restart.
197
- */
198
- onPlay(callback) {
199
- return this.on("play_again", (data) => callback(data));
34
+ this.eventListeners.get(eventType)?.delete(callback);
200
35
  }
201
- /** @deprecated Use {@link onPlay}. Retained for backward compatibility. */
202
36
  onPlayAgain(callback) {
203
- return this.onPlay(callback);
37
+ this.on("play_again", () => callback());
204
38
  }
205
39
  onToggleMute(callback) {
206
- return this.on("toggle_mute", callback);
40
+ this.on("toggle_mute", (data) => callback(data));
207
41
  }
208
42
  onGameStateUpdated(callback) {
209
- return this.on("game_state_updated", callback);
43
+ this.on("game_state_updated", (data) => callback(data));
210
44
  }
211
45
  onGameInfo(callback) {
212
- return this.on("game_info", callback);
46
+ this.on("game_info", (data) => callback(data));
213
47
  }
214
48
  onPurchaseComplete(callback) {
215
- return this.on("purchase_complete", callback);
49
+ this.on("purchase_complete", (data) => callback(data));
216
50
  }
217
- // end of explicitly named event listeners
218
51
  setTarget(target) {
219
52
  this.target = target;
220
53
  }
221
54
  get purchasedItems() {
222
- var _a;
223
- return ((_a = this._gameInfo) == null ? void 0 : _a.player.purchasedItems) || [];
55
+ return this._gameInfo?.player.purchasedItems || [];
224
56
  }
225
57
  get inventory() {
226
- const counts = /* @__PURE__ */ new Map();
58
+ const counts = new Map;
227
59
  for (const item of this.purchasedItems) {
228
60
  counts.set(item, (counts.get(item) || 0) + 1);
229
61
  }
230
62
  return [...counts.entries()].map(([slug, quantity]) => ({ slug, quantity }));
231
63
  }
232
64
  get shopItems() {
233
- var _a;
234
- return ((_a = this._gameInfo) == null ? void 0 : _a.shopItems) || [];
65
+ return this._gameInfo?.shopItems || [];
235
66
  }
236
67
  get gameInfo() {
237
68
  return this._gameInfo;
@@ -240,63 +71,76 @@ var RemixSDK = class {
240
71
  return this._gameState;
241
72
  }
242
73
  get players() {
243
- var _a;
244
- return (_a = this._gameInfo) == null ? void 0 : _a.players;
74
+ return this._gameInfo?.players;
245
75
  }
246
76
  get player() {
247
- var _a;
248
- return (_a = this._gameInfo) == null ? void 0 : _a.player;
77
+ return this._gameInfo?.player;
249
78
  }
250
79
  get isReady() {
251
80
  return !!this._gameInfo;
252
81
  }
253
- /** True when the host configured this game with a fixed level count. */
254
- get isLevelBased() {
255
- var _a, _b;
256
- const levelCount = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.levelCount;
257
- return levelCount != null && levelCount > 0;
258
- }
259
- /** Total levels for level-based games (from host metadata). */
260
- get levelCount() {
261
- var _a, _b;
262
- const levelCount = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.levelCount;
263
- return levelCount != null && levelCount > 0 ? levelCount : void 0;
264
- }
265
- /** 1-based level index from score-derived progression, defaulting to 1. */
266
- get currentLevelIndex() {
267
- var _a, _b, _c;
268
- const value = (_c = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.progress) == null ? void 0 : _c.currentLevelIndex;
269
- return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
270
- }
271
- /** Best stars earned per level from score-derived progression. Keys are 1-based level indices. */
272
- get levelStars() {
273
- return this._levelStars;
274
- }
275
- /** Highest unlocked level from score-derived progression, defaulting to 1. */
276
- get highestUnlockedLevel() {
277
- var _a, _b, _c;
278
- const value = (_c = (_b = (_a = this._gameInfo) == null ? void 0 : _a.levelBased) == null ? void 0 : _b.progress) == null ? void 0 : _c.highestUnlockedLevel;
279
- return typeof value === "number" && Number.isFinite(value) && value >= 1 ? value : 1;
280
- }
281
- /** Sum of best stars across all completed levels. */
282
- get totalStars() {
283
- return Object.values(this.levelStars).reduce((sum, stars) => sum + stars, 0);
284
- }
82
+ ready = () => {
83
+ if (this._gameInfo) {
84
+ return Promise.resolve(this._gameInfo);
85
+ }
86
+ return new Promise((resolve) => {
87
+ this.readyPromiseResolve = resolve;
88
+ });
89
+ };
90
+ purchase = (data) => {
91
+ this.sendMessage("purchase", data);
92
+ return new Promise((resolve) => {
93
+ this.purchasePromiseResolve = resolve;
94
+ });
95
+ };
96
+ reportError = (data) => {
97
+ this.sendMessage("error", data);
98
+ };
99
+ hapticFeedback = (type) => {
100
+ this.sendMessage("haptic_feedback", type ? { type } : undefined);
101
+ };
102
+ hasItem = (item) => {
103
+ return this.getItemPurchaseCount(item) > 0;
104
+ };
105
+ getItemPurchaseCount = (item) => {
106
+ const inventoryItem = this.inventory.find((entry) => entry.slug === item);
107
+ return inventoryItem?.quantity || 0;
108
+ };
109
+ getShopItem = (slug) => {
110
+ return this.shopItems.find((item) => item.slug === slug);
111
+ };
112
+ singlePlayer = {
113
+ actions: {
114
+ ready: this.ready,
115
+ hapticFeedback: this.hapticFeedback,
116
+ reportError: this.reportError,
117
+ purchase: this.purchase,
118
+ gameOver: (data) => {
119
+ this.sendMessage("game_over", data);
120
+ },
121
+ saveGameState: (data) => {
122
+ this.sendMessage("save_game_state", data);
123
+ }
124
+ }
125
+ };
126
+ multiplayer = {
127
+ actions: {
128
+ ...this.singlePlayer.actions,
129
+ gameOver: (data) => {
130
+ this.sendMessage("multiplayer_game_over", data);
131
+ },
132
+ refuteGameState: (data) => {
133
+ this.sendMessage("refute_game_state", data);
134
+ },
135
+ saveGameState: (data) => {
136
+ this.sendMessage("multiplayer_save_game_state", data);
137
+ }
138
+ }
139
+ };
285
140
  emit(eventType, data) {
286
- var _a, _b, _c;
287
141
  if (eventType === "game_info") {
288
142
  const eventData = data;
289
143
  this._gameInfo = eventData;
290
- const rawLevelStars = (_b = (_a = eventData.levelBased) == null ? void 0 : _a.progress) == null ? void 0 : _b.levelStars;
291
- const levelStars = {};
292
- if (rawLevelStars && typeof rawLevelStars === "object" && !Array.isArray(rawLevelStars)) {
293
- for (const [key, starValue] of Object.entries(rawLevelStars)) {
294
- if (starValue === 1 || starValue === 2 || starValue === 3)
295
- levelStars[key] = starValue;
296
- }
297
- }
298
- this._levelStars = Object.freeze(levelStars);
299
- this.cancelReadyResend();
300
144
  if (!this._gameState && eventData.initialGameState) {
301
145
  this._gameState = eventData.initialGameState.gameState;
302
146
  }
@@ -307,12 +151,12 @@ var RemixSDK = class {
307
151
  }
308
152
  if (eventType === "purchase_complete") {
309
153
  const eventData = data;
310
- if (eventData.success && eventData.item && ((_c = this._gameInfo) == null ? void 0 : _c.player)) {
154
+ if (eventData.success && eventData.item && this._gameInfo?.player) {
311
155
  this.applyPurchaseToCurrentPlayer(eventData.item);
312
156
  }
313
- const resolvePurchase = this.purchasePromiseResolvers.shift();
314
- if (resolvePurchase) {
315
- resolvePurchase(eventData);
157
+ if (this.purchasePromiseResolve) {
158
+ this.purchasePromiseResolve(eventData);
159
+ this.purchasePromiseResolve = null;
316
160
  }
317
161
  }
318
162
  if (eventType === "game_state_updated") {
@@ -327,27 +171,57 @@ var RemixSDK = class {
327
171
  callback(data);
328
172
  }
329
173
  }
174
+ handleMessage = (event) => {
175
+ if (event.data?.type !== "game_event")
176
+ return;
177
+ this.emit(event.data.event.type, event.data.event.data);
178
+ };
179
+ sendMessage = (type, data) => {
180
+ if (!this.isClient || !this.target)
181
+ return;
182
+ const gameEvent = { type: "game_event", event: { type, data } };
183
+ this.target.postMessage(gameEvent, "*");
184
+ };
185
+ handleGlobalError = (event) => {
186
+ this.sendMessage("error", {
187
+ message: event.message || "Unknown error",
188
+ source: event.filename,
189
+ lineno: event.lineno,
190
+ colno: event.colno,
191
+ error: event.error
192
+ });
193
+ };
194
+ handleUnhandledRejection = (event) => {
195
+ const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
196
+ this.sendMessage("error", {
197
+ message: error.message,
198
+ error
199
+ });
200
+ };
330
201
  applyPurchaseToCurrentPlayer(item) {
331
202
  if (!this._gameInfo)
332
203
  return;
333
204
  const currentPlayer = this._gameInfo.player;
334
205
  currentPlayer.purchasedItems = [...currentPlayer.purchasedItems, item];
335
- for (const player of this._gameInfo.players) {
336
- if (player !== currentPlayer && player.id === currentPlayer.id) {
337
- player.purchasedItems = currentPlayer.purchasedItems;
338
- }
339
- }
206
+ this._gameInfo.players = this._gameInfo.players.map((player) => {
207
+ if (player.id !== currentPlayer.id)
208
+ return player;
209
+ return {
210
+ ...player,
211
+ purchasedItems: currentPlayer.purchasedItems
212
+ };
213
+ });
340
214
  }
341
- };
342
- var sdk = new RemixSDK();
343
- if (isBrowserGameClient()) {
215
+ }
216
+ var sdk = new RemixSDK;
217
+ if (typeof window !== "undefined") {
344
218
  window.FarcadeSDK = sdk;
345
219
  window.RemixSDK = sdk;
346
220
  }
347
221
  export {
348
- RemixSDK,
222
+ sdk,
349
223
  ZERO_SAFE_AREA_INSET,
350
- createGameEventMessage,
351
- parseGameEventMessage,
352
- sdk
224
+ RemixSDK
353
225
  };
226
+
227
+ //# debugId=977CE63C2185488464756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-gg/sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",