@forge-connect/react 1.0.2 → 1.0.4

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.js CHANGED
@@ -812,1691 +812,1688 @@ function EmailOtpForm() {
812
812
 
813
813
  // src/components/tabs/wallet-connect.tsx
814
814
  import { useState as useState3, useMemo, useRef as useRef3, useCallback } from "react";
815
-
816
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/bufferToBase64URLString.js
817
- function bufferToBase64URLString(buffer) {
818
- const bytes = new Uint8Array(buffer);
819
- let str = "";
820
- for (const charCode of bytes) {
821
- str += String.fromCharCode(charCode);
815
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
816
+ var MOBILE_WALLETS = [
817
+ {
818
+ name: "Phantom",
819
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA4IiBoZWlnaHQ9IjEwOCIgdmlld0JveD0iMCAwIDEwOCAxMDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMDgiIGhlaWdodD0iMTA4IiByeD0iMjYiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00Ni41MjY3IDY5LjkyMjlDNDIuMDA1NCA3Ni44NTA5IDM0LjQyOTIgODUuNjE4MiAyNC4zNDggODUuNjE4MkMxOS41ODI0IDg1LjYxODIgMTUgODMuNjU2MyAxNSA3NS4xMzQyQzE1IDUzLjQzMDUgNDQuNjMyNiAxOS44MzI3IDcyLjEyNjggMTkuODMyN0M4Ny43NjggMTkuODMyNyA5NCAzMC42ODQ2IDk0IDQzLjAwNzlDOTQgNTguODI1OCA4My43MzU1IDc2LjkxMjIgNzMuNTMyMSA3Ni45MTIyQzcwLjI5MzkgNzYuOTEyMiA2OC43MDUzIDc1LjEzNDIgNjguNzA1MyA3Mi4zMTRDNjguNzA1MyA3MS41NzgzIDY4LjgyNzUgNzAuNzgxMiA2OS4wNzE5IDY5LjkyMjlDNjUuNTg5MyA3NS44Njk5IDU4Ljg2ODUgODEuMzg3OCA1Mi41NzU0IDgxLjM4NzhDNDcuOTkzIDgxLjM4NzggNDUuNjcxMyA3OC41MDYzIDQ1LjY3MTMgNzQuNDU5OEM0NS42NzEzIDcyLjk4ODQgNDUuOTc2OCA3MS40NTU2IDQ2LjUyNjcgNjkuOTIyOVpNODMuNjc2MSA0Mi41Nzk0QzgzLjY3NjEgNDYuMTcwNCA4MS41NTc1IDQ3Ljk2NTggNzkuMTg3NSA0Ny45NjU4Qzc2Ljc4MTYgNDcuOTY1OCA3NC42OTg5IDQ2LjE3MDQgNzQuNjk4OSA0Mi41Nzk0Qzc0LjY5ODkgMzguOTg4NSA3Ni43ODE2IDM3LjE5MzEgNzkuMTg3NSAzNy4xOTMxQzgxLjU1NzUgMzcuMTkzMSA4My42NzYxIDM4Ljk4ODUgODMuNjc2MSA0Mi41Nzk0Wk03MC4yMTAzIDQyLjU3OTVDNzAuMjEwMyA0Ni4xNzA0IDY4LjA5MTYgNDcuOTY1OCA2NS43MjE2IDQ3Ljk2NThDNjMuMzE1NyA0Ny45NjU4IDYxLjIzMyA0Ni4xNzA0IDYxLjIzMyA0Mi41Nzk1QzYxLjIzMyAzOC45ODg1IDYzLjMxNTcgMzcuMTkzMSA2NS43MjE2IDM3LjE5MzFDNjguMDkxNiAzNy4xOTMxIDcwLjIxMDMgMzguOTg4NSA3MC4yMTAzIDQyLjU3OTVaIiBmaWxsPSIjRkZGREY4Ii8+Cjwvc3ZnPgo=",
820
+ buildUrl: (url) => `https://phantom.app/ul/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
821
+ },
822
+ {
823
+ name: "Solflare",
824
+ icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJTIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MCA1MCI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMjA1MGE7c3Ryb2tlOiNmZmVmNDY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOi41cHg7fS5jbHMtMntmaWxsOiNmZmVmNDY7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMCIgd2lkdGg9IjUwIiBoZWlnaHQ9IjUwIiByeD0iMTIiIHJ5PSIxMiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTI0LjIzLDI2LjQybDIuNDYtMi4zOCw0LjU5LDEuNWMzLjAxLDEsNC41MSwyLjg0LDQuNTEsNS40MywwLDEuOTYtLjc1LDMuMjYtMi4yNSw0LjkzbC0uNDYuNS4xNy0xLjE3Yy42Ny00LjI2LS41OC02LjA5LTQuNzItNy40M2wtNC4zLTEuMzhoMFpNMTguMDUsMTEuODVsMTIuNTIsNC4xNy0yLjcxLDIuNTktNi41MS0yLjE3Yy0yLjI1LS43NS0zLjAxLTEuOTYtMy4zLTQuNTF2LS4wOGgwWk0xNy4zLDMzLjA2bDIuODQtMi43MSw1LjM0LDEuNzVjMi44LjkyLDMuNzYsMi4xMywzLjQ2LDUuMThsLTExLjY1LTQuMjJoMFpNMTMuNzEsMjAuOTVjMC0uNzkuNDItMS41NCwxLjEzLTIuMTcuNzUsMS4wOSwyLjA1LDIuMDUsNC4wOSwyLjcxbDQuNDIsMS40Ni0yLjQ2LDIuMzgtNC4zNC0xLjQyYy0yLS42Ny0yLjg0LTEuNjctMi44NC0yLjk2TTI2LjgyLDQyLjg3YzkuMTgtNi4wOSwxNC4xMS0xMC4yMywxNC4xMS0xNS4zMiwwLTMuMzgtMi01LjI2LTYuNDMtNi43MmwtMy4zNC0xLjEzLDkuMTQtOC43Ny0xLjg0LTEuOTYtMi43MSwyLjM4LTEyLjgxLTQuMjJjLTMuOTcsMS4yOS04Ljk3LDUuMDktOC45Nyw4Ljg5LDAsLjQyLjA0LjgzLjE3LDEuMjktMy4zLDEuODgtNC42MywzLjYzLTQuNjMsNS44LDAsMi4wNSwxLjA5LDQuMDksNC41NSw1LjIybDIuNzUuOTItOS41Miw5LjE0LDEuODQsMS45NiwyLjk2LTIuNzEsMTQuNzMsNS4yMmgwWiIvPjwvc3ZnPg==",
825
+ buildUrl: (url) => `https://solflare.com/ul/v1/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
826
+ },
827
+ {
828
+ name: "Backpack",
829
+ icon: "data:image/webp;base64,UklGRlgCAABXRUJQVlA4WAoAAAAQAAAAOwAAOwAAQUxQSC8AAAABL6CmbQOGP75e/cti0oiIOI0KBto2+a8ACZjAAQ7wL2gCSCL6PwER2t+KmZpFDwBWUDggAgIAAPALAJ0BKjwAPAA+SSCMRKKiIRYKrTQoBISyAGp7nL12mKuwHiZ/pX71XoA8m7rXfQA8q39lfhJ/cL0ZimIeNSGxOaSWtqEDDdJ++ARG5/LVm6OquItoAu4rpz8VksUr1NEDJ0CeZucwAP7+VdYX7J5e8V3cJZ9QoY9vj8KsNGOvZSm+khDZOvwCeiDFjGweq/8KIUmVQk3T/qW3ONyMcepN2uwyEXpZZiidST0x705r7ZnNuWX42r8oi+FEshvXGWm6DabjfrMbiaxJ15irl2Gj3nKWbZ2v3k/jd172plSBejBs7dNt90Re/m+nmv5kNjM/SPfa4EpzvOdu/8BR+hTnR4qe4eWhTsh/oecEfN31OExdi/6AYrsth4nlx/qGEP70b1/TNKmgaB/18+c9ntTfhku9wYpy17/wN3r+B/1geZPzth+AtNsMDXE99PaPLWdFP9MOIMD6ME+MpZu2H7WSfzY7MkWs/N/NNzAZ0P/9vMvxgcdzFHvs/doeUU9WyVv+Ll8QDJbn9NZl86ZZNYtfP4Ol1hiJHWosWyj/66vhMUeFIcDheXIOSRTzvKwj5ffEl7fn/QwflNU556SEjM8MriwSFJ/nrFBrr8O1/pqdrYGqPijG6tjsBVBHp+EDAbtNLL375I2jnG9i3Xb1ZphsFYNjdRaknYdFf4g5iJBoAAA=",
830
+ buildUrl: (url) => `https://backpack.app/ul/browse/${encodeURIComponent(url)}`
822
831
  }
823
- const base64String = btoa(str);
824
- return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
832
+ ];
833
+ function isMobile() {
834
+ if (typeof navigator === "undefined") return false;
835
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
825
836
  }
826
-
827
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/base64URLStringToBuffer.js
828
- function base64URLStringToBuffer(base64URLString) {
829
- const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
830
- const padLength = (4 - base64.length % 4) % 4;
831
- const padded = base64.padEnd(base64.length + padLength, "=");
832
- const binary = atob(padded);
833
- const buffer = new ArrayBuffer(binary.length);
834
- const bytes = new Uint8Array(buffer);
835
- for (let i = 0; i < binary.length; i++) {
836
- bytes[i] = binary.charCodeAt(i);
837
+ function WalletConnectForm() {
838
+ const { walletAdapter, setModalStep, config } = useForgeConnect();
839
+ const methods = resolveLoginMethods(config);
840
+ const showBack = methods.length > 1;
841
+ if (!walletAdapter && isMobile()) {
842
+ return /* @__PURE__ */ jsx4(MobileWalletFlow, {});
837
843
  }
838
- return buffer;
839
- }
840
-
841
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthn.js
842
- function browserSupportsWebAuthn() {
843
- return _browserSupportsWebAuthnInternals.stubThis(globalThis?.PublicKeyCredential !== void 0 && typeof globalThis.PublicKeyCredential === "function");
844
+ if (walletAdapter) {
845
+ return /* @__PURE__ */ jsx4(WalletAdapterFlow, {});
846
+ }
847
+ return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
848
+ /* @__PURE__ */ jsx4("h3", { className: "fc-tab-title", children: "Connect wallet" }),
849
+ /* @__PURE__ */ jsxs4("p", { className: "fc-text", children: [
850
+ "No wallet adapter detected. Pass ",
851
+ /* @__PURE__ */ jsx4("code", { children: "walletAdapter" }),
852
+ " to",
853
+ " ",
854
+ /* @__PURE__ */ jsx4("code", { children: "<ForgeConnectProvider>" }),
855
+ " to enable wallet login."
856
+ ] }),
857
+ showBack && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
858
+ ] });
844
859
  }
845
- var _browserSupportsWebAuthnInternals = {
846
- stubThis: (value) => value
847
- };
848
-
849
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toPublicKeyCredentialDescriptor.js
850
- function toPublicKeyCredentialDescriptor(descriptor) {
851
- const { id } = descriptor;
852
- return {
853
- ...descriptor,
854
- id: base64URLStringToBuffer(id),
855
- /**
856
- * `descriptor.transports` is an array of our `AuthenticatorTransportFuture` that includes newer
857
- * transports that TypeScript's DOM lib is ignorant of. Convince TS that our list of transports
858
- * are fine to pass to WebAuthn since browsers will recognize the new value.
859
- */
860
- transports: descriptor.transports
860
+ function MobileWalletFlow() {
861
+ const { setModalStep, config } = useForgeConnect();
862
+ const methods = resolveLoginMethods(config);
863
+ const showBack = methods.length > 1;
864
+ const walletConfig = config.walletConfig;
865
+ const preferred = walletConfig?.preferredWallets ?? [];
866
+ const onlyPreferred = walletConfig?.onlyPreferred ?? false;
867
+ const walletsToShow = useMemo(() => {
868
+ if (preferred.length > 0) {
869
+ const prefList = preferred.map((name) => MOBILE_WALLETS.find((mw) => mw.name === name)).filter(Boolean);
870
+ if (onlyPreferred) return prefList;
871
+ const prefNames = new Set(preferred);
872
+ const others = MOBILE_WALLETS.filter((mw) => !prefNames.has(mw.name));
873
+ return [...prefList, ...others];
874
+ }
875
+ return MOBILE_WALLETS;
876
+ }, [preferred, onlyPreferred]);
877
+ const handleOpen = (mw) => {
878
+ const pageUrl = window.location.href;
879
+ window.location.href = mw.buildUrl(pageUrl);
861
880
  };
881
+ return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
882
+ /* @__PURE__ */ jsx4("div", { className: "fc-wallet-list", children: walletsToShow.map((mw) => /* @__PURE__ */ jsxs4(
883
+ "button",
884
+ {
885
+ type: "button",
886
+ className: "fc-btn fc-btn-wallet",
887
+ onClick: () => handleOpen(mw),
888
+ children: [
889
+ /* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
890
+ /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
891
+ /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Open app" })
892
+ ]
893
+ },
894
+ mw.name
895
+ )) }),
896
+ /* @__PURE__ */ jsx4("p", { className: "fc-text", style: { textAlign: "center", fontSize: 12, opacity: 0.6 }, children: "You'll be redirected to the wallet app to sign in." }),
897
+ showBack && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
898
+ ] });
862
899
  }
863
-
864
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/isValidDomain.js
865
- function isValidDomain(hostname) {
866
- return (
867
- // Consider localhost valid as well since it's okay wrt Secure Contexts
868
- hostname === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname)
869
- );
870
- }
871
-
872
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnError.js
873
- var WebAuthnError = class extends Error {
874
- constructor({ message, code, cause, name }) {
875
- super(message, { cause });
876
- Object.defineProperty(this, "code", {
877
- enumerable: true,
878
- configurable: true,
879
- writable: true,
880
- value: void 0
881
- });
882
- this.name = name ?? cause.name;
883
- this.code = code;
884
- }
885
- };
886
-
887
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/identifyRegistrationError.js
888
- function identifyRegistrationError({ error, options }) {
889
- const { publicKey } = options;
890
- if (!publicKey) {
891
- throw Error("options was missing required publicKey property");
892
- }
893
- if (error.name === "AbortError") {
894
- if (options.signal instanceof AbortSignal) {
895
- return new WebAuthnError({
896
- message: "Registration ceremony was sent an abort signal",
897
- code: "ERROR_CEREMONY_ABORTED",
898
- cause: error
899
- });
900
- }
901
- } else if (error.name === "ConstraintError") {
902
- if (publicKey.authenticatorSelection?.requireResidentKey === true) {
903
- return new WebAuthnError({
904
- message: "Discoverable credentials were required but no available authenticator supported it",
905
- code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
906
- cause: error
907
- });
908
- } else if (
909
- // @ts-ignore: `mediation` doesn't yet exist on CredentialCreationOptions but it's possible as of Sept 2024
910
- options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required"
911
- ) {
912
- return new WebAuthnError({
913
- message: "User verification was required during automatic registration but it could not be performed",
914
- code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
915
- cause: error
916
- });
917
- } else if (publicKey.authenticatorSelection?.userVerification === "required") {
918
- return new WebAuthnError({
919
- message: "User verification was required but no available authenticator supported it",
920
- code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
921
- cause: error
922
- });
900
+ function WalletAdapterFlow() {
901
+ const { walletAdapter, loginWithWallet, setModalStep, config } = useForgeConnect();
902
+ const wallet = walletAdapter;
903
+ const walletConfig = config.walletConfig;
904
+ const methods = resolveLoginMethods(config);
905
+ const showBack = methods.length > 1;
906
+ const [error, setError] = useState3("");
907
+ const [loading, setLoading] = useState3(false);
908
+ const [showOther, setShowOther] = useState3(false);
909
+ const [coldWallet, setColdWallet] = useState3(false);
910
+ const mobile = useMemo(() => isMobile(), []);
911
+ const coldWalletRef = useRef3(coldWallet);
912
+ coldWalletRef.current = coldWallet;
913
+ const buildSignTxFnForAdapter = useCallback((adapter) => {
914
+ if (!adapter.signTransaction) return void 0;
915
+ const TxClass = walletConfig?.Transaction;
916
+ if (!TxClass) return void 0;
917
+ return async (txBase64) => {
918
+ const bytes = Uint8Array.from(atob(txBase64), (c) => c.charCodeAt(0));
919
+ const tx = TxClass.from(bytes);
920
+ const signedTx = await adapter.signTransaction(tx);
921
+ return btoa(String.fromCharCode(...new Uint8Array(signedTx.serialize())));
922
+ };
923
+ }, [walletConfig?.Transaction]);
924
+ const handleConnect = async (w) => {
925
+ if (w.readyState !== "Installed") {
926
+ if (mobile) {
927
+ const mw = MOBILE_WALLETS.find((m) => m.name === w.adapter.name);
928
+ if (mw) {
929
+ window.location.href = mw.buildUrl(window.location.href);
930
+ return;
931
+ }
932
+ }
933
+ const url = w.adapter.url;
934
+ if (url) window.open(url, "_blank");
935
+ return;
923
936
  }
924
- } else if (error.name === "InvalidStateError") {
925
- return new WebAuthnError({
926
- message: "The authenticator was previously registered",
927
- code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
928
- cause: error
929
- });
930
- } else if (error.name === "NotAllowedError") {
931
- return new WebAuthnError({
932
- message: error.message,
933
- code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
934
- cause: error
935
- });
936
- } else if (error.name === "NotSupportedError") {
937
- const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === "public-key");
938
- if (validPubKeyCredParams.length === 0) {
939
- return new WebAuthnError({
940
- message: 'No entry in pubKeyCredParams was of type "public-key"',
941
- code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
942
- cause: error
943
- });
937
+ setError("");
938
+ setLoading(true);
939
+ try {
940
+ wallet.select(w.adapter.name);
941
+ await wallet.connect();
942
+ const pk = w.adapter.publicKey ?? wallet.publicKey;
943
+ if (!pk) throw new Error("Wallet did not provide a public key.");
944
+ const address = pk.toBase58();
945
+ const useCold = coldWalletRef.current;
946
+ const adapterSignMessage = wallet.signMessage ? (msg) => wallet.signMessage(msg) : void 0;
947
+ await loginWithWallet(
948
+ address,
949
+ useCold ? void 0 : adapterSignMessage,
950
+ "solana",
951
+ useCold ? buildSignTxFnForAdapter(w.adapter) : void 0
952
+ );
953
+ } catch (err) {
954
+ setError(err instanceof Error ? err.message : "Could not verify your wallet. Please try again.");
955
+ setLoading(false);
944
956
  }
945
- return new WebAuthnError({
946
- message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
947
- code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
948
- cause: error
949
- });
950
- } else if (error.name === "SecurityError") {
951
- const effectiveDomain = globalThis.location.hostname;
952
- if (!isValidDomain(effectiveDomain)) {
953
- return new WebAuthnError({
954
- message: `${globalThis.location.hostname} is an invalid domain`,
955
- code: "ERROR_INVALID_DOMAIN",
956
- cause: error
957
- });
958
- } else if (publicKey.rp.id !== effectiveDomain) {
959
- return new WebAuthnError({
960
- message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
961
- code: "ERROR_INVALID_RP_ID",
962
- cause: error
963
- });
964
- }
965
- } else if (error.name === "TypeError") {
966
- if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
967
- return new WebAuthnError({
968
- message: "User ID was not between 1 and 64 characters",
969
- code: "ERROR_INVALID_USER_ID_LENGTH",
970
- cause: error
971
- });
972
- }
973
- } else if (error.name === "UnknownError") {
974
- return new WebAuthnError({
975
- message: "The authenticator was unable to process the specified options, or could not create a new credential",
976
- code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
977
- cause: error
978
- });
979
- }
980
- return error;
981
- }
982
-
983
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnAbortService.js
984
- var BaseWebAuthnAbortService = class {
985
- constructor() {
986
- Object.defineProperty(this, "controller", {
987
- enumerable: true,
988
- configurable: true,
989
- writable: true,
990
- value: void 0
991
- });
992
- }
993
- createNewAbortSignal() {
994
- if (this.controller) {
995
- const abortError = new Error("Cancelling existing WebAuthn API call for new one");
996
- abortError.name = "AbortError";
997
- this.controller.abort(abortError);
998
- }
999
- const newController = new AbortController();
1000
- this.controller = newController;
1001
- return newController.signal;
1002
- }
1003
- cancelCeremony() {
1004
- if (this.controller) {
1005
- const abortError = new Error("Manually cancelling existing WebAuthn API call");
1006
- abortError.name = "AbortError";
1007
- this.controller.abort(abortError);
1008
- this.controller = void 0;
957
+ };
958
+ const handleMobileOpen = (mw) => {
959
+ window.location.href = mw.buildUrl(window.location.href);
960
+ };
961
+ const preferred = walletConfig?.preferredWallets ?? [];
962
+ const onlyPreferred = walletConfig?.onlyPreferred ?? false;
963
+ const preferredSet = new Set(preferred);
964
+ const connectedWalletName = wallet.publicKey ? wallet.wallets.find((w) => w.adapter.connected)?.adapter.name ?? null : null;
965
+ const { preferredWallets, otherWallets } = useMemo(() => {
966
+ const all = wallet.wallets;
967
+ const prefList = preferred.map((name) => all.find((w) => w.adapter.name === name)).filter(Boolean);
968
+ if (onlyPreferred && preferred.length > 0) {
969
+ return { preferredWallets: prefList, otherWallets: [] };
1009
970
  }
1010
- }
1011
- };
1012
- var WebAuthnAbortService = new BaseWebAuthnAbortService();
1013
-
1014
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toAuthenticatorAttachment.js
1015
- var attachments = ["cross-platform", "platform"];
1016
- function toAuthenticatorAttachment(attachment) {
1017
- if (!attachment) {
1018
- return;
1019
- }
1020
- if (attachments.indexOf(attachment) < 0) {
1021
- return;
1022
- }
1023
- return attachment;
971
+ const others = all.filter(
972
+ (w) => !preferredSet.has(w.adapter.name) && w.readyState === "Installed"
973
+ );
974
+ return { preferredWallets: prefList, otherWallets: others };
975
+ }, [wallet.wallets, walletConfig]);
976
+ const mobileExtraWallets = useMemo(() => {
977
+ if (!mobile) return [];
978
+ const adapterNames = new Set(wallet.wallets.map((w) => w.adapter.name));
979
+ return MOBILE_WALLETS.filter((mw) => !adapterNames.has(mw.name));
980
+ }, [mobile, wallet.wallets]);
981
+ return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
982
+ loading ? /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
983
+ /* @__PURE__ */ jsx4("p", { className: "fc-tab-title", children: "Connecting..." }),
984
+ /* @__PURE__ */ jsx4("p", { className: "fc-text", children: coldWallet ? "Confirm the transaction on your device" : "Approve the connection, then sign the verification request in your wallet" }),
985
+ error && /* @__PURE__ */ jsxs4(Fragment, { children: [
986
+ /* @__PURE__ */ jsx4("p", { className: "fc-error", children: error }),
987
+ /* @__PURE__ */ jsx4(
988
+ "button",
989
+ {
990
+ type: "button",
991
+ className: "fc-btn fc-btn-secondary",
992
+ onClick: () => {
993
+ setLoading(false);
994
+ setError("");
995
+ },
996
+ style: { marginTop: 8 },
997
+ children: "Try again"
998
+ }
999
+ )
1000
+ ] })
1001
+ ] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
1002
+ /* @__PURE__ */ jsxs4("div", { className: "fc-wallet-list", children: [
1003
+ preferredWallets.map((w) => {
1004
+ const installed = w.readyState === "Installed";
1005
+ const isConnected = w.adapter.name === connectedWalletName;
1006
+ return /* @__PURE__ */ jsxs4(
1007
+ "button",
1008
+ {
1009
+ type: "button",
1010
+ className: "fc-btn fc-btn-wallet",
1011
+ onClick: () => handleConnect(w),
1012
+ children: [
1013
+ /* @__PURE__ */ jsx4("span", { className: installed ? "fc-installed-dot" : "", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
1014
+ /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
1015
+ isConnected ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" }) : /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Preferred" }),
1016
+ !installed && mobile && MOBILE_WALLETS.some((mw) => mw.name === w.adapter.name) ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" }) : !installed && /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Install" })
1017
+ ]
1018
+ },
1019
+ w.adapter.name
1020
+ );
1021
+ }),
1022
+ mobileExtraWallets.map((mw) => /* @__PURE__ */ jsxs4(
1023
+ "button",
1024
+ {
1025
+ type: "button",
1026
+ className: "fc-btn fc-btn-wallet",
1027
+ onClick: () => handleMobileOpen(mw),
1028
+ children: [
1029
+ /* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
1030
+ /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
1031
+ /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" })
1032
+ ]
1033
+ },
1034
+ mw.name
1035
+ )),
1036
+ otherWallets.length > 0 && !showOther && /* @__PURE__ */ jsxs4(
1037
+ "button",
1038
+ {
1039
+ type: "button",
1040
+ className: "fc-btn fc-btn-wallet",
1041
+ onClick: () => setShowOther(true),
1042
+ children: [
1043
+ /* @__PURE__ */ jsx4("span", { className: "fc-other-wallets-icon", children: /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
1044
+ /* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "16", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1045
+ /* @__PURE__ */ jsx4("path", { d: "M2 7h16", stroke: "currentColor", strokeWidth: "1.5" }),
1046
+ /* @__PURE__ */ jsx4("rect", { x: "11", y: "10", width: "7", height: "4", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
1047
+ ] }) }),
1048
+ /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: "Other wallets" })
1049
+ ]
1050
+ }
1051
+ ),
1052
+ showOther && otherWallets.map((w) => {
1053
+ const isConnected = w.adapter.name === connectedWalletName;
1054
+ return /* @__PURE__ */ jsxs4(
1055
+ "button",
1056
+ {
1057
+ type: "button",
1058
+ className: "fc-btn fc-btn-wallet",
1059
+ onClick: () => handleConnect(w),
1060
+ children: [
1061
+ /* @__PURE__ */ jsx4("span", { className: "fc-installed-dot", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
1062
+ /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
1063
+ isConnected && /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" })
1064
+ ]
1065
+ },
1066
+ w.adapter.name
1067
+ );
1068
+ })
1069
+ ] }),
1070
+ preferredWallets.length === 0 && otherWallets.length === 0 && mobileExtraWallets.length === 0 && /* @__PURE__ */ jsx4("p", { className: "fc-text", children: "No wallet found. Please install a Solana wallet (like Phantom) to continue." }),
1071
+ walletConfig?.Transaction && /* @__PURE__ */ jsxs4("label", { className: "fc-cold-wallet-toggle", children: [
1072
+ /* @__PURE__ */ jsx4(
1073
+ "input",
1074
+ {
1075
+ type: "checkbox",
1076
+ checked: coldWallet,
1077
+ onChange: (e) => setColdWallet(e.target.checked)
1078
+ }
1079
+ ),
1080
+ /* @__PURE__ */ jsx4("span", { className: "fc-toggle-track" }),
1081
+ /* @__PURE__ */ jsxs4("span", { className: "fc-cold-wallet-label", children: [
1082
+ /* @__PURE__ */ jsx4("span", { children: "Hardware wallet" }),
1083
+ /* @__PURE__ */ jsx4("span", { children: "Ledger, Trezor, Keystone..." })
1084
+ ] })
1085
+ ] }),
1086
+ error && /* @__PURE__ */ jsx4("p", { className: "fc-error", children: error })
1087
+ ] }),
1088
+ showBack && !loading && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
1089
+ ] });
1024
1090
  }
1025
1091
 
1026
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startRegistration.js
1027
- async function startRegistration(options) {
1028
- if (!options.optionsJSON && options.challenge) {
1029
- console.warn("startRegistration() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
1030
- options = { optionsJSON: options };
1031
- }
1032
- const { optionsJSON, useAutoRegister = false } = options;
1033
- if (!browserSupportsWebAuthn()) {
1034
- throw new Error("WebAuthn is not supported in this browser");
1035
- }
1036
- const publicKey = {
1037
- ...optionsJSON,
1038
- challenge: base64URLStringToBuffer(optionsJSON.challenge),
1039
- user: {
1040
- ...optionsJSON.user,
1041
- id: base64URLStringToBuffer(optionsJSON.user.id)
1042
- },
1043
- excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
1044
- };
1045
- const createOptions = {};
1046
- if (useAutoRegister) {
1047
- createOptions.mediation = "conditional";
1048
- }
1049
- createOptions.publicKey = publicKey;
1050
- createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
1051
- let credential;
1052
- try {
1053
- credential = await navigator.credentials.create(createOptions);
1054
- } catch (err) {
1055
- throw identifyRegistrationError({ error: err, options: createOptions });
1056
- }
1057
- if (!credential) {
1058
- throw new Error("Registration was not completed");
1059
- }
1060
- const { id, rawId, response, type } = credential;
1061
- let transports = void 0;
1062
- if (typeof response.getTransports === "function") {
1063
- transports = response.getTransports();
1064
- }
1065
- let responsePublicKeyAlgorithm = void 0;
1066
- if (typeof response.getPublicKeyAlgorithm === "function") {
1067
- try {
1068
- responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
1069
- } catch (error) {
1070
- warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
1071
- }
1072
- }
1073
- let responsePublicKey = void 0;
1074
- if (typeof response.getPublicKey === "function") {
1092
+ // src/components/tabs/forgot-password.tsx
1093
+ import { useState as useState4 } from "react";
1094
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1095
+ function ForgotPasswordForm() {
1096
+ const { forgotPassword, resetPassword, setModalStep } = useForgeConnect();
1097
+ const [step, setStep] = useState4("email");
1098
+ const [email, setEmail] = useState4("");
1099
+ const [token, setToken] = useState4("");
1100
+ const [password, setPassword] = useState4("");
1101
+ const [error, setError] = useState4("");
1102
+ const [loading, setLoading] = useState4(false);
1103
+ const handleSendCode = async (e) => {
1104
+ e.preventDefault();
1105
+ setError("");
1106
+ setLoading(true);
1075
1107
  try {
1076
- const _publicKey = response.getPublicKey();
1077
- if (_publicKey !== null) {
1078
- responsePublicKey = bufferToBase64URLString(_publicKey);
1079
- }
1080
- } catch (error) {
1081
- warnOnBrokenImplementation("getPublicKey()", error);
1108
+ await forgotPassword(email);
1109
+ setStep("reset");
1110
+ } catch (err) {
1111
+ setError(err instanceof Error ? err.message : "Could not send the reset code. Please try again.");
1112
+ } finally {
1113
+ setLoading(false);
1082
1114
  }
1083
- }
1084
- let responseAuthenticatorData;
1085
- if (typeof response.getAuthenticatorData === "function") {
1115
+ };
1116
+ const handleReset = async (e) => {
1117
+ e.preventDefault();
1118
+ setError("");
1119
+ setLoading(true);
1086
1120
  try {
1087
- responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
1088
- } catch (error) {
1089
- warnOnBrokenImplementation("getAuthenticatorData()", error);
1121
+ await resetPassword(token.trim(), password);
1122
+ setStep("done");
1123
+ } catch (err) {
1124
+ setError(err instanceof Error ? err.message : "Could not reset your password. Please try again.");
1125
+ } finally {
1126
+ setLoading(false);
1090
1127
  }
1091
- }
1092
- return {
1093
- id,
1094
- rawId: bufferToBase64URLString(rawId),
1095
- response: {
1096
- attestationObject: bufferToBase64URLString(response.attestationObject),
1097
- clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
1098
- transports,
1099
- publicKeyAlgorithm: responsePublicKeyAlgorithm,
1100
- publicKey: responsePublicKey,
1101
- authenticatorData: responseAuthenticatorData
1102
- },
1103
- type,
1104
- clientExtensionResults: credential.getClientExtensionResults(),
1105
- authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
1106
1128
  };
1107
- }
1108
- function warnOnBrokenImplementation(methodName, cause) {
1109
- console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.
1110
- `, cause);
1111
- }
1112
-
1113
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthnAutofill.js
1114
- function browserSupportsWebAuthnAutofill() {
1115
- if (!browserSupportsWebAuthn()) {
1116
- return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
1129
+ if (step === "done") {
1130
+ return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
1131
+ /* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Password updated" }),
1132
+ /* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Your password has been reset. You can now sign in." }),
1133
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: () => setModalStep("email-login"), children: "Sign in" })
1134
+ ] });
1117
1135
  }
1118
- const globalPublicKeyCredential = globalThis.PublicKeyCredential;
1119
- if (globalPublicKeyCredential?.isConditionalMediationAvailable === void 0) {
1120
- return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
1136
+ if (step === "reset") {
1137
+ return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
1138
+ /* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Set new password" }),
1139
+ /* @__PURE__ */ jsxs5("p", { className: "fc-text", children: [
1140
+ "We sent a reset code to ",
1141
+ /* @__PURE__ */ jsx5("strong", { children: email }),
1142
+ ". Paste it below with your new password."
1143
+ ] }),
1144
+ /* @__PURE__ */ jsxs5("form", { onSubmit: handleReset, className: "fc-form", children: [
1145
+ /* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
1146
+ "Reset code",
1147
+ /* @__PURE__ */ jsx5(
1148
+ "input",
1149
+ {
1150
+ type: "text",
1151
+ className: "fc-input",
1152
+ value: token,
1153
+ onChange: (e) => setToken(e.target.value),
1154
+ placeholder: "Paste the code from your email",
1155
+ required: true,
1156
+ autoComplete: "off"
1157
+ }
1158
+ )
1159
+ ] }),
1160
+ /* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
1161
+ "New password",
1162
+ /* @__PURE__ */ jsx5(
1163
+ "input",
1164
+ {
1165
+ type: "password",
1166
+ className: "fc-input",
1167
+ value: password,
1168
+ onChange: (e) => setPassword(e.target.value),
1169
+ placeholder: "8+ characters",
1170
+ required: true,
1171
+ autoComplete: "new-password",
1172
+ minLength: 8
1173
+ }
1174
+ )
1175
+ ] }),
1176
+ error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
1177
+ /* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Resetting..." : "Reset password" })
1178
+ ] }),
1179
+ /* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setStep("email"), children: "Resend code" }) })
1180
+ ] });
1121
1181
  }
1122
- return _browserSupportsWebAuthnAutofillInternals.stubThis(globalPublicKeyCredential.isConditionalMediationAvailable());
1182
+ return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
1183
+ /* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Reset your password" }),
1184
+ /* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Enter your email and we'll send you a reset code." }),
1185
+ /* @__PURE__ */ jsxs5("form", { onSubmit: handleSendCode, className: "fc-form", children: [
1186
+ /* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
1187
+ "Email",
1188
+ /* @__PURE__ */ jsx5(
1189
+ "input",
1190
+ {
1191
+ type: "email",
1192
+ className: "fc-input",
1193
+ value: email,
1194
+ onChange: (e) => setEmail(e.target.value),
1195
+ placeholder: "you@example.com",
1196
+ required: true,
1197
+ autoComplete: "email"
1198
+ }
1199
+ )
1200
+ ] }),
1201
+ error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
1202
+ /* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Sending..." : "Send reset code" })
1203
+ ] }),
1204
+ /* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setModalStep("email-login"), children: "Back to sign in" }) })
1205
+ ] });
1123
1206
  }
1124
- var _browserSupportsWebAuthnAutofillInternals = {
1125
- stubThis: (value) => value
1126
- };
1127
1207
 
1128
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/identifyAuthenticationError.js
1129
- function identifyAuthenticationError({ error, options }) {
1130
- const { publicKey } = options;
1131
- if (!publicKey) {
1132
- throw Error("options was missing required publicKey property");
1133
- }
1134
- if (error.name === "AbortError") {
1135
- if (options.signal instanceof AbortSignal) {
1136
- return new WebAuthnError({
1137
- message: "Authentication ceremony was sent an abort signal",
1138
- code: "ERROR_CEREMONY_ABORTED",
1139
- cause: error
1140
- });
1141
- }
1142
- } else if (error.name === "NotAllowedError") {
1143
- return new WebAuthnError({
1144
- message: error.message,
1145
- code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
1146
- cause: error
1147
- });
1148
- } else if (error.name === "SecurityError") {
1149
- const effectiveDomain = globalThis.location.hostname;
1150
- if (!isValidDomain(effectiveDomain)) {
1151
- return new WebAuthnError({
1152
- message: `${globalThis.location.hostname} is an invalid domain`,
1153
- code: "ERROR_INVALID_DOMAIN",
1154
- cause: error
1155
- });
1156
- } else if (publicKey.rpId !== effectiveDomain) {
1157
- return new WebAuthnError({
1158
- message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
1159
- code: "ERROR_INVALID_RP_ID",
1160
- cause: error
1161
- });
1208
+ // src/components/tabs/verify-2fa.tsx
1209
+ import { useState as useState5, useRef as useRef4, useEffect as useEffect3, useCallback as useCallback2 } from "react";
1210
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1211
+ function Verify2FAForm() {
1212
+ const { verify2FA, verifyRecoveryCode, setModalStep } = useForgeConnect();
1213
+ const [code, setCode] = useState5("");
1214
+ const [loading, setLoading] = useState5(false);
1215
+ const [error, setError] = useState5("");
1216
+ const [useRecovery, setUseRecovery] = useState5(false);
1217
+ const inputRef = useRef4(null);
1218
+ const submittingRef = useRef4(false);
1219
+ useEffect3(() => {
1220
+ inputRef.current?.focus();
1221
+ }, [useRecovery]);
1222
+ const submitCode = useCallback2(async (codeValue, isRecovery) => {
1223
+ if (submittingRef.current || !codeValue.trim()) return;
1224
+ submittingRef.current = true;
1225
+ setLoading(true);
1226
+ setError("");
1227
+ try {
1228
+ if (isRecovery) {
1229
+ await verifyRecoveryCode(codeValue.trim());
1230
+ } else {
1231
+ await verify2FA(codeValue.trim());
1232
+ }
1233
+ } catch (err) {
1234
+ setError(err instanceof Error ? err.message : "Verification failed. Please try again.");
1235
+ } finally {
1236
+ setLoading(false);
1237
+ submittingRef.current = false;
1162
1238
  }
1163
- } else if (error.name === "UnknownError") {
1164
- return new WebAuthnError({
1165
- message: "The authenticator was unable to process the specified options, or could not create a new assertion signature",
1166
- code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
1167
- cause: error
1168
- });
1169
- }
1170
- return error;
1171
- }
1172
-
1173
- // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startAuthentication.js
1174
- async function startAuthentication(options) {
1175
- if (!options.optionsJSON && options.challenge) {
1176
- console.warn("startAuthentication() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
1177
- options = { optionsJSON: options };
1178
- }
1179
- const { optionsJSON, useBrowserAutofill = false, verifyBrowserAutofillInput = true } = options;
1180
- if (!browserSupportsWebAuthn()) {
1181
- throw new Error("WebAuthn is not supported in this browser");
1182
- }
1183
- let allowCredentials;
1184
- if (optionsJSON.allowCredentials?.length !== 0) {
1185
- allowCredentials = optionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
1186
- }
1187
- const publicKey = {
1188
- ...optionsJSON,
1189
- challenge: base64URLStringToBuffer(optionsJSON.challenge),
1190
- allowCredentials
1239
+ }, [verify2FA, verifyRecoveryCode]);
1240
+ const handleSubmit = async (e) => {
1241
+ e?.preventDefault();
1242
+ await submitCode(code, useRecovery);
1191
1243
  };
1192
- const getOptions = {};
1193
- if (useBrowserAutofill) {
1194
- if (!await browserSupportsWebAuthnAutofill()) {
1195
- throw Error("Browser does not support WebAuthn autofill");
1196
- }
1197
- const eligibleInputs = document.querySelectorAll("input[autocomplete$='webauthn']");
1198
- if (eligibleInputs.length < 1 && verifyBrowserAutofillInput) {
1199
- throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');
1244
+ const handleCodeChange = (value) => {
1245
+ setCode(value);
1246
+ if (!useRecovery && value.length === 6 && /^\d{6}$/.test(value)) {
1247
+ submitCode(value, false);
1200
1248
  }
1201
- getOptions.mediation = "conditional";
1202
- publicKey.allowCredentials = [];
1203
- }
1204
- getOptions.publicKey = publicKey;
1205
- getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
1206
- let credential;
1207
- try {
1208
- credential = await navigator.credentials.get(getOptions);
1209
- } catch (err) {
1210
- throw identifyAuthenticationError({ error: err, options: getOptions });
1211
- }
1212
- if (!credential) {
1213
- throw new Error("Authentication was not completed");
1214
- }
1215
- const { id, rawId, response, type } = credential;
1216
- let userHandle = void 0;
1217
- if (response.userHandle) {
1218
- userHandle = bufferToBase64URLString(response.userHandle);
1219
- }
1220
- return {
1221
- id,
1222
- rawId: bufferToBase64URLString(rawId),
1223
- response: {
1224
- authenticatorData: bufferToBase64URLString(response.authenticatorData),
1225
- clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
1226
- signature: bufferToBase64URLString(response.signature),
1227
- userHandle
1228
- },
1229
- type,
1230
- clientExtensionResults: credential.getClientExtensionResults(),
1231
- authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
1232
1249
  };
1250
+ return /* @__PURE__ */ jsxs6("div", { className: "fc-tab", children: [
1251
+ /* @__PURE__ */ jsxs6("button", { type: "button", className: "fc-back", onClick: () => setModalStep("method-select"), children: [
1252
+ /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx6("path", { d: "M10 12L6 8l4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1253
+ "Back"
1254
+ ] }),
1255
+ /* @__PURE__ */ jsx6("p", { className: "fc-account-section-desc", style: { marginTop: 8 }, children: useRecovery ? "Enter one of your recovery codes." : "Enter the 6-digit code from your authenticator app." }),
1256
+ error && /* @__PURE__ */ jsx6("p", { className: "fc-error", children: error }),
1257
+ /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "fc-form", children: [
1258
+ /* @__PURE__ */ jsx6(
1259
+ "input",
1260
+ {
1261
+ ref: inputRef,
1262
+ className: "fc-input fc-input-code",
1263
+ type: "text",
1264
+ inputMode: useRecovery ? "text" : "numeric",
1265
+ autoComplete: "one-time-code",
1266
+ placeholder: useRecovery ? "Recovery code" : "000000",
1267
+ maxLength: useRecovery ? 20 : 6,
1268
+ value: code,
1269
+ onChange: (e) => handleCodeChange(e.target.value),
1270
+ style: useRecovery ? {} : { textAlign: "center", letterSpacing: "0.3em", fontSize: "1.25rem" }
1271
+ }
1272
+ ),
1273
+ /* @__PURE__ */ jsx6("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading || !code.trim(), children: loading ? "Verifying..." : "Verify" })
1274
+ ] }),
1275
+ /* @__PURE__ */ jsx6(
1276
+ "button",
1277
+ {
1278
+ type: "button",
1279
+ className: "fc-link",
1280
+ onClick: () => {
1281
+ setUseRecovery(!useRecovery);
1282
+ setCode("");
1283
+ setError("");
1284
+ },
1285
+ style: { marginTop: 12 },
1286
+ children: useRecovery ? "Use authenticator code instead" : "Use a recovery code"
1287
+ }
1288
+ )
1289
+ ] });
1233
1290
  }
1234
1291
 
1235
- // src/runtime-imports.ts
1236
- var importSolanaWeb3 = () => import("@solana/web3.js");
1292
+ // src/components/svg-icon.tsx
1293
+ import { useRef as useRef5, useEffect as useEffect4 } from "react";
1294
+ import { jsx as jsx7 } from "react/jsx-runtime";
1295
+ function SvgIcon({ svg, className }) {
1296
+ const ref = useRef5(null);
1297
+ useEffect4(() => {
1298
+ if (!ref.current || !svg) return;
1299
+ try {
1300
+ const doc = new DOMParser().parseFromString(svg, "text/html");
1301
+ const svgEl = doc.body.querySelector("svg");
1302
+ if (!svgEl) {
1303
+ ref.current.textContent = "";
1304
+ return;
1305
+ }
1306
+ const dangerous = svgEl.querySelectorAll("script,iframe,object,embed,foreignObject");
1307
+ dangerous.forEach((el) => el.remove());
1308
+ const all = svgEl.querySelectorAll("*");
1309
+ all.forEach((el) => {
1310
+ for (const attr of Array.from(el.attributes)) {
1311
+ if (attr.name.startsWith("on")) {
1312
+ el.removeAttribute(attr.name);
1313
+ }
1314
+ }
1315
+ });
1316
+ ref.current.textContent = "";
1317
+ ref.current.appendChild(svgEl);
1318
+ } catch {
1319
+ ref.current.textContent = "";
1320
+ }
1321
+ }, [svg]);
1322
+ return /* @__PURE__ */ jsx7("span", { ref, className });
1323
+ }
1237
1324
 
1238
- // src/components/tabs/wallet-connect.tsx
1239
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1240
- var MOBILE_WALLETS = [
1241
- {
1242
- name: "Phantom",
1243
- icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA4IiBoZWlnaHQ9IjEwOCIgdmlld0JveD0iMCAwIDEwOCAxMDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMDgiIGhlaWdodD0iMTA4IiByeD0iMjYiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00Ni41MjY3IDY5LjkyMjlDNDIuMDA1NCA3Ni44NTA5IDM0LjQyOTIgODUuNjE4MiAyNC4zNDggODUuNjE4MkMxOS41ODI0IDg1LjYxODIgMTUgODMuNjU2MyAxNSA3NS4xMzQyQzE1IDUzLjQzMDUgNDQuNjMyNiAxOS44MzI3IDcyLjEyNjggMTkuODMyN0M4Ny43NjggMTkuODMyNyA5NCAzMC42ODQ2IDk0IDQzLjAwNzlDOTQgNTguODI1OCA4My43MzU1IDc2LjkxMjIgNzMuNTMyMSA3Ni45MTIyQzcwLjI5MzkgNzYuOTEyMiA2OC43MDUzIDc1LjEzNDIgNjguNzA1MyA3Mi4zMTRDNjguNzA1MyA3MS41NzgzIDY4LjgyNzUgNzAuNzgxMiA2OS4wNzE5IDY5LjkyMjlDNjUuNTg5MyA3NS44Njk5IDU4Ljg2ODUgODEuMzg3OCA1Mi41NzU0IDgxLjM4NzhDNDcuOTkzIDgxLjM4NzggNDUuNjcxMyA3OC41MDYzIDQ1LjY3MTMgNzQuNDU5OEM0NS42NzEzIDcyLjk4ODQgNDUuOTc2OCA3MS40NTU2IDQ2LjUyNjcgNjkuOTIyOVpNODMuNjc2MSA0Mi41Nzk0QzgzLjY3NjEgNDYuMTcwNCA4MS41NTc1IDQ3Ljk2NTggNzkuMTg3NSA0Ny45NjU4Qzc2Ljc4MTYgNDcuOTY1OCA3NC42OTg5IDQ2LjE3MDQgNzQuNjk4OSA0Mi41Nzk0Qzc0LjY5ODkgMzguOTg4NSA3Ni43ODE2IDM3LjE5MzEgNzkuMTg3NSAzNy4xOTMxQzgxLjU1NzUgMzcuMTkzMSA4My42NzYxIDM4Ljk4ODUgODMuNjc2MSA0Mi41Nzk0Wk03MC4yMTAzIDQyLjU3OTVDNzAuMjEwMyA0Ni4xNzA0IDY4LjA5MTYgNDcuOTY1OCA2NS43MjE2IDQ3Ljk2NThDNjMuMzE1NyA0Ny45NjU4IDYxLjIzMyA0Ni4xNzA0IDYxLjIzMyA0Mi41Nzk1QzYxLjIzMyAzOC45ODg1IDYzLjMxNTcgMzcuMTkzMSA2NS43MjE2IDM3LjE5MzFDNjguMDkxNiAzNy4xOTMxIDcwLjIxMDMgMzguOTg4NSA3MC4yMTAzIDQyLjU3OTVaIiBmaWxsPSIjRkZGREY4Ii8+Cjwvc3ZnPgo=",
1244
- buildUrl: (url) => `https://phantom.app/ul/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
1325
+ // src/components/tabs/oauth-buttons.tsx
1326
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1327
+ var PROVIDER_INFO = {
1328
+ google: {
1329
+ label: "Google",
1330
+ icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>'
1245
1331
  },
1246
- {
1247
- name: "Solflare",
1248
- icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJTIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MCA1MCI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMjA1MGE7c3Ryb2tlOiNmZmVmNDY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOi41cHg7fS5jbHMtMntmaWxsOiNmZmVmNDY7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMCIgd2lkdGg9IjUwIiBoZWlnaHQ9IjUwIiByeD0iMTIiIHJ5PSIxMiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTI0LjIzLDI2LjQybDIuNDYtMi4zOCw0LjU5LDEuNWMzLjAxLDEsNC41MSwyLjg0LDQuNTEsNS40MywwLDEuOTYtLjc1LDMuMjYtMi4yNSw0LjkzbC0uNDYuNS4xNy0xLjE3Yy42Ny00LjI2LS41OC02LjA5LTQuNzItNy40M2wtNC4zLTEuMzhoMFpNMTguMDUsMTEuODVsMTIuNTIsNC4xNy0yLjcxLDIuNTktNi41MS0yLjE3Yy0yLjI1LS43NS0zLjAxLTEuOTYtMy4zLTQuNTF2LS4wOGgwWk0xNy4zLDMzLjA2bDIuODQtMi43MSw1LjM0LDEuNzVjMi44LjkyLDMuNzYsMi4xMywzLjQ2LDUuMThsLTExLjY1LTQuMjJoMFpNMTMuNzEsMjAuOTVjMC0uNzkuNDItMS41NCwxLjEzLTIuMTcuNzUsMS4wOSwyLjA1LDIuMDUsNC4wOSwyLjcxbDQuNDIsMS40Ni0yLjQ2LDIuMzgtNC4zNC0xLjQyYy0yLS42Ny0yLjg0LTEuNjctMi44NC0yLjk2TTI2LjgyLDQyLjg3YzkuMTgtNi4wOSwxNC4xMS0xMC4yMywxNC4xMS0xNS4zMiwwLTMuMzgtMi01LjI2LTYuNDMtNi43MmwtMy4zNC0xLjEzLDkuMTQtOC43Ny0xLjg0LTEuOTYtMi43MSwyLjM4LTEyLjgxLTQuMjJjLTMuOTcsMS4yOS04Ljk3LDUuMDktOC45Nyw4Ljg5LDAsLjQyLjA0LjgzLjE3LDEuMjktMy4zLDEuODgtNC42MywzLjYzLTQuNjMsNS44LDAsMi4wNSwxLjA5LDQuMDksNC41NSw1LjIybDIuNzUuOTItOS41Miw5LjE0LDEuODQsMS45NiwyLjk2LTIuNzEsMTQuNzMsNS4yMmgwWiIvPjwvc3ZnPg==",
1249
- buildUrl: (url) => `https://solflare.com/ul/v1/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
1332
+ discord: {
1333
+ label: "Discord",
1334
+ icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z" fill="#5865F2"/></svg>'
1250
1335
  },
1251
- {
1252
- name: "Backpack",
1253
- icon: "data:image/webp;base64,UklGRlgCAABXRUJQVlA4WAoAAAAQAAAAOwAAOwAAQUxQSC8AAAABL6CmbQOGP75e/cti0oiIOI0KBto2+a8ACZjAAQ7wL2gCSCL6PwER2t+KmZpFDwBWUDggAgIAAPALAJ0BKjwAPAA+SSCMRKKiIRYKrTQoBISyAGp7nL12mKuwHiZ/pX71XoA8m7rXfQA8q39lfhJ/cL0ZimIeNSGxOaSWtqEDDdJ++ARG5/LVm6OquItoAu4rpz8VksUr1NEDJ0CeZucwAP7+VdYX7J5e8V3cJZ9QoY9vj8KsNGOvZSm+khDZOvwCeiDFjGweq/8KIUmVQk3T/qW3ONyMcepN2uwyEXpZZiidST0x705r7ZnNuWX42r8oi+FEshvXGWm6DabjfrMbiaxJ15irl2Gj3nKWbZ2v3k/jd172plSBejBs7dNt90Re/m+nmv5kNjM/SPfa4EpzvOdu/8BR+hTnR4qe4eWhTsh/oecEfN31OExdi/6AYrsth4nlx/qGEP70b1/TNKmgaB/18+c9ntTfhku9wYpy17/wN3r+B/1geZPzth+AtNsMDXE99PaPLWdFP9MOIMD6ME+MpZu2H7WSfzY7MkWs/N/NNzAZ0P/9vMvxgcdzFHvs/doeUU9WyVv+Ll8QDJbn9NZl86ZZNYtfP4Ol1hiJHWosWyj/66vhMUeFIcDheXIOSRTzvKwj5ffEl7fn/QwflNU556SEjM8MriwSFJ/nrFBrr8O1/pqdrYGqPijG6tjsBVBHp+EDAbtNLL375I2jnG9i3Xb1ZphsFYNjdRaknYdFf4g5iJBoAAA=",
1254
- buildUrl: (url) => `https://backpack.app/ul/browse/${encodeURIComponent(url)}`
1255
- }
1256
- ];
1257
- function isMobile() {
1258
- if (typeof navigator === "undefined") return false;
1259
- return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1260
- }
1261
- function WalletConnectForm() {
1262
- const { walletAdapter, setModalStep, config } = useForgeConnect();
1263
- const methods = resolveLoginMethods(config);
1264
- const showBack = methods.length > 1;
1265
- if (!walletAdapter && isMobile()) {
1266
- return /* @__PURE__ */ jsx4(MobileWalletFlow, {});
1267
- }
1268
- if (walletAdapter) {
1269
- return /* @__PURE__ */ jsx4(WalletAdapterFlow, {});
1336
+ twitter: {
1337
+ label: "Twitter",
1338
+ icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="currentColor"/></svg>'
1339
+ },
1340
+ apple: {
1341
+ label: "Apple",
1342
+ icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.52-3.23 0-1.44.64-2.2.45-3.06-.4C3.79 16.17 4.36 9.02 8.8 8.78c1.27.06 2.15.72 2.91.76.93-.19 1.82-.88 2.83-.8 1.21.1 2.12.58 2.72 1.49-2.46 1.48-1.88 4.73.52 5.64-.42 1.13-.98 2.24-1.73 3.41zM12.03 8.7c-.12-2.35 1.82-4.38 4.04-4.54.29 2.56-2.34 4.68-4.04 4.54z" fill="currentColor"/></svg>'
1343
+ },
1344
+ telegram: {
1345
+ label: "Telegram",
1346
+ icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" fill="#2AABEE"/></svg>'
1270
1347
  }
1271
- return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
1272
- /* @__PURE__ */ jsx4("h3", { className: "fc-tab-title", children: "Connect wallet" }),
1273
- /* @__PURE__ */ jsxs4("p", { className: "fc-text", children: [
1274
- "No wallet adapter detected. Pass ",
1275
- /* @__PURE__ */ jsx4("code", { children: "walletAdapter" }),
1276
- " to",
1277
- " ",
1278
- /* @__PURE__ */ jsx4("code", { children: "<ForgeConnectProvider>" }),
1279
- " to enable wallet login."
1280
- ] }),
1281
- showBack && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
1282
- ] });
1348
+ };
1349
+ function OAuthButton({ provider }) {
1350
+ const { loginWithOAuth } = useForgeConnect();
1351
+ const info = PROVIDER_INFO[provider];
1352
+ return /* @__PURE__ */ jsxs7(
1353
+ "button",
1354
+ {
1355
+ type: "button",
1356
+ className: "fc-btn fc-btn-oauth",
1357
+ onClick: () => loginWithOAuth(provider),
1358
+ children: [
1359
+ /* @__PURE__ */ jsx8(SvgIcon, { svg: info.icon, className: "fc-oauth-icon" }),
1360
+ /* @__PURE__ */ jsx8("span", { className: "fc-btn-name", children: info.label })
1361
+ ]
1362
+ }
1363
+ );
1283
1364
  }
1284
- function MobileWalletFlow() {
1285
- const { setModalStep, config } = useForgeConnect();
1286
- const methods = resolveLoginMethods(config);
1287
- const showBack = methods.length > 1;
1288
- const walletConfig = config.walletConfig;
1289
- const preferred = walletConfig?.preferredWallets ?? [];
1290
- const onlyPreferred = walletConfig?.onlyPreferred ?? false;
1291
- const walletsToShow = useMemo(() => {
1292
- if (preferred.length > 0) {
1293
- const prefList = preferred.map((name) => MOBILE_WALLETS.find((mw) => mw.name === name)).filter(Boolean);
1294
- if (onlyPreferred) return prefList;
1295
- const prefNames = new Set(preferred);
1296
- const others = MOBILE_WALLETS.filter((mw) => !prefNames.has(mw.name));
1297
- return [...prefList, ...others];
1365
+
1366
+ // src/components/login-modal.tsx
1367
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1368
+ function LoginModal() {
1369
+ const { modal, closeModal, config } = useForgeConnect();
1370
+ const renderStep = () => {
1371
+ switch (modal.step) {
1372
+ case "email-login":
1373
+ return /* @__PURE__ */ jsx9(EmailLoginForm, {});
1374
+ case "email-register":
1375
+ return /* @__PURE__ */ jsx9(EmailRegisterForm, {});
1376
+ case "email-otp":
1377
+ return /* @__PURE__ */ jsx9(EmailOtpForm, {});
1378
+ case "wallet-connect":
1379
+ return /* @__PURE__ */ jsx9(WalletConnectForm, {});
1380
+ case "forgot-password":
1381
+ return /* @__PURE__ */ jsx9(ForgotPasswordForm, {});
1382
+ case "verify-2fa":
1383
+ return /* @__PURE__ */ jsx9(Verify2FAForm, {});
1384
+ case "oauth":
1385
+ return /* @__PURE__ */ jsx9(OAuthLoadingView, {});
1386
+ case "success":
1387
+ return /* @__PURE__ */ jsx9(SuccessView, {});
1388
+ case "method-select":
1389
+ default:
1390
+ return /* @__PURE__ */ jsx9(MethodSelect, {});
1298
1391
  }
1299
- return MOBILE_WALLETS;
1300
- }, [preferred, onlyPreferred]);
1301
- const handleOpen = (mw) => {
1302
- const pageUrl = window.location.href;
1303
- window.location.href = mw.buildUrl(pageUrl);
1304
1392
  };
1305
- return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
1306
- /* @__PURE__ */ jsx4("div", { className: "fc-wallet-list", children: walletsToShow.map((mw) => /* @__PURE__ */ jsxs4(
1307
- "button",
1308
- {
1309
- type: "button",
1310
- className: "fc-btn fc-btn-wallet",
1311
- onClick: () => handleOpen(mw),
1312
- children: [
1313
- /* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
1314
- /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
1315
- /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Open app" })
1316
- ]
1393
+ const renderLogo = () => {
1394
+ if (config.appearance?.logoNode) {
1395
+ return /* @__PURE__ */ jsx9("div", { className: "fc-logo", children: config.appearance.logoNode });
1396
+ }
1397
+ if (config.appearance?.logo) {
1398
+ return /* @__PURE__ */ jsx9("img", { src: config.appearance.logo, alt: "", className: "fc-logo" });
1399
+ }
1400
+ return null;
1401
+ };
1402
+ return /* @__PURE__ */ jsx9(ModalOverlay, { isOpen: modal.isOpen, onClose: closeModal, children: /* @__PURE__ */ jsx9(
1403
+ "div",
1404
+ {
1405
+ className: "fc-modal-content",
1406
+ style: {
1407
+ "--fc-accent": config.appearance?.accentColor ?? "#8b5cf6"
1317
1408
  },
1318
- mw.name
1319
- )) }),
1320
- /* @__PURE__ */ jsx4("p", { className: "fc-text", style: { textAlign: "center", fontSize: 12, opacity: 0.6 }, children: "You'll be redirected to the wallet app to sign in." }),
1321
- showBack && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
1322
- ] });
1323
- }
1324
- function WalletAdapterFlow() {
1325
- const { walletAdapter, loginWithWallet, setModalStep, config } = useForgeConnect();
1326
- const wallet = walletAdapter;
1327
- const walletConfig = config.walletConfig;
1328
- const methods = resolveLoginMethods(config);
1329
- const showBack = methods.length > 1;
1330
- const [error, setError] = useState3("");
1331
- const [loading, setLoading] = useState3(false);
1332
- const [showOther, setShowOther] = useState3(false);
1333
- const [coldWallet, setColdWallet] = useState3(false);
1334
- const mobile = useMemo(() => isMobile(), []);
1335
- const coldWalletRef = useRef3(coldWallet);
1336
- coldWalletRef.current = coldWallet;
1337
- const buildSignTxFnForAdapter = useCallback((adapter) => {
1338
- if (!adapter.signTransaction) return void 0;
1339
- return async (txBase64) => {
1340
- const { Transaction } = await importSolanaWeb3();
1341
- const bytes = Uint8Array.from(atob(txBase64), (c) => c.charCodeAt(0));
1342
- const tx = Transaction.from(bytes);
1343
- const signedTx = await adapter.signTransaction(tx);
1344
- return btoa(String.fromCharCode(...new Uint8Array(signedTx.serialize())));
1345
- };
1346
- }, []);
1347
- const handleConnect = async (w) => {
1348
- if (w.readyState !== "Installed") {
1349
- if (mobile) {
1350
- const mw = MOBILE_WALLETS.find((m) => m.name === w.adapter.name);
1351
- if (mw) {
1352
- window.location.href = mw.buildUrl(window.location.href);
1353
- return;
1354
- }
1355
- }
1356
- const url = w.adapter.url;
1357
- if (url) window.open(url, "_blank");
1358
- return;
1409
+ "data-theme": config.appearance?.theme ?? "light",
1410
+ children: modal.step === "success" || modal.step === "oauth" ? renderStep() : /* @__PURE__ */ jsxs8(Fragment2, { children: [
1411
+ renderLogo(),
1412
+ /* @__PURE__ */ jsx9("h2", { className: "fc-modal-title", children: config.appearance?.title ?? "Log in or sign up" }),
1413
+ renderStep(),
1414
+ (config.appearance?.termsUrl || config.appearance?.privacyUrl) && /* @__PURE__ */ jsxs8("p", { className: "fc-legal", children: [
1415
+ "By continuing, you agree to our",
1416
+ " ",
1417
+ config.appearance.termsUrl && /* @__PURE__ */ jsx9("a", { href: config.appearance.termsUrl, target: "_blank", rel: "noopener noreferrer", className: "fc-legal-link", children: "Terms" }),
1418
+ config.appearance.termsUrl && config.appearance.privacyUrl && " and ",
1419
+ config.appearance.privacyUrl && /* @__PURE__ */ jsx9("a", { href: config.appearance.privacyUrl, target: "_blank", rel: "noopener noreferrer", className: "fc-legal-link", children: "Privacy Policy" })
1420
+ ] })
1421
+ ] })
1359
1422
  }
1360
- setError("");
1361
- setLoading(true);
1423
+ ) });
1424
+ }
1425
+ var EmailIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1426
+ /* @__PURE__ */ jsx9("rect", { x: "2", y: "4", width: "16", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1427
+ /* @__PURE__ */ jsx9("path", { d: "M2 6l8 5 8-5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
1428
+ ] }) });
1429
+ var OtpIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1430
+ /* @__PURE__ */ jsx9("rect", { x: "3", y: "6", width: "14", height: "9", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
1431
+ /* @__PURE__ */ jsx9("circle", { cx: "6.5", cy: "10.5", r: "1", fill: "currentColor" }),
1432
+ /* @__PURE__ */ jsx9("circle", { cx: "10", cy: "10.5", r: "1", fill: "currentColor" }),
1433
+ /* @__PURE__ */ jsx9("circle", { cx: "13.5", cy: "10.5", r: "1", fill: "currentColor" })
1434
+ ] }) });
1435
+ var WalletIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1436
+ /* @__PURE__ */ jsx9("rect", { x: "2", y: "5", width: "16", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1437
+ /* @__PURE__ */ jsx9("rect", { x: "13", y: "9", width: "5", height: "3", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
1438
+ ] }) });
1439
+ var PasskeyIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1440
+ /* @__PURE__ */ jsx9("circle", { cx: "8", cy: "7", r: "3", stroke: "currentColor", strokeWidth: "1.5" }),
1441
+ /* @__PURE__ */ jsx9("path", { d: "M13 13.5a5 5 0 0 0-10 0", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
1442
+ /* @__PURE__ */ jsx9("path", { d: "M15 10v4m0 0l-1.5-1m1.5 1l1.5-1", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1443
+ ] }) });
1444
+ var LoadingSpinner = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsx9("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon fc-spin", children: /* @__PURE__ */ jsx9("path", { d: "M10 2a8 8 0 0 1 8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) });
1445
+ function MethodSelect() {
1446
+ const { setModalStep, loginWithPasskey, config } = useForgeConnect();
1447
+ const methods = resolveLoginMethods(config);
1448
+ const [passkeyLoading, setPasskeyLoading] = useState6(false);
1449
+ const [passkeyError, setPasskeyError] = useState6("");
1450
+ const handlePasskey = async () => {
1451
+ setPasskeyLoading(true);
1452
+ setPasskeyError("");
1362
1453
  try {
1363
- if (!w.adapter.connected) {
1364
- await w.adapter.connect();
1365
- }
1366
- const pk = w.adapter.publicKey;
1367
- if (!pk) throw new Error("Wallet did not provide a public key.");
1368
- const address = pk.toBase58();
1369
- const useCold = coldWalletRef.current;
1370
- const adapterSignMessage = w.adapter.signMessage ? (msg) => w.adapter.signMessage(msg) : void 0;
1371
- await loginWithWallet(
1372
- address,
1373
- useCold ? void 0 : adapterSignMessage,
1374
- "solana",
1375
- useCold ? buildSignTxFnForAdapter(w.adapter) : void 0
1376
- );
1454
+ await loginWithPasskey();
1377
1455
  } catch (err) {
1378
- setError(err instanceof Error ? err.message : "Could not verify your wallet. Please try again.");
1379
- setLoading(false);
1456
+ setPasskeyError(err instanceof Error ? err.message : "Passkey authentication failed.");
1457
+ } finally {
1458
+ setPasskeyLoading(false);
1380
1459
  }
1381
1460
  };
1382
- const handleMobileOpen = (mw) => {
1383
- window.location.href = mw.buildUrl(window.location.href);
1384
- };
1385
- const preferred = walletConfig?.preferredWallets ?? [];
1386
- const onlyPreferred = walletConfig?.onlyPreferred ?? false;
1387
- const preferredSet = new Set(preferred);
1388
- const connectedWalletName = wallet.publicKey ? wallet.wallets.find((w) => w.adapter.connected)?.adapter.name ?? null : null;
1389
- const { preferredWallets, otherWallets } = useMemo(() => {
1390
- const all = wallet.wallets;
1391
- const prefList = preferred.map((name) => all.find((w) => w.adapter.name === name)).filter(Boolean);
1392
- if (onlyPreferred && preferred.length > 0) {
1393
- return { preferredWallets: prefList, otherWallets: [] };
1394
- }
1395
- const others = all.filter(
1396
- (w) => !preferredSet.has(w.adapter.name) && w.readyState === "Installed"
1397
- );
1398
- return { preferredWallets: prefList, otherWallets: others };
1399
- }, [wallet.wallets, walletConfig]);
1400
- const mobileExtraWallets = useMemo(() => {
1401
- if (!mobile) return [];
1402
- const adapterNames = new Set(wallet.wallets.map((w) => w.adapter.name));
1403
- return MOBILE_WALLETS.filter((mw) => !adapterNames.has(mw.name));
1404
- }, [mobile, wallet.wallets]);
1405
- return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
1406
- loading ? /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
1407
- /* @__PURE__ */ jsx4("p", { className: "fc-tab-title", children: "Connecting..." }),
1408
- /* @__PURE__ */ jsx4("p", { className: "fc-text", children: coldWallet ? "Confirm the transaction on your device" : "Approve the connection, then sign the verification request in your wallet" }),
1409
- error && /* @__PURE__ */ jsxs4(Fragment, { children: [
1410
- /* @__PURE__ */ jsx4("p", { className: "fc-error", children: error }),
1411
- /* @__PURE__ */ jsx4(
1412
- "button",
1413
- {
1414
- type: "button",
1415
- className: "fc-btn fc-btn-secondary",
1416
- onClick: () => {
1417
- setLoading(false);
1418
- setError("");
1419
- },
1420
- style: { marginTop: 8 },
1421
- children: "Try again"
1422
- }
1423
- )
1424
- ] })
1425
- ] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
1426
- /* @__PURE__ */ jsxs4("div", { className: "fc-wallet-list", children: [
1427
- preferredWallets.map((w) => {
1428
- const installed = w.readyState === "Installed";
1429
- const isConnected = w.adapter.name === connectedWalletName;
1430
- return /* @__PURE__ */ jsxs4(
1431
- "button",
1432
- {
1433
- type: "button",
1434
- className: "fc-btn fc-btn-wallet",
1435
- onClick: () => handleConnect(w),
1436
- children: [
1437
- /* @__PURE__ */ jsx4("span", { className: installed ? "fc-installed-dot" : "", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
1438
- /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
1439
- isConnected ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" }) : /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Preferred" }),
1440
- !installed && mobile && MOBILE_WALLETS.some((mw) => mw.name === w.adapter.name) ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" }) : !installed && /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Install" })
1441
- ]
1442
- },
1443
- w.adapter.name
1461
+ const elements = [];
1462
+ let i = 0;
1463
+ while (i < methods.length) {
1464
+ const method = methods[i];
1465
+ if (isOAuthMethod(method)) {
1466
+ const oauthGroup = [];
1467
+ while (i < methods.length && isOAuthMethod(methods[i])) {
1468
+ oauthGroup.push(methods[i]);
1469
+ i++;
1470
+ }
1471
+ if (elements.length > 0) {
1472
+ elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-before-${oauthGroup[0]}`));
1473
+ }
1474
+ elements.push(
1475
+ /* @__PURE__ */ jsx9("div", { className: "fc-oauth-group", children: oauthGroup.map((p) => /* @__PURE__ */ jsx9(OAuthButton, { provider: p }, p)) }, `oauth-${oauthGroup.join("-")}`)
1476
+ );
1477
+ } else {
1478
+ if (elements.length > 0) {
1479
+ const prev = methods[i - 1];
1480
+ if (prev && isOAuthMethod(prev)) {
1481
+ elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-after-${prev}`));
1482
+ }
1483
+ }
1484
+ switch (method) {
1485
+ case "email":
1486
+ elements.push(
1487
+ /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-login"), children: [
1488
+ /* @__PURE__ */ jsx9(EmailIcon, {}),
1489
+ " ",
1490
+ /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Password" })
1491
+ ] }, "email")
1444
1492
  );
1445
- }),
1446
- mobileExtraWallets.map((mw) => /* @__PURE__ */ jsxs4(
1447
- "button",
1448
- {
1449
- type: "button",
1450
- className: "fc-btn fc-btn-wallet",
1451
- onClick: () => handleMobileOpen(mw),
1452
- children: [
1453
- /* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
1454
- /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
1455
- /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" })
1456
- ]
1457
- },
1458
- mw.name
1459
- )),
1460
- otherWallets.length > 0 && !showOther && /* @__PURE__ */ jsxs4(
1461
- "button",
1462
- {
1463
- type: "button",
1464
- className: "fc-btn fc-btn-wallet",
1465
- onClick: () => setShowOther(true),
1466
- children: [
1467
- /* @__PURE__ */ jsx4("span", { className: "fc-other-wallets-icon", children: /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
1468
- /* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "16", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1469
- /* @__PURE__ */ jsx4("path", { d: "M2 7h16", stroke: "currentColor", strokeWidth: "1.5" }),
1470
- /* @__PURE__ */ jsx4("rect", { x: "11", y: "10", width: "7", height: "4", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
1471
- ] }) }),
1472
- /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: "Other wallets" })
1473
- ]
1474
- }
1475
- ),
1476
- showOther && otherWallets.map((w) => {
1477
- const isConnected = w.adapter.name === connectedWalletName;
1478
- return /* @__PURE__ */ jsxs4(
1479
- "button",
1480
- {
1481
- type: "button",
1482
- className: "fc-btn fc-btn-wallet",
1483
- onClick: () => handleConnect(w),
1484
- children: [
1485
- /* @__PURE__ */ jsx4("span", { className: "fc-installed-dot", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
1486
- /* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
1487
- isConnected && /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" })
1488
- ]
1489
- },
1490
- w.adapter.name
1493
+ break;
1494
+ case "otp":
1495
+ elements.push(
1496
+ /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-otp"), children: [
1497
+ /* @__PURE__ */ jsx9(OtpIcon, {}),
1498
+ " ",
1499
+ /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Magic code" })
1500
+ ] }, "otp")
1491
1501
  );
1492
- })
1493
- ] }),
1494
- preferredWallets.length === 0 && otherWallets.length === 0 && mobileExtraWallets.length === 0 && /* @__PURE__ */ jsx4("p", { className: "fc-text", children: "No wallet found. Please install a Solana wallet (like Phantom) to continue." }),
1495
- /* @__PURE__ */ jsxs4("label", { className: "fc-cold-wallet-toggle", children: [
1496
- /* @__PURE__ */ jsx4(
1497
- "input",
1498
- {
1499
- type: "checkbox",
1500
- checked: coldWallet,
1501
- onChange: (e) => setColdWallet(e.target.checked)
1502
- }
1503
- ),
1504
- /* @__PURE__ */ jsx4("span", { className: "fc-toggle-track" }),
1505
- /* @__PURE__ */ jsxs4("span", { className: "fc-cold-wallet-label", children: [
1506
- /* @__PURE__ */ jsx4("span", { children: "Hardware wallet" }),
1507
- /* @__PURE__ */ jsx4("span", { children: "Ledger, Trezor, Keystone..." })
1508
- ] })
1509
- ] }),
1510
- error && /* @__PURE__ */ jsx4("p", { className: "fc-error", children: error })
1511
- ] }),
1512
- showBack && !loading && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
1502
+ break;
1503
+ case "wallet":
1504
+ elements.push(
1505
+ /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("wallet-connect"), children: [
1506
+ /* @__PURE__ */ jsx9(WalletIcon, {}),
1507
+ " ",
1508
+ /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Wallet" })
1509
+ ] }, "wallet")
1510
+ );
1511
+ break;
1512
+ case "passkey":
1513
+ elements.push(
1514
+ /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: handlePasskey, disabled: passkeyLoading, children: [
1515
+ passkeyLoading ? /* @__PURE__ */ jsx9(LoadingSpinner, {}) : /* @__PURE__ */ jsx9(PasskeyIcon, {}),
1516
+ /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: passkeyLoading ? "Waiting for passkey..." : "Passkey" })
1517
+ ] }, "passkey")
1518
+ );
1519
+ break;
1520
+ }
1521
+ i++;
1522
+ }
1523
+ }
1524
+ return /* @__PURE__ */ jsxs8("div", { className: "fc-method-select", children: [
1525
+ passkeyError && /* @__PURE__ */ jsx9("p", { className: "fc-error", children: passkeyError }),
1526
+ elements
1513
1527
  ] });
