@benji-money/connect-sdk 1.1.4-beta.5 → 1.1.4-beta.6

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.
@@ -110,7 +110,7 @@ var ConnectSDK = (() => {
110
110
  // src/utils/config.ts
111
111
  var buildContext = () => ({
112
112
  namespace: "benji-connect-sdk" ? "benji-connect-sdk" : "benji-connect-sdk",
113
- version: "1.1.4-beta.5" ? String("1.1.4-beta.5") : "0.0.0"
113
+ version: "1.1.4-beta.6" ? String("1.1.4-beta.6") : "0.0.0"
114
114
  });
115
115
  function isBenjiConnectEnvironment(environment) {
116
116
  return environment === "local" /* LOCAL */ || environment === "development" /* DEVELOPMENT */ || environment === "sandbox" /* SANDBOX */ || environment === "production" /* PRODUCTION */;
@@ -247,6 +247,150 @@ var ConnectSDK = (() => {
247
247
  return ensureAuthLinkStateNonce(url, nonce);
248
248
  }
249
249
 
250
+ // src/utils/verifyAppUrl.ts
251
+ var DEFAULT_LOCAL_VERIFY_ORIGIN = "http://localhost:3000";
252
+ var REMOTE_VERIFY_ORIGINS = [
253
+ "https://verifyapp-staging.withbenji.com",
254
+ "https://verifyapp-dev.withbenji.com",
255
+ "https://verifyapp.withbenji.com"
256
+ ];
257
+ function rewriteVerifyAppUrlForLocalConnect(partnerLoginLink, localOrigin = DEFAULT_LOCAL_VERIFY_ORIGIN) {
258
+ if (!partnerLoginLink) {
259
+ return partnerLoginLink;
260
+ }
261
+ let url = partnerLoginLink;
262
+ for (const remote of REMOTE_VERIFY_ORIGINS) {
263
+ url = url.replaceAll(remote, localOrigin);
264
+ url = url.replaceAll(
265
+ remote.replace(/^https:\/\//, "https%3A//"),
266
+ localOrigin.replace(/^http:\/\//, "http%3A//")
267
+ );
268
+ url = url.replaceAll(encodeURIComponent(remote), encodeURIComponent(localOrigin));
269
+ }
270
+ return url;
271
+ }
272
+
273
+ // src/utils/loginTransport.ts
274
+ var LOGIN_PENDING_STORAGE_KEY = "benji_login_pending";
275
+ var LOGIN_FORCE_REDIRECT_STORAGE_KEY = "benji_force_login_redirect";
276
+ var LOGIN_RESUME_QUERY_FLAG = "login_resume";
277
+ var LOGIN_RETURN_STATE_SEPARATOR = ".benji_return.";
278
+ function isIosSafari(userAgent, maxTouchPoints) {
279
+ const isIos = /iPhone|iPad|iPod/.test(userAgent) || userAgent.includes("Macintosh") && maxTouchPoints > 1;
280
+ const isSafari = /Safari/.test(userAgent) && !/CriOS|FxiOS|EdgiOS|OPiOS|Chrome/.test(userAgent);
281
+ return isIos && isSafari;
282
+ }
283
+ function shouldUseSameTabLogin(args) {
284
+ if (!args.isNonDummyLogin) return false;
285
+ if (args.forceRedirect) return true;
286
+ return isIosSafari(args.userAgent, args.maxTouchPoints);
287
+ }
288
+ function isNonDummyLogin(mode, authType) {
289
+ return mode === 5 /* LOGIN */ && authType != null && authType !== 0 /* DUMMY */;
290
+ }
291
+ function isLocalForceRedirectEnabled(environment, storage) {
292
+ if (environment !== "local" /* LOCAL */) return false;
293
+ if (!storage) return false;
294
+ return storage.getItem(LOGIN_FORCE_REDIRECT_STORAGE_KEY) === "1";
295
+ }
296
+ function canStampLoginReturnState(partnerAuthLink, configToken) {
297
+ try {
298
+ const state = new URL(partnerAuthLink).searchParams.get("state");
299
+ return !state || state === configToken;
300
+ } catch {
301
+ return false;
302
+ }
303
+ }
304
+ function writeLoginPending(storage, payload) {
305
+ storage.setItem(LOGIN_PENDING_STORAGE_KEY, JSON.stringify(payload));
306
+ }
307
+ function buildRequestCodeExchangeMessage(args) {
308
+ const { configToken } = decodeLoginReturnState(args.token);
309
+ return {
310
+ type: "request_code_exchange",
311
+ data: {
312
+ partnerId: args.partnerId,
313
+ code: args.code,
314
+ token: configToken,
315
+ redirect_uri: args.redirectUri
316
+ }
317
+ };
318
+ }
319
+ function toBase64Url(value) {
320
+ const bytes = new TextEncoder().encode(value);
321
+ let binary = "";
322
+ for (const byte of bytes) {
323
+ binary += String.fromCharCode(byte);
324
+ }
325
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
326
+ }
327
+ function fromBase64Url(value) {
328
+ try {
329
+ const padded = value.replace(/-/g, "+").replace(/_/g, "/");
330
+ const pad = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
331
+ const binary = atob(padded + pad);
332
+ const bytes = Uint8Array.from(binary, (ch) => ch.charCodeAt(0));
333
+ return new TextDecoder().decode(bytes);
334
+ } catch {
335
+ return null;
336
+ }
337
+ }
338
+ function encodeLoginReturnState(configToken, returnOrigin) {
339
+ return `${configToken}${LOGIN_RETURN_STATE_SEPARATOR}${toBase64Url(returnOrigin)}`;
340
+ }
341
+ function decodeLoginReturnState(state) {
342
+ const separatorIndex = state.indexOf(LOGIN_RETURN_STATE_SEPARATOR);
343
+ if (separatorIndex <= 0) {
344
+ return { configToken: state };
345
+ }
346
+ const configToken = state.slice(0, separatorIndex);
347
+ const encodedOrigin = state.slice(
348
+ separatorIndex + LOGIN_RETURN_STATE_SEPARATOR.length
349
+ );
350
+ if (!configToken || !encodedOrigin) {
351
+ return { configToken: state };
352
+ }
353
+ const returnOrigin = fromBase64Url(encodedOrigin);
354
+ if (!returnOrigin) {
355
+ return { configToken: state };
356
+ }
357
+ return { configToken, returnOrigin };
358
+ }
359
+ function stampLoginReturnState(url, configToken, returnOrigin) {
360
+ if (!canStampLoginReturnState(url, configToken)) {
361
+ throw new Error("Partner sign-in URL contains incompatible OAuth state.");
362
+ }
363
+ const state = encodeLoginReturnState(configToken, returnOrigin);
364
+ try {
365
+ const parsed = new URL(url);
366
+ parsed.searchParams.set("state", state);
367
+ return parsed.toString();
368
+ } catch {
369
+ if (url.includes("state=")) {
370
+ return url.replace(
371
+ /([?&]state=)[^&]*/,
372
+ `$1${encodeURIComponent(state)}`
373
+ );
374
+ }
375
+ const separator = url.includes("?") ? "&" : "?";
376
+ return `${url}${separator}state=${encodeURIComponent(state)}`;
377
+ }
378
+ }
379
+ function buildSameTabPartnerAuthUrl(args) {
380
+ let prepared = args.configToken ? preparePartnerAuthUrl(args.partnerAuthLink, args.configToken) : args.partnerAuthLink;
381
+ if (args.configToken && args.returnOrigin) {
382
+ prepared = stampLoginReturnState(
383
+ prepared,
384
+ args.configToken,
385
+ args.returnOrigin
386
+ );
387
+ }
388
+ if (!args.rewriteForLocalConnect) {
389
+ return prepared;
390
+ }
391
+ return rewriteVerifyAppUrlForLocalConnect(prepared);
392
+ }
393
+
250
394
  // src/utils/placement.ts
251
395
  var IFRAME_WIDTH = 400;
252
396
  var IFRAME_HEIGHT = 645;
@@ -918,6 +1062,7 @@ var ConnectSDK = (() => {
918
1062
 
919
1063
  // src/router/message.ts
920
1064
  var IFRAME_DELIVERABILITY_POLL_MS = 100;
1065
+ var HOST_AUTH_REDELIVERY_MS = 500;
921
1066
  var _MessageRouter = class _MessageRouter {
922
1067
  static configureMessageRouter(config) {
923
1068
  this.onSuccess = config.onSuccess;
@@ -934,6 +1079,7 @@ var ConnectSDK = (() => {
934
1079
  this.mode = context.mode;
935
1080
  this.connectOrigin = new URL(Endpoints.benji_connect_auth_url).origin;
936
1081
  if (this.canDeliverToConnectIframe()) {
1082
+ this.flushPendingHostAuth();
937
1083
  this.flushPendingPopupAuth();
938
1084
  } else {
939
1085
  this.startIframeDeliverabilityPoll();
@@ -960,6 +1106,7 @@ var ConnectSDK = (() => {
960
1106
  window.removeEventListener("unhandledrejection", this.errorEventHandler);
961
1107
  window.removeEventListener("message", this.messageEventHandler);
962
1108
  this.stopIframeDeliverabilityPoll();
1109
+ this.stopHostAuthRedelivery();
963
1110
  this.configuredListeners = false;
964
1111
  this.iframe = null;
965
1112
  this.loginPopup = null;
@@ -967,6 +1114,7 @@ var ConnectSDK = (() => {
967
1114
  this.connectOrigin = "";
968
1115
  this.isIframeReady = false;
969
1116
  this.pendingPopupAuth = [];
1117
+ this.pendingHostAuth = [];
970
1118
  this.lastVerifiedPopupSource = null;
971
1119
  }
972
1120
  static handleErrorMessage(event) {
@@ -1039,10 +1187,64 @@ var ConnectSDK = (() => {
1039
1187
  deliverable
1040
1188
  });
1041
1189
  }
1190
+ /**
1191
+ * Same-tab LOGIN resume: post immediately, then re-post on an interval until
1192
+ * FLOW_SUCCESS / onError / close stops redelivery. Connect dedupes repeats.
1193
+ */
1194
+ static startHostAuthRedelivery(payload, intervalMs = HOST_AUTH_REDELIVERY_MS) {
1195
+ this.stopHostAuthRedelivery();
1196
+ this.upsertPendingHostAuth(payload);
1197
+ this.flushPendingHostAuth();
1198
+ this.hostAuthRedeliveryId = window.setInterval(() => {
1199
+ if (!this.pendingHostAuth.length) {
1200
+ this.stopHostAuthRedelivery();
1201
+ return;
1202
+ }
1203
+ this.flushPendingHostAuth();
1204
+ }, intervalMs);
1205
+ }
1206
+ static stopHostAuthRedelivery() {
1207
+ if (this.hostAuthRedeliveryId !== null) {
1208
+ window.clearInterval(this.hostAuthRedeliveryId);
1209
+ this.hostAuthRedeliveryId = null;
1210
+ }
1211
+ this.pendingHostAuth = [];
1212
+ }
1213
+ static upsertPendingHostAuth(payload) {
1214
+ const type = authMessageType(payload);
1215
+ if (!type) {
1216
+ this.pendingHostAuth.push(payload);
1217
+ return;
1218
+ }
1219
+ const existingIndex = this.pendingHostAuth.findIndex(
1220
+ (item) => authMessageType(item) === type
1221
+ );
1222
+ if (existingIndex >= 0) {
1223
+ this.pendingHostAuth[existingIndex] = payload;
1224
+ } else {
1225
+ this.pendingHostAuth.push(payload);
1226
+ }
1227
+ }
1228
+ static flushPendingHostAuth() {
1229
+ var _a, _b;
1230
+ if (!this.pendingHostAuth.length) return;
1231
+ const iframeWindow = (_b = (_a = this.iframe) == null ? void 0 : _a.contentWindow) != null ? _b : null;
1232
+ if (!iframeWindow || !this.canDeliverToConnectIframe()) {
1233
+ return;
1234
+ }
1235
+ const queued = [...this.pendingHostAuth];
1236
+ for (const payload of queued) {
1237
+ iframeWindow.postMessage(payload, this.connectOrigin);
1238
+ }
1239
+ console.debug("[Benji Connect SDK] LOGIN flushPendingHostAuth", {
1240
+ flushedCount: queued.length
1241
+ });
1242
+ }
1042
1243
  static markIframeReady(iframe) {
1043
1244
  if (this.iframe !== iframe) return;
1044
1245
  this.isIframeReady = true;
1045
1246
  console.debug("[Benji Connect SDK] LOGIN iframe_load");
1247
+ this.flushPendingHostAuth();
1046
1248
  this.flushPendingPopupAuth();
1047
1249
  }
1048
1250
  static flushPendingPopupAuth() {
@@ -1248,6 +1450,16 @@ var ConnectSDK = (() => {
1248
1450
  _MessageRouter.connectOrigin = "";
1249
1451
  _MessageRouter.isIframeReady = false;
1250
1452
  _MessageRouter.pendingPopupAuth = [];
1453
+ /**
1454
+ * Sticky host→iframe auth (same-tab LOGIN resume). Kept after each post so
1455
+ * redelivery can cover the window where the iframe is "deliverable" but
1456
+ * Connect has not installed its message listener yet.
1457
+ * Invariant: only `startHostAuthRedelivery` writes this queue, and it always
1458
+ * owns a timer while the queue is non-empty. The deliverability poll is
1459
+ * popup-only so it cannot double-post this payload.
1460
+ */
1461
+ _MessageRouter.pendingHostAuth = [];
1462
+ _MessageRouter.hostAuthRedeliveryId = null;
1251
1463
  _MessageRouter.lastVerifiedPopupSource = null;
1252
1464
  _MessageRouter.iframeDeliverabilityPoll = null;
1253
1465
  _MessageRouter.errorEventHandler = (event) => {
@@ -1260,6 +1472,7 @@ var ConnectSDK = (() => {
1260
1472
 
1261
1473
  // src/connect-sdk.ts
1262
1474
  var POPUP_CLOSED_POLL_MS = 500;
1475
+ var SAME_TAB_RESUME_TIMEOUT_MS = 45e3;
1263
1476
  var ConnectSDK = class {
1264
1477
  constructor(config) {
1265
1478
  this.iframe = null;
@@ -1289,6 +1502,9 @@ var ConnectSDK = (() => {
1289
1502
  * while the in-flight open() still mounts the iframe.
1290
1503
  */
1291
1504
  this.openAbortController = null;
1505
+ /** Same-tab LOGIN resume mounts the iframe headless without partner auth type. */
1506
+ this.isResumingSameTabLogin = false;
1507
+ this.sameTabResumeTimeoutId = null;
1292
1508
  this.onIframeLoad = () => {
1293
1509
  if (this.isClosing || !this.iframe) return;
1294
1510
  MessageRouter.markIframeReady(this.iframe);
@@ -1306,9 +1522,64 @@ var ConnectSDK = (() => {
1306
1522
  }
1307
1523
  isLoginPopupOnly() {
1308
1524
  var _a;
1309
- const mode = (_a = this.sdkConfig.mode) != null ? _a : 1 /* CONNECT */;
1310
- const authType = this.resolveAuthType();
1311
- return mode === 5 /* LOGIN */ && authType != null && authType !== 0 /* DUMMY */;
1525
+ return isNonDummyLogin(
1526
+ (_a = this.sdkConfig.mode) != null ? _a : 1 /* CONNECT */,
1527
+ this.resolveAuthType()
1528
+ );
1529
+ }
1530
+ shouldOpenSameTabLogin() {
1531
+ var _a, _b, _c;
1532
+ if (!this.isLoginPopupOnly()) return false;
1533
+ const partnerAuthLink = (_a = this.sdkConfig.tokenData) == null ? void 0 : _a.partnerAuthLink;
1534
+ const configToken = (_b = this.sdkConfig.tokenData) == null ? void 0 : _b.configToken;
1535
+ if (!partnerAuthLink || !configToken || !canStampLoginReturnState(partnerAuthLink, configToken)) {
1536
+ return false;
1537
+ }
1538
+ const storage = typeof sessionStorage === "undefined" ? null : sessionStorage;
1539
+ return shouldUseSameTabLogin({
1540
+ isNonDummyLogin: true,
1541
+ userAgent: typeof navigator === "undefined" ? "" : navigator.userAgent,
1542
+ maxTouchPoints: typeof navigator === "undefined" ? 0 : (_c = navigator.maxTouchPoints) != null ? _c : 0,
1543
+ forceRedirect: isLocalForceRedirectEnabled(Environment, storage)
1544
+ });
1545
+ }
1546
+ openSameTabLogin() {
1547
+ var _a, _b, _c, _d, _e, _f;
1548
+ const partnerAuthLink = (_a = this.sdkConfig.tokenData) == null ? void 0 : _a.partnerAuthLink;
1549
+ if (!partnerAuthLink) {
1550
+ console.warn("[Connect SDK] Same-tab LOGIN missing partnerAuthLink");
1551
+ (_c = (_b = this.sdkConfig).onError) == null ? void 0 : _c.call(
1552
+ _b,
1553
+ new Error("Partner sign-in URL is missing."),
1554
+ "400",
1555
+ { context: buildContext() }
1556
+ );
1557
+ return;
1558
+ }
1559
+ const rewriteForLocalConnect = Environment === "local" /* LOCAL */;
1560
+ const returnOrigin = window.location.origin;
1561
+ const url = buildSameTabPartnerAuthUrl({
1562
+ partnerAuthLink,
1563
+ configToken: (_d = this.sdkConfig.tokenData) == null ? void 0 : _d.configToken,
1564
+ rewriteForLocalConnect,
1565
+ returnOrigin
1566
+ });
1567
+ try {
1568
+ writeLoginPending(sessionStorage, {
1569
+ connectToken: this.sdkConfig.token,
1570
+ path: `${window.location.pathname}${window.location.search}`
1571
+ });
1572
+ } catch (error) {
1573
+ console.warn("[Connect SDK] Failed to persist LOGIN pending state", error);
1574
+ (_f = (_e = this.sdkConfig).onError) == null ? void 0 : _f.call(
1575
+ _e,
1576
+ new Error("Sign-in could not be started."),
1577
+ "400",
1578
+ { context: buildContext() }
1579
+ );
1580
+ return;
1581
+ }
1582
+ window.location.assign(url);
1312
1583
  }
1313
1584
  isSessionActive() {
1314
1585
  return this.iframe != null || this.loginPopup != null;
@@ -1321,8 +1592,22 @@ var ConnectSDK = (() => {
1321
1592
  }
1322
1593
  initialize() {
1323
1594
  MessageRouter.configureMessageRouter({
1324
- onSuccess: this.sdkConfig.onSuccess,
1325
- onError: this.sdkConfig.onError,
1595
+ onSuccess: (token, metadata) => {
1596
+ var _a, _b;
1597
+ this.clearSameTabResumeTimeout();
1598
+ MessageRouter.stopHostAuthRedelivery();
1599
+ (_b = (_a = this.sdkConfig).onSuccess) == null ? void 0 : _b.call(_a, token, metadata);
1600
+ },
1601
+ onError: (error, errorId, metadata) => {
1602
+ var _a, _b;
1603
+ if (this.isResumingSameTabLogin) {
1604
+ this.close();
1605
+ } else {
1606
+ this.clearSameTabResumeTimeout();
1607
+ MessageRouter.stopHostAuthRedelivery();
1608
+ }
1609
+ (_b = (_a = this.sdkConfig).onError) == null ? void 0 : _b.call(_a, error, errorId, metadata);
1610
+ },
1326
1611
  onExit: this.sdkConfig.onExit,
1327
1612
  onEvent: this.sdkConfig.onEvent,
1328
1613
  close: () => this.close(),
@@ -1416,6 +1701,11 @@ var ConnectSDK = (() => {
1416
1701
  const abortController = new AbortController();
1417
1702
  this.openAbortController = abortController;
1418
1703
  const { signal } = abortController;
1704
+ if (this.shouldOpenSameTabLogin()) {
1705
+ this.openSameTabLogin();
1706
+ this.abortInflightOpen();
1707
+ return;
1708
+ }
1419
1709
  this.initialize();
1420
1710
  if (signal.aborted) return;
1421
1711
  MessageRouter.setMode(mode);
@@ -1436,6 +1726,55 @@ var ConnectSDK = (() => {
1436
1726
  this.openUI();
1437
1727
  this.syncLoginContext(mode);
1438
1728
  }
1729
+ /**
1730
+ * Finishes a same-tab LOGIN after the partner redirect returns to the host.
1731
+ * The Connect callback page has no session of its own, so the code exchange
1732
+ * runs in the headless Connect iframe, which authenticates with the connect
1733
+ * token. Completion arrives through the usual FLOW_SUCCESS → onSuccess path.
1734
+ */
1735
+ async resumeSameTabLogin(params) {
1736
+ var _a;
1737
+ if (this.isSessionActive()) return;
1738
+ this.sdkConfig.mode = 5 /* LOGIN */;
1739
+ this.isResumingSameTabLogin = true;
1740
+ this.initialize();
1741
+ MessageRouter.setMode(5 /* LOGIN */);
1742
+ MessageRouter.addEventListeners();
1743
+ this.openUI();
1744
+ this.syncLoginContext(5 /* LOGIN */);
1745
+ const payload = buildRequestCodeExchangeMessage({
1746
+ partnerId: params.partnerId,
1747
+ code: params.code,
1748
+ token: params.state,
1749
+ redirectUri: (_a = params.redirectUri) != null ? _a : this.buildCallbackUrl(params.partnerId)
1750
+ });
1751
+ MessageRouter.startHostAuthRedelivery(payload);
1752
+ this.startSameTabResumeTimeout();
1753
+ }
1754
+ buildCallbackUrl(partnerId) {
1755
+ const origin = new URL(Endpoints.benji_connect_auth_url).origin;
1756
+ return `${origin}/auth/${partnerId}/callback`;
1757
+ }
1758
+ startSameTabResumeTimeout() {
1759
+ this.clearSameTabResumeTimeout();
1760
+ this.sameTabResumeTimeoutId = setTimeout(() => {
1761
+ var _a, _b;
1762
+ this.sameTabResumeTimeoutId = null;
1763
+ console.warn("[Connect SDK] Same-tab LOGIN resume timed out");
1764
+ (_b = (_a = this.sdkConfig).onError) == null ? void 0 : _b.call(
1765
+ _a,
1766
+ new Error("Sign-in could not be completed. Please try again."),
1767
+ "408",
1768
+ { context: buildContext() }
1769
+ );
1770
+ this.close();
1771
+ }, SAME_TAB_RESUME_TIMEOUT_MS);
1772
+ }
1773
+ clearSameTabResumeTimeout() {
1774
+ if (this.sameTabResumeTimeoutId === null) return;
1775
+ clearTimeout(this.sameTabResumeTimeoutId);
1776
+ this.sameTabResumeTimeoutId = null;
1777
+ }
1439
1778
  syncLoginContext(mode) {
1440
1779
  MessageRouter.setLoginContext({
1441
1780
  iframe: this.iframe,
@@ -1447,7 +1786,7 @@ var ConnectSDK = (() => {
1447
1786
  var _a, _b;
1448
1787
  if (this.iframe) return;
1449
1788
  MessageRouter.resetIframeReady();
1450
- const headless = this.isLoginPopupOnly();
1789
+ const headless = this.isResumingSameTabLogin || this.isLoginPopupOnly();
1451
1790
  this.container = document.createElement("div");
1452
1791
  if (headless) {
1453
1792
  applyHeadlessContainerStyle(this.container);
@@ -1463,6 +1802,9 @@ var ConnectSDK = (() => {
1463
1802
  if (this.sdkConfig.mode != null) {
1464
1803
  url.searchParams.set("mode", String(this.sdkConfig.mode));
1465
1804
  }
1805
+ if (this.isResumingSameTabLogin) {
1806
+ url.searchParams.set(LOGIN_RESUME_QUERY_FLAG, "1");
1807
+ }
1466
1808
  this.iframe = document.createElement("iframe");
1467
1809
  this.syncLoginContext((_a = this.sdkConfig.mode) != null ? _a : 1 /* CONNECT */);
1468
1810
  this.iframe.addEventListener("load", this.onIframeLoad);
@@ -1509,6 +1851,10 @@ var ConnectSDK = (() => {
1509
1851
  this.syncLoginContext((_b = this.sdkConfig.mode) != null ? _b : 1 /* CONNECT */);
1510
1852
  }
1511
1853
  navigateLoginPopup(url) {
1854
+ if (this.isResumingSameTabLogin) {
1855
+ console.debug("[Connect SDK] Ignoring PARTNER_LOGIN_OPEN during same-tab resume");
1856
+ return;
1857
+ }
1512
1858
  if (this.hasLoginPopupUrl) return;
1513
1859
  if (!this.loginPopup || this.loginPopup.closed) {
1514
1860
  console.warn("[Connect SDK] Cannot navigate LOGIN popup \u2014 window unavailable");
@@ -1537,6 +1883,9 @@ var ConnectSDK = (() => {
1537
1883
  close() {
1538
1884
  var _a, _b;
1539
1885
  this.abortInflightOpen();
1886
+ this.clearSameTabResumeTimeout();
1887
+ MessageRouter.stopHostAuthRedelivery();
1888
+ this.isResumingSameTabLogin = false;
1540
1889
  this.isClosing = true;
1541
1890
  this.stopPopupClosedPoll();
1542
1891
  this.closeLoginPopup();