@swype-org/react-sdk 0.1.83 → 0.1.85

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/index.cjs CHANGED
@@ -716,7 +716,7 @@ function isSafari() {
716
716
  var POPUP_RESULT_TIMEOUT_MS = 12e4;
717
717
  var POPUP_CLOSED_POLL_MS = 500;
718
718
  var POPUP_CLOSED_GRACE_MS = 1e3;
719
- function createPasskeyViaPopup(options) {
719
+ function createPasskeyViaPopup(options, existingCredentialIds = []) {
720
720
  return new Promise((resolve, reject) => {
721
721
  const channelId = `swype-pk-${Date.now()}-${Math.random().toString(36).slice(2)}`;
722
722
  const payload = { ...options, channelId };
@@ -740,7 +740,19 @@ function createPasskeyViaPopup(options) {
740
740
  closedGraceTimer = setTimeout(() => {
741
741
  if (!settled) {
742
742
  cleanup();
743
- reject(new Error("Passkey setup window was closed before completing."));
743
+ checkServerForNewPasskey(
744
+ options.authToken,
745
+ options.apiBaseUrl,
746
+ existingCredentialIds
747
+ ).then((result) => {
748
+ if (result) {
749
+ resolve(result);
750
+ } else {
751
+ reject(new Error("Passkey setup window was closed before completing."));
752
+ }
753
+ }).catch(() => {
754
+ reject(new Error("Passkey setup window was closed before completing."));
755
+ });
744
756
  }
745
757
  }, POPUP_CLOSED_GRACE_MS);
746
758
  }
@@ -776,6 +788,78 @@ function createPasskeyViaPopup(options) {
776
788
  }
777
789
  });
778
790
  }
791
+ var VERIFY_POPUP_TIMEOUT_MS = 6e4;
792
+ function findDevicePasskeyViaPopup(options) {
793
+ return new Promise((resolve, reject) => {
794
+ const channelId = `swype-pv-${Date.now()}-${Math.random().toString(36).slice(2)}`;
795
+ const payload = { ...options, channelId };
796
+ const encoded = btoa(JSON.stringify(payload));
797
+ const popupUrl = `${window.location.origin}/passkey-verify#${encoded}`;
798
+ const popup = window.open(popupUrl, "swype-passkey-verify");
799
+ if (!popup) {
800
+ reject(new Error("Pop-up blocked. Please allow pop-ups for this site and try again."));
801
+ return;
802
+ }
803
+ let settled = false;
804
+ const channel = typeof BroadcastChannel !== "undefined" ? new BroadcastChannel(channelId) : null;
805
+ const timer = setTimeout(() => {
806
+ cleanup();
807
+ resolve(null);
808
+ }, VERIFY_POPUP_TIMEOUT_MS);
809
+ const closedPoll = setInterval(() => {
810
+ if (popup.closed && !settled) {
811
+ clearInterval(closedPoll);
812
+ setTimeout(() => {
813
+ if (!settled) {
814
+ cleanup();
815
+ resolve(null);
816
+ }
817
+ }, POPUP_CLOSED_GRACE_MS);
818
+ }
819
+ }, POPUP_CLOSED_POLL_MS);
820
+ function handleResult(data) {
821
+ if (settled) return;
822
+ if (!data || typeof data !== "object") return;
823
+ if (data.type !== "swype:passkey-verify-result") return;
824
+ settled = true;
825
+ cleanup();
826
+ if (data.error) {
827
+ resolve(null);
828
+ } else if (data.result && typeof data.result === "object") {
829
+ const result = data.result;
830
+ resolve(result.credentialId ?? null);
831
+ } else {
832
+ resolve(null);
833
+ }
834
+ }
835
+ if (channel) {
836
+ channel.onmessage = (event) => handleResult(event.data);
837
+ }
838
+ const postMessageHandler = (event) => {
839
+ if (event.source !== popup) return;
840
+ handleResult(event.data);
841
+ };
842
+ window.addEventListener("message", postMessageHandler);
843
+ function cleanup() {
844
+ clearTimeout(timer);
845
+ clearInterval(closedPoll);
846
+ window.removeEventListener("message", postMessageHandler);
847
+ channel?.close();
848
+ }
849
+ });
850
+ }
851
+ async function checkServerForNewPasskey(authToken, apiBaseUrl, existingCredentialIds) {
852
+ if (!authToken || !apiBaseUrl) return null;
853
+ const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
854
+ headers: { Authorization: `Bearer ${authToken}` }
855
+ });
856
+ if (!res.ok) return null;
857
+ const body = await res.json();
858
+ const passkeys = body.config.passkeys ?? [];
859
+ const existingSet = new Set(existingCredentialIds);
860
+ const newPasskey = passkeys.find((p) => !existingSet.has(p.credentialId));
861
+ return newPasskey ?? null;
862
+ }
779
863
 
