@genex-ai/embed-sdk 0.13.0 → 0.14.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.
@@ -1,12 +1,249 @@
1
+ // ../embed-protocol/src/constants.ts
2
+ var NATIVE_CHANNEL = "genex-native";
3
+ var NATIVE_PROTOCOL_VERSION = 1;
4
+ var NATIVE_MARKER_PARAM = "genex_native";
5
+ var NATIVE_MARKER_VALUE = "1";
6
+ var NATIVE_PROTOCOL_PARAM = "genex_protocol";
7
+ var NATIVE_ATTEMPT_PARAM = "genex_attempt";
8
+ var NATIVE_NONCE_PARAM = "genex_nonce";
9
+ var NATIVE_RECEIVER = "__genexNative";
10
+ var MAX_BRIDGE_MESSAGE_BYTES = 4096;
11
+ var BRIDGE_ID_RE = /^[0-9a-f]{32}$/;
12
+ var SLUG_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
13
+ function isBridgeId(value) {
14
+ return typeof value === "string" && BRIDGE_ID_RE.test(value);
15
+ }
16
+ function isBridgeSlug(value) {
17
+ return typeof value === "string" && SLUG_RE.test(value);
18
+ }
19
+
20
+ // ../embed-protocol/src/base64url.ts
21
+ var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
22
+ var LOOKUP = (() => {
23
+ const table = new Array(128).fill(-1);
24
+ for (let i = 0; i < ALPHABET.length; i++) table[ALPHABET.charCodeAt(i)] = i;
25
+ return table;
26
+ })();
27
+ function utf8Bytes(text) {
28
+ const out = [];
29
+ for (let i = 0; i < text.length; i++) {
30
+ let code = text.charCodeAt(i);
31
+ if (code >= 55296 && code <= 56319) {
32
+ const next = i + 1 < text.length ? text.charCodeAt(i + 1) : 0;
33
+ if (next >= 56320 && next <= 57343) {
34
+ code = 65536 + (code - 55296 << 10) + (next - 56320);
35
+ i++;
36
+ } else {
37
+ code = 65533;
38
+ }
39
+ } else if (code >= 56320 && code <= 57343) {
40
+ code = 65533;
41
+ }
42
+ if (code < 128) out.push(code);
43
+ else if (code < 2048) out.push(192 | code >> 6, 128 | code & 63);
44
+ else if (code < 65536) {
45
+ out.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
46
+ } else {
47
+ out.push(
48
+ 240 | code >> 18,
49
+ 128 | code >> 12 & 63,
50
+ 128 | code >> 6 & 63,
51
+ 128 | code & 63
52
+ );
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+ function utf8Text(bytes) {
58
+ let out = "";
59
+ for (let i = 0; i < bytes.length; ) {
60
+ const b0 = bytes[i];
61
+ let code;
62
+ let size;
63
+ if (b0 < 128) {
64
+ code = b0;
65
+ size = 1;
66
+ } else if ((b0 & 224) === 192) {
67
+ code = b0 & 31;
68
+ size = 2;
69
+ } else if ((b0 & 240) === 224) {
70
+ code = b0 & 15;
71
+ size = 3;
72
+ } else if ((b0 & 248) === 240) {
73
+ code = b0 & 7;
74
+ size = 4;
75
+ } else return null;
76
+ if (i + size > bytes.length) return null;
77
+ for (let k = 1; k < size; k++) {
78
+ const b = bytes[i + k];
79
+ if ((b & 192) !== 128) return null;
80
+ code = code << 6 | b & 63;
81
+ }
82
+ if (size === 2 && code < 128) return null;
83
+ if (size === 3 && (code < 2048 || code >= 55296 && code <= 57343)) return null;
84
+ if (size === 4 && (code < 65536 || code > 1114111)) return null;
85
+ if (code > 65535) {
86
+ const v = code - 65536;
87
+ out += String.fromCharCode(55296 + (v >> 10), 56320 + (v & 1023));
88
+ } else {
89
+ out += String.fromCharCode(code);
90
+ }
91
+ i += size;
92
+ }
93
+ return out;
94
+ }
95
+ function utf8ByteLength(text) {
96
+ return utf8Bytes(text).length;
97
+ }
98
+ function encodeBase64UrlJson(value) {
99
+ const bytes = utf8Bytes(JSON.stringify(value));
100
+ let out = "";
101
+ for (let i = 0; i < bytes.length; i += 3) {
102
+ const b0 = bytes[i];
103
+ const b1 = bytes[i + 1];
104
+ const b2 = bytes[i + 2];
105
+ out += ALPHABET[b0 >> 2];
106
+ out += ALPHABET[(b0 & 3) << 4 | (b1 ?? 0) >> 4];
107
+ if (b1 === void 0) break;
108
+ out += ALPHABET[(b1 & 15) << 2 | (b2 ?? 0) >> 6];
109
+ if (b2 === void 0) break;
110
+ out += ALPHABET[b2 & 63];
111
+ }
112
+ return out;
113
+ }
114
+ function decodeBase64UrlJson(encoded) {
115
+ if (encoded.length % 4 === 1) return void 0;
116
+ const bytes = [];
117
+ let buffer = 0;
118
+ let bits = 0;
119
+ for (let i = 0; i < encoded.length; i++) {
120
+ const code = encoded.charCodeAt(i);
121
+ const value = code < 128 ? LOOKUP[code] : -1;
122
+ if (value < 0) return void 0;
123
+ buffer = buffer << 6 | value;
124
+ bits += 6;
125
+ if (bits >= 8) {
126
+ bits -= 8;
127
+ bytes.push(buffer >> bits & 255);
128
+ }
129
+ }
130
+ if (bits > 0 && (buffer & (1 << bits) - 1) !== 0) return void 0;
131
+ const text = utf8Text(bytes);
132
+ if (text === null) return void 0;
133
+ try {
134
+ return JSON.parse(text);
135
+ } catch {
136
+ return void 0;
137
+ }
138
+ }
139
+
140
+ // ../embed-protocol/src/messages.ts
141
+ var MAX_TICKET_LENGTH = 2048;
142
+ var MAX_EXPIRES_AT_LENGTH = 32;
143
+ var ISO_INSTANT_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/;
144
+ function isRecord(value) {
145
+ return typeof value === "object" && value !== null && !Array.isArray(value);
146
+ }
147
+ function hasExactKeys(value, keys) {
148
+ const own = Object.keys(value);
149
+ if (own.length !== keys.length) return false;
150
+ for (const key of keys) {
151
+ if (!Object.prototype.hasOwnProperty.call(value, key)) return false;
152
+ }
153
+ return true;
154
+ }
155
+ var BASE_KEYS = ["channel", "v", "type", "slug", "attemptId", "nonce"];
156
+ function envelopeBaseIsValid(value) {
157
+ return value.channel === NATIVE_CHANNEL && value.v === NATIVE_PROTOCOL_VERSION && isBridgeSlug(value.slug) && isBridgeId(value.attemptId) && isBridgeId(value.nonce);
158
+ }
159
+ function parseBridgeNativeCommand(value) {
160
+ if (!isRecord(value) || !envelopeBaseIsValid(value)) return void 0;
161
+ switch (value.type) {
162
+ case "ticket":
163
+ if (!hasExactKeys(value, [...BASE_KEYS, "ticket", "expiresAt"])) return void 0;
164
+ if (typeof value.ticket !== "string" || value.ticket.length === 0 || value.ticket.length > MAX_TICKET_LENGTH) {
165
+ return void 0;
166
+ }
167
+ if (typeof value.expiresAt !== "string" || value.expiresAt.length > MAX_EXPIRES_AT_LENGTH || !ISO_INSTANT_RE.test(value.expiresAt)) {
168
+ return void 0;
169
+ }
170
+ return value;
171
+ case "guest":
172
+ case "cancel":
173
+ if (!hasExactKeys(value, BASE_KEYS)) return void 0;
174
+ return value;
175
+ case "foreground":
176
+ if (!hasExactKeys(value, [...BASE_KEYS, "active"])) return void 0;
177
+ if (typeof value.active !== "boolean") return void 0;
178
+ return value;
179
+ case "network":
180
+ if (!hasExactKeys(value, [...BASE_KEYS, "online"])) return void 0;
181
+ if (typeof value.online !== "boolean") return void 0;
182
+ return value;
183
+ default:
184
+ return void 0;
185
+ }
186
+ }
187
+
188
+ // ../embed-protocol/src/transport.ts
189
+ function read(raw, parse) {
190
+ if (typeof raw !== "string" || raw.length === 0) return { ok: false, reason: "undecodable" };
191
+ if (utf8ByteLength(raw) > MAX_BRIDGE_MESSAGE_BYTES) return { ok: false, reason: "too-large" };
192
+ const decoded = decodeBase64UrlJson(raw);
193
+ if (decoded === void 0) return { ok: false, reason: "undecodable" };
194
+ const message = parse(decoded);
195
+ if (message === void 0) return { ok: false, reason: "invalid" };
196
+ return { ok: true, message };
197
+ }
198
+ function readBridgeNativeCommand(raw) {
199
+ return read(raw, parseBridgeNativeCommand);
200
+ }
201
+ function encodeBridgeMessage(message) {
202
+ const encoded = encodeBase64UrlJson(message);
203
+ if (encoded.length > MAX_BRIDGE_MESSAGE_BYTES) return void 0;
204
+ return encoded;
205
+ }
206
+
207
+ // ../embed-protocol/src/entry-url.ts
208
+ function readParam(search, key) {
209
+ const query = search.charAt(0) === "?" ? search.slice(1) : search;
210
+ if (query.length === 0) return null;
211
+ for (const pair of query.split("&")) {
212
+ const eq = pair.indexOf("=");
213
+ if (eq === -1) continue;
214
+ if (pair.slice(0, eq) === key) return pair.slice(eq + 1);
215
+ }
216
+ return null;
217
+ }
218
+ function hasNativeMarker(search) {
219
+ return readParam(search, NATIVE_MARKER_PARAM) === NATIVE_MARKER_VALUE;
220
+ }
221
+ function readNativeEntry(search) {
222
+ if (!hasNativeMarker(search)) return null;
223
+ const attemptId = readParam(search, NATIVE_ATTEMPT_PARAM);
224
+ const nonce = readParam(search, NATIVE_NONCE_PARAM);
225
+ const protocol = Number(readParam(search, NATIVE_PROTOCOL_PARAM));
226
+ if (!isBridgeId(attemptId) || !isBridgeId(nonce)) return null;
227
+ if (protocol !== NATIVE_PROTOCOL_VERSION) return null;
228
+ return { attemptId, nonce, protocol };
229
+ }
230
+
1
231
  // src/index.ts
2
232
  var PROTOCOL_VERSION = 1;
3
233
  var RETRY_FLAG = "genex:embed:retry";
4
234
  var POPOVER_DISMISSED_FLAG = "genex:guest:popover-dismissed";
235
+ var LOCAL_AUTH_FLAG = "genex:embed:local-auth";
5
236
  var PLAYER_ID_KEY = "genex:player";
237
+ var SDK_VERSION = "0.14.0";
6
238
  var config = null;
7
239
  var state = "pending";
8
240
  var user = null;
9
241
  var localTestMode = false;
242
+ var nativeEntry = null;
243
+ var nativeReauthPending = false;
244
+ var nativeReauthTimer;
245
+ var heartbeatTimer;
246
+ var nativeBootAt = 0;
10
247
  var embedToken;
11
248
  var colyseusUrls;
12
249
  var parentOrigin = null;
@@ -18,6 +255,8 @@ var handshakeTimeoutMs = 1e4;
18
255
  var refreshDelayMs = 10 * 6e4;
19
256
  var refreshRetryMs = 6e4;
20
257
  var overlayTextDelayMs = 500;
258
+ var nativeReauthTimeoutMs = 3e4;
259
+ var heartbeatIntervalMs = 3e4;
21
260
  var handshakeTimer;
22
261
  var refreshTimer;
23
262
  var messageHandler;
@@ -58,6 +297,19 @@ function clearRetryFlag() {
58
297
  } catch {
59
298
  }
60
299
  }
300
+ function readLocalAuthFlag() {
301
+ try {
302
+ return win()?.sessionStorage?.getItem(LOCAL_AUTH_FLAG) === "1";
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+ function writeLocalAuthFlag() {
308
+ try {
309
+ win()?.sessionStorage?.setItem(LOCAL_AUTH_FLAG, "1");
310
+ } catch {
311
+ }
312
+ }
61
313
  function readPopoverDismissed() {
62
314
  try {
63
315
  return win()?.sessionStorage?.getItem(POPOVER_DISMISSED_FLAG) === "1";
@@ -96,11 +348,16 @@ function initEmbed(cfg) {
96
348
  dashboardOrigins: [...cfg.dashboardOrigins]
97
349
  };
98
350
  state = "pending";
99
- if (wantsLocalTest(w)) {
351
+ if (wantsLocalTestMode(w)) {
100
352
  enterLocalTestMode(w);
101
353
  return;
102
354
  }
103
355
  showOverlay("connecting");
356
+ const entry = detectNativeEntry(w);
357
+ if (entry) {
358
+ startNativeHandshake(w, entry);
359
+ return;
360
+ }
104
361
  if (isEmbedded()) {
105
362
  startEmbeddedHandshake(w);
106
363
  } else {
@@ -344,6 +601,120 @@ async function handleParentMessage(event) {
344
601
  }
345
602
  await redeemTicket(data.ticket);
346
603
  }
604
+ function nativeBridge(w) {
605
+ const candidate = w.ReactNativeWebView;
606
+ if (!candidate || typeof candidate !== "object") return null;
607
+ const post = candidate.postMessage;
608
+ return typeof post === "function" ? candidate : null;
609
+ }
610
+ function detectNativeEntry(w) {
611
+ if (isEmbedded()) return null;
612
+ let search;
613
+ try {
614
+ search = w.location.search ?? "";
615
+ } catch {
616
+ return null;
617
+ }
618
+ if (!hasNativeMarker(search)) return null;
619
+ if (!nativeBridge(w)) return null;
620
+ return readNativeEntry(search);
621
+ }
622
+ function inNativeMode() {
623
+ return nativeEntry !== null;
624
+ }
625
+ function postToNative(message) {
626
+ const w = win();
627
+ if (!w || !config || !nativeEntry) return;
628
+ const bridge = nativeBridge(w);
629
+ if (!bridge) return;
630
+ const encoded = encodeBridgeMessage({
631
+ channel: NATIVE_CHANNEL,
632
+ v: NATIVE_PROTOCOL_VERSION,
633
+ slug: config.slug,
634
+ attemptId: nativeEntry.attemptId,
635
+ nonce: nativeEntry.nonce,
636
+ ...message
637
+ });
638
+ if (!encoded) return;
639
+ try {
640
+ bridge.postMessage(encoded);
641
+ } catch {
642
+ }
643
+ }
644
+ function postNativeError(code) {
645
+ if (inNativeMode()) postToNative({ type: "error", code });
646
+ }
647
+ function startNativeHandshake(w, entry) {
648
+ nativeEntry = entry;
649
+ nativeBootAt = Date.now();
650
+ w[NATIVE_RECEIVER] = (payload) => {
651
+ void handleNativeCommand(payload);
652
+ };
653
+ const capabilities = ["native-ticket", "native-guest", "heartbeat"];
654
+ postToNative({ type: "ready", sdkVersion: SDK_VERSION, capabilities });
655
+ handshakeTimer = setTimeout(() => {
656
+ void requestGuestSession();
657
+ }, handshakeTimeoutMs);
658
+ }
659
+ async function handleNativeCommand(payload) {
660
+ if (!config || !nativeEntry) return;
661
+ const read2 = readBridgeNativeCommand(payload);
662
+ if (!read2.ok) return;
663
+ const command = read2.message;
664
+ if (command.slug !== config.slug || command.attemptId !== nativeEntry.attemptId || command.nonce !== nativeEntry.nonce) {
665
+ return;
666
+ }
667
+ switch (command.type) {
668
+ case "ticket":
669
+ if (handshakeTimer !== void 0) clearTimeout(handshakeTimer);
670
+ await redeemTicket(command.ticket);
671
+ return;
672
+ case "guest":
673
+ if (handshakeTimer !== void 0) clearTimeout(handshakeTimer);
674
+ await requestGuestSession();
675
+ return;
676
+ case "cancel":
677
+ stopNativeTimers();
678
+ return;
679
+ case "foreground":
680
+ case "network":
681
+ return;
682
+ }
683
+ }
684
+ function stopNativeTimers() {
685
+ if (handshakeTimer !== void 0) clearTimeout(handshakeTimer);
686
+ handshakeTimer = void 0;
687
+ if (refreshTimer !== void 0) clearTimeout(refreshTimer);
688
+ refreshTimer = void 0;
689
+ if (nativeReauthTimer !== void 0) clearTimeout(nativeReauthTimer);
690
+ nativeReauthTimer = void 0;
691
+ if (heartbeatTimer !== void 0) clearInterval(heartbeatTimer);
692
+ heartbeatTimer = void 0;
693
+ }
694
+ function startHeartbeat() {
695
+ if (!inNativeMode() || heartbeatTimer !== void 0) return;
696
+ heartbeatTimer = setInterval(() => {
697
+ postToNative({ type: "heartbeat", uptimeMs: Math.max(0, Date.now() - nativeBootAt) });
698
+ }, heartbeatIntervalMs);
699
+ heartbeatTimer.unref?.();
700
+ }
701
+ function requestNativeReauth() {
702
+ if (!inNativeMode() || nativeReauthPending) return;
703
+ nativeReauthPending = true;
704
+ postToNative({ type: "reauth-required" });
705
+ nativeReauthTimer = setTimeout(() => {
706
+ nativeReauthTimer = void 0;
707
+ if (!nativeReauthPending) return;
708
+ nativeReauthPending = false;
709
+ emit("error", { error: new Error("embed session expired") });
710
+ enterBlocked("session-expired");
711
+ }, nativeReauthTimeoutMs);
712
+ }
713
+ function clearNativeReauth() {
714
+ nativeReauthPending = false;
715
+ if (nativeReauthTimer !== void 0) clearTimeout(nativeReauthTimer);
716
+ nativeReauthTimer = void 0;
717
+ }
347
718
  function postToParent(message, targetOrigin) {
348
719
  const w = win();
349
720
  if (!w || !isEmbedded()) return;
@@ -411,6 +782,7 @@ function readFragment(w) {
411
782
  }
412
783
  async function redirectToAuthorize(w, opts) {
413
784
  if (!config || localTestMode) return;
785
+ if (inNativeMode()) return;
414
786
  const origin = opts?.guestOk ? config.dashboardOrigins[0] ?? (await fetchDashboardOrigins())[0] : (await fetchDashboardOrigins())[0] ?? config.dashboardOrigins[0];
415
787
  if (!origin) {
416
788
  enterBlocked();
@@ -435,7 +807,9 @@ async function fetchDashboardOrigins() {
435
807
  return config.dashboardOrigins;
436
808
  }
437
809
  async function redeemTicket(ticket) {
438
- if (!config || localTestMode || redeeming || state !== "pending" && state !== "guest") return;
810
+ if (!config || localTestMode || redeeming) return;
811
+ const reauthing = nativeReauthPending && state === "authenticated";
812
+ if (state !== "pending" && state !== "guest" && !reauthing) return;
439
813
  const upgradingFromGuest = state === "guest";
440
814
  const epoch = ++identityEpoch;
441
815
  redeeming = true;
@@ -464,6 +838,11 @@ async function redeemTicket(ticket) {
464
838
  if (isEmbedded() && parentOrigin) {
465
839
  postToParent({ type: "genex:embed:authenticated", v: PROTOCOL_VERSION }, parentOrigin);
466
840
  }
841
+ if (inNativeMode()) {
842
+ clearNativeReauth();
843
+ postToNative({ type: "authenticated", guest: false });
844
+ startHeartbeat();
845
+ }
467
846
  if (upgradingFromGuest) await flushGuestQueue();
468
847
  const ctx = { user };
469
848
  for (const waiter of authWaiters.splice(0)) waiter.resolve(ctx);
@@ -494,7 +873,8 @@ async function requestGuestSession() {
494
873
  if (epoch !== identityEpoch) return;
495
874
  if (!res.ok) {
496
875
  emit("error", { error: new Error(`guest session failed (${res.status})`) });
497
- enterBlocked();
876
+ postNativeError("guest-failed");
877
+ enterBlocked(res.status === 404 ? "not-available" : "no-identity");
498
878
  return;
499
879
  }
500
880
  const body = await res.json();
@@ -522,6 +902,9 @@ function enterGuest() {
522
902
  { type: "genex:embed:authenticated", v: PROTOCOL_VERSION, guest: true },
523
903
  parentOrigin ?? "*"
524
904
  );
905
+ } else if (inNativeMode()) {
906
+ postToNative({ type: "authenticated", guest: true });
907
+ startHeartbeat();
525
908
  } else if (firstEntry && !readPopoverDismissed()) {
526
909
  showGuestPopover();
527
910
  }
@@ -539,14 +922,26 @@ function isLoopbackLocation(w) {
539
922
  return false;
540
923
  }
541
924
  }
542
- function wantsLocalTest(w) {
925
+ function wantsLocalTestMode(w) {
543
926
  if (isEmbedded()) return false;
927
+ if (detectNativeEntry(w)) return false;
544
928
  if (!isLoopbackLocation(w)) return false;
929
+ let params = null;
545
930
  try {
546
- return new URLSearchParams(w.location.search).get("genex_local_test") === "1";
931
+ params = new URLSearchParams(w.location.search);
547
932
  } catch {
933
+ }
934
+ if (params?.get("genex_local_test") === "1") return true;
935
+ if (params?.get("genex_auth") === "1") {
936
+ writeLocalAuthFlag();
548
937
  return false;
549
938
  }
939
+ if (readLocalAuthFlag()) return false;
940
+ return !hasGenexFragment(w);
941
+ }
942
+ function hasGenexFragment(w) {
943
+ const hash = stashedTicketHash ?? w.location.hash;
944
+ return !!hash && hash.includes("genex_");
550
945
  }
551
946
  function enterLocalTestMode(w) {
552
947
  localTestMode = true;
@@ -566,6 +961,12 @@ function enterLocalTestMode(w) {
566
961
  }
567
962
  async function handleRedeemFailure() {
568
963
  const w = win();
964
+ if (inNativeMode()) {
965
+ clearNativeReauth();
966
+ postNativeError("ticket-rejected");
967
+ enterBlocked();
968
+ return;
969
+ }
569
970
  if (w && !isEmbedded() && !readRetryFlag()) {
570
971
  writeRetryFlag();
571
972
  showOverlay("redirecting");
@@ -611,8 +1012,12 @@ async function refreshToken() {
611
1012
  void requestGuestSession();
612
1013
  return;
613
1014
  }
1015
+ if (inNativeMode()) {
1016
+ requestNativeReauth();
1017
+ return;
1018
+ }
614
1019
  emit("error", { error: new Error("embed session expired") });
615
- enterBlocked();
1020
+ enterBlocked("session-expired");
616
1021
  return;
617
1022
  }
618
1023
  if (refreshTimer !== void 0) clearTimeout(refreshTimer);
@@ -620,7 +1025,7 @@ async function refreshToken() {
620
1025
  void refreshToken();
621
1026
  }, refreshRetryMs);
622
1027
  }
623
- function enterBlocked() {
1028
+ function enterBlocked(reason = "no-identity") {
624
1029
  if (state === "blocked") return;
625
1030
  identityEpoch++;
626
1031
  state = "blocked";
@@ -629,7 +1034,14 @@ function enterBlocked() {
629
1034
  if (handshakeTimer !== void 0) clearTimeout(handshakeTimer);
630
1035
  if (refreshTimer !== void 0) clearTimeout(refreshTimer);
631
1036
  removeGuestPopover();
632
- showOverlay(isEmbedded() ? "blocked-embedded" : "blocked-standalone");
1037
+ if (inNativeMode()) {
1038
+ stopNativeTimers();
1039
+ clearNativeReauth();
1040
+ removeOverlay();
1041
+ postToNative({ type: "blocked", reason });
1042
+ } else {
1043
+ showOverlay(isEmbedded() ? "blocked-embedded" : "blocked-standalone");
1044
+ }
633
1045
  if (isEmbedded()) {
634
1046
  postToParent({ type: "genex:embed:blocked", v: PROTOCOL_VERSION }, parentOrigin ?? "*");
635
1047
  }
@@ -944,6 +1356,11 @@ function __resetForTests(overrides) {
944
1356
  if (refreshTimer !== void 0) clearTimeout(refreshTimer);
945
1357
  handshakeTimer = void 0;
946
1358
  refreshTimer = void 0;
1359
+ stopNativeTimers();
1360
+ if (w) delete w[NATIVE_RECEIVER];
1361
+ nativeEntry = null;
1362
+ nativeReauthPending = false;
1363
+ nativeBootAt = 0;
947
1364
  removeOverlay(true);
948
1365
  removeGuestPopover();
949
1366
  overlayFontRequested = false;
@@ -969,6 +1386,8 @@ function __resetForTests(overrides) {
969
1386
  refreshDelayMs = overrides?.refreshDelayMs ?? 10 * 6e4;
970
1387
  refreshRetryMs = overrides?.refreshRetryMs ?? 6e4;
971
1388
  overlayTextDelayMs = overrides?.overlayTextDelayMs ?? 500;
1389
+ nativeReauthTimeoutMs = overrides?.nativeReauthTimeoutMs ?? 3e4;
1390
+ heartbeatIntervalMs = overrides?.heartbeatIntervalMs ?? 3e4;
972
1391
  }
973
1392
 
974
1393
  export {
package/dist/index.d.ts CHANGED
@@ -228,6 +228,8 @@ declare function __resetForTests(overrides?: {
228
228
  refreshDelayMs?: number;
229
229
  refreshRetryMs?: number;
230
230
  overlayTextDelayMs?: number;
231
+ nativeReauthTimeoutMs?: number;
232
+ heartbeatIntervalMs?: number;
231
233
  }): void;
232
234
 
233
235
  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
@@ -17,7 +17,7 @@ import {
17
17
  submitScore,
18
18
  waitForAuth,
19
19
  waitForPlayer
20
- } from "./chunk-XGTGIWVQ.js";
20
+ } from "./chunk-5SVRR26C.js";
21
21
  export {
22
22
  __resetForTests,
23
23
  _stashTicketFromUrl,
package/dist/sentry.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  _stashTicketFromUrl,
3
3
  getUser,
4
4
  on
5
- } from "./chunk-XGTGIWVQ.js";
5
+ } from "./chunk-5SVRR26C.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.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Player identity + durable game state for genex games \u2014 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",
@@ -40,9 +40,11 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@arethetypeswrong/cli": "^0.18.0",
43
+ "@genex/embed-protocol": "workspace:*",
43
44
  "@sentry/browser": "^10.63.0",
44
45
  "publint": "^0.3.0",
45
- "tsup": "^8.0.0"
46
+ "tsup": "^8.0.0",
47
+ "typescript": "^5.8.0"
46
48
  },
47
49
  "repository": {
48
50
  "type": "git",