1514
1528
  }
1515
-
1516
- // src/components/tabs/forgot-password.tsx
1517
- import { useState as useState4 } from "react";
1518
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1519
- function ForgotPasswordForm() {
1520
- const { forgotPassword, resetPassword, setModalStep } = useForgeConnect();
1521
- const [step, setStep] = useState4("email");
1522
- const [email, setEmail] = useState4("");
1523
- const [token, setToken] = useState4("");
1524
- const [password, setPassword] = useState4("");
1525
- const [error, setError] = useState4("");
1526
- const [loading, setLoading] = useState4(false);
1527
- const handleSendCode = async (e) => {
1528
- e.preventDefault();
1529
- setError("");
1529
+ function OAuthLoadingView() {
1530
+ const { setModalStep } = useForgeConnect();
1531
+ return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
1532
+ /* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsx9("svg", { width: "52", height: "52", viewBox: "0 0 52 52", className: "fc-spin", children: /* @__PURE__ */ jsx9("circle", { cx: "26", cy: "26", r: "24", fill: "none", stroke: "var(--fc-accent, #8b5cf6)", strokeWidth: "3", strokeLinecap: "round", strokeDasharray: "100", strokeDashoffset: "30" }) }) }),
1533
+ /* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "Completing sign in..." }),
1534
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "fc-btn fc-btn-secondary", style: { marginTop: "16px" }, onClick: () => setModalStep("method-select"), children: "Cancel" })
1535
+ ] });
1536
+ }
1537
+ function SuccessView() {
1538
+ return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
1539
+ /* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 52 52", className: "fc-success-check", children: [
1540
+ /* @__PURE__ */ jsx9("circle", { className: "fc-success-circle", cx: "26", cy: "26", r: "24", fill: "none" }),
1541
+ /* @__PURE__ */ jsx9("path", { className: "fc-success-tick", fill: "none", d: "M15 26l7.5 7.5L37 19" })
1542
+ ] }) }),
1543
+ /* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "You're in" })
1544
+ ] });
1545
+ }
1546
+
1547
+ // src/components/account-modal.tsx
1548
+ import { useState as useState14, useEffect as useEffect10, useRef as useRef7, useCallback as useCallback7 } from "react";
1549
+
1550
+ // src/hooks/use-user.ts
1551
+ import { useState as useState7, useCallback as useCallback3, useEffect as useEffect5, useRef as useRef6 } from "react";
1552
+ function useUser() {
1553
+ const { auth, api, config, getAccessToken } = useForgeConnect();
1554
+ const [authMethods, setAuthMethods] = useState7(null);
1555
+ const [loading, setLoading] = useState7(false);
1556
+ const pendingRefreshRef = useRef6(false);
1557
+ const updateProfile = useCallback3(
1558
+ async (data) => {
1559
+ const token = getAccessToken();
1560
+ if (!token) throw new Error("Please sign in to continue.");
1561
+ return api.updateMe(token, data);
1562
+ },
1563
+ [api, getAccessToken]
1564
+ );
1565
+ const fetchAuthMethods = useCallback3(async () => {
1566
+ const token = getAccessToken();
1567
+ if (!token) throw new Error("Please sign in to continue.");
1530
1568
  setLoading(true);
1531
1569
  try {
1532
- await forgotPassword(email);
1533
- setStep("reset");
1534
- } catch (err) {
1535
- setError(err instanceof Error ? err.message : "Could not send the reset code. Please try again.");
1570
+ const methods = await api.getAuthMethods(token);
1571
+ setAuthMethods(methods);
1572
+ return methods;
1536
1573
  } finally {
1537
1574
  setLoading(false);
1538
1575
  }
1576
+ }, [api, getAccessToken]);
1577
+ const linkAuthMethod = useCallback3(
1578
+ async (data) => {
1579
+ const token = getAccessToken();
1580
+ if (!token) throw new Error("Please sign in to continue.");
1581
+ await api.linkAuthMethod(token, data);
1582
+ await fetchAuthMethods();
1583
+ },
1584
+ [api, getAccessToken, fetchAuthMethods]
1585
+ );
1586
+ const linkOtpSend = useCallback3(
1587
+ async (email) => {
1588
+ const token = getAccessToken();
1589
+ if (!token) throw new Error("Please sign in to continue.");
1590
+ await api.linkOtpSend(token, email);
1591
+ },
1592
+ [api, getAccessToken]
1593
+ );
1594
+ const linkOtpVerify = useCallback3(
1595
+ async (email, code) => {
1596
+ const token = getAccessToken();
1597
+ if (!token) throw new Error("Please sign in to continue.");
1598
+ await api.linkOtpVerify(token, email, code);
1599
+ await fetchAuthMethods();
1600
+ },
1601
+ [api, getAccessToken, fetchAuthMethods]
1602
+ );
1603
+ const unlinkAuthMethod = useCallback3(
1604
+ async (id) => {
1605
+ const token = getAccessToken();
1606
+ if (!token) throw new Error("Please sign in to continue.");
1607
+ await api.unlinkAuthMethod(token, id);
1608
+ await fetchAuthMethods();
1609
+ },
1610
+ [api, getAccessToken, fetchAuthMethods]
1611
+ );
1612
+ const linkOAuth = useCallback3(
1613
+ async (provider) => {
1614
+ const token = getAccessToken();
1615
+ if (!token) throw new Error("Please sign in to continue.");
1616
+ const { intentToken } = await api.createLinkIntent(token);
1617
+ const redirectUri = encodeURIComponent(window.location.origin + "/fc-oauth-callback");
1618
+ const url = `${config.apiUrl}/auth/oauth/${provider}/link?intent=${encodeURIComponent(intentToken)}&redirect_uri=${redirectUri}`;
1619
+ const width = 500;
1620
+ const height = 600;
1621
+ const left = window.screenX + (window.innerWidth - width) / 2;
1622
+ const top = window.screenY + (window.innerHeight - height) / 2;
1623
+ window.open(url, "fc_oauth_link", `width=${width},height=${height},left=${left},top=${top}`);
1624
+ pendingRefreshRef.current = true;
1625
+ },
1626
+ [api, config.apiUrl, getAccessToken]
1627
+ );
1628
+ useEffect5(() => {
1629
+ const handleMessage = (event) => {
1630
+ if (event.origin !== window.location.origin) return;
1631
+ if (event.data?.type === "fc_oauth_link_success" && pendingRefreshRef.current) {
1632
+ pendingRefreshRef.current = false;
1633
+ fetchAuthMethods().catch(() => {
1634
+ });
1635
+ }
1636
+ };
1637
+ window.addEventListener("message", handleMessage);
1638
+ return () => window.removeEventListener("message", handleMessage);
1639
+ }, [fetchAuthMethods]);
1640
+ return {
1641
+ user: auth.user,
1642
+ authMethods,
1643
+ loading,
1644
+ updateProfile,
1645
+ fetchAuthMethods,
1646
+ linkAuthMethod,
1647
+ linkOtpSend,
1648
+ linkOtpVerify,
1649
+ unlinkAuthMethod,
1650
+ linkOAuth
1539
1651
  };
1540
- const handleReset = async (e) => {
1541
- e.preventDefault();
1542
- setError("");
1543
- setLoading(true);
1544
- try {
1545
- await resetPassword(token.trim(), password);
1546
- setStep("done");
1547
- } catch (err) {
1548
- setError(err instanceof Error ? err.message : "Could not reset your password. Please try again.");
1549
- } finally {
1550
- setLoading(false);
1652
+ }
1653
+
1654
+ // src/hooks/use-wallets.ts
1655
+ import { useState as useState8, useCallback as useCallback4 } from "react";
1656
+
1657
+ // src/lib/utils.ts
1658
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
1659
+ function uint8ArrayToBase58(bytes) {
1660
+ const digits = [0];
1661
+ for (const byte of bytes) {
1662
+ let carry = byte;
1663
+ for (let j = 0; j < digits.length; j++) {
1664
+ carry += digits[j] << 8;
1665
+ digits[j] = carry % 58;
1666
+ carry = carry / 58 | 0;
1667
+ }
1668
+ while (carry > 0) {
1669
+ digits.push(carry % 58);
1670
+ carry = carry / 58 | 0;
1551
1671
  }
1552
- };
1553
- if (step === "done") {
1554
- return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
1555
- /* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Password updated" }),
1556
- /* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Your password has been reset. You can now sign in." }),
1557
- /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: () => setModalStep("email-login"), children: "Sign in" })
1558
- ] });
1559
1672
  }
1560
- if (step === "reset") {
1561
- return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
1562
- /* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Set new password" }),
1563
- /* @__PURE__ */ jsxs5("p", { className: "fc-text", children: [
1564
- "We sent a reset code to ",
1565
- /* @__PURE__ */ jsx5("strong", { children: email }),
1566
- ". Paste it below with your new password."
1567
- ] }),
1568
- /* @__PURE__ */ jsxs5("form", { onSubmit: handleReset, className: "fc-form", children: [
1569
- /* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
1570
- "Reset code",
1571
- /* @__PURE__ */ jsx5(
1572
- "input",
1573
- {
1574
- type: "text",
1575
- className: "fc-input",
1576
- value: token,
1577
- onChange: (e) => setToken(e.target.value),
1578
- placeholder: "Paste the code from your email",
1579
- required: true,
1580
- autoComplete: "off"
1581
- }
1582
- )
1583
- ] }),
1584
- /* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
1585
- "New password",
1586
- /* @__PURE__ */ jsx5(
1587
- "input",
1588
- {
1589
- type: "password",
1590
- className: "fc-input",
1591
- value: password,
1592
- onChange: (e) => setPassword(e.target.value),
1593
- placeholder: "8+ characters",
1594
- required: true,
1595
- autoComplete: "new-password",
1596
- minLength: 8
1597
- }
1598
- )
1599
- ] }),
1600
- error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
1601
- /* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Resetting..." : "Reset password" })
1602
- ] }),
1603
- /* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setStep("email"), children: "Resend code" }) })
1604
- ] });
1673
+ let str = "";
1674
+ for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
1675
+ str += "1";
1605
1676
  }
1606
- return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
1607
- /* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Reset your password" }),
1608
- /* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Enter your email and we'll send you a reset code." }),
1609
- /* @__PURE__ */ jsxs5("form", { onSubmit: handleSendCode, className: "fc-form", children: [
1610
- /* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
1611
- "Email",
1612
- /* @__PURE__ */ jsx5(
1613
- "input",
1614
- {
1615
- type: "email",
1616
- className: "fc-input",
1617
- value: email,
1618
- onChange: (e) => setEmail(e.target.value),
1619
- placeholder: "you@example.com",
1620
- required: true,
1621
- autoComplete: "email"
1622
- }
1623
- )
1624
- ] }),
1625
- error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
1626
- /* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Sending..." : "Send reset code" })
1627
- ] }),
1628
- /* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setModalStep("email-login"), children: "Back to sign in" }) })
1629
- ] });
1677
+ for (let i = digits.length - 1; i >= 0; i--) {
1678
+ str += BASE58_ALPHABET[digits[i]];
1679
+ }
1680
+ return str;
1681
+ }
1682
+ function timeAgo(dateStr) {
1683
+ const now = Date.now();
1684
+ const then = new Date(dateStr).getTime();
1685
+ const diff = now - then;
1686
+ const mins = Math.floor(diff / 6e4);
1687
+ if (mins < 1) return "just now";
1688
+ if (mins < 60) return `${mins}m ago`;
1689
+ const hours = Math.floor(mins / 60);
1690
+ if (hours < 24) return `${hours}h ago`;
1691
+ const days = Math.floor(hours / 24);
1692
+ if (days < 30) return `${days}d ago`;
1693
+ return new Date(dateStr).toLocaleDateString();
1630
1694
  }
1631
1695
 
1632
- // src/components/tabs/verify-2fa.tsx
1633
- import { useState as useState5, useRef as useRef4, useEffect as useEffect3, useCallback as useCallback2 } from "react";
1634
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1635
- function Verify2FAForm() {
1636
- const { verify2FA, verifyRecoveryCode, setModalStep } = useForgeConnect();
1637
- const [code, setCode] = useState5("");
1638
- const [loading, setLoading] = useState5(false);
1639
- const [error, setError] = useState5("");
1640
- const [useRecovery, setUseRecovery] = useState5(false);
1641
- const inputRef = useRef4(null);
1642
- const submittingRef = useRef4(false);
1643
- useEffect3(() => {
1644
- inputRef.current?.focus();
1645
- }, [useRecovery]);
1646
- const submitCode = useCallback2(async (codeValue, isRecovery) => {
1647
- if (submittingRef.current || !codeValue.trim()) return;
1648
- submittingRef.current = true;
1696
+ // src/hooks/use-wallets.ts
1697
+ function useWallets() {
1698
+ const { api, getAccessToken } = useForgeConnect();
1699
+ const [wallets, setWallets] = useState8(null);
1700
+ const [loading, setLoading] = useState8(false);
1701
+ const fetchWallets = useCallback4(async () => {
1702
+ const token = getAccessToken();
1703
+ if (!token) throw new Error("Please sign in to continue.");
1649
1704
  setLoading(true);
1650
- setError("");
1651
1705
  try {
1652
- if (isRecovery) {
1653
- await verifyRecoveryCode(codeValue.trim());
1654
- } else {
1655
- await verify2FA(codeValue.trim());
1656
- }
1657
- } catch (err) {
1658
- setError(err instanceof Error ? err.message : "Verification failed. Please try again.");
1706
+ const data = await api.getWallets(token);
1707
+ setWallets(data);
1708
+ return data;
1659
1709
  } finally {
1660
1710
  setLoading(false);
1661
- submittingRef.current = false;
1662
- }
1663
- }, [verify2FA, verifyRecoveryCode]);
1664
- const handleSubmit = async (e) => {
1665
- e?.preventDefault();
1666
- await submitCode(code, useRecovery);
1667
- };
1668
- const handleCodeChange = (value) => {
1669
- setCode(value);
1670
- if (!useRecovery && value.length === 6 && /^\d{6}$/.test(value)) {
1671
- submitCode(value, false);
1672
1711
  }
1673
- };
1674
- return /* @__PURE__ */ jsxs6("div", { className: "fc-tab", children: [
1675
- /* @__PURE__ */ jsxs6("button", { type: "button", className: "fc-back", onClick: () => setModalStep("method-select"), children: [
1676
- /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx6("path", { d: "M10 12L6 8l4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1677
- "Back"
1678
- ] }),
1679
- /* @__PURE__ */ jsx6("p", { className: "fc-account-section-desc", style: { marginTop: 8 }, children: useRecovery ? "Enter one of your recovery codes." : "Enter the 6-digit code from your authenticator app." }),
1680
- error && /* @__PURE__ */ jsx6("p", { className: "fc-error", children: error }),
1681
- /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "fc-form", children: [
1682
- /* @__PURE__ */ jsx6(
1683
- "input",
1684
- {
1685
- ref: inputRef,
1686
- className: "fc-input fc-input-code",
1687
- type: "text",
1688
- inputMode: useRecovery ? "text" : "numeric",
1689
- autoComplete: "one-time-code",
1690
- placeholder: useRecovery ? "Recovery code" : "000000",
1691
- maxLength: useRecovery ? 20 : 6,
1692
- value: code,
1693
- onChange: (e) => handleCodeChange(e.target.value),
1694
- style: useRecovery ? {} : { textAlign: "center", letterSpacing: "0.3em", fontSize: "1.25rem" }
1712
+ }, [api, getAccessToken]);
1713
+ const updateWallet = useCallback4(
1714
+ async (id, data) => {
1715
+ const token = getAccessToken();
1716
+ if (!token) throw new Error("Please sign in to continue.");
1717
+ await api.updateWallet(token, id, data);
1718
+ await fetchWallets();
1719
+ },
1720
+ [api, getAccessToken, fetchWallets]
1721
+ );
1722
+ const linkWallet = useCallback4(
1723
+ async (walletAddress, signMessage, chain = "solana", signTransaction) => {
1724
+ const token = getAccessToken();
1725
+ if (!token) throw new Error("Please sign in to continue.");
1726
+ if (signMessage) {
1727
+ const { challengeId, message: challengeMessage } = await api.walletChallenge(walletAddress, chain);
1728
+ const encoded = new TextEncoder().encode(challengeMessage);
1729
+ const signatureBytes = await signMessage(encoded);
1730
+ const signature = chain === "solana" ? uint8ArrayToBase58(signatureBytes) : Array.from(signatureBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1731
+ await api.linkAuthMethod(token, {
1732
+ provider: `${chain}_wallet`,
1733
+ challengeId,
1734
+ signature,
1735
+ walletAddress
1736
+ });
1737
+ } else {
1738
+ if (!signTransaction) {
1739
+ throw new Error("Wallet does not support message signing or transaction signing.");
1695
1740
  }
1696
- ),
1697
- /* @__PURE__ */ jsx6("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading || !code.trim(), children: loading ? "Verifying..." : "Verify" })
1698
- ] }),
1699
- /* @__PURE__ */ jsx6(
1700
- "button",
1701
- {
1702
- type: "button",
1703
- className: "fc-link",
1704
- onClick: () => {
1705
- setUseRecovery(!useRecovery);
1706
- setCode("");
1707
- setError("");
1708
- },
1709
- style: { marginTop: 12 },
1710
- children: useRecovery ? "Use authenticator code instead" : "Use a recovery code"
1741
+ const { challengeId, transaction: txBase64 } = await api.walletChallengeTx(walletAddress, chain);
1742
+ const signedTxBase64 = await signTransaction(txBase64);
1743
+ await api.linkAuthMethod(token, {
1744
+ provider: `${chain}_wallet_tx`,
1745
+ challengeId,
1746
+ signedTransaction: signedTxBase64,
1747
+ walletAddress
1748
+ });
1711
1749
  }
1712
- )
1713
- ] });
1750
+ await fetchWallets();
1751
+ },
1752
+ [api, getAccessToken, fetchWallets]
1753
+ );
1754
+ return {
1755
+ wallets,
1756
+ loading,
1757
+ fetchWallets,
1758
+ updateWallet,
1759
+ linkWallet
1760
+ };
1714
1761
  }
1715
1762
 
1716
- // src/components/svg-icon.tsx
1717
- import { useRef as useRef5, useEffect as useEffect4 } from "react";
1718
- import { jsx as jsx7 } from "react/jsx-runtime";
1719
- function SvgIcon({ svg, className }) {
1720
- const ref = useRef5(null);
1721
- useEffect4(() => {
1722
- if (!ref.current || !svg) return;
1763
+ // src/hooks/use-sessions.ts
1764
+ import { useState as useState9, useCallback as useCallback5 } from "react";
1765
+ function useSessions() {
1766
+ const { api, getAccessToken } = useForgeConnect();
1767
+ const [sessions, setSessions] = useState9(null);
1768
+ const [loading, setLoading] = useState9(false);
1769
+ const fetchSessions = useCallback5(async () => {
1770
+ const token = getAccessToken();
1771
+ if (!token) throw new Error("Please sign in to continue.");
1772
+ setLoading(true);
1723
1773
  try {
1724
- const doc = new DOMParser().parseFromString(svg, "text/html");
1725
- const svgEl = doc.body.querySelector("svg");
1726
- if (!svgEl) {
1727
- ref.current.textContent = "";
1728
- return;
1729
- }
1730
- const dangerous = svgEl.querySelectorAll("script,iframe,object,embed,foreignObject");
1731
- dangerous.forEach((el) => el.remove());
1732
- const all = svgEl.querySelectorAll("*");
1733
- all.forEach((el) => {
1734
- for (const attr of Array.from(el.attributes)) {
1735
- if (attr.name.startsWith("on")) {
1736
- el.removeAttribute(attr.name);
1737
- }
1738
- }
1739
- });
1740
- ref.current.textContent = "";
1741
- ref.current.appendChild(svgEl);
1742
- } catch {
1743
- ref.current.textContent = "";
1744
- }
1745
- }, [svg]);
1746
- return /* @__PURE__ */ jsx7("span", { ref, className });
1747
- }
1748
-
1749
- // src/components/tabs/oauth-buttons.tsx
1750
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1751
- var PROVIDER_INFO = {
1752
- google: {
1753
- label: "Google",
1754
- icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>'
1755
- },
1756
- discord: {
1757
- label: "Discord",
1758
- icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z" fill="#5865F2"/></svg>'
1759
- },
1760
- twitter: {
1761
- label: "Twitter",
1762
- icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="currentColor"/></svg>'
1763
- },
1764
- apple: {
1765
- label: "Apple",
1766
- icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.52-3.23 0-1.44.64-2.2.45-3.06-.4C3.79 16.17 4.36 9.02 8.8 8.78c1.27.06 2.15.72 2.91.76.93-.19 1.82-.88 2.83-.8 1.21.1 2.12.58 2.72 1.49-2.46 1.48-1.88 4.73.52 5.64-.42 1.13-.98 2.24-1.73 3.41zM12.03 8.7c-.12-2.35 1.82-4.38 4.04-4.54.29 2.56-2.34 4.68-4.04 4.54z" fill="currentColor"/></svg>'
1767
- },
1768
- telegram: {
1769
- label: "Telegram",
1770
- icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" fill="#2AABEE"/></svg>'
1771
- }
1772
- };
1773
- function OAuthButton({ provider }) {
1774
- const { loginWithOAuth } = useForgeConnect();
1775
- const info = PROVIDER_INFO[provider];
1776
- return /* @__PURE__ */ jsxs7(
1777
- "button",
1778
- {
1779
- type: "button",
1780
- className: "fc-btn fc-btn-oauth",
1781
- onClick: () => loginWithOAuth(provider),
1782
- children: [
1783
- /* @__PURE__ */ jsx8(SvgIcon, { svg: info.icon, className: "fc-oauth-icon" }),
1784
- /* @__PURE__ */ jsx8("span", { className: "fc-btn-name", children: info.label })
1785
- ]
1774
+ const data = await api.getSessions(token);
1775
+ setSessions(data);
1776
+ return data;
1777
+ } finally {
1778
+ setLoading(false);
1786
1779
  }
1780
+ }, [api, getAccessToken]);
1781
+ const revokeSession = useCallback5(
1782
+ async (id) => {
1783
+ const token = getAccessToken();
1784
+ if (!token) throw new Error("Please sign in to continue.");
1785
+ await api.revokeSession(token, id);
1786
+ await fetchSessions();
1787
+ },
1788
+ [api, getAccessToken, fetchSessions]
1787
1789
  );
1790
+ return {
1791
+ sessions,
1792
+ loading,
1793
+ fetchSessions,
1794
+ revokeSession
1795
+ };
1788
1796
  }
1789
1797
 
1790
- // src/components/login-modal.tsx
1791
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1792
- function LoginModal() {
1793
- const { modal, closeModal, config } = useForgeConnect();
1794
- const renderStep = () => {
1795
- switch (modal.step) {
1796
- case "email-login":
1797
- return /* @__PURE__ */ jsx9(EmailLoginForm, {});
1798
- case "email-register":
1799
- return /* @__PURE__ */ jsx9(EmailRegisterForm, {});
1800
- case "email-otp":
1801
- return /* @__PURE__ */ jsx9(EmailOtpForm, {});
1802
- case "wallet-connect":
1803
- return /* @__PURE__ */ jsx9(WalletConnectForm, {});
1804
- case "forgot-password":
1805
- return /* @__PURE__ */ jsx9(ForgotPasswordForm, {});
1806
- case "verify-2fa":
1807
- return /* @__PURE__ */ jsx9(Verify2FAForm, {});
1808
- case "oauth":
1809
- return /* @__PURE__ */ jsx9(OAuthLoadingView, {});
1810
- case "success":
1811
- return /* @__PURE__ */ jsx9(SuccessView, {});
1812
- case "method-select":
1813
- default:
1814
- return /* @__PURE__ */ jsx9(MethodSelect, {});
1798
+ // src/components/two-factor-modal.tsx
1799
+ import { useState as useState10, useEffect as useEffect6 } from "react";
1800
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1801
+ function TwoFactorModal({ isOpen, onClose, initialEnabled, onStatusChange }) {
1802
+ const { api, getAccessToken, config } = useForgeConnect();
1803
+ const theme = config.appearance?.theme ?? "light";
1804
+ const [step, setStep] = useState10(initialEnabled ? "manage" : "setup");
1805
+ const [setupData, setSetupData] = useState10(null);
1806
+ const [code, setCode] = useState10("");
1807
+ const [loading, setLoading] = useState10(false);
1808
+ const [msg, setMsg] = useState10(null);
1809
+ const [showSecret, setShowSecret] = useState10(false);
1810
+ const [recoveryCodes, setRecoveryCodes] = useState10([]);
1811
+ useEffect6(() => {
1812
+ if (isOpen) {
1813
+ setStep(initialEnabled ? "manage" : "setup");
1814
+ setSetupData(null);
1815
+ setCode("");
1816
+ setMsg(null);
1817
+ setLoading(false);
1818
+ setShowSecret(false);
1819
+ setRecoveryCodes([]);
1820
+ if (!initialEnabled) {
1821
+ const token = getAccessToken();
1822
+ if (token) {
1823
+ setLoading(true);
1824
+ api.setup2FA(token).then((data) => {
1825
+ setSetupData(data);
1826
+ setRecoveryCodes(data.recoveryCodes);
1827
+ }).catch((err) => {
1828
+ setMsg({ text: err instanceof Error ? err.message : "Could not set up 2FA.", ok: false });
1829
+ }).finally(() => setLoading(false));
1830
+ }
1831
+ }
1832
+ }
1833
+ }, [isOpen, initialEnabled, api, getAccessToken]);
1834
+ const handleEnable = async () => {
1835
+ const token = getAccessToken();
1836
+ if (!token || !code) return;
1837
+ setLoading(true);
1838
+ setMsg(null);
1839
+ try {
1840
+ await api.enable2FA(token, code);
1841
+ onStatusChange(true);
1842
+ setStep("recovery-codes");
1843
+ setCode("");
1844
+ } catch (err) {
1845
+ setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
1846
+ } finally {
1847
+ setLoading(false);
1815
1848
  }
1816
1849
  };
1817
- const renderLogo = () => {
1818
- if (config.appearance?.logoNode) {
1819
- return /* @__PURE__ */ jsx9("div", { className: "fc-logo", children: config.appearance.logoNode });
1850
+ const handleDisable = async () => {
1851
+ const token = getAccessToken();
1852
+ if (!token || !code) return;
1853
+ setLoading(true);
1854
+ setMsg(null);
1855
+ try {
1856
+ await api.disable2FA(token, code);
1857
+ onStatusChange(false);
1858
+ setCode("");
1859
+ onClose();
1860
+ } catch (err) {
1861
+ setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
1862
+ } finally {
1863
+ setLoading(false);
1820
1864
  }
1821
- if (config.appearance?.logo) {
1822
- return /* @__PURE__ */ jsx9("img", { src: config.appearance.logo, alt: "", className: "fc-logo" });
1865
+ };
1866
+ const handleRegenerate = async () => {
1867
+ const token = getAccessToken();
1868
+ if (!token || !code) return;
1869
+ setLoading(true);
1870
+ setMsg(null);
1871
+ try {
1872
+ const { recoveryCodes: codes } = await api.regenerateRecoveryCodes(token, code);
1873
+ setRecoveryCodes(codes);
1874
+ setStep("recovery-codes");
1875
+ setCode("");
1876
+ } catch (err) {
1877
+ setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
1878
+ } finally {
1879
+ setLoading(false);
1823
1880
  }
1824
- return null;
1825
1881
  };
1826
- return /* @__PURE__ */ jsx9(ModalOverlay, { isOpen: modal.isOpen, onClose: closeModal, children: /* @__PURE__ */ jsx9(
1882
+ const handleCopyAll = () => {
1883
+ navigator.clipboard.writeText(recoveryCodes.join("\n")).catch(() => {
1884
+ });
1885
+ };
1886
+ const goBack = () => {
1887
+ setCode("");
1888
+ setMsg(null);
1889
+ setStep("manage");
1890
+ };
1891
+ if (!isOpen) return null;
1892
+ return /* @__PURE__ */ jsx10(ModalOverlay, { isOpen, onClose, children: /* @__PURE__ */ jsxs9(
1827
1893
  "div",
1828
1894
  {
1829
1895
  className: "fc-modal-content",
1830
- style: {
1831
- "--fc-accent": config.appearance?.accentColor ?? "#8b5cf6"
1832
- },
1833
- "data-theme": config.appearance?.theme ?? "light",
1834
- children: modal.step === "success" || modal.step === "oauth" ? renderStep() : /* @__PURE__ */ jsxs8(Fragment2, { children: [
1835
- renderLogo(),
1836
- /* @__PURE__ */ jsx9("h2", { className: "fc-modal-title", children: config.appearance?.title ?? "Log in or sign up" }),
1837
- renderStep(),
1838
- (config.appearance?.termsUrl || config.appearance?.privacyUrl) && /* @__PURE__ */ jsxs8("p", { className: "fc-legal", children: [
1839
- "By continuing, you agree to our",
1840
- " ",
1841
- config.appearance.termsUrl && /* @__PURE__ */ jsx9("a", { href: config.appearance.termsUrl, target: "_blank", rel: "noopener noreferrer", className: "fc-legal-link", children: "Terms" }),
1842
- config.appearance.termsUrl && config.appearance.privacyUrl && " and ",
1843
- config.appearance.privacyUrl && /* @__PURE__ */ jsx9("a", { href: config.appearance.privacyUrl, target: "_blank", rel: "noopener noreferrer", className: "fc-legal-link", children: "Privacy Policy" })
1896
+ style: { "--fc-accent": config.appearance?.accentColor ?? "#8b5cf6" },
1897
+ "data-theme": theme,
1898
+ children: [
1899
+ step === "setup" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
1900
+ /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Set up 2FA" }),
1901
+ msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
1902
+ loading && !setupData && /* @__PURE__ */ jsx10("p", { className: "fc-text", children: "Loading..." }),
1903
+ setupData && /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
1904
+ /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Scan this QR code with your authenticator app" }),
1905
+ /* @__PURE__ */ jsx10("div", { style: { display: "flex", justifyContent: "center", margin: "12px 0" }, children: /* @__PURE__ */ jsx10(
1906
+ "img",
1907
+ {
1908
+ src: setupData.qrCodeDataUrl,
1909
+ alt: "TOTP QR code",
1910
+ style: { width: 180, height: 180, borderRadius: 12 }
1911
+ }
1912
+ ) }),
1913
+ /* @__PURE__ */ jsxs9("div", { style: { textAlign: "center", marginBottom: 12 }, children: [
1914
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: () => setShowSecret(!showSecret), children: showSecret ? "Hide code" : "Can't scan? Enter manually" }),
1915
+ showSecret && /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontFamily: "monospace", fontSize: 12, opacity: 0.7, wordBreak: "break-all" }, children: setupData.secret })
1916
+ ] }),
1917
+ /* @__PURE__ */ jsx10(
1918
+ "input",
1919
+ {
1920
+ className: "fc-input",
1921
+ type: "text",
1922
+ inputMode: "numeric",
1923
+ placeholder: "Enter 6-digit code",
1924
+ maxLength: 6,
1925
+ value: code,
1926
+ onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
1927
+ style: { textAlign: "center", letterSpacing: "0.2em" },
1928
+ autoFocus: true
1929
+ }
1930
+ ),
1931
+ /* @__PURE__ */ jsx10(
1932
+ "button",
1933
+ {
1934
+ type: "button",
1935
+ className: "fc-btn fc-btn-primary",
1936
+ onClick: handleEnable,
1937
+ disabled: loading || code.length !== 6,
1938
+ style: { marginTop: 10 },
1939
+ children: loading ? "Verifying..." : "Verify & Enable"
1940
+ }
1941
+ )
1942
+ ] })
1943
+ ] }),
1944
+ step === "manage" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
1945
+ /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Two-factor authentication" }),
1946
+ /* @__PURE__ */ jsx10("div", { style: { textAlign: "center", marginBottom: 20 }, children: /* @__PURE__ */ jsx10("span", { className: "fc-badge-verified", style: { fontSize: 12, padding: "4px 12px" }, children: "Enabled" }) }),
1947
+ /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
1948
+ /* @__PURE__ */ jsxs9(
1949
+ "button",
1950
+ {
1951
+ type: "button",
1952
+ className: "fc-security-card",
1953
+ onClick: () => {
1954
+ setCode("");
1955
+ setMsg(null);
1956
+ setStep("confirm-regenerate");
1957
+ },
1958
+ children: [
1959
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
1960
+ /* @__PURE__ */ jsx10("path", { d: "M3 10a7 7 0 0 1 7-7m0 14a7 7 0 0 1-7-7m14 0a7 7 0 0 1-7 7m0-14a7 7 0 0 1 7 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
1961
+ /* @__PURE__ */ jsx10("path", { d: "M14 3l-1 3h3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1962
+ ] }) }),
1963
+ /* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
1964
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", children: "Regenerate recovery codes" }),
1965
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Get new backup codes" })
1966
+ ] }),
1967
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
1968
+ ]
1969
+ }
1970
+ ),
1971
+ /* @__PURE__ */ jsxs9(
1972
+ "button",
1973
+ {
1974
+ type: "button",
1975
+ className: "fc-security-card",
1976
+ onClick: () => {
1977
+ setCode("");
1978
+ setMsg(null);
1979
+ setStep("confirm-disable");
1980
+ },
1981
+ children: [
1982
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", style: { color: "#dc2626" }, children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
1983
+ /* @__PURE__ */ jsx10("path", { d: "M10 2l6 3v4c0 4.42-2.56 8.22-6 9.5C6.56 17.22 4 13.42 4 9V5l6-3z", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }),
1984
+ /* @__PURE__ */ jsx10("path", { d: "M7.5 7.5l5 5M12.5 7.5l-5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
1985
+ ] }) }),
1986
+ /* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
1987
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", style: { color: "#dc2626" }, children: "Disable 2FA" }),
1988
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Remove two-factor authentication" })
1989
+ ] }),
1990
+ /* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
1991
+ ]
1992
+ }
1993
+ )
1994
+ ] })
1995
+ ] }),
1996
+ step === "confirm-regenerate" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
1997
+ /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Regenerate recovery codes" }),
1998
+ /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to generate new recovery codes. Your old codes will stop working." }),
1999
+ msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
2000
+ /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
2001
+ /* @__PURE__ */ jsx10(
2002
+ "input",
2003
+ {
2004
+ className: "fc-input",
2005
+ type: "text",
2006
+ inputMode: "numeric",
2007
+ placeholder: "Enter 6-digit code",
2008
+ maxLength: 6,
2009
+ value: code,
2010
+ onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
2011
+ style: { textAlign: "center", letterSpacing: "0.2em" },
2012
+ autoFocus: true
2013
+ }
2014
+ ),
2015
+ /* @__PURE__ */ jsx10(
2016
+ "button",
2017
+ {
2018
+ type: "button",
2019
+ className: "fc-btn fc-btn-primary",
2020
+ onClick: handleRegenerate,
2021
+ disabled: loading || code.length !== 6,
2022
+ style: { marginTop: 10 },
2023
+ children: loading ? "Generating..." : "Regenerate codes"
2024
+ }
2025
+ ),
2026
+ /* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
2027
+ ] })
2028
+ ] }),
2029
+ step === "confirm-disable" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2030
+ /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Disable 2FA" }),
2031
+ /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to disable two-factor authentication." }),
2032
+ msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
2033
+ /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
2034
+ /* @__PURE__ */ jsx10(
2035
+ "input",
2036
+ {
2037
+ className: "fc-input",
2038
+ type: "text",
2039
+ inputMode: "numeric",
2040
+ placeholder: "Enter 6-digit code",
2041
+ maxLength: 6,
2042
+ value: code,
2043
+ onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
2044
+ style: { textAlign: "center", letterSpacing: "0.2em" },
2045
+ autoFocus: true
2046
+ }
2047
+ ),
2048
+ /* @__PURE__ */ jsx10(
2049
+ "button",
2050
+ {
2051
+ type: "button",
2052
+ className: "fc-btn fc-btn-secondary",
2053
+ onClick: handleDisable,
2054
+ disabled: loading || code.length !== 6,
2055
+ style: { marginTop: 10, background: "rgba(220, 38, 38, 0.1)", color: "#dc2626", borderColor: "rgba(220, 38, 38, 0.2)" },
2056
+ children: loading ? "Disabling..." : "Disable 2FA"
2057
+ }
2058
+ ),
2059
+ /* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
2060
+ ] })
2061
+ ] }),
2062
+ step === "recovery-codes" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2063
+ /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Save your recovery codes" }),
2064
+ /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Store these codes somewhere safe. Each code can only be used once." }),
2065
+ /* @__PURE__ */ jsx10("div", { className: "fc-recovery-grid", children: recoveryCodes.map((c, i) => /* @__PURE__ */ jsx10("div", { className: "fc-recovery-code", children: c }, i)) }),
2066
+ /* @__PURE__ */ jsx10("div", { style: { textAlign: "center", margin: "8px 0 12px" }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: handleCopyAll, children: "Copy all" }) }),
2067
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: onClose, children: "Done" })
1844
2068
  ] })
1845
- ] })
2069
+ ]
1846
2070
  }
1847
2071
  ) });
