@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.esm.js
CHANGED
|
@@ -137,6 +137,9 @@ var LooplayAuth = class {
|
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
// src/version.ts
|
|
141
|
+
var LOOPLAY_SDK_VERSION = "0.8.5";
|
|
142
|
+
|
|
140
143
|
// src/apps/http.ts
|
|
141
144
|
function getFetchFn(fetchOverride) {
|
|
142
145
|
if (fetchOverride) {
|
|
@@ -164,7 +167,10 @@ var HttpClient = class {
|
|
|
164
167
|
constructor(options) {
|
|
165
168
|
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
166
169
|
this.fetchFn = getFetchFn(options.fetch);
|
|
167
|
-
this.defaultHeaders =
|
|
170
|
+
this.defaultHeaders = {
|
|
171
|
+
"x-looplay-sdk-version": LOOPLAY_SDK_VERSION,
|
|
172
|
+
...options.defaultHeaders ?? {}
|
|
173
|
+
};
|
|
168
174
|
}
|
|
169
175
|
async request(method, path, options) {
|
|
170
176
|
const url = this.buildUrl(path, options?.query);
|
|
@@ -267,6 +273,19 @@ var ServiceClient = class {
|
|
|
267
273
|
body
|
|
268
274
|
});
|
|
269
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Re-issues a fresh `playSessionToken` for the SAME `playSessionId` — call
|
|
278
|
+
* this instead of `startPlaySession` when refreshing an about-to-expire
|
|
279
|
+
* (or just-expired, within the server's renewal grace window) token, so
|
|
280
|
+
* long-running sessions keep a single `playSessionId` throughout.
|
|
281
|
+
*/
|
|
282
|
+
async renewPlaySession(gameKey, auth = {}, body) {
|
|
283
|
+
return this.http.request("POST", "/sdk/games/play-sessions/renew", {
|
|
284
|
+
...auth,
|
|
285
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
286
|
+
body
|
|
287
|
+
});
|
|
288
|
+
}
|
|
270
289
|
async completePlaySession(gameKey, auth, body) {
|
|
271
290
|
await this.http.request("POST", "/sdk/games/play-sessions/complete", {
|
|
272
291
|
...auth,
|
|
@@ -436,9 +455,11 @@ var ApiClient = class {
|
|
|
436
455
|
getAccessToken;
|
|
437
456
|
getAnonymousId;
|
|
438
457
|
trackingSession;
|
|
458
|
+
trackingSessionPromiseKey;
|
|
439
459
|
trackingSessionPromise;
|
|
440
460
|
currentPlaySessionId;
|
|
441
461
|
playSession;
|
|
462
|
+
playSessionPromiseKey;
|
|
442
463
|
playSessionPromise;
|
|
443
464
|
constructor(options) {
|
|
444
465
|
if (!options.baseUrl) throw new MissingBaseUrlError();
|
|
@@ -479,7 +500,10 @@ var ApiClient = class {
|
|
|
479
500
|
const bearerToken = await this.getAccessToken?.();
|
|
480
501
|
const anonId = bearerToken ? void 0 : this.getAnonymousId?.();
|
|
481
502
|
if (!bearerToken && !anonId) throw new NotAuthenticatedError();
|
|
482
|
-
const session = await this.ensureTrackingSession(
|
|
503
|
+
const session = await this.ensureTrackingSession(
|
|
504
|
+
bearerToken ? `bearer:${bearerToken}` : `anon:${anonId}`,
|
|
505
|
+
anonId
|
|
506
|
+
);
|
|
483
507
|
return {
|
|
484
508
|
bearerToken,
|
|
485
509
|
anonId,
|
|
@@ -492,14 +516,16 @@ var ApiClient = class {
|
|
|
492
516
|
* session. Swallows failures — tracking calls still work without it,
|
|
493
517
|
* just without unified session tracing.
|
|
494
518
|
*/
|
|
495
|
-
async ensureTrackingSession(anonId) {
|
|
519
|
+
async ensureTrackingSession(cacheKey, anonId) {
|
|
496
520
|
const now = Date.now();
|
|
497
|
-
if (this.trackingSession && this.trackingSession.expiresAtMs > now + 3e4) {
|
|
521
|
+
if (this.trackingSession && this.trackingSession.cacheKey === cacheKey && this.trackingSession.expiresAtMs > now + 3e4) {
|
|
498
522
|
return this.trackingSession;
|
|
499
523
|
}
|
|
500
|
-
if (!this.trackingSessionPromise) {
|
|
524
|
+
if (!this.trackingSessionPromise || this.trackingSessionPromiseKey !== cacheKey) {
|
|
525
|
+
this.trackingSessionPromiseKey = cacheKey;
|
|
501
526
|
this.trackingSessionPromise = this.raw.bootstrapTrackingSession(anonId).then((dto) => {
|
|
502
527
|
this.trackingSession = {
|
|
528
|
+
cacheKey,
|
|
503
529
|
trackingSessionId: dto.trackingSessionId,
|
|
504
530
|
anonTrackingToken: dto.trackingToken,
|
|
505
531
|
expiresAtMs: new Date(dto.expiresAt).getTime()
|
|
@@ -507,10 +533,18 @@ var ApiClient = class {
|
|
|
507
533
|
return this.trackingSession;
|
|
508
534
|
}).catch(() => void 0).finally(() => {
|
|
509
535
|
this.trackingSessionPromise = void 0;
|
|
536
|
+
this.trackingSessionPromiseKey = void 0;
|
|
510
537
|
});
|
|
511
538
|
}
|
|
512
539
|
return this.trackingSessionPromise;
|
|
513
540
|
}
|
|
541
|
+
getTrackingAuthKey(auth) {
|
|
542
|
+
return [
|
|
543
|
+
auth.bearerToken ? `bearer:${auth.bearerToken}` : `anon:${auth.anonId ?? ""}`,
|
|
544
|
+
auth.trackingSessionId ?? "",
|
|
545
|
+
auth.anonTrackingToken ?? ""
|
|
546
|
+
].join("\n");
|
|
547
|
+
}
|
|
514
548
|
/**
|
|
515
549
|
* Signs (and caches until near expiry) the `playSessionToken` required by
|
|
516
550
|
* `trackPlay`/`trackMatch`/`emit` for the current play session — does not
|
|
@@ -519,12 +553,20 @@ var ApiClient = class {
|
|
|
519
553
|
*/
|
|
520
554
|
async ensurePlaySession(gameKey, auth, playSessionId) {
|
|
521
555
|
const now = Date.now();
|
|
522
|
-
|
|
556
|
+
const cacheKey = [gameKey, playSessionId, this.getTrackingAuthKey(auth)].join("\n");
|
|
557
|
+
if (this.playSession && this.playSession.cacheKey === cacheKey && this.playSession.gameKey === gameKey && this.playSession.playSessionId === playSessionId && this.playSession.expiresAtMs > now + 1e4) {
|
|
523
558
|
return this.playSession;
|
|
524
559
|
}
|
|
525
|
-
|
|
526
|
-
|
|
560
|
+
const previous = this.playSession && this.playSession.cacheKey === cacheKey && this.playSession.gameKey === gameKey && this.playSession.playSessionId === playSessionId ? this.playSession : void 0;
|
|
561
|
+
if (!this.playSessionPromise || this.playSessionPromiseKey !== cacheKey) {
|
|
562
|
+
this.playSessionPromiseKey = cacheKey;
|
|
563
|
+
const issue = previous ? this.raw.renewPlaySession(gameKey, auth, {
|
|
564
|
+
playSessionId,
|
|
565
|
+
playSessionToken: previous.playSessionToken
|
|
566
|
+
}).catch(() => this.raw.startPlaySession(gameKey, auth, { playSessionId })) : this.raw.startPlaySession(gameKey, auth, { playSessionId });
|
|
567
|
+
this.playSessionPromise = issue.then((dto) => {
|
|
527
568
|
this.playSession = {
|
|
569
|
+
cacheKey,
|
|
528
570
|
gameKey,
|
|
529
571
|
playSessionId: dto.playSessionId,
|
|
530
572
|
playSessionToken: dto.playSessionToken,
|
|
@@ -533,6 +575,7 @@ var ApiClient = class {
|
|
|
533
575
|
return this.playSession;
|
|
534
576
|
}).finally(() => {
|
|
535
577
|
this.playSessionPromise = void 0;
|
|
578
|
+
this.playSessionPromiseKey = void 0;
|
|
536
579
|
});
|
|
537
580
|
}
|
|
538
581
|
return this.playSessionPromise;
|
|
@@ -883,6 +926,7 @@ var LooplayIframeAuth = class {
|
|
|
883
926
|
initialized = false;
|
|
884
927
|
lifecycleTrackingInitialized = false;
|
|
885
928
|
lastGameStartedKey = null;
|
|
929
|
+
integrationDetectionKeys = /* @__PURE__ */ new Set();
|
|
886
930
|
client = null;
|
|
887
931
|
listeners = /* @__PURE__ */ new Set();
|
|
888
932
|
options;
|
|
@@ -984,6 +1028,7 @@ var LooplayIframeAuth = class {
|
|
|
984
1028
|
init() {
|
|
985
1029
|
if (this.initialized || typeof window === "undefined") return;
|
|
986
1030
|
this.initialized = true;
|
|
1031
|
+
void this.detectIntegrationOnce();
|
|
987
1032
|
const isEmbedded = window.parent !== window;
|
|
988
1033
|
this.debug("auth listener initialized", {
|
|
989
1034
|
isIframe: isEmbedded,
|
|
@@ -1034,6 +1079,7 @@ var LooplayIframeAuth = class {
|
|
|
1034
1079
|
this.anonymousFallbackTimer = null;
|
|
1035
1080
|
this.initialized = false;
|
|
1036
1081
|
this.lifecycleTrackingInitialized = false;
|
|
1082
|
+
this.integrationDetectionKeys.clear();
|
|
1037
1083
|
this.listeners.clear();
|
|
1038
1084
|
}
|
|
1039
1085
|
/** Lazily builds (and rebuilds on auth change) the `ApiClient` bound to the current auth mode. */
|
|
@@ -1102,7 +1148,15 @@ var LooplayIframeAuth = class {
|
|
|
1102
1148
|
this.debug("emitGameEvent dispatched", { gameId, actionCode, ...opts });
|
|
1103
1149
|
return this.getClient().emit(gameId, actionCode, opts);
|
|
1104
1150
|
}
|
|
1105
|
-
/**
|
|
1151
|
+
/**
|
|
1152
|
+
* Idempotent; auto-emits GAME_STARTED exactly once per distinct auth
|
|
1153
|
+
* session (bearer or anonymous), and self-reports SDK integration the
|
|
1154
|
+
* same way the standalone `LooplaySDK.init()` does — without this, games
|
|
1155
|
+
* embedded via iframe never trigger the active detect call, only the
|
|
1156
|
+
* passive heartbeat that tracking calls leave behind (which doesn't fire
|
|
1157
|
+
* at all while the game is still IN_REVIEW/not LIVE, since tracking is
|
|
1158
|
+
* blocked until then).
|
|
1159
|
+
*/
|
|
1106
1160
|
initLifecycleTracking() {
|
|
1107
1161
|
if (this.lifecycleTrackingInitialized || typeof window === "undefined")
|
|
1108
1162
|
return;
|
|
@@ -1112,6 +1166,11 @@ var LooplayIframeAuth = class {
|
|
|
1112
1166
|
const key = this.getTrackingKey();
|
|
1113
1167
|
if (!key || this.lastGameStartedKey === key) return;
|
|
1114
1168
|
this.lastGameStartedKey = key;
|
|
1169
|
+
const gameId = this.getGameId();
|
|
1170
|
+
if (gameId) {
|
|
1171
|
+
this.debug("auto detectIntegration dispatch", { gameId });
|
|
1172
|
+
void this.getClient().detectIntegration(gameId).catch(() => void 0);
|
|
1173
|
+
}
|
|
1115
1174
|
this.debug("auto GAME_STARTED dispatch");
|
|
1116
1175
|
void this.emitGameEvent("GAME_STARTED");
|
|
1117
1176
|
});
|
|
@@ -1231,8 +1290,11 @@ ${identity}`;
|
|
|
1231
1290
|
}
|
|
1232
1291
|
}
|
|
1233
1292
|
setState(next) {
|
|
1293
|
+
const currentApiUrl = this.getApiUrl();
|
|
1234
1294
|
this.state = next;
|
|
1235
|
-
this.
|
|
1295
|
+
if (this.getApiUrl() !== currentApiUrl) {
|
|
1296
|
+
this.client = null;
|
|
1297
|
+
}
|
|
1236
1298
|
this.debug("auth state updated", {
|
|
1237
1299
|
gameId: this.getGameId(),
|
|
1238
1300
|
apiUrl: this.getApiUrl(),
|
|
@@ -1241,6 +1303,28 @@ ${identity}`;
|
|
|
1241
1303
|
hasAnonymousId: Boolean(next.anonymousId)
|
|
1242
1304
|
});
|
|
1243
1305
|
this.listeners.forEach((listener) => listener(next));
|
|
1306
|
+
void this.detectIntegrationOnce();
|
|
1307
|
+
}
|
|
1308
|
+
/** Detects each resolved game/API pair at most once for this iframe session. */
|
|
1309
|
+
async detectIntegrationOnce() {
|
|
1310
|
+
const gameId = this.getGameId();
|
|
1311
|
+
const apiUrl = this.getApiUrl();
|
|
1312
|
+
if (!gameId || !apiUrl) return;
|
|
1313
|
+
const key = `${apiUrl}
|
|
1314
|
+
${gameId}`;
|
|
1315
|
+
if (this.integrationDetectionKeys.has(key)) return;
|
|
1316
|
+
this.integrationDetectionKeys.add(key);
|
|
1317
|
+
try {
|
|
1318
|
+
await this.getClient().detectIntegration(gameId);
|
|
1319
|
+
this.debug("integration detected", { gameId, apiUrl });
|
|
1320
|
+
} catch (error) {
|
|
1321
|
+
this.integrationDetectionKeys.delete(key);
|
|
1322
|
+
this.debug("integration detection failed", {
|
|
1323
|
+
gameId,
|
|
1324
|
+
apiUrl,
|
|
1325
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1244
1328
|
}
|
|
1245
1329
|
resolveParentOrigin() {
|
|
1246
1330
|
if (this.options.parentOrigin) return this.options.parentOrigin;
|
|
@@ -1285,6 +1369,6 @@ ${identity}`;
|
|
|
1285
1369
|
}
|
|
1286
1370
|
};
|
|
1287
1371
|
|
|
1288
|
-
export { ApiClient, BrowserLocalStorageAuthStorage, HttpClient, HttpError, LooplayAuth, LooplayIframeAuth, LooplaySDK, LooplaySDKError, MemoryAuthStorage, MissingAuthError, MissingBaseUrlError, NotAuthenticatedError, NotInitializedError, ServiceClient, TelegramAuthProvider };
|
|
1372
|
+
export { ApiClient, BrowserLocalStorageAuthStorage, HttpClient, HttpError, LOOPLAY_SDK_VERSION, LooplayAuth, LooplayIframeAuth, LooplaySDK, LooplaySDKError, MemoryAuthStorage, MissingAuthError, MissingBaseUrlError, NotAuthenticatedError, NotInitializedError, ServiceClient, TelegramAuthProvider };
|
|
1289
1373
|
//# sourceMappingURL=looplay-sdk.esm.js.map
|
|
1290
1374
|
//# sourceMappingURL=looplay-sdk.esm.js.map
|