@youidian/sdk 3.3.10 → 3.4.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.
package/dist/client.cjs CHANGED
@@ -26,6 +26,7 @@ __export(client_exports, {
26
26
  PaymentUI: () => PaymentUI,
27
27
  createLoginUI: () => createLoginUI,
28
28
  createPaymentUI: () => createPaymentUI,
29
+ detectLoginEnvironment: () => detectLoginEnvironment,
29
30
  handleLoginCallbackIfPresent: () => handleLoginCallbackIfPresent
30
31
  });
31
32
  module.exports = __toCommonJS(client_exports);
@@ -232,6 +233,91 @@ var HostedFrameModal = class {
232
233
  }
233
234
  };
234
235
 
236
+ // src/environment.ts
237
+ function normalizeText(value) {
238
+ return value || "";
239
+ }
240
+ function getRuntimeInput() {
241
+ if (typeof navigator === "undefined") {
242
+ return {};
243
+ }
244
+ const nav = navigator;
245
+ const coarsePointer = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(pointer: coarse)").matches : void 0;
246
+ return {
247
+ coarsePointer,
248
+ maxTouchPoints: nav.maxTouchPoints || 0,
249
+ platform: nav.platform || "",
250
+ standalone: nav.standalone,
251
+ userAgent: nav.userAgent || "",
252
+ userAgentDataMobile: nav.userAgentData?.mobile
253
+ };
254
+ }
255
+ function includesAny(value, patterns) {
256
+ return patterns.some((pattern) => pattern.test(value));
257
+ }
258
+ function detectLoginEnvironment(input = getRuntimeInput()) {
259
+ const userAgent = normalizeText(input.userAgent);
260
+ const platform = normalizeText(input.platform);
261
+ const rawMaxTouchPoints = input.maxTouchPoints ?? 0;
262
+ const maxTouchPoints = Number.isFinite(rawMaxTouchPoints) ? Math.max(0, rawMaxTouchPoints) : 0;
263
+ const isCoarsePointer = input.coarsePointer === true;
264
+ const isTouch = maxTouchPoints > 0 || isCoarsePointer;
265
+ const isWeChat = /micromessenger/i.test(userAgent);
266
+ const isAndroid = /\bandroid\b/i.test(userAgent);
267
+ const hasExplicitIOSDevice = /\b(iPad|iPhone|iPod)\b/i.test(userAgent);
268
+ const hasMacintoshUserAgent = /\bMacintosh\b/i.test(userAgent);
269
+ const hasMacPlatform = /^Mac/i.test(platform);
270
+ const isIPadOS = !hasExplicitIOSDevice && hasMacintoshUserAgent && hasMacPlatform && typeof input.standalone !== "undefined" && maxTouchPoints > 2;
271
+ const isIOS = hasExplicitIOSDevice || isIPadOS;
272
+ const isTabletByUa = includesAny(userAgent, [
273
+ /\biPad\b/i,
274
+ /\btablet\b/i,
275
+ /\bplaybook\b/i,
276
+ /\bsilk\b(?!.*\bmobile\b)/i,
277
+ /\bkindle\b/i,
278
+ /\bnexus 7\b/i,
279
+ /\bnexus 9\b/i,
280
+ /\bxoom\b/i,
281
+ /\bsm-t\d+/i,
282
+ /\bgt-p\d+/i,
283
+ /\bmi pad\b/i
284
+ ]);
285
+ const isMobileByUa = includesAny(userAgent, [
286
+ /\bMobile\b/i,
287
+ /\biPhone\b/i,
288
+ /\biPod\b/i,
289
+ /\bWindows Phone\b/i,
290
+ /\bIEMobile\b/i,
291
+ /\bBlackBerry\b/i,
292
+ /\bBB10\b/i,
293
+ /\bOpera Mini\b/i,
294
+ /\bOpera Mobi\b/i,
295
+ /\bwebOS\b/i
296
+ ]);
297
+ const isAndroidTablet = isAndroid && !/\bMobile\b/i.test(userAgent);
298
+ const isTablet = isIPadOS || isTabletByUa || isAndroidTablet;
299
+ const isMobile = input.userAgentDataMobile === true || isMobileByUa || isAndroid && !isTablet || isIOS && !isTablet;
300
+ const deviceType = isTablet ? "tablet" : isMobile ? "mobile" : userAgent || platform || isTouch ? "desktop" : "unknown";
301
+ const shouldUseRedirectLogin = deviceType === "mobile" || deviceType === "tablet" || isWeChat;
302
+ return {
303
+ deviceType,
304
+ isAndroid,
305
+ isCoarsePointer,
306
+ isDesktop: deviceType === "desktop",
307
+ isIOS,
308
+ isIPadOS,
309
+ isMobile,
310
+ isStandalone: input.standalone === true,
311
+ isTablet,
312
+ isTouch,
313
+ isWeChat,
314
+ maxTouchPoints,
315
+ platform,
316
+ shouldUseRedirectLogin,
317
+ userAgent
318
+ };
319
+ }
320
+
235
321
  // src/login.ts
