@looplay/sdk 0.8.5 → 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.
- package/dist/looplay-sdk.cjs.js +95 -10
- package/dist/looplay-sdk.cjs.js.map +1 -1
- package/dist/looplay-sdk.esm.js +95 -11
- package/dist/looplay-sdk.esm.js.map +1 -1
- package/dist/looplay-sdk.min.js +5 -2
- package/dist/looplay-sdk.min.js.map +1 -1
- package/dist/types/apps/LooplaySDK.types.d.ts +6 -0
- package/dist/types/apps/api-client.d.ts +3 -0
- package/dist/types/apps/service-client.d.ts +8 -1
- package/dist/types/iframe/iframe-auth.d.ts +12 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/version.d.ts +1 -0
- package/package.json +1 -1
package/dist/looplay-sdk.cjs.js
CHANGED
|
@@ -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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
528
|
-
|
|
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;
|
|
@@ -885,6 +928,7 @@ var LooplayIframeAuth = class {
|
|
|
885
928
|
initialized = false;
|
|
886
929
|
lifecycleTrackingInitialized = false;
|
|
887
930
|
lastGameStartedKey = null;
|
|
931
|
+
integrationDetectionKeys = /* @__PURE__ */ new Set();
|
|
888
932
|
client = null;
|
|
889
933
|
listeners = /* @__PURE__ */ new Set();
|
|
890
934
|
options;
|
|
@@ -986,6 +1030,7 @@ var LooplayIframeAuth = class {
|
|
|
986
1030
|
init() {
|
|
987
1031
|
if (this.initialized || typeof window === "undefined") return;
|
|
988
1032
|
this.initialized = true;
|
|
1033
|
+
void this.detectIntegrationOnce();
|
|
989
1034
|
const isEmbedded = window.parent !== window;
|
|
990
1035
|
this.debug("auth listener initialized", {
|
|
991
1036
|
isIframe: isEmbedded,
|
|
@@ -1036,6 +1081,7 @@ var LooplayIframeAuth = class {
|
|
|
1036
1081
|
this.anonymousFallbackTimer = null;
|
|
1037
1082
|
this.initialized = false;
|
|
1038
1083
|
this.lifecycleTrackingInitialized = false;
|
|
1084
|
+
this.integrationDetectionKeys.clear();
|
|
1039
1085
|
this.listeners.clear();
|
|
1040
1086
|
}
|
|
1041
1087
|
/** Lazily builds (and rebuilds on auth change) the `ApiClient` bound to the current auth mode. */
|
|
@@ -1104,7 +1150,15 @@ var LooplayIframeAuth = class {
|
|
|
1104
1150
|
this.debug("emitGameEvent dispatched", { gameId, actionCode, ...opts });
|
|
1105
1151
|
return this.getClient().emit(gameId, actionCode, opts);
|
|
1106
1152
|
}
|
|
1107
|
-
/**
|
|
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
|
+
*/
|
|
1108
1162
|
initLifecycleTracking() {
|
|
1109
1163
|
if (this.lifecycleTrackingInitialized || typeof window === "undefined")
|
|
1110
1164
|
return;
|
|
@@ -1114,6 +1168,11 @@ var LooplayIframeAuth = class {
|
|
|
1114
1168
|
const key = this.getTrackingKey();
|
|
1115
1169
|
if (!key || this.lastGameStartedKey === key) return;
|
|
1116
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
|
+
}
|
|
1117
1176
|
this.debug("auto GAME_STARTED dispatch");
|
|
1118
1177
|
void this.emitGameEvent("GAME_STARTED");
|
|
1119
1178
|
});
|
|
@@ -1233,8 +1292,11 @@ ${identity}`;
|
|
|
1233
1292
|
}
|
|
1234
1293
|
}
|
|
1235
1294
|
setState(next) {
|
|
1295
|
+
const currentApiUrl = this.getApiUrl();
|
|
1236
1296
|
this.state = next;
|
|
1237
|
-
this.
|
|
1297
|
+
if (this.getApiUrl() !== currentApiUrl) {
|
|
1298
|
+
this.client = null;
|
|
1299
|
+
}
|
|
1238
1300
|
this.debug("auth state updated", {
|
|
1239
1301
|
gameId: this.getGameId(),
|
|
1240
1302
|
apiUrl: this.getApiUrl(),
|
|
@@ -1243,6 +1305,28 @@ ${identity}`;
|
|
|
1243
1305
|
hasAnonymousId: Boolean(next.anonymousId)
|
|
1244
1306
|
});
|
|
1245
1307
|
this.listeners.forEach((listener) => listener(next));
|
|
1308
|
+
void this.detectIntegrationOnce();
|
|
1309
|
+
}
|
|
1310
|
+
/** Detects each resolved game/API pair at most once for this iframe session. */
|
|
1311
|
+
async detectIntegrationOnce() {
|
|
1312
|
+
const gameId = this.getGameId();
|
|
1313
|
+
const apiUrl = this.getApiUrl();
|
|
1314
|
+
if (!gameId || !apiUrl) return;
|
|
1315
|
+
const key = `${apiUrl}
|
|
1316
|
+
${gameId}`;
|
|
1317
|
+
if (this.integrationDetectionKeys.has(key)) return;
|
|
1318
|
+
this.integrationDetectionKeys.add(key);
|
|
1319
|
+
try {
|
|
1320
|
+
await this.getClient().detectIntegration(gameId);
|
|
1321
|
+
this.debug("integration detected", { gameId, apiUrl });
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
this.integrationDetectionKeys.delete(key);
|
|
1324
|
+
this.debug("integration detection failed", {
|
|
1325
|
+
gameId,
|
|
1326
|
+
apiUrl,
|
|
1327
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1246
1330
|
}
|
|
1247
1331
|
resolveParentOrigin() {
|
|
1248
1332
|
if (this.options.parentOrigin) return this.options.parentOrigin;
|
|
@@ -1291,6 +1375,7 @@ exports.ApiClient = ApiClient;
|
|
|
1291
1375
|
exports.BrowserLocalStorageAuthStorage = BrowserLocalStorageAuthStorage;
|
|
1292
1376
|
exports.HttpClient = HttpClient;
|
|
1293
1377
|
exports.HttpError = HttpError;
|
|
1378
|
+
exports.LOOPLAY_SDK_VERSION = LOOPLAY_SDK_VERSION;
|
|
1294
1379
|
exports.LooplayAuth = LooplayAuth;
|
|
1295
1380
|
exports.LooplayIframeAuth = LooplayIframeAuth;
|
|
1296
1381
|
exports.LooplaySDK = LooplaySDK;
|