@youidian/sdk 3.3.10 → 3.4.2

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.
@@ -21,8 +21,8 @@ declare class HostedFrameModal<TData extends HostedFrameMessage> {
21
21
  close(): void;
22
22
  }
23
23
 
24
- type LoginEventType = "LOGIN_READY" | "LOGIN_SUCCESS" | "LOGIN_CANCELLED" | "LOGIN_CLOSE" | "LOGIN_RESIZE" | "LOGIN_ERROR";
25
- type LoginDisplayMode = "modal" | "popup";
24
+ type LoginEventType = "LOGIN_READY" | "LOGIN_SUCCESS" | "LOGIN_CANCELLED" | "LOGIN_CLOSE" | "LOGIN_RESIZE" | "LOGIN_REDIRECT_REQUIRED" | "LOGIN_ERROR";
25
+ type LoginDisplayMode = "auto" | "modal" | "popup" | "redirect";
26
26
  type LoginFallbackRedirectMode = "auto" | "manual";
27
27
  interface LoginResult {
28
28
  avatar?: string | null;
@@ -43,6 +43,9 @@ interface LoginResult {
43
43
  }
44
44
  interface LoginEventData extends HostedFrameMessage, LoginResult {
45
45
  type: LoginEventType;
46
+ attemptId?: string;
47
+ authUrl?: string;
48
+ channel?: string;
46
49
  error?: string;
47
50
  message?: string;
48
51
  }
@@ -54,8 +57,8 @@ interface LoginUIOptions {
54
57
  /** Optional same-origin URL used as the no-API OAuth callback landing page. */
55
58
  callbackUrl?: string;
56
59
  /**
57
- * How to open the hosted login page. Defaults to "modal", which embeds the
58
- * login page in an iframe modal. Use "popup" to keep the legacy popup flow.
60
+ * How to open the hosted login page. Defaults to "auto", which uses an
61
+ * iframe modal on desktop and redirects on mobile/embedded browsers.
59
62
  */
60
63
  displayMode?: LoginDisplayMode;
61
64
  /** Text for the fallback button shown when the iframe does not report readiness. */
@@ -142,6 +145,7 @@ declare class LoginUI extends HostedFrameModal<LoginEventData> {
142
145
  private showIframeFallback;
143
146
  private startIframeWatchdog;
144
147
  private handleLoginEvent;
148
+ private openLoginRedirect;
145
149
  private listenForLoginCallback;
146
150
  close(): void;
147
151
  private buildOpenContext;
@@ -21,8 +21,8 @@ declare class HostedFrameModal<TData extends HostedFrameMessage> {
21
21
  close(): void;
22
22
  }
23
23
 
24
- type LoginEventType = "LOGIN_READY" | "LOGIN_SUCCESS" | "LOGIN_CANCELLED" | "LOGIN_CLOSE" | "LOGIN_RESIZE" | "LOGIN_ERROR";
25
- type LoginDisplayMode = "modal" | "popup";
24
+ type LoginEventType = "LOGIN_READY" | "LOGIN_SUCCESS" | "LOGIN_CANCELLED" | "LOGIN_CLOSE" | "LOGIN_RESIZE" | "LOGIN_REDIRECT_REQUIRED" | "LOGIN_ERROR";
25
+ type LoginDisplayMode = "auto" | "modal" | "popup" | "redirect";
26
26
  type LoginFallbackRedirectMode = "auto" | "manual";
27
27
  interface LoginResult {
28
28
  avatar?: string | null;
@@ -43,6 +43,9 @@ interface LoginResult {
43
43
  }
44
44
  interface LoginEventData extends HostedFrameMessage, LoginResult {
45
45
  type: LoginEventType;
46
+ attemptId?: string;
47
+ authUrl?: string;
48
+ channel?: string;
46
49
  error?: string;
47
50
  message?: string;
48
51
  }
@@ -54,8 +57,8 @@ interface LoginUIOptions {
54
57
  /** Optional same-origin URL used as the no-API OAuth callback landing page. */
55
58
  callbackUrl?: string;
56
59
  /**
57
- * How to open the hosted login page. Defaults to "modal", which embeds the
58
- * login page in an iframe modal. Use "popup" to keep the legacy popup flow.
60
+ * How to open the hosted login page. Defaults to "auto", which uses an
61
+ * iframe modal on desktop and redirects on mobile/embedded browsers.
59
62
  */
60
63
  displayMode?: LoginDisplayMode;
61
64
  /** Text for the fallback button shown when the iframe does not report readiness. */
@@ -142,6 +145,7 @@ declare class LoginUI extends HostedFrameModal<LoginEventData> {
142
145
  private showIframeFallback;
143
146
  private startIframeWatchdog;
144
147
  private handleLoginEvent;
148
+ private openLoginRedirect;
145
149
  private listenForLoginCallback;
146
150
  close(): void;
147
151
  private buildOpenContext;
package/dist/login.cjs CHANGED
@@ -28,6 +28,91 @@ __export(login_exports, {
28
28
  });
29
29
  module.exports = __toCommonJS(login_exports);
30
30
 
31
+ // src/environment.ts
32
+ function normalizeText(value) {
33
+ return value || "";
34
+ }
35
+ function getRuntimeInput() {
36
+ if (typeof navigator === "undefined") {
37
+ return {};
38
+ }
39
+ const nav = navigator;
40
+ const coarsePointer = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(pointer: coarse)").matches : void 0;
41
+ return {
42
+ coarsePointer,
43
+ maxTouchPoints: nav.maxTouchPoints || 0,
44
+ platform: nav.platform || "",
45
+ standalone: nav.standalone,
46
+ userAgent: nav.userAgent || "",
47
+ userAgentDataMobile: nav.userAgentData?.mobile
48
+ };
49
+ }
50
+ function includesAny(value, patterns) {
51
+ return patterns.some((pattern) => pattern.test(value));
52
+ }
53
+ function detectLoginEnvironment(input = getRuntimeInput()) {
54
+ const userAgent = normalizeText(input.userAgent);
55
+ const platform = normalizeText(input.platform);
56
+ const rawMaxTouchPoints = input.maxTouchPoints ?? 0;
57
+ const maxTouchPoints = Number.isFinite(rawMaxTouchPoints) ? Math.max(0, rawMaxTouchPoints) : 0;
58
+ const isCoarsePointer = input.coarsePointer === true;
59
+ const isTouch = maxTouchPoints > 0 || isCoarsePointer;
60
+ const isWeChat = /micromessenger/i.test(userAgent);
61
+ const isAndroid = /\bandroid\b/i.test(userAgent);
62
+ const hasExplicitIOSDevice = /\b(iPad|iPhone|iPod)\b/i.test(userAgent);
63
+ const hasMacintoshUserAgent = /\bMacintosh\b/i.test(userAgent);
64
+ const hasMacPlatform = /^Mac/i.test(platform);
65
+ const isIPadOS = !hasExplicitIOSDevice && hasMacintoshUserAgent && hasMacPlatform && typeof input.standalone !== "undefined" && maxTouchPoints > 2;
66
+ const isIOS = hasExplicitIOSDevice || isIPadOS;
67
+ const isTabletByUa = includesAny(userAgent, [
68
+ /\biPad\b/i,
69
+ /\btablet\b/i,
70
+ /\bplaybook\b/i,
71
+ /\bsilk\b(?!.*\bmobile\b)/i,
72
+ /\bkindle\b/i,
73
+ /\bnexus 7\b/i,
74
+ /\bnexus 9\b/i,
75
+ /\bxoom\b/i,
76
+ /\bsm-t\d+/i,
77
+ /\bgt-p\d+/i,
78
+ /\bmi pad\b/i
79
+ ]);
80
+ const isMobileByUa = includesAny(userAgent, [
81
+ /\bMobile\b/i,
82
+ /\biPhone\b/i,
83
+ /\biPod\b/i,
84
+ /\bWindows Phone\b/i,
85
+ /\bIEMobile\b/i,
86
+ /\bBlackBerry\b/i,
87
+ /\bBB10\b/i,
88
+ /\bOpera Mini\b/i,
89
+ /\bOpera Mobi\b/i,
90
+ /\bwebOS\b/i
91
+ ]);
92
+ const isAndroidTablet = isAndroid && !/\bMobile\b/i.test(userAgent);
93
+ const isTablet = isIPadOS || isTabletByUa || isAndroidTablet;
94
+ const isMobile = input.userAgentDataMobile === true || isMobileByUa || isAndroid && !isTablet || isIOS && !isTablet;
95
+ const deviceType = isTablet ? "tablet" : isMobile ? "mobile" : userAgent || platform || isTouch ? "desktop" : "unknown";
96
+ const shouldUseRedirectLogin = deviceType === "mobile" || deviceType === "tablet" || isWeChat;
97
+ return {
98
+ deviceType,
99
+ isAndroid,
100
+ isCoarsePointer,
101
+ isDesktop: deviceType === "desktop",
102
+ isIOS,
103
+ isIPadOS,
104
+ isMobile,
105
+ isStandalone: input.standalone === true,
106
+ isTablet,
107
+ isTouch,
108
+ isWeChat,
109
+ maxTouchPoints,
110
+ platform,
111
+ shouldUseRedirectLogin,
112
+ userAgent
113
+ };
114
+ }
115
+
31
116
  // src/hosted-modal.ts
32
117
  var SDK_SUPPORTED_LOCALES = [
33
118
  "en",
@@ -151,7 +236,7 @@ var HostedFrameModal = class {
151
236
  const container = document.createElement("div");
152
237
  Object.assign(container.style, {
153
238
  width: options.width || "450px",
154
- height: options.height || "min(600px, 90vh)",
239
+ height: options.height || "min(760px, calc(100vh - 32px))",
155
240
  backgroundColor: "transparent",
156
241
  borderRadius: "28px",
157
242
  overflow: "visible",
@@ -159,8 +244,7 @@ var HostedFrameModal = class {
159
244
  boxShadow: "none",
160
245
  maxWidth: "calc(100vw - 32px)",
161
246
  maxHeight: "calc(100vh - 32px)",
162
- transition: "height 180ms ease",
163
- willChange: "height"
247
+ transition: "none"
164
248
  });
165
249
  const closeBtn = document.createElement("button");
166
250
  closeBtn.innerHTML = "\xD7";
@@ -239,6 +323,15 @@ function getOrigin(value) {
239
323
  return null;
240
324
  }
241
325
  }
326
+ function isValidLoginRedirectUrl(value) {
327
+ if (!value) return false;
328
+ try {
329
+ const url = new URL(value);
330
+ return url.protocol === "https:" || url.protocol === "http:";
331
+ } catch {
332
+ return false;
333
+ }
334
+ }
242
335
  function createLoginCallbackState() {
243
336
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
244
337
  return crypto.randomUUID().replace(/-/g, "");
@@ -585,6 +678,24 @@ function applyLoginPanelStyle(panel) {
585
678
  backdropFilter: "blur(16px)"
586
679
  });
587
680
  }
681
+ function applyLoginLoadingPanelStyle(panel) {
682
+ Object.assign(panel.style, {
683
+ position: "absolute",
684
+ inset: "0",
685
+ display: "flex",
686
+ alignItems: "center",
687
+ justifyContent: "center",
688
+ border: "0",
689
+ borderRadius: "28px",
690
+ background: "rgba(255,255,255,0.94)",
691
+ color: "#0f172a",
692
+ padding: "24px",
693
+ textAlign: "center",
694
+ zIndex: "1",
695
+ boxShadow: "none",
696
+ backdropFilter: "blur(16px)"
697
+ });
698
+ }
588
699
  function createLoginPanelContent() {
589
700
  const content = document.createElement("div");
590
701
  Object.assign(content.style, {
@@ -609,6 +720,71 @@ function createLoginPanelContent() {
609
720
  content.appendChild(badge);
610
721
  return content;
611
722
  }
723
+ function createBrandLoadingMark() {
724
+ const logo = document.createElementNS("http://www.w3.org/2000/svg", "svg");
725
+ logo.setAttribute("viewBox", "0 0 1024 1024");
726
+ logo.setAttribute("fill", "none");
727
+ logo.setAttribute("aria-hidden", "true");
728
+ Object.assign(logo.style, {
729
+ width: "112px",
730
+ height: "112px",
731
+ color: "#22c55e",
732
+ display: "block",
733
+ overflow: "visible"
734
+ });
735
+ const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
736
+ group.setAttribute("fill", "currentColor");
737
+ const ring = document.createElementNS("http://www.w3.org/2000/svg", "path");
738
+ ring.setAttribute(
739
+ "d",
740
+ "M704.298 679.54A264 264 0 1 1 704.298 309.46A49 49 0 0 1 634.4 378.149A166 166 0 1 0 634.4 610.851A49 49 0 0 1 704.298 679.54Z"
741
+ );
742
+ ring.setAttribute("class", "youidian-sdk-brand-loading__ring");
743
+ const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
744
+ dot.setAttribute("class", "youidian-sdk-brand-loading__dot");
745
+ dot.setAttribute("cx", "714.5");
746
+ dot.setAttribute("cy", "490.5");
747
+ dot.setAttribute("r", "68");
748
+ group.appendChild(ring);
749
+ group.appendChild(dot);
750
+ logo.appendChild(group);
751
+ return logo;
752
+ }
753
+ function createBrandLoadingCopy() {
754
+ const brand = document.createElement("div");
755
+ brand.className = "youidian-sdk-brand-loading__copy";
756
+ Object.assign(brand.style, {
757
+ display: "flex",
758
+ flexDirection: "column",
759
+ alignItems: "flex-start",
760
+ justifyContent: "center",
761
+ lineHeight: "1"
762
+ });
763
+ const name = document.createElement("div");
764
+ name.className = "youidian-sdk-brand-loading__name";
765
+ name.textContent = "\u4F18\u6613\u70B9";
766
+ Object.assign(name.style, {
767
+ color: "#0f172a",
768
+ fontSize: "34px",
769
+ fontWeight: "900",
770
+ letterSpacing: "0",
771
+ lineHeight: "1"
772
+ });
773
+ const tagline = document.createElement("div");
774
+ tagline.className = "youidian-sdk-brand-loading__tagline";
775
+ tagline.textContent = "\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0";
776
+ Object.assign(tagline.style, {
777
+ color: "#64748b",
778
+ fontSize: "16px",
779
+ fontWeight: "700",
780
+ letterSpacing: "0.24em",
781
+ lineHeight: "1",
782
+ marginTop: "6px"
783
+ });
784
+ brand.appendChild(name);
785
+ brand.appendChild(tagline);
786
+ return brand;
787
+ }
612
788
  function createLoginPanelTitle(value) {
613
789
  const title = document.createElement("div");
614
790
  title.textContent = value;
@@ -638,32 +814,111 @@ function ensureLoginLoadingStyles() {
638
814
  }
639
815
  const style = document.createElement("style");
640
816
  style.id = "youidian-login-loading-style";
641
- style.textContent = "@keyframes youidian-login-spin{to{transform:rotate(360deg)}}";
817
+ style.textContent = `
818
+ .youidian-sdk-brand-loading__ring,
819
+ .youidian-sdk-brand-loading__dot {
820
+ transform-box: fill-box;
821
+ transform-origin: center;
822
+ will-change: opacity, transform;
823
+ }
824
+
825
+ .youidian-sdk-brand-loading__ring {
826
+ animation: youidian-sdk-brand-loading-ring 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
827
+ }
828
+
829
+ .youidian-sdk-brand-loading__dot {
830
+ animation: youidian-sdk-brand-loading-dot 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
831
+ }
832
+
833
+ .youidian-sdk-brand-loading__copy {
834
+ will-change: opacity, transform;
835
+ animation: youidian-sdk-brand-loading-copy 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
836
+ }
837
+
838
+ .youidian-sdk-brand-loading__tagline {
839
+ will-change: opacity;
840
+ animation: youidian-sdk-brand-loading-tagline 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
841
+ }
842
+
843
+ @keyframes youidian-sdk-brand-loading-ring {
844
+ 0%,
845
+ 100% {
846
+ opacity: 0.72;
847
+ transform: rotate(0deg) scale(0.98);
848
+ }
849
+ 50% {
850
+ opacity: 1;
851
+ transform: rotate(8deg) scale(1.02);
852
+ }
853
+ }
854
+
855
+ @keyframes youidian-sdk-brand-loading-dot {
856
+ 0%,
857
+ 100% {
858
+ opacity: 0.55;
859
+ transform: scale(0.9);
860
+ }
861
+ 50% {
862
+ opacity: 1;
863
+ transform: scale(1.08);
864
+ }
865
+ }
866
+
867
+ @keyframes youidian-sdk-brand-loading-copy {
868
+ 0%,
869
+ 100% {
870
+ opacity: 0.86;
871
+ transform: translateY(0);
872
+ }
873
+ 50% {
874
+ opacity: 1;
875
+ transform: translateY(-1px);
876
+ }
877
+ }
878
+
879
+ @keyframes youidian-sdk-brand-loading-tagline {
880
+ 0%,
881
+ 100% {
882
+ opacity: 0.64;
883
+ }
884
+ 50% {
885
+ opacity: 0.92;
886
+ }
887
+ }
888
+
889
+ @media (prefers-reduced-motion: reduce) {
890
+ .youidian-sdk-brand-loading__ring,
891
+ .youidian-sdk-brand-loading__dot,
892
+ .youidian-sdk-brand-loading__copy,
893
+ .youidian-sdk-brand-loading__tagline {
894
+ animation: none;
895
+ opacity: 1;
896
+ transform: none;
897
+ }
898
+ }
899
+ `;
642
900
  document.head.appendChild(style);
643
901
  }
644
- function createLoginLoadingPanel(options) {
902
+ function createLoginLoadingPanel() {
645
903
  ensureLoginLoadingStyles();
646
904
  const panel = document.createElement("div");
647
- applyLoginPanelStyle(panel);
648
- const content = createLoginPanelContent();
649
- const spinner = document.createElement("div");
650
- Object.assign(spinner.style, {
651
- width: "22px",
652
- height: "22px",
653
- borderRadius: "9999px",
654
- border: "2px solid rgba(23,23,23,0.16)",
655
- borderTopColor: "#171717",
656
- margin: "0 auto 18px",
657
- animation: "youidian-login-spin 780ms linear infinite"
905
+ applyLoginLoadingPanelStyle(panel);
906
+ panel.setAttribute("role", "status");
907
+ panel.setAttribute("aria-label", "\u4F18\u6613\u70B9\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0");
908
+ const content = document.createElement("div");
909
+ Object.assign(content.style, {
910
+ display: "flex",
911
+ alignItems: "center",
912
+ justifyContent: "center",
913
+ gap: "12px"
658
914
  });
659
- content.appendChild(spinner);
660
- content.appendChild(createLoginPanelTitle(options.title));
661
- content.appendChild(createLoginPanelDescription(options.description));
915
+ content.appendChild(createBrandLoadingMark());
916
+ content.appendChild(createBrandLoadingCopy());
662
917
  panel.appendChild(content);
663
918
  return panel;
664
919
  }
665
- function createLoginRedirectingPanel(options) {
666
- return createLoginLoadingPanel(options);
920
+ function createLoginRedirectingPanel(_options) {
921
+ return createLoginLoadingPanel();
667
922
  }
668
923
  function createLoginFallbackPanel(options) {
669
924
  const panel = document.createElement("div");
@@ -745,6 +1000,7 @@ var LoginUI = class extends HostedFrameModal {
745
1000
  this.completed = false;
746
1001
  }
747
1002
  markIframeReady() {
1003
+ if (this.iframeReady) return;
748
1004
  this.iframeReady = true;
749
1005
  if (typeof window !== "undefined" && this.iframeLoadTimer) {
750
1006
  window.clearTimeout(this.iframeLoadTimer);
@@ -763,14 +1019,11 @@ var LoginUI = class extends HostedFrameModal {
763
1019
  this.iframeFallbackPanel = null;
764
1020
  this.iframeLoadingPanel = null;
765
1021
  }
766
- showIframeLoading(container, options) {
1022
+ showIframeLoading(container) {
767
1023
  if (this.iframeLoadingPanel || this.iframeReady) {
768
1024
  return;
769
1025
  }
770
- const panel = createLoginLoadingPanel({
771
- description: options?.loadingDescription || "Please wait while the secure login page opens.",
772
- title: options?.loadingTitle || "Opening login page"
773
- });
1026
+ const panel = createLoginLoadingPanel();
774
1027
  this.iframeLoadingPanel = panel;
775
1028
  container.appendChild(panel);
776
1029
  }
@@ -858,6 +1111,20 @@ var LoginUI = class extends HostedFrameModal {
858
1111
  break;
859
1112
  case "LOGIN_RESIZE":
860
1113
  break;
1114
+ case "LOGIN_REDIRECT_REQUIRED":
1115
+ if (!isValidLoginRedirectUrl(data.authUrl)) {
1116
+ logLoginWarn("Login redirect requested with invalid authUrl");
1117
+ options?.onError?.("Login redirect URL is invalid", data);
1118
+ break;
1119
+ }
1120
+ logLoginInfo("Login redirect requested by hosted page", {
1121
+ attemptId: data.attemptId || null,
1122
+ channel: data.channel || null,
1123
+ authUrl: data.authUrl
1124
+ });
1125
+ this.close();
1126
+ window.location.assign(data.authUrl);
1127
+ break;
861
1128
  case "LOGIN_ERROR":
862
1129
  if (this.completed) {
863
1130
  break;
@@ -881,6 +1148,25 @@ var LoginUI = class extends HostedFrameModal {
881
1148
  break;
882
1149
  }
883
1150
  }
1151
+ openLoginRedirect(params, options) {
1152
+ if (typeof window === "undefined") return;
1153
+ const { callbackState, finalUrl, launchPayload } = this.buildOpenContext(
1154
+ params,
1155
+ options
1156
+ );
1157
+ logLoginInfo("Redirecting to hosted login page", {
1158
+ appId: params.appId,
1159
+ callbackState,
1160
+ callbackUrl: launchPayload.callbackUrl || null,
1161
+ hasCallbackUrl: Boolean(launchPayload.callbackUrl),
1162
+ loginUrl: finalUrl,
1163
+ autoClose: options?.autoClose ?? true,
1164
+ displayMode: "redirect",
1165
+ pageUrl: window.location.href,
1166
+ parentOrigin: launchPayload.origin || null
1167
+ });
1168
+ window.location.assign(finalUrl);
1169
+ }
884
1170
  listenForLoginCallback(callbackState, options) {
885
1171
  try {
886
1172
  this.callbackChannel = new BroadcastChannel(
@@ -957,21 +1243,28 @@ var LoginUI = class extends HostedFrameModal {
957
1243
  options?.onCancel?.();
958
1244
  options?.onClose?.();
959
1245
  },
960
- onMessage: (data, container, event) => {
1246
+ onMessage: (data, _container, event) => {
961
1247
  const isIframeEvent = event.source === this.iframe?.contentWindow;
1248
+ if (!isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE")) {
1249
+ logLoginWarn(
1250
+ "Login lifecycle event ignored: event.source does not match iframe contentWindow",
1251
+ {
1252
+ type: data.type,
1253
+ eventOrigin: event.origin,
1254
+ hasIframe: Boolean(this.iframe),
1255
+ hasIframeContentWindow: Boolean(this.iframe?.contentWindow)
1256
+ }
1257
+ );
1258
+ }
962
1259
  if (isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE") || data.type === "LOGIN_SUCCESS" || data.type === "LOGIN_ERROR") {
963
1260
  this.markIframeReady();
964
1261
  }
965
- if (data.type === "LOGIN_RESIZE" && data.height) {
966
- const maxHeight = window.innerHeight * 0.94;
967
- container.style.height = `${Math.min(data.height, maxHeight)}px`;
968
- }
969
1262
  this.handleLoginEvent(data, options);
970
1263
  },
971
1264
  width: "min(520px, 100%)"
972
1265
  });
973
1266
  if (hostedFrame) {
974
- this.showIframeLoading(hostedFrame.container, options);
1267
+ this.showIframeLoading(hostedFrame.container);
975
1268
  this.startIframeWatchdog({
976
1269
  container: hostedFrame.container,
977
1270
  finalUrl,
@@ -1075,11 +1368,19 @@ var LoginUI = class extends HostedFrameModal {
1075
1368
  }
1076
1369
  openLogin(params, options) {
1077
1370
  if (typeof window === "undefined") return;
1078
- const displayMode = options?.displayMode ?? "modal";
1371
+ const displayMode = options?.displayMode ?? "auto";
1372
+ if (displayMode === "redirect") {
1373
+ this.openLoginRedirect(params, options);
1374
+ return;
1375
+ }
1079
1376
  if (displayMode === "popup") {
1080
1377
  this.openLoginPopup(params, options);
1081
1378
  return;
1082
1379
  }
1380
+ if (displayMode === "auto" && detectLoginEnvironment().shouldUseRedirectLogin) {
1381
+ this.openLoginRedirect(params, options);
1382
+ return;
1383
+ }
1083
1384
  this.openLoginModal(params, options);
1084
1385
  }
1085
1386
  };