780
864
  // src/hooks.ts
781
865
  var WALLET_CLIENT_MAX_ATTEMPTS = 15;
@@ -970,7 +1054,9 @@ function buildPasskeyPopupOptions(params) {
970
1054
  residentKey: "preferred",
971
1055
  userVerification: "required"
972
1056
  },
973
- timeout: 6e4
1057
+ timeout: 6e4,
1058
+ authToken: params.authToken,
1059
+ apiBaseUrl: params.apiBaseUrl
974
1060
  };
975
1061
  }
976
1062
  async function deviceHasPasskey(credentialId) {
@@ -2747,6 +2833,72 @@ var errorBannerStyle2 = (tokens) => ({
2747
2833
  width: "100%",
2748
2834
  textAlign: "left"
2749
2835
  });
2836
+ function VerifyPasskeyScreen({
2837
+ onVerify,
2838
+ onBack,
2839
+ verifying,
2840
+ error
2841
+ }) {
2842
+ const { tokens } = useSwypeConfig();
2843
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2844
+ ScreenLayout,
2845
+ {
2846
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2847
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onVerify, disabled: verifying, loading: verifying, children: "Verify passkey" }),
2848
+ /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
2849
+ ] }),
2850
+ children: [
2851
+ /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack }),
2852
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle4, children: [
2853
+ /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "accent", size: 64, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "36", height: "36", viewBox: "0 0 24 24", fill: "none", children: [
2854
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "4", y: "4", width: "16", height: "16", rx: "3", stroke: tokens.accent, strokeWidth: "1.5", strokeDasharray: "3 2" }),
2855
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "10", r: "1", fill: tokens.accent }),
2856
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "10", r: "1", fill: tokens.accent }),
2857
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 14c0 1.5 1.34 2.5 3 2.5s3-1 3-2.5", stroke: tokens.accent, strokeWidth: "1.2", strokeLinecap: "round" })
2858
+ ] }) }),
2859
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle4(tokens.text), children: "Verify your passkey" }),
2860
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle4(tokens.textSecondary), children: "Your browser requires a separate window to verify your passkey. Tap the button below to continue." }),
2861
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle3(tokens), children: error }),
2862
+ /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: "Your passkey is stored securely on your device. Swype never sees your biometric data." })
2863
+ ] })
2864
+ ]
2865
+ }
2866
+ );
2867
+ }
2868
+ var contentStyle4 = {
2869
+ textAlign: "center",
2870
+ flex: 1,
2871
+ display: "flex",
2872
+ flexDirection: "column",
2873
+ alignItems: "center",
2874
+ paddingTop: 32
2875
+ };
2876
+ var headingStyle4 = (color) => ({
2877
+ fontSize: "1.45rem",
2878
+ fontWeight: 700,
2879
+ letterSpacing: "-0.02em",
2880
+ color,
2881
+ margin: "24px 0 8px"
2882
+ });
2883
+ var subtitleStyle4 = (color) => ({
2884
+ fontSize: "0.9rem",
2885
+ color,
2886
+ margin: "0 0 28px",
2887
+ lineHeight: 1.5,
2888
+ maxWidth: 280
2889
+ });
2890
+ var errorBannerStyle3 = (tokens) => ({
2891
+ background: tokens.errorBg,
2892
+ border: `1px solid ${tokens.error}66`,
2893
+ borderRadius: 16,
2894
+ padding: "11px 14px",
2895
+ color: tokens.error,
2896
+ fontSize: "0.84rem",
2897
+ marginBottom: 14,
2898
+ lineHeight: 1.5,
2899
+ width: "100%",
2900
+ textAlign: "left"
2901
+ });
2750
2902
  var WALLET_EMOJIS = {
2751
2903
  rabby: "\u{1F430}",
2752
2904
  ora: "\u2666\uFE0F",
@@ -2789,8 +2941,8 @@ function WalletPickerScreen({
2789
2941
  children: [
2790
2942
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: "Set up Swype", onBack }),
2791
2943
  hasPending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2792
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle4(tokens.text), children: "Continue where you left off" }),
2793
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle4(tokens.textSecondary), children: "You have a wallet that still needs setup" }),
2944
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle5(tokens.text), children: "Continue where you left off" }),
2945
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "You have a wallet that still needs setup" }),
2794
2946
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: pendingListStyle, children: pendingConnections.map((acct) => {
2795
2947
  const wallet = acct.wallets[0];
2796
2948
  const address = wallet ? truncateAddress(wallet.name) : void 0;
@@ -2830,8 +2982,8 @@ function WalletPickerScreen({
2830
2982
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dividerStyle2(tokens.border), children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: dividerTextStyle(tokens.textMuted), children: "Or connect a new wallet" }) })
2831
2983
  ] }),
