@looplay/sdk 0.8.3 → 0.8.5

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.
@@ -259,16 +259,16 @@ var ServiceClient = class {
259
259
  });
260
260
  return true;
261
261
  }
262
- /** Signs a fresh `attemptToken` — required by `completeGameplayAttempt`/`completeGameplayMatch`/`recordGameplayAction`. */
263
- async startGameplayAttempt(gameKey, auth = {}, body = {}) {
264
- return this.http.request("POST", "/sdk/games/attempts/start", {
262
+ /** Signs a fresh `playSessionToken` — required by `completePlaySession`/`completeGameplayMatch`/`recordGameplayAction`. */
263
+ async startPlaySession(gameKey, auth = {}, body = {}) {
264
+ return this.http.request("POST", "/sdk/games/play-sessions/start", {
265
265
  ...auth,
266
266
  headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
267
267
  body
268
268
  });
269
269
  }
270
- async completeGameplayAttempt(gameKey, auth, body) {
271
- await this.http.request("POST", "/sdk/games/attempts/complete", {
270
+ async completePlaySession(gameKey, auth, body) {
271
+ await this.http.request("POST", "/sdk/games/play-sessions/complete", {
272
272
  ...auth,
273
273
  headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
274
274
  body
@@ -300,6 +300,16 @@ var ServiceClient = class {
300
300
  headers: { "x-looplay-anon-id": anonId }
301
301
  });
302
302
  }
303
+ /**
304
+ * Lightweight integration heartbeat for review/liveops. This intentionally
305
+ * does not record a view/play metric.
306
+ */
307
+ async detectGameIntegration(gameKey) {
308
+ await this.http.request("POST", "/sdk/games/integration/detect", {
309
+ headers: { "x-game-key": gameKey }
310
+ });
311
+ return true;
312
+ }
303
313
  trackingHeaders(auth) {
304
314
  return {
305
315
  "x-game-tracking-session-id": auth.trackingSessionId,
@@ -337,7 +347,7 @@ var ServiceClient = class {
337
347
  // `x-game-key` + the purchasing player's own bearer token, no backend or
338
348
  // secret pair required. Safe to call directly from client/browser code —
339
349
  // `checkoutToken` (short-lived, server-signed) plays the same role as
340
- // `attemptToken` on the tracking routes. `ApiClient.purchaseStoreOffer`
350
+ // `playSessionToken` on the tracking routes. `ApiClient.purchaseStoreOffer`
341
351
  // wraps this pair for you.
342
352
  // ─────────────────────────────────────────────────────────────────────────────
343
353
  /** Signs a `checkoutToken` for one purchase — required by `purchaseMyStoreOffer`. */
@@ -408,30 +418,6 @@ var ServiceClient = class {
408
418
  async getMyBalance(bearerToken) {
409
419
  return this.http.request("GET", "/sdk/user/balance", { bearerToken });
410
420
  }
411
- // ─────────────────────────────────────────────────────────────────────────────
412
- // Referrals
413
- // ─────────────────────────────────────────────────────────────────────────────
414
- async listReferrals(bearerToken, query) {
415
- return this.http.request("GET", "/sdk/referrals", { bearerToken, query });
416
- }
417
- async setReferral(bearerToken, body) {
418
- return this.http.request("POST", "/sdk/referrals/set", { bearerToken, body });
419
- }
420
- // ─────────────────────────────────────────────────────────────────────────────
421
- // Tasks
422
- // ─────────────────────────────────────────────────────────────────────────────
423
- async listTasks(bearerToken, query) {
424
- return this.http.request("GET", "/sdk/task", { bearerToken, query: { ...query } });
425
- }
426
- async listFinishedTasks(bearerToken, query) {
427
- return this.http.request("GET", "/sdk/task/finished", { bearerToken, query: { ...query } });
428
- }
429
- async startTask(bearerToken, body) {
430
- return this.http.request("POST", "/sdk/task/start", { bearerToken, body });
431
- }
432
- async claimTask(bearerToken, body) {
433
- return this.http.request("POST", "/sdk/task/claim", { bearerToken, body });
434
- }
435
421
  };
436
422
 
437
423
  // src/apps/api-client.ts
@@ -451,9 +437,9 @@ var ApiClient = class {
451
437
  getAnonymousId;
452
438
  trackingSession;
453
439
  trackingSessionPromise;
454
- currentAttemptId;
455
- gameplayAttempt;
456
- gameplayAttemptPromise;
440
+ currentPlaySessionId;
441
+ playSession;
442
+ playSessionPromise;
457
443
  constructor(options) {
458
444
  if (!options.baseUrl) throw new MissingBaseUrlError();
459
445
  this.raw = new ServiceClient({
@@ -468,6 +454,9 @@ var ApiClient = class {
468
454
  unsafeRaw() {
469
455
  return this.raw;
470
456
  }
457
+ async detectIntegration(gameKey) {
458
+ return this.raw.detectGameIntegration(gameKey);
459
+ }
471
460
  // Real-time (balance_change / store_purchase_completed WebSocket events)
472
461
  // deliberately lives outside this class — import `WsClient` from
473
462
  // '@looplay/sdk/realtime' and construct it with the same `getAccessToken`
@@ -523,30 +512,30 @@ var ApiClient = class {
523
512
  return this.trackingSessionPromise;
524
513
  }
525
514
  /**
526
- * Signs (and caches until near expiry) the `attemptToken` required by
527
- * `trackPlay`/`trackMatch`/`emit` for the current attempt — does not
515
+ * Signs (and caches until near expiry) the `playSessionToken` required by
516
+ * `trackPlay`/`trackMatch`/`emit` for the current play session — does not
528
517
  * swallow failures, unlike `ensureTrackingSession`, since these calls
529
518
  * are rejected outright without a valid token.
530
519
  */
531
- async ensureGameplayAttempt(gameKey, auth, attemptId) {
520
+ async ensurePlaySession(gameKey, auth, playSessionId) {
532
521
  const now = Date.now();
533
- if (this.gameplayAttempt && this.gameplayAttempt.gameKey === gameKey && this.gameplayAttempt.attemptId === attemptId && this.gameplayAttempt.expiresAtMs > now + 1e4) {
534
- return this.gameplayAttempt;
522
+ if (this.playSession && this.playSession.gameKey === gameKey && this.playSession.playSessionId === playSessionId && this.playSession.expiresAtMs > now + 1e4) {
523
+ return this.playSession;
535
524
  }
536
- if (!this.gameplayAttemptPromise) {
537
- this.gameplayAttemptPromise = this.raw.startGameplayAttempt(gameKey, auth, { attemptId }).then((dto) => {
538
- this.gameplayAttempt = {
525
+ if (!this.playSessionPromise) {
526
+ this.playSessionPromise = this.raw.startPlaySession(gameKey, auth, { playSessionId }).then((dto) => {
527
+ this.playSession = {
539
528
  gameKey,
540
- attemptId: dto.attemptId,
541
- attemptToken: dto.attemptToken,
529
+ playSessionId: dto.playSessionId,
530
+ playSessionToken: dto.playSessionToken,
542
531
  expiresAtMs: new Date(dto.expiresAt).getTime()
543
532
  };
544
- return this.gameplayAttempt;
533
+ return this.playSession;
545
534
  }).finally(() => {
546
- this.gameplayAttemptPromise = void 0;
535
+ this.playSessionPromise = void 0;
547
536
  });
548
537
  }
549
- return this.gameplayAttemptPromise;
538
+ return this.playSessionPromise;
550
539
  }
551
540
  /**
552
541
  * Call once when the game view opens, before any play/match tracking.
@@ -557,35 +546,35 @@ var ApiClient = class {
557
546
  return this.raw.recordGameView(gameKey, auth);
558
547
  }
559
548
  /**
560
- * Starts a new play attempt and returns its id. Call when the user
549
+ * Starts a new play session and returns its id. Call when the user
561
550
  * presses Play; `trackPlay`/`trackMatch`/`emit` reuse this id (and the
562
- * signed `attemptToken` backing it) across calls until the next
563
- * `startAttempt()`, auto-starting one on first use if none was started.
551
+ * signed `playSessionToken` backing it) across calls until the next
552
+ * `startPlaySession()`, auto-starting one on first use if none was started.
564
553
  */
565
- startAttempt() {
566
- this.currentAttemptId = generateId();
567
- this.gameplayAttempt = void 0;
568
- return this.currentAttemptId;
554
+ startPlaySession() {
555
+ this.currentPlaySessionId = generateId();
556
+ this.playSession = void 0;
557
+ return this.currentPlaySessionId;
569
558
  }
570
559
  async trackPlay(gameKey, playTimeSeconds) {
571
560
  const auth = await this.resolveTrackingAuth();
572
- const attemptId = this.currentAttemptId ?? this.startAttempt();
573
- const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
574
- return this.raw.completeGameplayAttempt(gameKey, auth, { playTimeSeconds, attemptId, attemptToken });
561
+ const playSessionId = this.currentPlaySessionId ?? this.startPlaySession();
562
+ const { playSessionToken } = await this.ensurePlaySession(gameKey, auth, playSessionId);
563
+ return this.raw.completePlaySession(gameKey, auth, { playTimeSeconds, playSessionId, playSessionToken });
575
564
  }
576
565
  async trackMatch(gameKey, body) {
577
566
  const auth = await this.resolveTrackingAuth();
578
- const attemptId = this.currentAttemptId ?? this.startAttempt();
579
- const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
580
- return this.raw.completeGameplayMatch(gameKey, auth, { ...body, attemptId, attemptToken });
567
+ const playSessionId = this.currentPlaySessionId ?? this.startPlaySession();
568
+ const { playSessionToken } = await this.ensurePlaySession(gameKey, auth, playSessionId);
569
+ return this.raw.completeGameplayMatch(gameKey, auth, { ...body, playSessionId, playSessionToken });
581
570
  }
582
571
  async emit(gameKey, actionCode, opts) {
583
572
  const auth = await this.resolveTrackingAuth();
584
- const attemptId = this.currentAttemptId ?? this.startAttempt();
585
- const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
573
+ const playSessionId = this.currentPlaySessionId ?? this.startPlaySession();
574
+ const { playSessionToken } = await this.ensurePlaySession(gameKey, auth, playSessionId);
586
575
  const body = {
587
- attemptId,
588
- attemptToken,
576
+ playSessionId,
577
+ playSessionToken,
589
578
  actionCode,
590
579
  value: opts?.value,
591
580
  refId: opts?.refId,
@@ -613,7 +602,7 @@ var ApiClient = class {
613
602
  }
614
603
  /**
615
604
  * Spends coin balance to purchase a store offer — signs a checkout intent
616
- * first, then redeems it, mirroring the gameplay attempt flow above.
605
+ * first, then redeems it, mirroring the play session flow above.
617
606
  * Self-serve: only the player's own access token is used, no secret pair
618
607
  * required, safe to call directly from the game client. Also see
619
608
  * `onStorePurchase` to react to the result in real time from any tab.
@@ -648,30 +637,6 @@ var ApiClient = class {
648
637
  const token = await this.requireToken();
649
638
  return this.raw.getBalanceHistory(token, query);
650
639
  }
651
- async listReferrals(query) {
652
- const token = await this.requireToken();
653
- return this.raw.listReferrals(token, query);
654
- }
655
- async setReferral(body) {
656
- const token = await this.requireToken();
657
- return this.raw.setReferral(token, body);
658
- }
659
- async listTasks(query) {
660
- const token = await this.requireToken();
661
- return this.raw.listTasks(token, query);
662
- }
663
- async listFinishedTasks(query) {
664
- const token = await this.requireToken();
665
- return this.raw.listFinishedTasks(token, query);
666
- }
667
- async startTask(body) {
668
- const token = await this.requireToken();
669
- return this.raw.startTask(token, body);
670
- }
671
- async claimTask(body) {
672
- const token = await this.requireToken();
673
- return this.raw.claimTask(token, body);
674
- }
675
640
  };
676
641
 
677
642
  // src/looplay-sdk.ts
@@ -697,6 +662,7 @@ var LooplaySDK = class {
697
662
  async init(params) {
698
663
  this.gameId = params.gameId;
699
664
  await this.auth?.init();
665
+ await this.detectIntegration(params);
700
666
  await this.verifyGameId(params);
701
667
  this.initialized = true;
702
668
  }
@@ -718,11 +684,11 @@ var LooplaySDK = class {
718
684
  if (!this.gameId) throw new NotInitializedError();
719
685
  return this.api.trackView(this.gameId);
720
686
  }
721
- /** Starts a new play attempt; call when the user presses Play. Returns the attempt id. */
722
- startAttempt() {
687
+ /** Starts a new play session; call when the user presses Play. Returns the play session id. */
688
+ startPlaySession() {
723
689
  this.assertInitialized();
724
690
  if (!this.api) throw new MissingBaseUrlError();
725
- return this.api.startAttempt();
691
+ return this.api.startPlaySession();
726
692
  }
727
693
  async trackPlay(playTimeSeconds) {
728
694
  this.assertInitialized();
@@ -765,6 +731,10 @@ var LooplaySDK = class {
765
731
  if (!this.api) throw new MissingBaseUrlError();
766
732
  await this.api.listGameAssets(params.gameId);
767
733
  }
734
+ async detectIntegration(params) {
735
+ if (!this.api) return;
736
+ await this.api.detectIntegration(params.gameId);
737
+ }
768
738
  };
769
739
 
770
740
  // src/auth/jwt.ts
@@ -934,11 +904,11 @@ var LooplayIframeAuth = class {
934
904
  return this.state.jwt;
935
905
  }
936
906
  getApiUrl() {
937
- return this.options.apiUrl ?? this.state.apiUrl;
907
+ return this.state.apiUrl ?? this.options.apiUrl ?? null;
938
908
  }
939
909
  /** Resolves to the configured `appId`, falling back to whatever the parent pushed as `gameId`. */
940
910
  getGameId() {
941
- return this.options.appId ?? this.state.gameId;
911
+ return this.state.gameId ?? this.options.appId ?? null;
942
912
  }
943
913
  getAnonymousId() {
944
914
  return this.state.anonymousId;
@@ -1090,9 +1060,9 @@ var LooplayIframeAuth = class {
1090
1060
  this.debug("trackView dispatched", { gameId });
1091
1061
  return this.getClient().trackView(gameId);
1092
1062
  }
1093
- /** Starts a new play attempt; call when the user presses Play. Returns the attempt id. */
1094
- startAttempt() {
1095
- return this.getClient().startAttempt();
1063
+ /** Starts a new play session; call when the user presses Play. Returns the play session id. */
1064
+ startPlaySession() {
1065
+ return this.getClient().startPlaySession();
1096
1066
  }
1097
1067
  trackPlay(playTimeSeconds) {
1098
1068
  const gameId = this.getGameId();