1848
2072
  }
1849
- var EmailIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1850
- /* @__PURE__ */ jsx9("rect", { x: "2", y: "4", width: "16", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1851
- /* @__PURE__ */ jsx9("path", { d: "M2 6l8 5 8-5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
1852
- ] }) });
1853
- var OtpIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1854
- /* @__PURE__ */ jsx9("rect", { x: "3", y: "6", width: "14", height: "9", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
1855
- /* @__PURE__ */ jsx9("circle", { cx: "6.5", cy: "10.5", r: "1", fill: "currentColor" }),
1856
- /* @__PURE__ */ jsx9("circle", { cx: "10", cy: "10.5", r: "1", fill: "currentColor" }),
1857
- /* @__PURE__ */ jsx9("circle", { cx: "13.5", cy: "10.5", r: "1", fill: "currentColor" })
1858
- ] }) });
1859
- var WalletIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1860
- /* @__PURE__ */ jsx9("rect", { x: "2", y: "5", width: "16", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1861
- /* @__PURE__ */ jsx9("rect", { x: "13", y: "9", width: "5", height: "3", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
1862
- ] }) });
1863
- var PasskeyIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
1864
- /* @__PURE__ */ jsx9("circle", { cx: "8", cy: "7", r: "3", stroke: "currentColor", strokeWidth: "1.5" }),
1865
- /* @__PURE__ */ jsx9("path", { d: "M13 13.5a5 5 0 0 0-10 0", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
1866
- /* @__PURE__ */ jsx9("path", { d: "M15 10v4m0 0l-1.5-1m1.5 1l1.5-1", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1867
- ] }) });
1868
- var LoadingSpinner = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsx9("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon fc-spin", children: /* @__PURE__ */ jsx9("path", { d: "M10 2a8 8 0 0 1 8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) });
1869
- function MethodSelect() {
1870
- const { setModalStep, loginWithPasskey, config } = useForgeConnect();
1871
- const methods = resolveLoginMethods(config);
1872
- const [passkeyLoading, setPasskeyLoading] = useState6(false);
1873
- const [passkeyError, setPasskeyError] = useState6("");
1874
- const handlePasskey = async () => {
1875
- setPasskeyLoading(true);
1876
- setPasskeyError("");
1877
- try {
1878
- await loginWithPasskey();
1879
- } catch (err) {
1880
- setPasskeyError(err instanceof Error ? err.message : "Passkey authentication failed.");
1881
- } finally {
1882
- setPasskeyLoading(false);
1883
- }
1884
- };
1885
- const elements = [];
1886
- let i = 0;
1887
- while (i < methods.length) {
1888
- const method = methods[i];
1889
- if (isOAuthMethod(method)) {
1890
- const oauthGroup = [];
1891
- while (i < methods.length && isOAuthMethod(methods[i])) {
1892
- oauthGroup.push(methods[i]);
1893
- i++;
1894
- }
1895
- if (elements.length > 0) {
1896
- elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-before-${oauthGroup[0]}`));
1897
- }
1898
- elements.push(
1899
- /* @__PURE__ */ jsx9("div", { className: "fc-oauth-group", children: oauthGroup.map((p) => /* @__PURE__ */ jsx9(OAuthButton, { provider: p }, p)) }, `oauth-${oauthGroup.join("-")}`)
1900
- );
1901
- } else {
1902
- if (elements.length > 0) {
1903
- const prev = methods[i - 1];
1904
- if (prev && isOAuthMethod(prev)) {
1905
- elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-after-${prev}`));
1906
- }
1907
- }
1908
- switch (method) {
1909
- case "email":
1910
- elements.push(
1911
- /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-login"), children: [
1912
- /* @__PURE__ */ jsx9(EmailIcon, {}),
1913
- " ",
1914
- /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Password" })
1915
- ] }, "email")
1916
- );
1917
- break;
1918
- case "otp":
1919
- elements.push(
1920
- /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-otp"), children: [
1921
- /* @__PURE__ */ jsx9(OtpIcon, {}),
1922
- " ",
1923
- /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Magic code" })
1924
- ] }, "otp")
1925
- );
1926
- break;
1927
- case "wallet":
1928
- elements.push(
1929
- /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("wallet-connect"), children: [
1930
- /* @__PURE__ */ jsx9(WalletIcon, {}),
1931
- " ",
1932
- /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Wallet" })
1933
- ] }, "wallet")
1934
- );
1935
- break;
1936
- case "passkey":
1937
- elements.push(
1938
- /* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: handlePasskey, disabled: passkeyLoading, children: [
1939
- passkeyLoading ? /* @__PURE__ */ jsx9(LoadingSpinner, {}) : /* @__PURE__ */ jsx9(PasskeyIcon, {}),
1940
- /* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: passkeyLoading ? "Waiting for passkey..." : "Passkey" })
1941
- ] }, "passkey")
1942
- );
1943
- break;
1944
- }
1945
- i++;
1946
- }
2073
+
2074
+ // src/components/passkeys-modal.tsx
2075
+ import { useState as useState11, useEffect as useEffect7, useCallback as useCallback6 } from "react";
2076
+
2077
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/bufferToBase64URLString.js
2078
+ function bufferToBase64URLString(buffer) {
2079
+ const bytes = new Uint8Array(buffer);
2080
+ let str = "";
2081
+ for (const charCode of bytes) {
2082
+ str += String.fromCharCode(charCode);
1947
2083
  }
1948
- return /* @__PURE__ */ jsxs8("div", { className: "fc-method-select", children: [
1949
- passkeyError && /* @__PURE__ */ jsx9("p", { className: "fc-error", children: passkeyError }),
1950
- elements
1951
- ] });
1952
- }
1953
- function OAuthLoadingView() {
1954
- const { setModalStep } = useForgeConnect();
1955
- return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
1956
- /* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsx9("svg", { width: "52", height: "52", viewBox: "0 0 52 52", className: "fc-spin", children: /* @__PURE__ */ jsx9("circle", { cx: "26", cy: "26", r: "24", fill: "none", stroke: "var(--fc-accent, #8b5cf6)", strokeWidth: "3", strokeLinecap: "round", strokeDasharray: "100", strokeDashoffset: "30" }) }) }),
1957
- /* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "Completing sign in..." }),
1958
- /* @__PURE__ */ jsx9("button", { type: "button", className: "fc-btn fc-btn-secondary", style: { marginTop: "16px" }, onClick: () => setModalStep("method-select"), children: "Cancel" })
1959
- ] });
2084
+ const base64String = btoa(str);
2085
+ return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1960
2086
  }