2832
2984
  !hasPending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2833
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle4(tokens.text), children: "Where are your stablecoins?" }),
2834
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle4(tokens.textSecondary), children: "Select the wallet you want to deposit from" })
2985
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle5(tokens.text), children: "Where are your stablecoins?" }),
2986
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "Select the wallet you want to deposit from" })
2835
2987
  ] }),
2836
2988
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: gridStyle, children: [
2837
2989
  displayProviders.map((p) => {
@@ -2884,14 +3036,14 @@ function WalletPickerScreen({
2884
3036
  }
2885
3037
  );
2886
3038
  }
2887
- var headingStyle4 = (color) => ({
3039
+ var headingStyle5 = (color) => ({
2888
3040
  fontSize: "1.35rem",
2889
3041
  fontWeight: 700,
2890
3042
  letterSpacing: "-0.02em",
2891
3043
  color,
2892
3044
  margin: "8px 0 4px"
2893
3045
  });
2894
- var subtitleStyle4 = (color) => ({
3046
+ var subtitleStyle5 = (color) => ({
2895
3047
  fontSize: "0.88rem",
2896
3048
  color,
2897
3049
  margin: "0 0 24px"
@@ -3067,9 +3219,9 @@ function SetupScreen({
3067
3219
  ] }),
3068
3220
  children: [
3069
3221
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: "Swype Setup", onBack, right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
3070
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle5(tokens.text), children: "Set up One-Tap deposits" }),
3071
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "Set your limit for instant deposits. Like a contactless card \u2014 you choose the max." }),
3072
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle3(tokens), children: error }),
3222
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle6(tokens.text), children: "Set up One-Tap deposits" }),
3223
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle6(tokens.textSecondary), children: "Set your limit for instant deposits. Like a contactless card \u2014 you choose the max." }),
3224
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle4(tokens), children: error }),
3073
3225
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceRowStyle, children: [
3074
3226
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceLeftStyle, children: [
3075
3227
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: coinIconStyle(tokens.accent), children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
@@ -3147,20 +3299,20 @@ function SetupScreen({
3147
3299
  }
3148
3300
  );
3149
3301
  }
3150
- var headingStyle5 = (color) => ({
3302
+ var headingStyle6 = (color) => ({
3151
3303
  fontSize: "1.3rem",
3152
3304
  fontWeight: 700,
3153
3305
  letterSpacing: "-0.02em",
3154
3306
  color,
3155
3307
  margin: "8px 0 4px"
3156
3308
  });
3157
- var subtitleStyle5 = (color) => ({
3309
+ var subtitleStyle6 = (color) => ({
3158
3310
  fontSize: "0.86rem",
3159
3311
  color,
3160
3312
  margin: "0 0 24px",
3161
3313
  lineHeight: 1.5
3162
3314
  });
3163
- var errorBannerStyle3 = (tokens) => ({
3315
+ var errorBannerStyle4 = (tokens) => ({
3164
3316
  background: tokens.errorBg,
3165
3317
  border: `1px solid ${tokens.error}66`,
3166
3318
  borderRadius: 16,
@@ -3428,7 +3580,7 @@ function DepositScreen({
3428
3580
  "%)"
3429
3581
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: detailRowStyle(tokens.textMuted), children: "Fees calculated at time of transfer" })
3430
3582
  ] }),
3431
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle4(tokens), children: error })
3583
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error })
3432
3584
  ]
3433
3585
  }
