@looplay/sdk 0.8.6 → 0.8.7

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.
@@ -139,6 +139,9 @@ var LooplayAuth = class {
139
139
  }
140
140
  };
141
141
 
142
+ // src/version.ts
143
+ var LOOPLAY_SDK_VERSION = "0.8.5";
144
+
142
145
  // src/apps/http.ts
143
146
  function getFetchFn(fetchOverride) {
144
147
  if (fetchOverride) {
@@ -166,7 +169,10 @@ var HttpClient = class {
166
169
  constructor(options) {
167
170
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
168
171
  this.fetchFn = getFetchFn(options.fetch);
169
- this.defaultHeaders = options.defaultHeaders ?? {};
172
+ this.defaultHeaders = {
173
+ "x-looplay-sdk-version": LOOPLAY_SDK_VERSION,
174
+ ...options.defaultHeaders ?? {}
175
+ };
170
176
  }
171
177
  async request(method, path, options) {
172
178
  const url = this.buildUrl(path, options?.query);
@@ -269,6 +275,19 @@ var ServiceClient = class {
269
275
  body
270
276
  });
271
277
  }
278
+ /**
279
+ * Re-issues a fresh `playSessionToken` for the SAME `playSessionId` — call
280
+ * this instead of `startPlaySession` when refreshing an about-to-expire
281
+ * (or just-expired, within the server's renewal grace window) token, so
282
+ * long-running sessions keep a single `playSessionId` throughout.
283
+ */
284
+ async renewPlaySession(gameKey, auth = {}, body) {
285
+ return this.http.request("POST", "/sdk/games/play-sessions/renew", {
286
+ ...auth,
287
+ headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
288
+ body
289
+ });
290
+ }
272
291
  async completePlaySession(gameKey, auth, body) {
273
292
  await this.http.request("POST", "/sdk/games/play-sessions/complete", {
274
293
  ...auth,
@@ -438,9 +457,11 @@ var ApiClient = class {
438
457
  getAccessToken;
439
458
  getAnonymousId;
440
459
  trackingSession;
460
+ trackingSessionPromiseKey;
441
461
  trackingSessionPromise;
442
462
  currentPlaySessionId;
443
463
  playSession;
464
+ playSessionPromiseKey;
444
465
  playSessionPromise;
445
466
  constructor(options) {
446
467
  if (!options.baseUrl) throw new MissingBaseUrlError();
@@ -481,7 +502,10 @@ var ApiClient = class {
481
502
  const bearerToken = await this.getAccessToken?.();
482
503
  const anonId = bearerToken ? void 0 : this.getAnonymousId?.();
483
504
  if (!bearerToken && !anonId) throw new NotAuthenticatedError();
484
- const session = await this.ensureTrackingSession(anonId);
505
+ const session = await this.ensureTrackingSession(
506
+ bearerToken ? `bearer:${bearerToken}` : `anon:${anonId}`,
507
+ anonId
508
+ );
485
509
  return {
486
510
  bearerToken,
487
511
  anonId,
@@ -494,14 +518,16 @@ var ApiClient = class {
494
518
  * session. Swallows failures — tracking calls still work without it,
495
519
  * just without unified session tracing.
496
520
  */
497
- async ensureTrackingSession(anonId) {
521
+ async ensureTrackingSession(cacheKey, anonId) {
498
522
  const now = Date.now();
499
- if (this.trackingSession && this.trackingSession.expiresAtMs > now + 3e4) {
523
+ if (this.trackingSession && this.trackingSession.cacheKey === cacheKey && this.trackingSession.expiresAtMs > now + 3e4) {
500
524
  return this.trackingSession;
501
525
  }
502
- if (!this.trackingSessionPromise) {
526
+ if (!this.trackingSessionPromise || this.trackingSessionPromiseKey !== cacheKey) {
527
+ this.trackingSessionPromiseKey = cacheKey;
503
528
  this.trackingSessionPromise = this.raw.bootstrapTrackingSession(anonId).then((dto) => {
504
529
  this.trackingSession = {
530
+ cacheKey,
505
531
  trackingSessionId: dto.trackingSessionId,
506
532
  anonTrackingToken: dto.trackingToken,
507
533
  expiresAtMs: new Date(dto.expiresAt).getTime()
@@ -509,10 +535,18 @@ var ApiClient = class {
509
535
  return this.trackingSession;
510
536
  }).catch(() => void 0).finally(() => {
511
537
  this.trackingSessionPromise = void 0;
538
+ this.trackingSessionPromiseKey = void 0;
512
539
  });
513
540
  }
514
541
  return this.trackingSessionPromise;
515
542
  }
543
+ getTrackingAuthKey(auth) {
544
+ return [
545
+ auth.bearerToken ? `bearer:${auth.bearerToken}` : `anon:${auth.anonId ?? ""}`,
546
+ auth.trackingSessionId ?? "",
547
+ auth.anonTrackingToken ?? ""
548
+ ].join("\n");
549
+ }
516
550
  /**
517
551
  * Signs (and caches until near expiry) the `playSessionToken` required by
518
552
  * `trackPlay`/`trackMatch`/`emit` for the current play session — does not
@@ -521,12 +555,20 @@ var ApiClient = class {
521
555
  */
522
556
  async ensurePlaySession(gameKey, auth, playSessionId) {
523
557
  const now = Date.now();
524
- if (this.playSession && this.playSession.gameKey === gameKey && this.playSession.playSessionId === playSessionId && this.playSession.expiresAtMs > now + 1e4) {
558
+ const cacheKey = [gameKey, playSessionId, this.getTrackingAuthKey(auth)].join("\n");
559
+ if (this.playSession && this.playSession.cacheKey === cacheKey && this.playSession.gameKey === gameKey && this.playSession.playSessionId === playSessionId && this.playSession.expiresAtMs > now + 1e4) {
525
560
  return this.playSession;
526
561
  }
527
- if (!this.playSessionPromise) {
528
- this.playSessionPromise = this.raw.startPlaySession(gameKey, auth, { playSessionId }).then((dto) => {
562
+ const previous = this.playSession && this.playSession.cacheKey === cacheKey && this.playSession.gameKey === gameKey && this.playSession.playSessionId === playSessionId ? this.playSession : void 0;
563
+ if (!this.playSessionPromise || this.playSessionPromiseKey !== cacheKey) {
564
+ this.playSessionPromiseKey = cacheKey;
565
+ const issue = previous ? this.raw.renewPlaySession(gameKey, auth, {
566
+ playSessionId,
567
+ playSessionToken: previous.playSessionToken
568
+ }).catch(() => this.raw.startPlaySession(gameKey, auth, { playSessionId })) : this.raw.startPlaySession(gameKey, auth, { playSessionId });
569
+ this.playSessionPromise = issue.then((dto) => {
529
570
  this.playSession = {
571
+ cacheKey,
530
572
  gameKey,
531
573
  playSessionId: dto.playSessionId,
532
574
  playSessionToken: dto.playSessionToken,
@@ -535,6 +577,7 @@ var ApiClient = class {
535
577
  return this.playSession;
536
578
  }).finally(() => {
537
579
  this.playSessionPromise = void 0;
580
+ this.playSessionPromiseKey = void 0;
538
581
  });
539
582
  }
540
583
  return this.playSessionPromise;
@@ -1107,7 +1150,15 @@ var LooplayIframeAuth = class {
1107
1150
  this.debug("emitGameEvent dispatched", { gameId, actionCode, ...opts });
1108
1151
  return this.getClient().emit(gameId, actionCode, opts);
1109
1152
  }
1110
- /** Idempotent; auto-emits GAME_STARTED exactly once per distinct auth session (bearer or anonymous). */
1153
+ /**
1154
+ * Idempotent; auto-emits GAME_STARTED exactly once per distinct auth
1155
+ * session (bearer or anonymous), and self-reports SDK integration the
1156
+ * same way the standalone `LooplaySDK.init()` does — without this, games
1157
+ * embedded via iframe never trigger the active detect call, only the
1158
+ * passive heartbeat that tracking calls leave behind (which doesn't fire
1159
+ * at all while the game is still IN_REVIEW/not LIVE, since tracking is
1160
+ * blocked until then).
1161
+ */
1111
1162
  initLifecycleTracking() {
1112
1163
  if (this.lifecycleTrackingInitialized || typeof window === "undefined")
1113
1164
  return;
@@ -1117,6 +1168,11 @@ var LooplayIframeAuth = class {
1117
1168
  const key = this.getTrackingKey();
1118
1169
  if (!key || this.lastGameStartedKey === key) return;
1119
1170
  this.lastGameStartedKey = key;
1171
+ const gameId = this.getGameId();
1172
+ if (gameId) {
1173
+ this.debug("auto detectIntegration dispatch", { gameId });
1174
+ void this.getClient().detectIntegration(gameId).catch(() => void 0);
1175
+ }
1120
1176
  this.debug("auto GAME_STARTED dispatch");
1121
1177
  void this.emitGameEvent("GAME_STARTED");
1122
1178
  });
@@ -1236,8 +1292,11 @@ ${identity}`;
1236
1292
  }
1237
1293
  }
1238
1294
  setState(next) {
1295
+ const currentApiUrl = this.getApiUrl();
1239
1296
  this.state = next;
1240
- this.client = null;
1297
+ if (this.getApiUrl() !== currentApiUrl) {
1298
+ this.client = null;
1299
+ }
1241
1300
  this.debug("auth state updated", {
1242
1301
  gameId: this.getGameId(),
1243
1302
  apiUrl: this.getApiUrl(),
@@ -1316,6 +1375,7 @@ exports.ApiClient = ApiClient;
1316
1375
  exports.BrowserLocalStorageAuthStorage = BrowserLocalStorageAuthStorage;
1317
1376
  exports.HttpClient = HttpClient;
1318
1377
  exports.HttpError = HttpError;
1378
+ exports.LOOPLAY_SDK_VERSION = LOOPLAY_SDK_VERSION;
1319
1379
  exports.LooplayAuth = LooplayAuth;
1320
1380
  exports.LooplayIframeAuth = LooplayIframeAuth;
1321
1381
  exports.LooplaySDK = LooplaySDK;