@genex-ai/embed-sdk 0.5.0 → 0.6.1

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.
@@ -7,11 +7,12 @@ var state = "pending";
7
7
  var user = null;
8
8
  var localTestMode = false;
9
9
  var embedToken;
10
- var colyseusUrl;
10
+ var colyseusUrls;
11
11
  var parentOrigin = null;
12
12
  var initialized = false;
13
13
  var redeeming = false;
14
14
  var requestingGuest = false;
15
+ var identityEpoch = 0;
15
16
  var handshakeTimeoutMs = 1e4;
16
17
  var refreshDelayMs = 10 * 6e4;
17
18
  var refreshRetryMs = 6e4;
@@ -110,6 +111,9 @@ function getEmbedToken() {
110
111
  function getColyseusAuth() {
111
112
  return embedToken ? { embedToken } : void 0;
112
113
  }
114
+ function getColyseusUrls() {
115
+ return colyseusUrls ?? [];
116
+ }
113
117
  function waitForAuth() {
114
118
  if (state === "authenticated" && user) return Promise.resolve({ user });
115
119
  if (state === "blocked") return Promise.reject(new Error("genex embed auth: blocked"));
@@ -416,6 +420,7 @@ async function fetchDashboardOrigins() {
416
420
  async function redeemTicket(ticket) {
417
421
  if (!config || localTestMode || redeeming || state !== "pending" && state !== "guest") return;
418
422
  const upgradingFromGuest = state === "guest";
423
+ const epoch = ++identityEpoch;
419
424
  redeeming = true;
420
425
  try {
421
426
  const res = await doFetch(`${config.apiUrl}/api/embed/session`, {
@@ -423,16 +428,17 @@ async function redeemTicket(ticket) {
423
428
  headers: { "Content-Type": "application/json" },
424
429
  body: JSON.stringify({ ticket })
425
430
  });
431
+ if (epoch !== identityEpoch) return;
426
432
  if (!res.ok) {
427
433
  emit("error", { error: new Error(`ticket redemption failed (${res.status})`) });
428
434
  if (!upgradingFromGuest) await handleRedeemFailure();
429
435
  return;
430
436
  }
431
437
  const body = await res.json();
438
+ if (epoch !== identityEpoch) return;
432
439
  embedToken = body.embedToken;
433
440
  user = body.user;
434
- colyseusUrl = body.colyseus?.url;
435
- void colyseusUrl;
441
+ colyseusUrls = body.colyseus?.urls;
436
442
  state = "authenticated";
437
443
  clearRetryFlag();
438
444
  removeOverlay();
@@ -447,6 +453,7 @@ async function redeemTicket(ticket) {
447
453
  for (const waiter of playerWaiters.splice(0)) waiter.resolve({ user, guest: false });
448
454
  emit("authenticated", ctx);
449
455
  } catch (error) {
456
+ if (epoch !== identityEpoch) return;
450
457
  emit("error", { error });
451
458
  if (!upgradingFromGuest) await handleRedeemFailure();
452
459
  } finally {
@@ -456,6 +463,7 @@ async function redeemTicket(ticket) {
456
463
  async function requestGuestSession() {
457
464
  if (!config || localTestMode || requestingGuest || redeeming) return;
458
465
  if (state !== "pending" && state !== "guest") return;
466
+ const epoch = ++identityEpoch;
459
467
  requestingGuest = true;
460
468
  try {
461
469
  const res = await doFetch(`${config.apiUrl}/api/embed/guest-session`, {
@@ -463,17 +471,20 @@ async function requestGuestSession() {
463
471
  headers: { "Content-Type": "application/json" },
464
472
  body: JSON.stringify({ slug: config.slug })
465
473
  });
474
+ if (epoch !== identityEpoch) return;
466
475
  if (!res.ok) {
467
476
  emit("error", { error: new Error(`guest session failed (${res.status})`) });
468
477
  enterBlocked();
469
478
  return;
470
479
  }
471
480
  const body = await res.json();
481
+ if (epoch !== identityEpoch) return;
472
482
  embedToken = body.embedToken;
473
483
  user = body.user;
474
- colyseusUrl = body.colyseus?.url;
484
+ colyseusUrls = body.colyseus?.urls;
475
485
  enterGuest();
476
486
  } catch (error) {
487
+ if (epoch !== identityEpoch) return;
477
488
  emit("error", { error });
478
489
  enterBlocked();
479
490
  } finally {
@@ -551,18 +562,22 @@ function scheduleRefresh() {
551
562
  }
552
563
  async function refreshToken() {
553
564
  if (!config || state !== "authenticated" && state !== "guest" || !embedToken) return;
565
+ const epoch = identityEpoch;
566
+ const tokenAtStart = embedToken;
554
567
  let status;
555
568
  try {
556
569
  const res = await doFetch(`${config.apiUrl}/api/embed/session/refresh`, {
557
570
  method: "POST",
558
- headers: { Authorization: `Bearer ${embedToken}` },
571
+ headers: { Authorization: `Bearer ${tokenAtStart}` },
559
572
  // Best-effort completion if the tab starts unloading mid-refresh; zero
560
573
  // security consequence either way (worst case the token just expires).
561
574
  keepalive: true
562
575
  });
563
576
  status = res.status;
577
+ if (epoch !== identityEpoch || embedToken !== tokenAtStart) return;
564
578
  if (res.ok) {
565
579
  const body = await res.json();
580
+ if (epoch !== identityEpoch || embedToken !== tokenAtStart) return;
566
581
  embedToken = body.embedToken;
567
582
  scheduleRefresh();
568
583
  return;
@@ -570,6 +585,7 @@ async function refreshToken() {
570
585
  } catch {
571
586
  status = void 0;
572
587
  }
588
+ if (epoch !== identityEpoch || embedToken !== tokenAtStart) return;
573
589
  if (status === 401) {
574
590
  if (state === "guest") {
575
591
  void requestGuestSession();
@@ -586,6 +602,7 @@ async function refreshToken() {
586
602
  }
587
603
  function enterBlocked() {
588
604
  if (state === "blocked") return;
605
+ identityEpoch++;
589
606
  state = "blocked";
590
607
  user = null;
591
608
  embedToken = void 0;
@@ -724,11 +741,12 @@ function __resetForTests(overrides) {
724
741
  user = null;
725
742
  localTestMode = false;
726
743
  embedToken = void 0;
727
- colyseusUrl = void 0;
744
+ colyseusUrls = void 0;
728
745
  parentOrigin = null;
729
746
  initialized = false;
730
747
  redeeming = false;
731
748
  requestingGuest = false;
749
+ identityEpoch++;
732
750
  listeners.clear();
733
751
  authWaiters = [];
734
752
  playerWaiters = [];
@@ -748,6 +766,7 @@ export {
748
766
  getUser,
749
767
  getEmbedToken,
750
768
  getColyseusAuth,
769
+ getColyseusUrls,
751
770
  waitForAuth,
752
771
  waitForPlayer,
753
772
  on,
package/dist/index.d.ts CHANGED
@@ -116,7 +116,7 @@ declare function getUser(): EmbedUser | null;
116
116
  */
117
117
  declare function getEmbedToken(): string | undefined;
118
118
  /**
119
- * The credential for multiplayer: pass as `connect({ ..., auth: getColyseusAuth() })`
119
+ * The credential for multiplayer: pass as `connect({ ..., auth: () => getColyseusAuth() })`
120
120
  * AFTER `await waitForPlayer()` — the relay rejects tokenless joins, but
121
121
  * accepts guest tokens. Read it fresh at every connect() call (tokens rotate
122
122
  * ~every 10 minutes). NEVER log this value.
@@ -124,6 +124,9 @@ declare function getEmbedToken(): string | undefined;
124
124
  declare function getColyseusAuth(): {
125
125
  embedToken: string;
126
126
  } | undefined;
127
+ /** The relay URLs for this session (server-owned; a regional list when configured). Pass to
128
+ * connect()/matchmake() as `urls` so the SDK joins the lowest-latency one; single-url fallback. */
129
+ declare function getColyseusUrls(): string[];
127
130
  /**
128
131
  * Resolves with the signed-in ACCOUNT once authenticated; rejects if the
129
132
  * session ends up blocked. THE gate for /state saves and any account-bound
@@ -220,4 +223,4 @@ declare function __resetForTests(overrides?: {
220
223
  refreshRetryMs?: number;
221
224
  }): void;
222
225
 
223
- export { type AuthState, type EmbedConfig, type EmbedEvent, type EmbedUser, type Leaderboard, type LeaderboardEntry, type PlayerStateResult, type SaveStateResult, type SubmitScoreResult, type WorldStateResult, __resetForTests, _stashTicketFromUrl, getAuthState, getColyseusAuth, getEmbedToken, getLeaderboard, getUser, initEmbed, isEmbedded, loadPlayerState, loadWorldState, on, savePlayerState, saveWorldState, submitScore, waitForAuth, waitForPlayer };
226
+ export { type AuthState, type EmbedConfig, type EmbedEvent, type EmbedUser, type Leaderboard, type LeaderboardEntry, type PlayerStateResult, type SaveStateResult, type SubmitScoreResult, type WorldStateResult, __resetForTests, _stashTicketFromUrl, getAuthState, getColyseusAuth, getColyseusUrls, getEmbedToken, getLeaderboard, getUser, initEmbed, isEmbedded, loadPlayerState, loadWorldState, on, savePlayerState, saveWorldState, submitScore, waitForAuth, waitForPlayer };
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  _stashTicketFromUrl,
4
4
  getAuthState,
5
5
  getColyseusAuth,
6
+ getColyseusUrls,
6
7
  getEmbedToken,
7
8
  getLeaderboard,
8
9
  getUser,
@@ -16,12 +17,13 @@ import {
16
17
  submitScore,
17
18
  waitForAuth,
18
19
  waitForPlayer
19
- } from "./chunk-GDJLPL6W.js";
20
+ } from "./chunk-UOC55SJL.js";
20
21
  export {
21
22
  __resetForTests,
22
23
  _stashTicketFromUrl,
23
24
  getAuthState,
24
25
  getColyseusAuth,
26
+ getColyseusUrls,
25
27
  getEmbedToken,
26
28
  getLeaderboard,
27
29
  getUser,
package/dist/sentry.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  _stashTicketFromUrl,
3
3
  getUser,
4
4
  on
5
- } from "./chunk-GDJLPL6W.js";
5
+ } from "./chunk-UOC55SJL.js";
6
6
 
7
7
  // src/sentry.ts
8
8
  import * as Sentry from "@sentry/browser";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/embed-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Player identity + durable game state for genex games — signed-in or guest play, per-player save slots, shared world state, and soft-trust leaderboards.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",