236
322
  function getOrigin(value) {
237
323
  if (!value) return null;
@@ -241,6 +327,15 @@ function getOrigin(value) {
241
327
  return null;
242
328
  }
243
329
  }
330
+ function isValidLoginRedirectUrl(value) {
331
+ if (!value) return false;
332
+ try {
333
+ const url = new URL(value);
334
+ return url.protocol === "https:" || url.protocol === "http:";
335
+ } catch {
336
+ return false;
337
+ }
338
+ }
244
339
  function createLoginCallbackState() {
245
340
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
246
341
  return crypto.randomUUID().replace(/-/g, "");
@@ -587,6 +682,24 @@ function applyLoginPanelStyle(panel) {
587
682
  backdropFilter: "blur(16px)"
588
683
  });
589
684
  }
685
+ function applyLoginLoadingPanelStyle(panel) {
686
+ Object.assign(panel.style, {
687
+ position: "absolute",
688
+ inset: "0",
689
+ display: "flex",
690
+ alignItems: "center",
691
+ justifyContent: "center",
692
+ border: "0",
693
+ borderRadius: "28px",
694
+ background: "rgba(255,255,255,0.94)",
695
+ color: "#0f172a",
696
+ padding: "24px",
697
+ textAlign: "center",
698
+ zIndex: "1",
699
+ boxShadow: "none",
700
+ backdropFilter: "blur(16px)"
701
+ });
702
+ }
590
703
  function createLoginPanelContent() {
591
704
  const content = document.createElement("div");
592
705
  Object.assign(content.style, {
@@ -611,6 +724,71 @@ function createLoginPanelContent() {
611
724
  content.appendChild(badge);
612
725
  return content;
613
726
  }
727
+ function createBrandLoadingMark() {
728
+ const logo = document.createElementNS("http://www.w3.org/2000/svg", "svg");
729
+ logo.setAttribute("viewBox", "0 0 1024 1024");
730
+ logo.setAttribute("fill", "none");
731
+ logo.setAttribute("aria-hidden", "true");
732
+ Object.assign(logo.style, {
733
+ width: "112px",
734
+ height: "112px",
735
+ color: "#22c55e",
736
+ display: "block",
737
+ overflow: "visible"
738
+ });
739
+ const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
740
+ group.setAttribute("fill", "currentColor");
741
+ const ring = document.createElementNS("http://www.w3.org/2000/svg", "path");
742
+ ring.setAttribute(
743
+ "d",
744
+ "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"
745
+ );
746
+ ring.setAttribute("class", "youidian-sdk-brand-loading__ring");
747
+ const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
748
+ dot.setAttribute("class", "youidian-sdk-brand-loading__dot");
749
+ dot.setAttribute("cx", "714.5");
750
+ dot.setAttribute("cy", "490.5");
751
+ dot.setAttribute("r", "68");
752
+ group.appendChild(ring);
753
+ group.appendChild(dot);
754
+ logo.appendChild(group);
755
+ return logo;
756
+ }
757
+ function createBrandLoadingCopy() {
758
+ const brand = document.createElement("div");
759
+ brand.className = "youidian-sdk-brand-loading__copy";
760
+ Object.assign(brand.style, {
761
+ display: "flex",
762
+ flexDirection: "column",
763
+ alignItems: "flex-start",
764
+ justifyContent: "center",
765
+ lineHeight: "1"
766
+ });
767
+ const name = document.createElement("div");
768
+ name.className = "youidian-sdk-brand-loading__name";
769
+ name.textContent = "\u4F18\u6613\u70B9";
770
+ Object.assign(name.style, {
771
+ color: "#0f172a",
772
+ fontSize: "34px",
773
+ fontWeight: "900",
774
+ letterSpacing: "0",
775
+ lineHeight: "1"
776
+ });
777
+ const tagline = document.createElement("div");
778
+ tagline.className = "youidian-sdk-brand-loading__tagline";
779
+ tagline.textContent = "\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0";
780
+ Object.assign(tagline.style, {
781
+ color: "#64748b",
782
+ fontSize: "16px",
783
+ fontWeight: "700",
784
+ letterSpacing: "0.24em",
785
+ lineHeight: "1",
786
+ marginTop: "6px"
787
+ });
788
+ brand.appendChild(name);
789
+ brand.appendChild(tagline);
790
+ return brand;
791
+ }
614
792
  function createLoginPanelTitle(value) {
615
793
  const title = document.createElement("div");
616
794
  title.textContent = value;
@@ -640,32 +818,111 @@ function ensureLoginLoadingStyles() {
640
818
  }
641
819
  const style = document.createElement("style");
642
820
  style.id = "youidian-login-loading-style";
643
- style.textContent = "@keyframes youidian-login-spin{to{transform:rotate(360deg)}}";
821
+ style.textContent = `
822
+ .youidian-sdk-brand-loading__ring,
823
+ .youidian-sdk-brand-loading__dot {
824
+ transform-box: fill-box;
825
+ transform-origin: center;
826
+ will-change: opacity, transform;
827
+ }
828
+
829
+ .youidian-sdk-brand-loading__ring {
830
+ animation: youidian-sdk-brand-loading-ring 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
831
+ }
832
+
833
+ .youidian-sdk-brand-loading__dot {
834
+ animation: youidian-sdk-brand-loading-dot 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
835
+ }
836
+
837
+ .youidian-sdk-brand-loading__copy {
838
+ will-change: opacity, transform;
839
+ animation: youidian-sdk-brand-loading-copy 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
840
+ }
841
+
842
+ .youidian-sdk-brand-loading__tagline {
843
+ will-change: opacity;
844
+ animation: youidian-sdk-brand-loading-tagline 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
845
+ }
846
+
847
+ @keyframes youidian-sdk-brand-loading-ring {
848
+ 0%,
849
+ 100% {
850
+ opacity: 0.72;
851
+ transform: rotate(0deg) scale(0.98);
852
+ }
853
+ 50% {
854
+ opacity: 1;
855
+ transform: rotate(8deg) scale(1.02);
856
+ }
857
+ }
858
+
859
+ @keyframes youidian-sdk-brand-loading-dot {
860
+ 0%,
861
+ 100% {
862
+ opacity: 0.55;
863
+ transform: scale(0.9);
864
+ }
865
+ 50% {
866
+ opacity: 1;
867
+ transform: scale(1.08);
868
+ }
869
+ }
870
+
871
+ @keyframes youidian-sdk-brand-loading-copy {
872
+ 0%,
873
+ 100% {
874
+ opacity: 0.86;
875
+ transform: translateY(0);
876
+ }
877
+ 50% {
878
+ opacity: 1;
879
+ transform: translateY(-1px);
880
+ }
881
+ }
882
+
883
+ @keyframes youidian-sdk-brand-loading-tagline {
884
+ 0%,
885
+ 100% {
886
+ opacity: 0.64;
887
+ }
888
+ 50% {
889
+ opacity: 0.92;
890
+ }
891
+ }
892
+
893
+ @media (prefers-reduced-motion: reduce) {
894
+ .youidian-sdk-brand-loading__ring,
895
+ .youidian-sdk-brand-loading__dot,
896
+ .youidian-sdk-brand-loading__copy,
897
+ .youidian-sdk-brand-loading__tagline {
898
+ animation: none;
899
+ opacity: 1;
900
+ transform: none;
901
+ }
902
+ }
903
+ `;
644
904
  document.head.appendChild(style);
645
905
  }
646
- function createLoginLoadingPanel(options) {
906
+ function createLoginLoadingPanel() {
647
907
  ensureLoginLoadingStyles();
648
908
  const panel = document.createElement("div");
649
- applyLoginPanelStyle(panel);
650
- const content = createLoginPanelContent();
651
- const spinner = document.createElement("div");
652
- Object.assign(spinner.style, {
653
- width: "22px",
654
- height: "22px",
655
- borderRadius: "9999px",
656
- border: "2px solid rgba(23,23,23,0.16)",
657
- borderTopColor: "#171717",
658
- margin: "0 auto 18px",
659
- animation: "youidian-login-spin 780ms linear infinite"
909
+ applyLoginLoadingPanelStyle(panel);
910
+ panel.setAttribute("role", "status");
911
+ panel.setAttribute("aria-label", "\u4F18\u6613\u70B9\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0");
912
+ const content = document.createElement("div");
913
+ Object.assign(content.style, {
914
+ display: "flex",
915
+ alignItems: "center",
916
+ justifyContent: "center",
917
+ gap: "12px"
660
918
  });
661
- content.appendChild(spinner);
662
- content.appendChild(createLoginPanelTitle(options.title));
663
- content.appendChild(createLoginPanelDescription(options.description));
919
+ content.appendChild(createBrandLoadingMark());
920
+ content.appendChild(createBrandLoadingCopy());
664
921
  panel.appendChild(content);
665
922
  return panel;
666
923
  }
667
- function createLoginRedirectingPanel(options) {
668
- return createLoginLoadingPanel(options);
924
+ function createLoginRedirectingPanel(_options) {
925
+ return createLoginLoadingPanel();
669
926
  }
670
927
  function createLoginFallbackPanel(options) {
671
928
  const panel = document.createElement("div");
@@ -765,14 +1022,11 @@ var LoginUI = class extends HostedFrameModal {
765
1022
  this.iframeFallbackPanel = null;
766
1023
  this.iframeLoadingPanel = null;
767
1024
  }
768
- showIframeLoading(container, options) {
1025
+ showIframeLoading(container) {
769
1026
  if (this.iframeLoadingPanel || this.iframeReady) {
770
1027
  return;
771
1028
  }
772
- const panel = createLoginLoadingPanel({
773
- description: options?.loadingDescription || "Please wait while the secure login page opens.",
774
- title: options?.loadingTitle || "Opening login page"
775
- });
1029
+ const panel = createLoginLoadingPanel();
776
1030
  this.iframeLoadingPanel = panel;
777
1031
  container.appendChild(panel);
778
1032
  }
@@ -860,6 +1114,20 @@ var LoginUI = class extends HostedFrameModal {
860
1114
  break;
861
1115
  case "LOGIN_RESIZE":
862
1116
  break;
1117
+ case "LOGIN_REDIRECT_REQUIRED":
1118
+ if (!isValidLoginRedirectUrl(data.authUrl)) {
1119
+ logLoginWarn("Login redirect requested with invalid authUrl");
1120
+ options?.onError?.("Login redirect URL is invalid", data);
1121
+ break;
1122
+ }
1123
+ logLoginInfo("Login redirect requested by hosted page", {
1124
+ attemptId: data.attemptId || null,
1125
+ channel: data.channel || null,
1126
+ authUrl: data.authUrl
1127
+ });
1128
+ this.close();
1129
+ window.location.assign(data.authUrl);
1130
+ break;
863
1131
  case "LOGIN_ERROR":
864
1132
  if (this.completed) {
865
1133
  break;
@@ -883,6 +1151,25 @@ var LoginUI = class extends HostedFrameModal {
883
1151
  break;
884
1152
  }
885
1153
  }
1154
+ openLoginRedirect(params, options) {
1155
+ if (typeof window === "undefined") return;
1156
+ const { callbackState, finalUrl, launchPayload } = this.buildOpenContext(
1157
+ params,
1158
+ options
1159
+ );
1160
+ logLoginInfo("Redirecting to hosted login page", {
1161
+ appId: params.appId,
1162
+ callbackState,
1163
+ callbackUrl: launchPayload.callbackUrl || null,
1164
+ hasCallbackUrl: Boolean(launchPayload.callbackUrl),
1165
+ loginUrl: finalUrl,
1166
+ autoClose: options?.autoClose ?? true,
1167
+ displayMode: "redirect",
1168
+ pageUrl: window.location.href,
1169
+ parentOrigin: launchPayload.origin || null
1170
+ });
1171
+ window.location.assign(finalUrl);
1172
+ }
886
1173
  listenForLoginCallback(callbackState, options) {
887
1174
  try {
888
1175
  this.callbackChannel = new BroadcastChannel(
@@ -959,21 +1246,17 @@ var LoginUI = class extends HostedFrameModal {
959
1246
  options?.onCancel?.();
960
1247
  options?.onClose?.();
961
1248
  },
962
- onMessage: (data, container, event) => {
1249
+ onMessage: (data, _container, event) => {
963
1250
  const isIframeEvent = event.source === this.iframe?.contentWindow;
964
1251
  if (isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE") || data.type === "LOGIN_SUCCESS" || data.type === "LOGIN_ERROR") {
965
1252
  this.markIframeReady();
966
1253
  }
967
- if (data.type === "LOGIN_RESIZE" && data.height) {
968
- const maxHeight = window.innerHeight * 0.94;
969
- container.style.height = `${Math.min(data.height, maxHeight)}px`;
970
- }
971
1254
  this.handleLoginEvent(data, options);
972
1255
  },
973
1256
  width: "min(520px, 100%)"
974
1257
  });
975
1258
  if (hostedFrame) {
976
- this.showIframeLoading(hostedFrame.container, options);
1259
+ this.showIframeLoading(hostedFrame.container);
977
1260
  this.startIframeWatchdog({
978
1261
  container: hostedFrame.container,
979
1262
  finalUrl,
@@ -1077,11 +1360,19 @@ var LoginUI = class extends HostedFrameModal {
1077
1360
  }
1078
1361
  openLogin(params, options) {
1079
1362
  if (typeof window === "undefined") return;
1080
- const displayMode = options?.displayMode ?? "modal";
1363
+ const displayMode = options?.displayMode ?? "auto";
1364
+ if (displayMode === "redirect") {
1365
+ this.openLoginRedirect(params, options);
1366
+ return;
1367
+ }
1081
1368
  if (displayMode === "popup") {
1082
1369
  this.openLoginPopup(params, options);
1083
1370
  return;
1084
1371
  }
1372
+ if (displayMode === "auto" && detectLoginEnvironment().shouldUseRedirectLogin) {
1373
+ this.openLoginRedirect(params, options);
1374
+ return;
1375
+ }
1085
1376
  this.openLoginModal(params, options);
1086
1377
  }
1087
1378
  };
@@ -1299,6 +1590,7 @@ function createPaymentUI() {
1299
1590
  PaymentUI,
1300
1591
  createLoginUI,
1301
1592
  createPaymentUI,
1593
+ detectLoginEnvironment,
1302
1594
  handleLoginCallbackIfPresent
1303
1595
  });
1304
1596
  //# sourceMappingURL=client.cjs.map