@looplay/sdk 0.6.0 → 0.7.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/README.md +141 -4
- package/dist/looplay-sdk.cjs.js +74 -179
- package/dist/looplay-sdk.cjs.js.map +1 -1
- package/dist/looplay-sdk.esm.js +75 -179
- package/dist/looplay-sdk.esm.js.map +1 -1
- package/dist/looplay-sdk.min.js +2 -2
- package/dist/looplay-sdk.min.js.map +1 -1
- package/dist/types/apps/LooplaySDK.types.d.ts +13 -23
- package/dist/types/apps/api-client.d.ts +18 -23
- package/dist/types/apps/service-client.d.ts +14 -22
- package/dist/types/errors.d.ts +0 -3
- package/dist/types/types.d.ts +2 -1
- package/package.json +1 -1
package/dist/looplay-sdk.esm.js
CHANGED
|
@@ -29,12 +29,6 @@ var NotAuthenticatedError = class extends LooplaySDKError {
|
|
|
29
29
|
this.name = "NotAuthenticatedError";
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
-
var MissingStoreError = class extends LooplaySDKError {
|
|
33
|
-
constructor(gameId) {
|
|
34
|
-
super(`Game ${gameId} has no storefront configured.`);
|
|
35
|
-
this.name = "MissingStoreError";
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
32
|
|
|
39
33
|
// src/auth/storage.ts
|
|
40
34
|
var MemoryAuthStorage = class {
|
|
@@ -250,54 +244,49 @@ var ServiceClient = class {
|
|
|
250
244
|
return this.http.request("POST", "/account/logout", { body });
|
|
251
245
|
}
|
|
252
246
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
253
|
-
// Games
|
|
247
|
+
// Games — one SDK integration = one game, resolved on every call from the
|
|
248
|
+
// public `x-game-key` header (the game's `appId`). There is no multi-game
|
|
249
|
+
// catalog browsing here (list/search/detail across games) and no `:id`/
|
|
250
|
+
// `storeId` path segments — that's the app-facing surface, out of scope
|
|
251
|
+
// for this per-game SDK. Never use the creator-secret tier
|
|
252
|
+
// (`x-creator-key`/`x-game-secret`) from client/browser code — that would
|
|
253
|
+
// leak the secret to every player.
|
|
254
254
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
255
|
-
async
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
async getGameDetail(gameId, bearerToken) {
|
|
259
|
-
return this.http.request("GET", `/sdk/games/${encodeURIComponent(gameId)}`, {
|
|
260
|
-
bearerToken
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
async listRecentPlayed(bearerToken, query) {
|
|
264
|
-
return this.http.request("GET", "/sdk/games/recent-play", { bearerToken, query });
|
|
265
|
-
}
|
|
266
|
-
async recordGameView(auth, gameId) {
|
|
267
|
-
await this.http.request("POST", `/sdk/games/${encodeURIComponent(gameId)}/views/record`, {
|
|
255
|
+
async recordGameView(gameKey, auth = {}) {
|
|
256
|
+
await this.http.request("POST", "/sdk/games/views/record", {
|
|
268
257
|
...auth,
|
|
269
|
-
headers: this.trackingHeaders(auth)
|
|
258
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) }
|
|
270
259
|
});
|
|
271
260
|
return true;
|
|
272
261
|
}
|
|
273
262
|
/** Signs a fresh `attemptToken` — required by `completeGameplayAttempt`/`completeGameplayMatch`/`recordGameplayAction`. */
|
|
274
|
-
async startGameplayAttempt(
|
|
275
|
-
return this.http.request("POST",
|
|
263
|
+
async startGameplayAttempt(gameKey, auth = {}, body = {}) {
|
|
264
|
+
return this.http.request("POST", "/sdk/games/attempts/start", {
|
|
276
265
|
...auth,
|
|
277
|
-
headers: this.trackingHeaders(auth),
|
|
266
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
278
267
|
body
|
|
279
268
|
});
|
|
280
269
|
}
|
|
281
|
-
async completeGameplayAttempt(
|
|
282
|
-
await this.http.request("POST",
|
|
270
|
+
async completeGameplayAttempt(gameKey, auth, body) {
|
|
271
|
+
await this.http.request("POST", "/sdk/games/attempts/complete", {
|
|
283
272
|
...auth,
|
|
284
|
-
headers: this.trackingHeaders(auth),
|
|
273
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
285
274
|
body
|
|
286
275
|
});
|
|
287
276
|
return true;
|
|
288
277
|
}
|
|
289
|
-
async completeGameplayMatch(
|
|
290
|
-
await this.http.request("POST",
|
|
278
|
+
async completeGameplayMatch(gameKey, auth, body) {
|
|
279
|
+
await this.http.request("POST", "/sdk/games/matches/complete", {
|
|
291
280
|
...auth,
|
|
292
|
-
headers: this.trackingHeaders(auth),
|
|
281
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
293
282
|
body
|
|
294
283
|
});
|
|
295
284
|
return true;
|
|
296
285
|
}
|
|
297
|
-
async recordGameplayAction(
|
|
298
|
-
return this.http.request("POST",
|
|
286
|
+
async recordGameplayAction(gameKey, auth, body) {
|
|
287
|
+
return this.http.request("POST", "/sdk/games/actions/record", {
|
|
299
288
|
...auth,
|
|
300
|
-
headers: this.trackingHeaders(auth),
|
|
289
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
301
290
|
body
|
|
302
291
|
});
|
|
303
292
|
}
|
|
@@ -317,104 +306,36 @@ var ServiceClient = class {
|
|
|
317
306
|
"x-looplay-anon-token": auth.anonTrackingToken
|
|
318
307
|
};
|
|
319
308
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// (single asset or bundle) defined by the game's creator. Coin balance
|
|
323
|
-
// itself comes from getBalances()/getMyBalance(). Purchases require a
|
|
324
|
-
// signed `checkoutToken` from `createStoreCheckoutIntent` first.
|
|
325
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
326
|
-
async getStoreByStoreId(storeId) {
|
|
327
|
-
return this.http.request("GET", `/sdk/games/store/${encodeURIComponent(storeId)}`);
|
|
328
|
-
}
|
|
329
|
-
async listMyStorePurchasesByStoreId(bearerToken, storeId) {
|
|
330
|
-
return this.http.request("GET", `/sdk/games/store/${encodeURIComponent(storeId)}/purchases/me`, {
|
|
331
|
-
bearerToken
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
async createStoreCheckoutIntent(bearerToken, body, trackingSessionId) {
|
|
335
|
-
return this.http.request("POST", "/sdk/games/store/by-store-id/checkout-intent", {
|
|
336
|
-
bearerToken,
|
|
337
|
-
headers: { "x-game-tracking-session-id": trackingSessionId },
|
|
338
|
-
body
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
async purchaseStoreOffer(bearerToken, body, trackingSessionId) {
|
|
342
|
-
return this.http.request("POST", "/sdk/games/store/by-store-id/purchase", {
|
|
343
|
-
bearerToken,
|
|
344
|
-
headers: { "x-game-tracking-session-id": trackingSessionId },
|
|
345
|
-
body
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
349
|
-
// Runtime tier (`x-game-key`) — for a game embedding the SDK with its own
|
|
350
|
-
// public `appId`, without needing a resolved `gameId`/`storeId` first or a
|
|
351
|
-
// logged-in user (anonymous tracking still works via `TrackingAuth.anonId`).
|
|
352
|
-
// Never use the creator-secret tier (`x-creator-key`/`x-game-secret`) from
|
|
353
|
-
// client/browser code — that would leak the secret to every player.
|
|
354
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
355
|
-
async recordGameViewForGameKey(gameKey, auth = {}) {
|
|
356
|
-
await this.http.request("POST", "/sdk/games/runtime/views/record", {
|
|
357
|
-
...auth,
|
|
358
|
-
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) }
|
|
359
|
-
});
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
async startGameplayAttemptForGameKey(gameKey, auth = {}, body = {}) {
|
|
363
|
-
return this.http.request("POST", "/sdk/games/runtime/attempts/start", {
|
|
364
|
-
...auth,
|
|
365
|
-
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
366
|
-
body
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
async completeGameplayAttemptForGameKey(gameKey, auth = {}, body) {
|
|
370
|
-
await this.http.request("POST", "/sdk/games/runtime/attempts/complete", {
|
|
371
|
-
...auth,
|
|
372
|
-
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
373
|
-
body
|
|
374
|
-
});
|
|
375
|
-
return true;
|
|
376
|
-
}
|
|
377
|
-
async completeGameplayMatchForGameKey(gameKey, auth = {}, body) {
|
|
378
|
-
await this.http.request("POST", "/sdk/games/runtime/matches/complete", {
|
|
379
|
-
...auth,
|
|
380
|
-
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
381
|
-
body
|
|
382
|
-
});
|
|
383
|
-
return true;
|
|
384
|
-
}
|
|
385
|
-
async recordGameplayActionForGameKey(gameKey, auth = {}, body) {
|
|
386
|
-
return this.http.request("POST", "/sdk/games/runtime/actions/record", {
|
|
387
|
-
...auth,
|
|
388
|
-
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
389
|
-
body
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
async getStoreForGameKey(gameKey) {
|
|
309
|
+
/** Public storefront (sections + offers) for the integration's game. Read-only. */
|
|
310
|
+
async getStore(gameKey) {
|
|
393
311
|
return this.http.request("GET", "/sdk/games/store/current", {
|
|
394
312
|
headers: { "x-game-key": gameKey }
|
|
395
313
|
});
|
|
396
314
|
}
|
|
397
|
-
|
|
315
|
+
/** Public catalog of purchasable/ownable assets for the game. Read-only. */
|
|
316
|
+
async listGameAssets(gameKey) {
|
|
398
317
|
return this.http.request("GET", "/sdk/games/store/assets", {
|
|
399
318
|
headers: { "x-game-key": gameKey }
|
|
400
319
|
});
|
|
401
320
|
}
|
|
402
|
-
|
|
321
|
+
/** Assets the authenticated player already owns for this game. */
|
|
322
|
+
async listMyGameAssets(bearerToken, gameKey) {
|
|
403
323
|
return this.http.request("GET", "/sdk/games/store/assets/me", {
|
|
404
324
|
bearerToken,
|
|
405
325
|
headers: { "x-game-key": gameKey }
|
|
406
326
|
});
|
|
407
327
|
}
|
|
408
|
-
|
|
328
|
+
/** The authenticated player's past store purchases for this game. */
|
|
329
|
+
async listMyStorePurchases(bearerToken, gameKey) {
|
|
409
330
|
return this.http.request("GET", "/sdk/games/store/purchases/me", {
|
|
410
331
|
bearerToken,
|
|
411
332
|
headers: { "x-game-key": gameKey }
|
|
412
333
|
});
|
|
413
334
|
}
|
|
414
|
-
// No
|
|
415
|
-
//
|
|
416
|
-
// (
|
|
417
|
-
//
|
|
335
|
+
// No purchase route is reachable from client/browser code — spending coin
|
|
336
|
+
// balance always requires the creator+game secret pair
|
|
337
|
+
// (`x-creator-key`/`x-game-secret`), called from the game's own backend.
|
|
338
|
+
// This SDK intentionally never wires that tier up.
|
|
418
339
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
419
340
|
// Balance
|
|
420
341
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -489,15 +410,10 @@ var ApiClient = class {
|
|
|
489
410
|
this.getAccessToken = options.getAccessToken;
|
|
490
411
|
this.getAnonymousId = options.getAnonymousId;
|
|
491
412
|
}
|
|
492
|
-
/** Expose the underlying route-level client (requires manual bearerToken passing). */
|
|
413
|
+
/** Expose the underlying route-level client (requires manual bearerToken/gameKey passing). */
|
|
493
414
|
unsafeRaw() {
|
|
494
415
|
return this.raw;
|
|
495
416
|
}
|
|
496
|
-
// Public endpoints
|
|
497
|
-
async listGames(query) {
|
|
498
|
-
return this.raw.listGames(query);
|
|
499
|
-
}
|
|
500
|
-
// Authenticated endpoints
|
|
501
417
|
async requireToken() {
|
|
502
418
|
const token = await this.getAccessToken?.();
|
|
503
419
|
if (!token) throw new NotAuthenticatedError();
|
|
@@ -553,15 +469,15 @@ var ApiClient = class {
|
|
|
553
469
|
* swallow failures, unlike `ensureTrackingSession`, since these calls
|
|
554
470
|
* are rejected outright without a valid token.
|
|
555
471
|
*/
|
|
556
|
-
async ensureGameplayAttempt(
|
|
472
|
+
async ensureGameplayAttempt(gameKey, auth, attemptId) {
|
|
557
473
|
const now = Date.now();
|
|
558
|
-
if (this.gameplayAttempt && this.gameplayAttempt.
|
|
474
|
+
if (this.gameplayAttempt && this.gameplayAttempt.gameKey === gameKey && this.gameplayAttempt.attemptId === attemptId && this.gameplayAttempt.expiresAtMs > now + 1e4) {
|
|
559
475
|
return this.gameplayAttempt;
|
|
560
476
|
}
|
|
561
477
|
if (!this.gameplayAttemptPromise) {
|
|
562
|
-
this.gameplayAttemptPromise = this.raw.startGameplayAttempt(
|
|
478
|
+
this.gameplayAttemptPromise = this.raw.startGameplayAttempt(gameKey, auth, { attemptId }).then((dto) => {
|
|
563
479
|
this.gameplayAttempt = {
|
|
564
|
-
|
|
480
|
+
gameKey,
|
|
565
481
|
attemptId: dto.attemptId,
|
|
566
482
|
attemptToken: dto.attemptToken,
|
|
567
483
|
expiresAtMs: new Date(dto.expiresAt).getTime()
|
|
@@ -573,24 +489,13 @@ var ApiClient = class {
|
|
|
573
489
|
}
|
|
574
490
|
return this.gameplayAttemptPromise;
|
|
575
491
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
async
|
|
581
|
-
const detail = await this.getGameDetail(gameId);
|
|
582
|
-
const storeId = detail?.storeId;
|
|
583
|
-
if (!storeId) throw new MissingStoreError(gameId);
|
|
584
|
-
return storeId;
|
|
585
|
-
}
|
|
586
|
-
async listRecentPlayed(query) {
|
|
587
|
-
const token = await this.requireToken();
|
|
588
|
-
return this.raw.listRecentPlayed(token, query);
|
|
589
|
-
}
|
|
590
|
-
/** Call once when the game view opens, before any play/match tracking. */
|
|
591
|
-
async trackView(gameId) {
|
|
492
|
+
/**
|
|
493
|
+
* Call once when the game view opens, before any play/match tracking.
|
|
494
|
+
* `gameKey` is the game's public `appId`.
|
|
495
|
+
*/
|
|
496
|
+
async trackView(gameKey) {
|
|
592
497
|
const auth = await this.resolveTrackingAuth();
|
|
593
|
-
return this.raw.recordGameView(
|
|
498
|
+
return this.raw.recordGameView(gameKey, auth);
|
|
594
499
|
}
|
|
595
500
|
/**
|
|
596
501
|
* Starts a new play attempt and returns its id. Call when the user
|
|
@@ -603,22 +508,22 @@ var ApiClient = class {
|
|
|
603
508
|
this.gameplayAttempt = void 0;
|
|
604
509
|
return this.currentAttemptId;
|
|
605
510
|
}
|
|
606
|
-
async trackPlay(
|
|
511
|
+
async trackPlay(gameKey, playTimeSeconds) {
|
|
607
512
|
const auth = await this.resolveTrackingAuth();
|
|
608
513
|
const attemptId = this.currentAttemptId ?? this.startAttempt();
|
|
609
|
-
const { attemptToken } = await this.ensureGameplayAttempt(
|
|
610
|
-
return this.raw.completeGameplayAttempt(
|
|
514
|
+
const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
|
|
515
|
+
return this.raw.completeGameplayAttempt(gameKey, auth, { playTimeSeconds, attemptId, attemptToken });
|
|
611
516
|
}
|
|
612
|
-
async trackMatch(
|
|
517
|
+
async trackMatch(gameKey, body) {
|
|
613
518
|
const auth = await this.resolveTrackingAuth();
|
|
614
519
|
const attemptId = this.currentAttemptId ?? this.startAttempt();
|
|
615
|
-
const { attemptToken } = await this.ensureGameplayAttempt(
|
|
616
|
-
return this.raw.completeGameplayMatch(
|
|
520
|
+
const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
|
|
521
|
+
return this.raw.completeGameplayMatch(gameKey, auth, { ...body, attemptId, attemptToken });
|
|
617
522
|
}
|
|
618
|
-
async emit(
|
|
523
|
+
async emit(gameKey, actionCode, opts) {
|
|
619
524
|
const auth = await this.resolveTrackingAuth();
|
|
620
525
|
const attemptId = this.currentAttemptId ?? this.startAttempt();
|
|
621
|
-
const { attemptToken } = await this.ensureGameplayAttempt(
|
|
526
|
+
const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
|
|
622
527
|
const body = {
|
|
623
528
|
attemptId,
|
|
624
529
|
attemptToken,
|
|
@@ -627,38 +532,30 @@ var ApiClient = class {
|
|
|
627
532
|
refId: opts?.refId,
|
|
628
533
|
payload: opts?.payload
|
|
629
534
|
};
|
|
630
|
-
return this.raw.recordGameplayAction(
|
|
535
|
+
return this.raw.recordGameplayAction(gameKey, auth, body);
|
|
631
536
|
}
|
|
632
|
-
/** Fetches the storefront (sections + offers) for
|
|
633
|
-
async getStore(
|
|
634
|
-
|
|
635
|
-
return this.raw.getStoreByStoreId(storeId);
|
|
537
|
+
/** Fetches the storefront (sections + offers) for the integration's game. Read-only, no auth required. */
|
|
538
|
+
async getStore(gameKey) {
|
|
539
|
+
return this.raw.getStore(gameKey);
|
|
636
540
|
}
|
|
637
|
-
/**
|
|
638
|
-
async
|
|
639
|
-
|
|
640
|
-
return this.raw.listMyStorePurchasesByStoreId(token, storeId);
|
|
541
|
+
/** Public catalog of purchasable/ownable assets for the game. Read-only, no auth required. */
|
|
542
|
+
async listGameAssets(gameKey) {
|
|
543
|
+
return this.raw.listGameAssets(gameKey);
|
|
641
544
|
}
|
|
642
|
-
/**
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
requestId: opts?.requestId,
|
|
652
|
-
quantity: opts?.quantity
|
|
653
|
-
});
|
|
654
|
-
return this.raw.purchaseStoreOffer(token, {
|
|
655
|
-
storeId,
|
|
656
|
-
offerCode,
|
|
657
|
-
requestId: intent.requestId,
|
|
658
|
-
quantity: intent.quantity,
|
|
659
|
-
checkoutToken: intent.checkoutToken
|
|
660
|
-
});
|
|
545
|
+
/** Assets the authenticated player already owns for this game. */
|
|
546
|
+
async listMyGameAssets(gameKey) {
|
|
547
|
+
const token = await this.requireToken();
|
|
548
|
+
return this.raw.listMyGameAssets(token, gameKey);
|
|
549
|
+
}
|
|
550
|
+
/** Purchase history for the authenticated player in the game's store. */
|
|
551
|
+
async listMyStorePurchases(gameKey) {
|
|
552
|
+
const token = await this.requireToken();
|
|
553
|
+
return this.raw.listMyStorePurchases(token, gameKey);
|
|
661
554
|
}
|
|
555
|
+
// There is no purchaseStoreOffer() here by design — spending coin balance
|
|
556
|
+
// requires the creator+game secret pair, which must be called from the
|
|
557
|
+
// game's own backend, never from this client-side SDK. Implement a
|
|
558
|
+
// checkout endpoint on your backend that calls gbs-service directly.
|
|
662
559
|
async getMyProfile() {
|
|
663
560
|
const token = await this.requireToken();
|
|
664
561
|
return this.raw.getMyProfile(token);
|
|
@@ -789,9 +686,8 @@ var LooplaySDK = class {
|
|
|
789
686
|
}
|
|
790
687
|
async verifyGameId(params) {
|
|
791
688
|
if ((params.verifyMode ?? "none") === "none") return;
|
|
792
|
-
if (!this.auth) throw new MissingAuthError();
|
|
793
689
|
if (!this.api) throw new MissingBaseUrlError();
|
|
794
|
-
await this.api.
|
|
690
|
+
await this.api.listGameAssets(params.gameId);
|
|
795
691
|
}
|
|
796
692
|
};
|
|
797
693
|
|
|
@@ -1343,6 +1239,6 @@ ${identity}`;
|
|
|
1343
1239
|
}
|
|
1344
1240
|
};
|
|
1345
1241
|
|
|
1346
|
-
export { ApiClient, BrowserLocalStorageAuthStorage, HttpClient, HttpError, LooplayAuth, LooplayIframeAuth, LooplaySDK, LooplaySDKError, MemoryAuthStorage, MissingAuthError, MissingBaseUrlError,
|
|
1242
|
+
export { ApiClient, BrowserLocalStorageAuthStorage, HttpClient, HttpError, LooplayAuth, LooplayIframeAuth, LooplaySDK, LooplaySDKError, MemoryAuthStorage, MissingAuthError, MissingBaseUrlError, NotAuthenticatedError, NotInitializedError, ServiceClient, TelegramAuthProvider };
|
|
1347
1243
|
//# sourceMappingURL=looplay-sdk.esm.js.map
|
|
1348
1244
|
//# sourceMappingURL=looplay-sdk.esm.js.map
|