3434
3586
  );
@@ -3496,7 +3648,7 @@ var detailRowStyle = (color) => ({
3496
3648
  color,
3497
3649
  marginBottom: 4
3498
3650
  });
3499
- var errorBannerStyle4 = (tokens) => ({
3651
+ var errorBannerStyle5 = (tokens) => ({
3500
3652
  background: tokens.errorBg,
3501
3653
  border: `1px solid ${tokens.error}66`,
3502
3654
  borderRadius: 16,
@@ -3595,22 +3747,22 @@ function SuccessScreen({
3595
3747
  right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout })
3596
3748
  }
3597
3749
  ),
3598
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle4, children: [
3750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle5, children: [
3599
3751
  succeeded ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3600
3752
  /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z", fill: tokens.success }) }) }),
3601
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle6(tokens.text), children: [
3753
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle7(tokens.text), children: [
3602
3754
  "$",
3603
3755
  amount.toFixed(2),
3604
3756
  " deposited"
3605
3757
  ] }),
3606
- merchantName && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle6(tokens.textSecondary), children: [
3758
+ merchantName && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle7(tokens.textSecondary), children: [
3607
3759
  "to ",
3608
3760
  merchantName
3609
3761
  ] })
3610
3762
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3611
3763
  /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "error", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z", fill: tokens.error }) }) }),
3612
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle6(tokens.text), children: "Transfer failed" }),
3613
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle6(tokens.error), children: error })
3764
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: "Transfer failed" }),
3765
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.error), children: error })
3614
3766
  ] }),
3615
3767
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
3616
3768
  sourceName && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
@@ -3642,21 +3794,21 @@ function SuccessScreen({
3642
3794
  }
3643
3795
  );
3644
3796
  }
3645
- var contentStyle4 = {
3797
+ var contentStyle5 = {
3646
3798
  flex: 1,
3647
3799
  display: "flex",
3648
3800
  flexDirection: "column",
3649
3801
  alignItems: "center",
3650
3802
  paddingTop: 16
3651
3803
  };
3652
- var headingStyle6 = (color) => ({
3804
+ var headingStyle7 = (color) => ({
3653
3805
  fontSize: "1.5rem",
3654
3806
  fontWeight: 700,
3655
3807
  letterSpacing: "-0.02em",
3656
3808
  color,
3657
3809
  margin: "20px 0 4px"
3658
3810
  });
3659
- var subtitleStyle6 = (color) => ({
3811
+ var subtitleStyle7 = (color) => ({
3660
3812
  fontSize: "0.9rem",
3661
3813
  color,
3662
3814
  margin: "0 0 20px"
@@ -3762,7 +3914,7 @@ function SelectSourceScreen({
3762
3914
  right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout })
3763
3915
  }
3764
3916
  ),
3765
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.textMuted), children: "Choose which chain and token to pay from." }),
3917
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle8(tokens.textMuted), children: "Choose which chain and token to pay from." }),
3766
3918
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle2(tokens.textSecondary), children: "Chain" }),
3767
3919
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: optionListStyle, children: choices.map((chain) => {
3768
3920
  const isSelected = chain.chainName === selectedChainName;
@@ -3819,7 +3971,7 @@ function SelectSourceScreen({
3819
3971
  }
3820
3972
  );
3821
3973
  }
3822
- var subtitleStyle7 = (color) => ({
3974
+ var subtitleStyle8 = (color) => ({
3823
3975
  fontSize: "0.85rem",
3824
3976
  color,
3825
3977
  margin: "0 0 20px",
@@ -3934,8 +4086,8 @@ function AdvancedSourceScreen({
3934
4086
  right: /* @__PURE__ */ jsxRuntime.jsx("span", { style: advancedBadgeStyle(tokens.accent), children: "Advanced" })
3935
4087
  }
3936
4088
  ),
3937
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: "Set up One-Tap deposits" }),
3938
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle8(tokens.textSecondary), children: "Select a token source for your One-Tap deposits." }),
4089
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle8(tokens.text), children: "Set up One-Tap deposits" }),
4090
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle9(tokens.textSecondary), children: "Select a token source for your One-Tap deposits." }),
3939
4091
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle3(tokens.textSecondary), children: "Select tokens to approve" }),
3940
4092
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: chainListStyle, children: choices.map((chain) => {
3941
4093
  const isExpanded = expandedChain === chain.chainName;
@@ -3998,14 +4150,14 @@ var advancedBadgeStyle = (color) => ({
3998
4150
  padding: "3px 10px",
3999
4151
  letterSpacing: "0.02em"
4000
4152
  });
4001
- var headingStyle7 = (color) => ({
4153
+ var headingStyle8 = (color) => ({
4002
4154
  fontSize: "1.3rem",
4003
4155
  fontWeight: 700,
4004
4156
  letterSpacing: "-0.02em",
4005
4157
  color,
4006
4158
  margin: "8px 0 4px"
4007
4159
  });
4008
- var subtitleStyle8 = (color) => ({
4160
+ var subtitleStyle9 = (color) => ({
4009
4161
  fontSize: "0.86rem",
4010
4162
  color,
4011
4163
  margin: "0 0 20px",
@@ -4133,16 +4285,16 @@ function TransferStatusScreen({
4133
4285
  const steps = buildSteps(phase);
4134
4286
  return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
4135
4287
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4136
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle5, children: [
4288
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
4137
4289
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4138
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle8(tokens.text), children: "Processing Transfer..." }),
4139
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error }),
4290
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9(tokens.text), children: "Processing Transfer..." }),
4291
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle6(tokens), children: error }),
4140
4292
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: stepsWrapStyle, children: /* @__PURE__ */ jsxRuntime.jsx(StepList, { steps }) }),
4141
4293
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: waitHintStyle(tokens.textMuted), children: "Usually takes a few seconds" })
4142
4294
  ] })