1961
- function SuccessView() {
1962
- return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
1963
- /* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 52 52", className: "fc-success-check", children: [
1964
- /* @__PURE__ */ jsx9("circle", { className: "fc-success-circle", cx: "26", cy: "26", r: "24", fill: "none" }),
1965
- /* @__PURE__ */ jsx9("path", { className: "fc-success-tick", fill: "none", d: "M15 26l7.5 7.5L37 19" })
1966
- ] }) }),
1967
- /* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "You're in" })
1968
- ] });
2087
+
2088
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/base64URLStringToBuffer.js
2089
+ function base64URLStringToBuffer(base64URLString) {
2090
+ const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
2091
+ const padLength = (4 - base64.length % 4) % 4;
2092
+ const padded = base64.padEnd(base64.length + padLength, "=");
2093
+ const binary = atob(padded);
2094
+ const buffer = new ArrayBuffer(binary.length);
2095
+ const bytes = new Uint8Array(buffer);
2096
+ for (let i = 0; i < binary.length; i++) {
2097
+ bytes[i] = binary.charCodeAt(i);
2098
+ }
2099
+ return buffer;
1969
2100
  }
1970
2101
 
1971
- // src/components/account-modal.tsx
1972
- import { useState as useState14, useEffect as useEffect10, useRef as useRef7, useCallback as useCallback7 } from "react";
2102
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthn.js
2103
+ function browserSupportsWebAuthn() {
2104
+ return _browserSupportsWebAuthnInternals.stubThis(globalThis?.PublicKeyCredential !== void 0 && typeof globalThis.PublicKeyCredential === "function");
2105
+ }
2106
+ var _browserSupportsWebAuthnInternals = {
2107
+ stubThis: (value) => value
2108
+ };
1973
2109
 
1974
- // src/hooks/use-user.ts
1975
- import { useState as useState7, useCallback as useCallback3, useEffect as useEffect5, useRef as useRef6 } from "react";
1976
- function useUser() {
1977
- const { auth, api, config, getAccessToken } = useForgeConnect();
1978
- const [authMethods, setAuthMethods] = useState7(null);
1979
- const [loading, setLoading] = useState7(false);
1980
- const pendingRefreshRef = useRef6(false);
1981
- const updateProfile = useCallback3(
1982
- async (data) => {
1983
- const token = getAccessToken();
1984
- if (!token) throw new Error("Please sign in to continue.");
1985
- return api.updateMe(token, data);
1986
- },
1987
- [api, getAccessToken]
1988
- );
1989
- const fetchAuthMethods = useCallback3(async () => {
1990
- const token = getAccessToken();
1991
- if (!token) throw new Error("Please sign in to continue.");
1992
- setLoading(true);
1993
- try {
1994
- const methods = await api.getAuthMethods(token);
1995
- setAuthMethods(methods);
1996
- return methods;
1997
- } finally {
1998
- setLoading(false);
1999
- }
2000
- }, [api, getAccessToken]);
2001
- const linkAuthMethod = useCallback3(
2002
- async (data) => {
2003
- const token = getAccessToken();
2004
- if (!token) throw new Error("Please sign in to continue.");
2005
- await api.linkAuthMethod(token, data);
2006
- await fetchAuthMethods();
2007
- },
2008
- [api, getAccessToken, fetchAuthMethods]
2009
- );
2010
- const linkOtpSend = useCallback3(
2011
- async (email) => {
2012
- const token = getAccessToken();
2013
- if (!token) throw new Error("Please sign in to continue.");
2014
- await api.linkOtpSend(token, email);
2015
- },
2016
- [api, getAccessToken]
2017
- );
2018
- const linkOtpVerify = useCallback3(
2019
- async (email, code) => {
2020
- const token = getAccessToken();
2021
- if (!token) throw new Error("Please sign in to continue.");
2022
- await api.linkOtpVerify(token, email, code);
2023
- await fetchAuthMethods();
2024
- },
2025
- [api, getAccessToken, fetchAuthMethods]
2026
- );
2027
- const unlinkAuthMethod = useCallback3(
2028
- async (id) => {
2029
- const token = getAccessToken();
2030
- if (!token) throw new Error("Please sign in to continue.");
2031
- await api.unlinkAuthMethod(token, id);
2032
- await fetchAuthMethods();
2033
- },
2034
- [api, getAccessToken, fetchAuthMethods]
2035
- );
2036
- const linkOAuth = useCallback3(
2037
- async (provider) => {
2038
- const token = getAccessToken();
2039
- if (!token) throw new Error("Please sign in to continue.");
2040
- const { intentToken } = await api.createLinkIntent(token);
2041
- const redirectUri = encodeURIComponent(window.location.origin + "/fc-oauth-callback");
2042
- const url = `${config.apiUrl}/auth/oauth/${provider}/link?intent=${encodeURIComponent(intentToken)}&redirect_uri=${redirectUri}`;
2043
- const width = 500;
2044
- const height = 600;
2045
- const left = window.screenX + (window.innerWidth - width) / 2;
2046
- const top = window.screenY + (window.innerHeight - height) / 2;
2047
- window.open(url, "fc_oauth_link", `width=${width},height=${height},left=${left},top=${top}`);
2048
- pendingRefreshRef.current = true;
2049
- },
2050
- [api, config.apiUrl, getAccessToken]
2051
- );
2052
- useEffect5(() => {
2053
- const handleMessage = (event) => {
2054
- if (event.origin !== window.location.origin) return;
2055
- if (event.data?.type === "fc_oauth_link_success" && pendingRefreshRef.current) {
2056
- pendingRefreshRef.current = false;
2057
- fetchAuthMethods().catch(() => {
2058
- });
2059
- }
2060
- };
2061
- window.addEventListener("message", handleMessage);
2062
- return () => window.removeEventListener("message", handleMessage);
2063
- }, [fetchAuthMethods]);
2110
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toPublicKeyCredentialDescriptor.js
2111
+ function toPublicKeyCredentialDescriptor(descriptor) {
2112
+ const { id } = descriptor;
2064
2113
  return {
2065
- user: auth.user,
2066
- authMethods,
2067
- loading,
2068
- updateProfile,
2069
- fetchAuthMethods,
2070
- linkAuthMethod,
2071
- linkOtpSend,
2072
- linkOtpVerify,
2073
- unlinkAuthMethod,
2074
- linkOAuth
2114
+ ...descriptor,
2115
+ id: base64URLStringToBuffer(id),
2116
+ /**
2117
+ * `descriptor.transports` is an array of our `AuthenticatorTransportFuture` that includes newer
2118
+ * transports that TypeScript's DOM lib is ignorant of. Convince TS that our list of transports
2119
+ * are fine to pass to WebAuthn since browsers will recognize the new value.
2120
+ */
2121
+ transports: descriptor.transports
2075
2122
  };
2076
2123
  }
2077
2124
 
2078
- // src/hooks/use-wallets.ts
2079
- import { useState as useState8, useCallback as useCallback4 } from "react";
2125
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/isValidDomain.js
2126
+ function isValidDomain(hostname) {
2127
+ return (
2128
+ // Consider localhost valid as well since it's okay wrt Secure Contexts
2129
+ hostname === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname)
2130
+ );
2131
+ }
2080
2132
 
2081
- // src/lib/utils.ts
2082
- var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
2083
- function uint8ArrayToBase58(bytes) {
2084
- const digits = [0];
2085
- for (const byte of bytes) {
2086
- let carry = byte;
2087
- for (let j = 0; j < digits.length; j++) {
2088
- carry += digits[j] << 8;
2089
- digits[j] = carry % 58;
2090
- carry = carry / 58 | 0;
2091
- }
2092
- while (carry > 0) {
2093
- digits.push(carry % 58);
2094
- carry = carry / 58 | 0;
2095
- }
2133
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnError.js
2134
+ var WebAuthnError = class extends Error {
2135
+ constructor({ message, code, cause, name }) {
2136
+ super(message, { cause });
2137
+ Object.defineProperty(this, "code", {
2138
+ enumerable: true,
2139
+ configurable: true,
2140
+ writable: true,
2141
+ value: void 0
2142
+ });
2143
+ this.name = name ?? cause.name;
2144
+ this.code = code;
2096
2145
  }
2097
- let str = "";
2098
- for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
2099
- str += "1";
2146
+ };
2147
+
2148
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/identifyRegistrationError.js
2149
+ function identifyRegistrationError({ error, options }) {
2150
+ const { publicKey } = options;
2151
+ if (!publicKey) {
2152
+ throw Error("options was missing required publicKey property");
2100
2153
  }
2101
- for (let i = digits.length - 1; i >= 0; i--) {
2102
- str += BASE58_ALPHABET[digits[i]];
2154
+ if (error.name === "AbortError") {
2155
+ if (options.signal instanceof AbortSignal) {
2156
+ return new WebAuthnError({
2157
+ message: "Registration ceremony was sent an abort signal",
2158
+ code: "ERROR_CEREMONY_ABORTED",
2159
+ cause: error
2160
+ });
2161
+ }
2162
+ } else if (error.name === "ConstraintError") {
2163
+ if (publicKey.authenticatorSelection?.requireResidentKey === true) {
2164
+ return new WebAuthnError({
2165
+ message: "Discoverable credentials were required but no available authenticator supported it",
2166
+ code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
2167
+ cause: error
2168
+ });
2169
+ } else if (
2170
+ // @ts-ignore: `mediation` doesn't yet exist on CredentialCreationOptions but it's possible as of Sept 2024
2171
+ options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required"
2172
+ ) {
2173
+ return new WebAuthnError({
2174
+ message: "User verification was required during automatic registration but it could not be performed",
2175
+ code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
2176
+ cause: error
2177
+ });
2178
+ } else if (publicKey.authenticatorSelection?.userVerification === "required") {
2179
+ return new WebAuthnError({
2180
+ message: "User verification was required but no available authenticator supported it",
2181
+ code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
2182
+ cause: error
2183
+ });
2184
+ }
2185
+ } else if (error.name === "InvalidStateError") {
2186
+ return new WebAuthnError({
2187
+ message: "The authenticator was previously registered",
2188
+ code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
2189
+ cause: error
2190
+ });
2191
+ } else if (error.name === "NotAllowedError") {
2192
+ return new WebAuthnError({
2193
+ message: error.message,
2194
+ code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
2195
+ cause: error
2196
+ });
2197
+ } else if (error.name === "NotSupportedError") {
2198
+ const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === "public-key");
2199
+ if (validPubKeyCredParams.length === 0) {
2200
+ return new WebAuthnError({
2201
+ message: 'No entry in pubKeyCredParams was of type "public-key"',
2202
+ code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
2203
+ cause: error
2204
+ });
2205
+ }
2206
+ return new WebAuthnError({
2207
+ message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
2208
+ code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
2209
+ cause: error
2210
+ });
2211
+ } else if (error.name === "SecurityError") {
2212
+ const effectiveDomain = globalThis.location.hostname;
2213
+ if (!isValidDomain(effectiveDomain)) {
2214
+ return new WebAuthnError({
2215
+ message: `${globalThis.location.hostname} is an invalid domain`,
2216
+ code: "ERROR_INVALID_DOMAIN",
2217
+ cause: error
2218
+ });
2219
+ } else if (publicKey.rp.id !== effectiveDomain) {
2220
+ return new WebAuthnError({
2221
+ message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
2222
+ code: "ERROR_INVALID_RP_ID",
2223
+ cause: error
2224
+ });
2225
+ }
2226
+ } else if (error.name === "TypeError") {
2227
+ if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
2228
+ return new WebAuthnError({
2229
+ message: "User ID was not between 1 and 64 characters",
2230
+ code: "ERROR_INVALID_USER_ID_LENGTH",
2231
+ cause: error
2232
+ });
2233
+ }
2234
+ } else if (error.name === "UnknownError") {
2235
+ return new WebAuthnError({
2236
+ message: "The authenticator was unable to process the specified options, or could not create a new credential",
2237
+ code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
2238
+ cause: error
2239
+ });
2103
2240
  }
2104
- return str;
2105
- }
2106
- function timeAgo(dateStr) {
2107
- const now = Date.now();
2108
- const then = new Date(dateStr).getTime();
2109
- const diff = now - then;
2110
- const mins = Math.floor(diff / 6e4);
2111
- if (mins < 1) return "just now";
2112
- if (mins < 60) return `${mins}m ago`;
2113
- const hours = Math.floor(mins / 60);
2114
- if (hours < 24) return `${hours}h ago`;
2115
- const days = Math.floor(hours / 24);
2116
- if (days < 30) return `${days}d ago`;
2117
- return new Date(dateStr).toLocaleDateString();
2241
+ return error;
2118
2242
  }
2119
2243
 
2120
- // src/hooks/use-wallets.ts
2121
- function useWallets() {
2122
- const { api, getAccessToken } = useForgeConnect();
2123
- const [wallets, setWallets] = useState8(null);
2124
- const [loading, setLoading] = useState8(false);
2125
- const fetchWallets = useCallback4(async () => {
2126
- const token = getAccessToken();
2127
- if (!token) throw new Error("Please sign in to continue.");
2128
- setLoading(true);
2129
- try {
2130
- const data = await api.getWallets(token);
2131
- setWallets(data);
2132
- return data;
2133
- } finally {
2134
- setLoading(false);
2244
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnAbortService.js
2245
+ var BaseWebAuthnAbortService = class {
2246
+ constructor() {
2247
+ Object.defineProperty(this, "controller", {
2248
+ enumerable: true,
2249
+ configurable: true,
2250
+ writable: true,
2251
+ value: void 0
2252
+ });
2253
+ }
2254
+ createNewAbortSignal() {
2255
+ if (this.controller) {
2256
+ const abortError = new Error("Cancelling existing WebAuthn API call for new one");
2257
+ abortError.name = "AbortError";
2258
+ this.controller.abort(abortError);
2135
2259
  }
2136
- }, [api, getAccessToken]);
2137
- const updateWallet = useCallback4(
2138
- async (id, data) => {
2139
- const token = getAccessToken();
2140
- if (!token) throw new Error("Please sign in to continue.");
2141
- await api.updateWallet(token, id, data);
2142
- await fetchWallets();
2143
- },
2144
- [api, getAccessToken, fetchWallets]
2145
- );
2146
- const linkWallet = useCallback4(
2147
- async (walletAddress, signMessage, chain = "solana", signTransaction) => {
2148
- const token = getAccessToken();
2149
- if (!token) throw new Error("Please sign in to continue.");
2150
- if (signMessage) {
2151
- const { challengeId, message: challengeMessage } = await api.walletChallenge(walletAddress, chain);
2152
- const encoded = new TextEncoder().encode(challengeMessage);
2153
- const signatureBytes = await signMessage(encoded);
2154
- const signature = chain === "solana" ? uint8ArrayToBase58(signatureBytes) : Array.from(signatureBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2155
- await api.linkAuthMethod(token, {
2156
- provider: `${chain}_wallet`,
2157
- challengeId,
2158
- signature,
2159
- walletAddress
2160
- });
2161
- } else {
2162
- if (!signTransaction) {
2163
- throw new Error("Wallet does not support message signing or transaction signing.");
2164
- }
2165
- const { challengeId, transaction: txBase64 } = await api.walletChallengeTx(walletAddress, chain);
2166
- const signedTxBase64 = await signTransaction(txBase64);
2167
- await api.linkAuthMethod(token, {
2168
- provider: `${chain}_wallet_tx`,
2169
- challengeId,
2170
- signedTransaction: signedTxBase64,
2171
- walletAddress
2172
- });
2173
- }
2174
- await fetchWallets();
2175
- },
2176
- [api, getAccessToken, fetchWallets]
2177
- );
2178
- return {
2179
- wallets,
2180
- loading,
2181
- fetchWallets,
2182
- updateWallet,
2183
- linkWallet
2184
- };
2260
+ const newController = new AbortController();
2261
+ this.controller = newController;
2262
+ return newController.signal;
2263
+ }
2264
+ cancelCeremony() {
2265
+ if (this.controller) {
2266
+ const abortError = new Error("Manually cancelling existing WebAuthn API call");
2267
+ abortError.name = "AbortError";
2268
+ this.controller.abort(abortError);
2269
+ this.controller = void 0;
2270
+ }
2271
+ }
2272
+ };
2273
+ var WebAuthnAbortService = new BaseWebAuthnAbortService();
2274
+
2275
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toAuthenticatorAttachment.js
2276
+ var attachments = ["cross-platform", "platform"];
2277
+ function toAuthenticatorAttachment(attachment) {
2278
+ if (!attachment) {
2279
+ return;
2280
+ }
2281
+ if (attachments.indexOf(attachment) < 0) {
2282
+ return;
2283
+ }
2284
+ return attachment;
2185
2285
  }
2186
2286
 
2187
- // src/hooks/use-sessions.ts
2188
- import { useState as useState9, useCallback as useCallback5 } from "react";
2189
- function useSessions() {
2190
- const { api, getAccessToken } = useForgeConnect();
2191
- const [sessions, setSessions] = useState9(null);
2192
- const [loading, setLoading] = useState9(false);
2193
- const fetchSessions = useCallback5(async () => {
2194
- const token = getAccessToken();
2195
- if (!token) throw new Error("Please sign in to continue.");
2196
- setLoading(true);
2197
- try {
2198
- const data = await api.getSessions(token);
2199
- setSessions(data);
2200
- return data;
2201
- } finally {
2202
- setLoading(false);
2203
- }
2204
- }, [api, getAccessToken]);
2205
- const revokeSession = useCallback5(
2206
- async (id) => {
2207
- const token = getAccessToken();
2208
- if (!token) throw new Error("Please sign in to continue.");
2209
- await api.revokeSession(token, id);
2210
- await fetchSessions();
2287
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startRegistration.js
2288
+ async function startRegistration(options) {
2289
+ if (!options.optionsJSON && options.challenge) {
2290
+ console.warn("startRegistration() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
2291
+ options = { optionsJSON: options };
2292
+ }
2293
+ const { optionsJSON, useAutoRegister = false } = options;
2294
+ if (!browserSupportsWebAuthn()) {
2295
+ throw new Error("WebAuthn is not supported in this browser");
2296
+ }
2297
+ const publicKey = {
2298
+ ...optionsJSON,
2299
+ challenge: base64URLStringToBuffer(optionsJSON.challenge),
2300
+ user: {
2301
+ ...optionsJSON.user,
2302
+ id: base64URLStringToBuffer(optionsJSON.user.id)
2211
2303
  },
2212
- [api, getAccessToken, fetchSessions]
2213
- );
2214
- return {
2215
- sessions,
2216
- loading,
2217
- fetchSessions,
2218
- revokeSession
2304
+ excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
2219
2305
  };
2220
- }
2221
-
2222
- // src/components/two-factor-modal.tsx
2223
- import { useState as useState10, useEffect as useEffect6 } from "react";
2224
- import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2225
- function TwoFactorModal({ isOpen, onClose, initialEnabled, onStatusChange }) {
2226
- const { api, getAccessToken, config } = useForgeConnect();
2227
- const theme = config.appearance?.theme ?? "light";
2228
- const [step, setStep] = useState10(initialEnabled ? "manage" : "setup");
2229
- const [setupData, setSetupData] = useState10(null);
2230
- const [code, setCode] = useState10("");
2231
- const [loading, setLoading] = useState10(false);
2232
- const [msg, setMsg] = useState10(null);
2233
- const [showSecret, setShowSecret] = useState10(false);
2234
- const [recoveryCodes, setRecoveryCodes] = useState10([]);
2235
- useEffect6(() => {
2236
- if (isOpen) {
2237
- setStep(initialEnabled ? "manage" : "setup");
2238
- setSetupData(null);
2239
- setCode("");
2240
- setMsg(null);
2241
- setLoading(false);
2242
- setShowSecret(false);
2243
- setRecoveryCodes([]);
2244
- if (!initialEnabled) {
2245
- const token = getAccessToken();
2246
- if (token) {
2247
- setLoading(true);
2248
- api.setup2FA(token).then((data) => {
2249
- setSetupData(data);
2250
- setRecoveryCodes(data.recoveryCodes);
2251
- }).catch((err) => {
2252
- setMsg({ text: err instanceof Error ? err.message : "Could not set up 2FA.", ok: false });
2253
- }).finally(() => setLoading(false));
2254
- }
2255
- }
2256
- }
2257
- }, [isOpen, initialEnabled, api, getAccessToken]);
2258
- const handleEnable = async () => {
2259
- const token = getAccessToken();
2260
- if (!token || !code) return;
2261
- setLoading(true);
2262
- setMsg(null);
2306
+ const createOptions = {};
2307
+ if (useAutoRegister) {
2308
+ createOptions.mediation = "conditional";
2309
+ }
2310
+ createOptions.publicKey = publicKey;
2311
+ createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
2312
+ let credential;
2313
+ try {
2314
+ credential = await navigator.credentials.create(createOptions);
2315
+ } catch (err) {
2316
+ throw identifyRegistrationError({ error: err, options: createOptions });
2317
+ }
2318
+ if (!credential) {
2319
+ throw new Error("Registration was not completed");
2320
+ }
2321
+ const { id, rawId, response, type } = credential;
2322
+ let transports = void 0;
2323
+ if (typeof response.getTransports === "function") {
2324
+ transports = response.getTransports();
2325
+ }
2326
+ let responsePublicKeyAlgorithm = void 0;
2327
+ if (typeof response.getPublicKeyAlgorithm === "function") {
2263
2328
  try {
2264
- await api.enable2FA(token, code);
2265
- onStatusChange(true);
2266
- setStep("recovery-codes");
2267
- setCode("");
2268
- } catch (err) {
2269
- setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
2270
- } finally {
2271
- setLoading(false);
2329
+ responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
2330
+ } catch (error) {
2331
+ warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
2272
2332
  }
2273
- };
2274
- const handleDisable = async () => {
2275
- const token = getAccessToken();
2276
- if (!token || !code) return;
2277
- setLoading(true);
2278
- setMsg(null);
2333
+ }
2334
+ let responsePublicKey = void 0;
2335
+ if (typeof response.getPublicKey === "function") {
2279
2336
  try {
2280
- await api.disable2FA(token, code);
2281
- onStatusChange(false);
2282
- setCode("");
2283
- onClose();
2284
- } catch (err) {
2285
- setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
2286
- } finally {
2287
- setLoading(false);
2337
+ const _publicKey = response.getPublicKey();
2338
+ if (_publicKey !== null) {
2339
+ responsePublicKey = bufferToBase64URLString(_publicKey);
2340
+ }
2341
+ } catch (error) {
2342
+ warnOnBrokenImplementation("getPublicKey()", error);
2288
2343
  }
2289
- };
2290
- const handleRegenerate = async () => {
2291
- const token = getAccessToken();
2292
- if (!token || !code) return;
2293
- setLoading(true);
2294
- setMsg(null);
2344
+ }
2345
+ let responseAuthenticatorData;
2346
+ if (typeof response.getAuthenticatorData === "function") {
2295
2347
  try {
2296
- const { recoveryCodes: codes } = await api.regenerateRecoveryCodes(token, code);
2297
- setRecoveryCodes(codes);
2298
- setStep("recovery-codes");
2299
- setCode("");
2300
- } catch (err) {
2301
- setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
2302
- } finally {
2303
- setLoading(false);
2348
+ responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
2349
+ } catch (error) {
2350
+ warnOnBrokenImplementation("getAuthenticatorData()", error);
2304
2351
  }
2352
+ }
2353
+ return {
2354
+ id,
2355
+ rawId: bufferToBase64URLString(rawId),
2356
+ response: {
2357
+ attestationObject: bufferToBase64URLString(response.attestationObject),
2358
+ clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
2359
+ transports,
2360
+ publicKeyAlgorithm: responsePublicKeyAlgorithm,
2361
+ publicKey: responsePublicKey,
2362
+ authenticatorData: responseAuthenticatorData
2363
+ },
2364
+ type,
2365
+ clientExtensionResults: credential.getClientExtensionResults(),
2366
+ authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
2305
2367
  };
2306
- const handleCopyAll = () => {
2307
- navigator.clipboard.writeText(recoveryCodes.join("\n")).catch(() => {
2368
+ }
2369
+ function warnOnBrokenImplementation(methodName, cause) {
2370
+ console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.
2371
+ `, cause);
2372
+ }
2373
+
2374
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthnAutofill.js
2375
+ function browserSupportsWebAuthnAutofill() {
2376
+ if (!browserSupportsWebAuthn()) {
2377
+ return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
2378
+ }
2379
+ const globalPublicKeyCredential = globalThis.PublicKeyCredential;
2380
+ if (globalPublicKeyCredential?.isConditionalMediationAvailable === void 0) {
2381
+ return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
2382
+ }
2383
+ return _browserSupportsWebAuthnAutofillInternals.stubThis(globalPublicKeyCredential.isConditionalMediationAvailable());
2384
+ }
2385
+ var _browserSupportsWebAuthnAutofillInternals = {
2386
+ stubThis: (value) => value
2387
+ };
2388
+
2389
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/identifyAuthenticationError.js
2390
+ function identifyAuthenticationError({ error, options }) {
2391
+ const { publicKey } = options;
2392
+ if (!publicKey) {
2393
+ throw Error("options was missing required publicKey property");
2394
+ }
2395
+ if (error.name === "AbortError") {
2396
+ if (options.signal instanceof AbortSignal) {
2397
+ return new WebAuthnError({
2398
+ message: "Authentication ceremony was sent an abort signal",
2399
+ code: "ERROR_CEREMONY_ABORTED",
2400
+ cause: error
2401
+ });
2402
+ }
2403
+ } else if (error.name === "NotAllowedError") {
2404
+ return new WebAuthnError({
2405
+ message: error.message,
2406
+ code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
2407
+ cause: error
2408
+ });
2409
+ } else if (error.name === "SecurityError") {
2410
+ const effectiveDomain = globalThis.location.hostname;
2411
+ if (!isValidDomain(effectiveDomain)) {
2412
+ return new WebAuthnError({
2413
+ message: `${globalThis.location.hostname} is an invalid domain`,
2414
+ code: "ERROR_INVALID_DOMAIN",
2415
+ cause: error
2416
+ });
2417
+ } else if (publicKey.rpId !== effectiveDomain) {
2418
+ return new WebAuthnError({
2419
+ message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
2420
+ code: "ERROR_INVALID_RP_ID",
2421
+ cause: error
2422
+ });
2423
+ }
2424
+ } else if (error.name === "UnknownError") {
2425
+ return new WebAuthnError({
2426
+ message: "The authenticator was unable to process the specified options, or could not create a new assertion signature",
2427
+ code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
2428
+ cause: error
2308
2429
  });
2430
+ }
2431
+ return error;
2432
+ }
2433
+
2434
+ // ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startAuthentication.js
2435
+ async function startAuthentication(options) {
2436
+ if (!options.optionsJSON && options.challenge) {
2437
+ console.warn("startAuthentication() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
2438
+ options = { optionsJSON: options };
2439
+ }
2440
+ const { optionsJSON, useBrowserAutofill = false, verifyBrowserAutofillInput = true } = options;
2441
+ if (!browserSupportsWebAuthn()) {
2442
+ throw new Error("WebAuthn is not supported in this browser");
2443
+ }
2444
+ let allowCredentials;
2445
+ if (optionsJSON.allowCredentials?.length !== 0) {
2446
+ allowCredentials = optionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
2447
+ }
2448
+ const publicKey = {
2449
+ ...optionsJSON,
2450
+ challenge: base64URLStringToBuffer(optionsJSON.challenge),
2451
+ allowCredentials
2309
2452
  };
2310
- const goBack = () => {
2311
- setCode("");
2312
- setMsg(null);
2313
- setStep("manage");
2314
- };
2315
- if (!isOpen) return null;
2316
- return /* @__PURE__ */ jsx10(ModalOverlay, { isOpen, onClose, children: /* @__PURE__ */ jsxs9(
2317
- "div",
2318
- {
2319
- className: "fc-modal-content",
2320
- style: { "--fc-accent": config.appearance?.accentColor ?? "#8b5cf6" },
2321
- "data-theme": theme,
2322
- children: [
2323
- step === "setup" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2324
- /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Set up 2FA" }),
2325
- msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
2326
- loading && !setupData && /* @__PURE__ */ jsx10("p", { className: "fc-text", children: "Loading..." }),
2327
- setupData && /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
2328
- /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Scan this QR code with your authenticator app" }),
2329
- /* @__PURE__ */ jsx10("div", { style: { display: "flex", justifyContent: "center", margin: "12px 0" }, children: /* @__PURE__ */ jsx10(
2330
- "img",
2331
- {
2332
- src: setupData.qrCodeDataUrl,
2333
- alt: "TOTP QR code",
2334
- style: { width: 180, height: 180, borderRadius: 12 }
2335
- }
2336
- ) }),
2337
- /* @__PURE__ */ jsxs9("div", { style: { textAlign: "center", marginBottom: 12 }, children: [
2338
- /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: () => setShowSecret(!showSecret), children: showSecret ? "Hide code" : "Can't scan? Enter manually" }),
2339
- showSecret && /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontFamily: "monospace", fontSize: 12, opacity: 0.7, wordBreak: "break-all" }, children: setupData.secret })
2340
- ] }),
2341
- /* @__PURE__ */ jsx10(
2342
- "input",
2343
- {
2344
- className: "fc-input",
2345
- type: "text",
2346
- inputMode: "numeric",
2347
- placeholder: "Enter 6-digit code",
2348
- maxLength: 6,
2349
- value: code,
2350
- onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
2351
- style: { textAlign: "center", letterSpacing: "0.2em" },
2352
- autoFocus: true
2353
- }
2354
- ),
2355
- /* @__PURE__ */ jsx10(
2356
- "button",
2357
- {
2358
- type: "button",
2359
- className: "fc-btn fc-btn-primary",
2360
- onClick: handleEnable,
2361
- disabled: loading || code.length !== 6,
2362
- style: { marginTop: 10 },
2363
- children: loading ? "Verifying..." : "Verify & Enable"
2364
- }
2365
- )
2366
- ] })
2367
- ] }),
2368
- step === "manage" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2369
- /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Two-factor authentication" }),
2370
- /* @__PURE__ */ jsx10("div", { style: { textAlign: "center", marginBottom: 20 }, children: /* @__PURE__ */ jsx10("span", { className: "fc-badge-verified", style: { fontSize: 12, padding: "4px 12px" }, children: "Enabled" }) }),
2371
- /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
2372
- /* @__PURE__ */ jsxs9(
2373
- "button",
2374
- {
2375
- type: "button",
2376
- className: "fc-security-card",
2377
- onClick: () => {
2378
- setCode("");
2379
- setMsg(null);
2380
- setStep("confirm-regenerate");
2381
- },
2382
- children: [
2383
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
2384
- /* @__PURE__ */ jsx10("path", { d: "M3 10a7 7 0 0 1 7-7m0 14a7 7 0 0 1-7-7m14 0a7 7 0 0 1-7 7m0-14a7 7 0 0 1 7 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
2385
- /* @__PURE__ */ jsx10("path", { d: "M14 3l-1 3h3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
2386
- ] }) }),
2387
- /* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
2388
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", children: "Regenerate recovery codes" }),
2389
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Get new backup codes" })
2390
- ] }),
2391
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
2392
- ]
2393
- }
2394
- ),
2395
- /* @__PURE__ */ jsxs9(
2396
- "button",
2397
- {
2398
- type: "button",
2399
- className: "fc-security-card",
2400
- onClick: () => {
2401
- setCode("");
2402
- setMsg(null);
2403
- setStep("confirm-disable");
2404
- },
2405
- children: [
2406
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", style: { color: "#dc2626" }, children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
2407
- /* @__PURE__ */ jsx10("path", { d: "M10 2l6 3v4c0 4.42-2.56 8.22-6 9.5C6.56 17.22 4 13.42 4 9V5l6-3z", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }),
2408
- /* @__PURE__ */ jsx10("path", { d: "M7.5 7.5l5 5M12.5 7.5l-5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
2409
- ] }) }),
2410
- /* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
2411
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", style: { color: "#dc2626" }, children: "Disable 2FA" }),
2412
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Remove two-factor authentication" })
2413
- ] }),
2414
- /* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
2415
- ]
2416
- }
2417
- )
2418
- ] })
2419
- ] }),
2420
- step === "confirm-regenerate" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2421
- /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Regenerate recovery codes" }),
2422
- /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to generate new recovery codes. Your old codes will stop working." }),
2423
- msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
2424
- /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
2425
- /* @__PURE__ */ jsx10(
2426
- "input",
2427
- {
2428
- className: "fc-input",
2429
- type: "text",
2430
- inputMode: "numeric",
2431
- placeholder: "Enter 6-digit code",
2432
- maxLength: 6,
2433
- value: code,
2434
- onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
2435
- style: { textAlign: "center", letterSpacing: "0.2em" },
2436
- autoFocus: true
2437
- }
2438
- ),
2439
- /* @__PURE__ */ jsx10(
2440
- "button",
2441
- {
2442
- type: "button",
2443
- className: "fc-btn fc-btn-primary",
2444
- onClick: handleRegenerate,
2445
- disabled: loading || code.length !== 6,
2446
- style: { marginTop: 10 },
2447
- children: loading ? "Generating..." : "Regenerate codes"
2448
- }
2449
- ),
2450
- /* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
2451
- ] })
2452
- ] }),
2453
- step === "confirm-disable" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2454
- /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Disable 2FA" }),
2455
- /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to disable two-factor authentication." }),
2456
- msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
2457
- /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
2458
- /* @__PURE__ */ jsx10(
2459
- "input",
2460
- {
2461
- className: "fc-input",
2462
- type: "text",
2463
- inputMode: "numeric",
2464
- placeholder: "Enter 6-digit code",
2465
- maxLength: 6,
2466
- value: code,
2467
- onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
2468
- style: { textAlign: "center", letterSpacing: "0.2em" },
2469
- autoFocus: true
2470
- }
2471
- ),
2472
- /* @__PURE__ */ jsx10(
2473
- "button",
2474
- {
2475
- type: "button",
2476
- className: "fc-btn fc-btn-secondary",
2477
- onClick: handleDisable,
2478
- disabled: loading || code.length !== 6,
2479
- style: { marginTop: 10, background: "rgba(220, 38, 38, 0.1)", color: "#dc2626", borderColor: "rgba(220, 38, 38, 0.2)" },
2480
- children: loading ? "Disabling..." : "Disable 2FA"
2481
- }
2482
- ),
2483
- /* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
2484
- ] })
2485
- ] }),
2486
- step === "recovery-codes" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
2487
- /* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Save your recovery codes" }),
2488
- /* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Store these codes somewhere safe. Each code can only be used once." }),
2489
- /* @__PURE__ */ jsx10("div", { className: "fc-recovery-grid", children: recoveryCodes.map((c, i) => /* @__PURE__ */ jsx10("div", { className: "fc-recovery-code", children: c }, i)) }),
2490
- /* @__PURE__ */ jsx10("div", { style: { textAlign: "center", margin: "8px 0 12px" }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: handleCopyAll, children: "Copy all" }) }),
2491
- /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: onClose, children: "Done" })
2492
- ] })
2493
- ]
2453
+ const getOptions = {};
2454
+ if (useBrowserAutofill) {
2455
+ if (!await browserSupportsWebAuthnAutofill()) {
2456
+ throw Error("Browser does not support WebAuthn autofill");
2494
2457
  }
2495
- ) });
2458
+ const eligibleInputs = document.querySelectorAll("input[autocomplete$='webauthn']");
2459
+ if (eligibleInputs.length < 1 && verifyBrowserAutofillInput) {
2460
+ throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');
2461
+ }
2462
+ getOptions.mediation = "conditional";
2463
+ publicKey.allowCredentials = [];
2464
+ }
2465
+ getOptions.publicKey = publicKey;
2466
+ getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
2467
+ let credential;
2468
+ try {
2469
+ credential = await navigator.credentials.get(getOptions);
2470
+ } catch (err) {
2471
+ throw identifyAuthenticationError({ error: err, options: getOptions });
2472
+ }
2473
+ if (!credential) {
2474
+ throw new Error("Authentication was not completed");
2475
+ }
2476
+ const { id, rawId, response, type } = credential;
2477
+ let userHandle = void 0;
2478
+ if (response.userHandle) {
2479
+ userHandle = bufferToBase64URLString(response.userHandle);
2480
+ }
2481
+ return {
2482
+ id,
2483
+ rawId: bufferToBase64URLString(rawId),
2484
+ response: {
2485
+ authenticatorData: bufferToBase64URLString(response.authenticatorData),
2486
+ clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
2487
+ signature: bufferToBase64URLString(response.signature),
2488
+ userHandle
2489
+ },
2490
+ type,
2491
+ clientExtensionResults: credential.getClientExtensionResults(),
2492
+ authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
2493
+ };
2496
2494
  }
2497
2495
 
2498
2496
  // src/components/passkeys-modal.tsx
2499
- import { useState as useState11, useEffect as useEffect7, useCallback as useCallback6 } from "react";
2500
2497
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2501
2498
  function PasskeysModal({ isOpen, onClose, onCountChange }) {
2502
2499
  const { api, getAccessToken, config } = useForgeConnect();
@@ -3571,14 +3568,16 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
3571
3568
  coldWalletRef.current = coldWallet;
3572
3569
  const buildSignTxFnForAdapter = useCallback8((adapter) => {
3573
3570
  if (!adapter.signTransaction) return void 0;
3571
+ const TxClass = walletConfig?.Transaction;
3572
+ if (!TxClass) return void 0;
3574
3573
  return async (txBase64) => {
3575
- const { Transaction } = await importSolanaWeb3();
3576
3574
  const bytes = Uint8Array.from(atob(txBase64), (c) => c.charCodeAt(0));
3577
- const tx = Transaction.from(bytes);
3575
+ const tx = TxClass.from(bytes);
3578
3576
  const signedTx = await adapter.signTransaction(tx);
3579
3577
  return btoa(String.fromCharCode(...new Uint8Array(signedTx.serialize())));
3580
3578
  };
3581
- }, []);
3579
+ }, [walletConfig?.Transaction]);
3580
+ const wallet = walletAdapter;
3582
3581
  const handleConnect = async (w) => {
3583
3582
  if (w.readyState !== "Installed") {
3584
3583
  if (mobile) {
@@ -3595,11 +3594,12 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
3595
3594
  setError("");
3596
3595
  setLoading(true);
3597
3596
  try {
3598
- if (!w.adapter.connected) await w.adapter.connect();
3599
- const pk = w.adapter.publicKey;
3597
+ wallet.select(w.adapter.name);
3598
+ await wallet.connect();
3599
+ const pk = w.adapter.publicKey ?? wallet.publicKey;
3600
3600
  if (!pk) throw new Error("Wallet did not provide a public key.");
3601
3601
  const useCold = coldWalletRef.current;
3602
- const adapterSignMessage = w.adapter.signMessage ? (msg) => w.adapter.signMessage(msg) : void 0;
3602
+ const adapterSignMessage = wallet.signMessage ? (msg) => wallet.signMessage(msg) : void 0;
3603
3603
  await linkWallet(
3604
3604
  pk.toBase58(),
3605
3605
  useCold ? void 0 : adapterSignMessage,
@@ -3720,7 +3720,7 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
3720
3720
  ] }, w.adapter.name))
3721
3721
  ] }),
3722
3722
  preferredWallets.length === 0 && otherWallets.length === 0 && mobileExtraWallets.length === 0 && /* @__PURE__ */ jsx16("p", { className: "fc-text", children: "No wallet found. Please install a Solana wallet (like Phantom) to continue." }),
3723
- /* @__PURE__ */ jsxs15("label", { className: "fc-cold-wallet-toggle", children: [
3723
+ walletConfig?.Transaction && /* @__PURE__ */ jsxs15("label", { className: "fc-cold-wallet-toggle", children: [
3724
3724
  /* @__PURE__ */ jsx16(
3725
3725
  "input",
3726
3726
  {