4143
4295
  ] });
4144
4296
  }
4145
- var contentStyle5 = {
4297
+ var contentStyle6 = {
4146
4298
  flex: 1,
4147
4299
  display: "flex",
4148
4300
  flexDirection: "column",
@@ -4151,14 +4303,14 @@ var contentStyle5 = {
4151
4303
  textAlign: "center",
4152
4304
  padding: "0 24px"
4153
4305
  };
4154
- var headingStyle8 = (color) => ({
4306
+ var headingStyle9 = (color) => ({
4155
4307
  fontSize: "1.45rem",
4156
4308
  fontWeight: 700,
4157
4309
  letterSpacing: "-0.02em",
4158
4310
  color,
4159
4311
  margin: "20px 0 16px"
4160
4312
  });
4161
- var errorBannerStyle5 = (tokens) => ({
4313
+ var errorBannerStyle6 = (tokens) => ({
4162
4314
  background: tokens.errorBg,
4163
4315
  border: `1px solid ${tokens.error}66`,
4164
4316
  borderRadius: 16,
@@ -4218,10 +4370,10 @@ function OpenWalletScreen({
4218
4370
  ] }),
4219
4371
  children: [
4220
4372
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4221
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
4373
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
4222
4374
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4223
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
4224
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle9(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
4375
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle10(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
4376
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle10(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
4225
4377
  !loading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: waitingBadgeStyle(tokens), children: [
4226
4378
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 14 }),
4227
4379
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Waiting for authorization..." })
@@ -4231,7 +4383,7 @@ function OpenWalletScreen({
4231
4383
  }
4232
4384
  );
4233
4385
  }
4234
- var contentStyle6 = {
4386
+ var contentStyle7 = {
4235
4387
  flex: 1,
4236
4388
  display: "flex",
4237
4389
  flexDirection: "column",
@@ -4251,14 +4403,14 @@ var logoStyle = {
4251
4403
  borderRadius: 14,
4252
4404
  objectFit: "contain"
4253
4405
  };
4254
- var headingStyle9 = (color) => ({
4406
+ var headingStyle10 = (color) => ({
4255
4407
  fontSize: "1.45rem",
4256
4408
  fontWeight: 700,
4257
4409
  letterSpacing: "-0.02em",
4258
4410
  color,
4259
4411
  margin: "20px 0 8px"
4260
4412
  });
4261
- var subtitleStyle9 = (color) => ({
4413
+ var subtitleStyle10 = (color) => ({
4262
4414
  fontSize: "0.9rem",
4263
4415
  color,
4264
4416
  margin: "0 0 24px",
@@ -4302,10 +4454,10 @@ function ConfirmSignScreen({
4302
4454
  ] }),
4303
4455
  children: [
4304
4456
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4305
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
4457
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
4306
4458
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle2 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4307
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle10(tokens.text), children: "Wallet authorized" }),
4308
- /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle10(tokens.textSecondary), children: [
4459
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle11(tokens.text), children: "Wallet authorized" }),
4460
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle11(tokens.textSecondary), children: [
4309
4461
  displayName,
4310
4462
  " approved the connection. Tap below to confirm your payment."
4311
4463
  ] }),
@@ -4318,7 +4470,7 @@ function ConfirmSignScreen({
4318
4470
  }
4319
4471
  );
4320
4472
  }
4321
- var contentStyle7 = {
4473
+ var contentStyle8 = {
4322
4474
  flex: 1,
4323
4475
  display: "flex",
4324
4476
  flexDirection: "column",
@@ -4333,14 +4485,14 @@ var logoStyle2 = {
4333
4485
  borderRadius: 14,
4334
4486
  objectFit: "contain"
4335
4487
  };
4336
- var headingStyle10 = (color) => ({
4488
+ var headingStyle11 = (color) => ({
4337
4489
  fontSize: "1.45rem",
4338
4490
  fontWeight: 700,
4339
4491
  letterSpacing: "-0.02em",
4340
4492
  color,
4341
4493
  margin: "20px 0 8px"
4342
4494
  });
4343
- var subtitleStyle10 = (color) => ({
4495
+ var subtitleStyle11 = (color) => ({
4344
4496
  fontSize: "0.9rem",
4345
4497
  color,
4346
4498
  margin: "0 0 24px",
@@ -4399,7 +4551,7 @@ var PaymentErrorBoundary = class extends react.Component {
4399
4551
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8v5", stroke: "#ef4444", strokeWidth: "1.5", strokeLinecap: "round" }),
4400
4552
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "16", r: "0.75", fill: "#ef4444" })
4401
4553
  ] }) }),
4402
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle11, children: "Something went wrong" }),
4554
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle12, children: "Something went wrong" }),
4403
4555
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: messageStyle, children: "An unexpected error occurred. Please try again." }),
4404
4556
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: this.handleReset, style: buttonStyle3, children: "Try again" })
4405
4557
  ] });
@@ -4419,7 +4571,7 @@ var containerStyle8 = {
4419
4571
  var iconStyle4 = {
4420
4572
  marginBottom: 20
4421
4573
  };
4422
- var headingStyle11 = {
4574
+ var headingStyle12 = {
4423
4575
  fontSize: "1.25rem",
4424
4576
  fontWeight: 700,
4425
4577
  color: "#1a1a1a",
@@ -4582,6 +4734,7 @@ function SwypePaymentInner({
4582
4734
  const [transfer, setTransfer] = react.useState(null);
4583
4735
  const [creatingTransfer, setCreatingTransfer] = react.useState(false);
4584
4736
  const [registeringPasskey, setRegisteringPasskey] = react.useState(false);
4737
+ const [verifyingPasskeyPopup, setVerifyingPasskeyPopup] = react.useState(false);
4585
4738
  const [passkeyPopupNeeded, setPasskeyPopupNeeded] = react.useState(
4586
4739
  () => isSafari() && isInCrossOriginIframe()
4587
4740
  );
@@ -4589,6 +4742,7 @@ function SwypePaymentInner({
4589
4742
  if (typeof window === "undefined") return null;
4590
4743
  return window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
4591
4744
  });
4745
+ const [knownCredentialIds, setKnownCredentialIds] = react.useState([]);
4592
4746
  const [authInput, setAuthInput] = react.useState("");
4593
4747
  const [verificationTarget, setVerificationTarget] = react.useState(null);
4594
4748
  const [otpCode, setOtpCode] = react.useState("");
@@ -4878,6 +5032,7 @@ function SwypePaymentInner({
4878
5032
  setOneTapLimit(config.defaultAllowance);
4879
5033
  }
4880
5034
  const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
5035
+ setKnownCredentialIds(allPasskeys.map((p) => p.credentialId));
4881
5036
  if (allPasskeys.length === 0) {
4882
5037
  setStep("create-passkey");
4883
5038
  return;
@@ -4888,6 +5043,10 @@ function SwypePaymentInner({
4888
5043
  }
4889
5044
  if (cancelled) return;
4890
5045
  const credentialIds = allPasskeys.map((p) => p.credentialId);
5046
+ if (isSafari() && isInCrossOriginIframe()) {
5047
+ setStep("verify-passkey");
5048
+ return;
5049
+ }
4891
5050
  const matched = await findDevicePasskey(credentialIds);
4892
5051
  if (cancelled) return;
4893
5052
  if (matched) {
@@ -5394,12 +5553,18 @@ function SwypePaymentInner({
5394
5553
  setRegisteringPasskey(true);
5395
5554
  setError(null);
5396
5555
  try {
5556
+ const token = await getAccessToken();
5397
5557
  const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
5398
5558
  const popupOptions = buildPasskeyPopupOptions({
5399
5559
  userId: user?.id ?? "unknown",
5400
- displayName: passkeyDisplayName
5560
+ displayName: passkeyDisplayName,
5561
+ authToken: token ?? void 0,
5562
+ apiBaseUrl
5401
5563
  });
5402
- const { credentialId, publicKey } = await createPasskeyViaPopup(popupOptions);
5564
+ const { credentialId, publicKey } = await createPasskeyViaPopup(
5565
+ popupOptions,
5566
+ knownCredentialIds
5567
+ );
5403
5568
  await completePasskeyRegistration(credentialId, publicKey);
5404
5569
  } catch (err) {
5405
5570
  captureException(err);
@@ -5407,7 +5572,28 @@ function SwypePaymentInner({
5407
5572
  } finally {
5408
5573
  setRegisteringPasskey(false);
5409
5574
  }
5410
- }, [user, completePasskeyRegistration]);
5575
+ }, [user, completePasskeyRegistration, getAccessToken, apiBaseUrl, knownCredentialIds]);
5576
+ const handleVerifyPasskeyViaPopup = react.useCallback(async () => {
5577
+ setVerifyingPasskeyPopup(true);
5578
+ setError(null);
5579
+ try {
5580
+ const matched = await findDevicePasskeyViaPopup({
5581
+ credentialIds: knownCredentialIds,
5582
+ rpId: resolvePasskeyRpId()
5583
+ });
5584
+ if (matched) {
5585
+ setActiveCredentialId(matched);
5586
+ window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
5587
+ setStep("login");
5588
+ } else {
5589
+ setStep("create-passkey");
5590
+ }
5591
+ } catch {
5592
+ setStep("create-passkey");
5593
+ } finally {
5594
+ setVerifyingPasskeyPopup(false);
5595
+ }
5596
+ }, [knownCredentialIds]);
5411
5597
  const handleSelectProvider = react.useCallback((providerId) => {
5412
5598
  setSelectedProviderId(providerId);
5413
5599
  setSelectedAccountId(null);
@@ -5539,6 +5725,17 @@ function SwypePaymentInner({
5539
5725
  if ((step === "login" || step === "otp-verify") && authenticated) {
5540
5726
  return /* @__PURE__ */ jsxRuntime.jsx(ScreenLayout, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { label: "Verifying your passkey..." }) }) });
5541
5727
  }
5728
+ if (step === "verify-passkey") {
5729
+ return /* @__PURE__ */ jsxRuntime.jsx(
5730
+ VerifyPasskeyScreen,
5731
+ {
5732
+ onVerify: handleVerifyPasskeyViaPopup,
5733
+ onBack: handleLogout,
5734
+ verifying: verifyingPasskeyPopup,
5735
+ error
5736
+ }
5737
+ );
5738
+ }
5542
5739
  if (step === "create-passkey") {
5543
5740
  return /* @__PURE__ */ jsxRuntime.jsx(
5544
5741
  CreatePasskeyScreen,
@@ -5723,8 +5920,10 @@ exports.createPasskeyViaPopup = createPasskeyViaPopup;
5723
5920
  exports.darkTheme = darkTheme;
5724
5921
  exports.deviceHasPasskey = deviceHasPasskey;
5725
5922
  exports.findDevicePasskey = findDevicePasskey;
5923
+ exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
5726
5924
  exports.getTheme = getTheme;
5727
5925
  exports.lightTheme = lightTheme;
5926
+ exports.resolvePasskeyRpId = resolvePasskeyRpId;
5728
5927
  exports.swypeApi = api_exports;
5729
5928
  exports.useAuthorizationExecutor = useAuthorizationExecutor;
5730
5929
  exports.useSwypeConfig = useSwypeConfig;