@cedros/login-react 0.0.25 → 0.0.26

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.
@@ -1,13 +1,13 @@
1
- import { jsx as c, jsxs as _ } from "react/jsx-runtime";
2
- import { useState as v, useMemo as Q, useCallback as W, useRef as q, useEffect as M } from "react";
3
- import { WalletProvider as X, useWallet as Z } from "@solana/wallet-adapter-react";
4
- import { WalletModalProvider as F, useWalletModal as x } from "@solana/wallet-adapter-react-ui";
5
- import { u as ee, A as te, h as H } from "./useCedrosLogin-_94MmGGq.js";
6
- import { a as V } from "./validation-B8kMV3BL.js";
7
- import { L as ne } from "./LoadingSpinner-6vml-zwr.js";
8
- function ae() {
9
- const { config: e, _internal: t } = ee(), [w, s] = v(!1), [D, l] = v(null), f = Q(
10
- () => new te({
1
+ import { jsx as c, jsxs as F } from "react/jsx-runtime";
2
+ import { useState as v, useMemo as X, useCallback as W, useRef as K, useEffect as M } from "react";
3
+ import { WalletProvider as Z, useWallet as x } from "@solana/wallet-adapter-react";
4
+ import { WalletModalProvider as H, useWalletModal as ee } from "@solana/wallet-adapter-react-ui";
5
+ import { u as te, A as ne, h as V } from "./useCedrosLogin-_94MmGGq.js";
6
+ import { a as O } from "./validation-B8kMV3BL.js";
7
+ import { L as ae } from "./LoadingSpinner-6vml-zwr.js";
8
+ function re() {
9
+ const { config: e, _internal: n } = te(), [b, s] = v(!1), [D, l] = v(null), f = X(
10
+ () => new ne({
11
11
  baseUrl: e.serverUrl,
12
12
  timeoutMs: e.requestTimeout,
13
13
  retryAttempts: e.retryAttempts
@@ -15,7 +15,7 @@ function ae() {
15
15
  [e.serverUrl, e.requestTimeout, e.retryAttempts]
16
16
  ), S = W(
17
17
  async (d) => {
18
- if (!V(d)) {
18
+ if (!O(d)) {
19
19
  const i = {
20
20
  code: "INVALID_PUBLIC_KEY",
21
21
  message: "Invalid Solana public key format"
@@ -30,7 +30,7 @@ function ae() {
30
30
  { credentials: "omit" }
31
31
  );
32
32
  } catch (i) {
33
- const a = H(i, "Failed to get challenge");
33
+ const a = V(i, "Failed to get challenge");
34
34
  throw l(a), a;
35
35
  } finally {
36
36
  s(!1);
@@ -39,7 +39,7 @@ function ae() {
39
39
  [f]
40
40
  ), o = W(
41
41
  async (d, i, a) => {
42
- if (!V(d)) {
42
+ if (!O(d)) {
43
43
  const r = {
44
44
  code: "INVALID_PUBLIC_KEY",
45
45
  message: "Invalid Solana public key format"
@@ -53,32 +53,32 @@ function ae() {
53
53
  signature: i,
54
54
  message: a
55
55
  });
56
- return e.callbacks?.onLoginSuccess?.(r.user, "solana"), t?.handleLoginSuccess(r.user, r.tokens), r;
56
+ return e.callbacks?.onLoginSuccess?.(r.user, "solana"), n?.handleLoginSuccess(r.user, r.tokens), r;
57
57
  } catch (r) {
58
- const C = H(r, "Solana sign-in failed");
58
+ const C = V(r, "Solana sign-in failed");
59
59
  throw l(C), C;
60
60
  } finally {
61
61
  s(!1);
62
62
  }
63
63
  },
64
- [f, e.callbacks, t]
64
+ [f, e.callbacks, n]
65
65
  ), L = W(() => l(null), []);
66
66
  return {
67
67
  requestChallenge: S,
68
68
  signIn: o,
69
- isLoading: w,
69
+ isLoading: b,
70
70
  error: D,
71
71
  clearError: L
72
72
  };
73
73
  }
74
- const re = [];
75
- function fe(e) {
76
- return e.walletContext ? /* @__PURE__ */ c(F, { children: /* @__PURE__ */ c(R, { ...e }) }) : /* @__PURE__ */ c(X, { wallets: re, localStorageKey: "cedros-walletName", children: /* @__PURE__ */ c(F, { children: /* @__PURE__ */ c(R, { ...e }) }) });
74
+ const se = [];
75
+ function ge(e) {
76
+ return e.walletContext ? /* @__PURE__ */ c(H, { children: /* @__PURE__ */ c(R, { ...e }) }) : /* @__PURE__ */ c(Z, { wallets: se, localStorageKey: "cedros-walletName", children: /* @__PURE__ */ c(H, { children: /* @__PURE__ */ c(R, { ...e }) }) });
77
77
  }
78
78
  function R({
79
79
  onSuccess: e,
80
- onError: t,
81
- className: w = "",
80
+ onError: n,
81
+ className: b = "",
82
82
  variant: s = "default",
83
83
  size: D = "md",
84
84
  disabled: l = !1,
@@ -86,67 +86,73 @@ function R({
86
86
  onLoadingChange: S,
87
87
  walletContext: o
88
88
  }) {
89
- const { requestChallenge: L, signIn: d, isLoading: i } = ae(), a = Z(), { visible: r, setVisible: C } = x(), [g, h] = v(!1), [K, P] = v(!1), E = q(!1), k = q(!1), u = o?.connected ?? a.connected, m = o?.connecting ?? a.connecting, p = o?.publicKey ?? a.publicKey, y = o?.signMessage ?? a.signMessage, b = o?.wallet ?? a.wallet, Y = o?.wallets ?? a.wallets, $ = o ? o.select : (n) => a.select(n), T = o?.connect ?? a.connect, z = Y.filter(
90
- (n) => n.adapter.readyState === "Installed" || n.adapter.readyState === "Loadable"
89
+ const { requestChallenge: L, signIn: d, isLoading: i } = re(), a = x(), { visible: r, setVisible: C } = ee(), [g, h] = v(!1), [T, P] = v(!1), E = K(!1), k = K(!1), U = K(null), u = o?.connected ?? a.connected, m = o?.connecting ?? a.connecting, p = o?.publicKey ?? a.publicKey, y = o?.signMessage ?? a.signMessage, w = o?.wallet ?? a.wallet, Y = o?.wallets ?? a.wallets, $ = o ? o.select : (t) => a.select(t), _ = o?.connect ?? a.connect, z = Y.filter(
90
+ (t) => t.adapter.readyState === "Installed" || t.adapter.readyState === "Loadable"
91
91
  ), N = W(async () => {
92
92
  if (!E.current) {
93
93
  if (!p || !y) {
94
- t?.(new Error("Wallet not ready"));
94
+ n?.(new Error("Wallet not ready"));
95
95
  return;
96
96
  }
97
97
  E.current = !0;
98
98
  try {
99
- const n = p.toBase58(), I = await L(n), J = new TextEncoder().encode(I.message), B = await y(J);
99
+ const t = p.toBase58(), I = await L(t), Q = new TextEncoder().encode(I.message), B = await y(Q);
100
100
  if (!(B instanceof Uint8Array) || B.length === 0)
101
101
  throw new Error("Wallet returned invalid signature");
102
- let U;
102
+ let q;
103
103
  try {
104
- U = btoa(String.fromCharCode(...B));
104
+ q = btoa(String.fromCharCode(...B));
105
105
  } catch {
106
106
  throw new Error("Failed to encode signature");
107
107
  }
108
- await d(n, U, I.message), e?.();
109
- } catch (n) {
110
- const I = n instanceof Error ? n : new Error(String(n));
111
- t?.(I);
108
+ await d(t, q, I.message), e?.();
109
+ } catch (t) {
110
+ const I = t instanceof Error ? t : new Error(String(t));
111
+ n?.(I);
112
112
  } finally {
113
113
  E.current = !1, h(!1);
114
114
  }
115
115
  }
116
- }, [p, y, L, d, e, t]);
116
+ }, [p, y, L, d, e, n]);
117
117
  if (M(() => {
118
- K && b && !u && !m && (P(!1), T().catch((n) => {
119
- t?.(n instanceof Error ? n : new Error(String(n))), h(!1);
118
+ T && w && !u && !m && (P(!1), _().catch((t) => {
119
+ n?.(t instanceof Error ? t : new Error(String(t))), h(!1);
120
120
  }));
121
- }, [K, b, u, m, T, t]), M(() => {
121
+ }, [T, w, u, m, _, n]), M(() => {
122
122
  g && u && p && y && !E.current && N().catch(() => {
123
123
  });
124
124
  }, [g, u, p, y, N]), M(() => {
125
- r ? k.current = !0 : k.current && (k.current = !1, g && !u && b && !m ? P(!0) : g && !u && !b && h(!1));
126
- }, [r, g, u, b, m]), f && z.length === 0)
125
+ if (r)
126
+ k.current = !0, U.current = w?.adapter.name ?? null;
127
+ else if (k.current) {
128
+ k.current = !1;
129
+ const t = (w?.adapter.name ?? null) !== U.current;
130
+ g && !u && w && t && !m ? P(!0) : g && !u && h(!1);
131
+ }
132
+ }, [r, g, u, w, m]), f && z.length === 0)
127
133
  return null;
128
134
  const j = async () => {
129
135
  l || i || m || (u && p && y ? (h(!0), await N()) : z.length === 1 ? ($(z[0].adapter.name), h(!0), P(!0)) : (C(!0), h(!0)));
130
- }, O = {
136
+ }, G = {
131
137
  sm: "cedros-button-sm",
132
138
  md: "cedros-button-md",
133
139
  lg: "cedros-button-lg"
134
- }, G = {
140
+ }, J = {
135
141
  default: "cedros-button-social",
136
142
  outline: "cedros-button-social-outline"
137
143
  }, A = i || m || g && !u;
138
144
  return M(() => {
139
145
  S?.(A);
140
- }, [A, S]), /* @__PURE__ */ _(
146
+ }, [A, S]), /* @__PURE__ */ F(
141
147
  "button",
142
148
  {
143
149
  type: "button",
144
- className: `cedros-button ${G[s]} ${O[D]} ${w}`,
150
+ className: `cedros-button ${J[s]} ${G[D]} ${b}`,
145
151
  onClick: j,
146
152
  disabled: l || A,
147
153
  "aria-label": "Continue with Solana",
148
154
  children: [
149
- A ? /* @__PURE__ */ c(ne, { size: "sm" }) : /* @__PURE__ */ _(
155
+ A ? /* @__PURE__ */ c(ae, { size: "sm" }) : /* @__PURE__ */ F(
150
156
  "svg",
151
157
  {
152
158
  className: "cedros-button-icon",
@@ -167,25 +173,25 @@ function R({
167
173
  }
168
174
  );
169
175
  }
170
- function ge(e) {
176
+ function he(e) {
171
177
  if (typeof window > "u")
172
178
  return !1;
173
179
  try {
174
- const t = require("@solana-mobile/wallet-standard-mobile"), w = e?.chains ?? ["solana:mainnet"], s = {
180
+ const n = require("@solana-mobile/wallet-standard-mobile"), b = e?.chains ?? ["solana:mainnet"], s = {
175
181
  appIdentity: {
176
182
  name: e?.name,
177
183
  uri: e?.uri,
178
184
  icon: e?.icon
179
185
  },
180
- chains: w
186
+ chains: b
181
187
  };
182
- return typeof t.createDefaultAuthorizationCache == "function" && (s.authorizationCache = t.createDefaultAuthorizationCache()), typeof t.createDefaultChainSelector == "function" && (s.chainSelector = t.createDefaultChainSelector()), typeof t.createDefaultWalletNotFoundHandler == "function" && (s.onWalletNotFound = t.createDefaultWalletNotFoundHandler()), t.registerMwa(s), !0;
188
+ return typeof n.createDefaultAuthorizationCache == "function" && (s.authorizationCache = n.createDefaultAuthorizationCache()), typeof n.createDefaultChainSelector == "function" && (s.chainSelector = n.createDefaultChainSelector()), typeof n.createDefaultWalletNotFoundHandler == "function" && (s.onWalletNotFound = n.createDefaultWalletNotFoundHandler()), n.registerMwa(s), !0;
183
189
  } catch {
184
190
  return !1;
185
191
  }
186
192
  }
187
193
  export {
188
- fe as S,
189
- ge as r,
190
- ae as u
194
+ ge as S,
195
+ he as r,
196
+ re as u
191
197
  };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobileWalletAdapter-DMWmo9II.js","sources":["../src/hooks/useSolanaAuth.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { WalletProvider, useWallet } from '@solana/wallet-adapter-react';\nimport { WalletModalProvider, useWalletModal } from '@solana/wallet-adapter-react-ui';\nimport type { WalletName } from '@solana/wallet-adapter-base';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /** Called when the button's loading state changes (connecting, signing, etc.). */\n onLoadingChange?: (loading: boolean) => void;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * When provided, the component assumes a WalletProvider exists in the React tree and\n * uses the consumer's wallet context for wallet discovery and connection.\n * When omitted, the component provides its own WalletProvider with wallet-standard discovery.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/** Stable empty array to avoid re-renders in self-contained WalletProvider. */\nconst EMPTY_ADAPTERS: [] = [];\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Uses the standard wallet adapter modal for wallet selection, which provides\n * real brand icons and discovers all wallet-standard-compliant wallets.\n *\n * When `walletContext` is provided, assumes a WalletProvider exists in the tree.\n * Otherwise, wraps itself with WalletProvider for self-contained operation.\n */\nexport function SolanaLoginButton(props: SolanaLoginButtonProps) {\n if (props.walletContext) {\n // Consumer has their own WalletProvider; just add modal capability\n return (\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n );\n }\n\n // Self-contained: provide wallet-standard discovery + modal\n return (\n <WalletProvider wallets={EMPTY_ADAPTERS} localStorageKey=\"cedros-walletName\">\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n </WalletProvider>\n );\n}\n\nfunction SolanaLoginInner({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n onLoadingChange,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const adapterWallet = useWallet();\n const { visible: modalVisible, setVisible: setModalVisible } = useWalletModal();\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n const modalWasOpen = useRef(false);\n const walletOnModalOpen = useRef<string | null>(null);\n\n // Use walletContext if provided, otherwise use adapter's useWallet()\n const connected = walletContext?.connected ?? adapterWallet.connected;\n const connecting = walletContext?.connecting ?? adapterWallet.connecting;\n const publicKey = walletContext?.publicKey ?? adapterWallet.publicKey;\n const signMessage = walletContext?.signMessage ?? adapterWallet.signMessage;\n const wallet = walletContext?.wallet ?? adapterWallet.wallet;\n const wallets = walletContext?.wallets ?? adapterWallet.wallets;\n const select = walletContext\n ? walletContext.select\n : (name: string) => adapterWallet.select(name as WalletName);\n const connect = walletContext?.connect ?? adapterWallet.connect;\n\n // Get installed/ready wallets\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Execute the sign-in flow (challenge → sign → verify)\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n const challenge = await requestChallenge(pubKeyString);\n\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n await signIn(pubKeyString, signature, challenge.message);\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting) {\n setTriggerConnect(false);\n connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, connect, onError]);\n\n // Auto-execute sign-in when connected with pending login\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // When modal closes: connect if a NEW wallet was selected, else reset\n useEffect(() => {\n if (modalVisible) {\n modalWasOpen.current = true;\n walletOnModalOpen.current = wallet?.adapter.name ?? null;\n } else if (modalWasOpen.current) {\n modalWasOpen.current = false;\n const walletChanged = (wallet?.adapter.name ?? null) !== walletOnModalOpen.current;\n if (pendingLogin && !connected && wallet && walletChanged && !connecting) {\n // User selected a different wallet in the modal — trigger connection\n setTriggerConnect(true);\n } else if (pendingLogin && !connected) {\n // Modal dismissed without selecting a new wallet\n setPendingLogin(false);\n }\n }\n }, [modalVisible, pendingLogin, connected, wallet, connecting]);\n\n // Hide button if no wallets detected\n if (hideIfNoWallet && installedWallets.length === 0) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected — sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (installedWallets.length === 1) {\n // Single installed wallet — auto-select + connect\n select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else {\n // Multiple or zero wallets — open standard wallet modal\n // Always show modal even if a wallet was previously selected (via localStorage),\n // so each login attempt lets the user choose.\n setModalVisible(true);\n setPendingLogin(true);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n // Notify parent of loading state changes\n useEffect(() => {\n onLoadingChange?.(isLoading);\n }, [isLoading, onLoadingChange]);\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","EMPTY_ADAPTERS","SolanaLoginButton","props","WalletModalProvider","jsx","SolanaLoginInner","WalletProvider","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","onLoadingChange","walletContext","isAuthLoading","adapterWallet","useWallet","modalVisible","setModalVisible","useWalletModal","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","modalWasOpen","walletOnModalOpen","connected","connecting","signMessage","wallet","wallets","select","name","connect","installedWallets","w","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","walletChanged","handleClick","sizeClasses","variantClasses","jsxs","LoadingSpinner","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ "use strict";const l=require("react/jsx-runtime"),a=require("react"),T=require("@solana/wallet-adapter-react"),D=require("@solana/wallet-adapter-react-ui"),I=require("./useCedrosLogin-C9MrcZvh.cjs"),K=require("./validation-BuGQrA-K.cjs"),G=require("./LoadingSpinner-d6sSxgQN.cjs");function _(){const{config:e,_internal:n}=I.useCedrosLogin(),[S,o]=a.useState(!1),[W,i]=a.useState(null),g=a.useMemo(()=>new I.ApiClient({baseUrl:e.serverUrl,timeoutMs:e.requestTimeout,retryAttempts:e.retryAttempts}),[e.serverUrl,e.requestTimeout,e.retryAttempts]),L=a.useCallback(async f=>{if(!K.validateSolanaPublicKey(f)){const u={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw i(u),u}o(!0),i(null);try{return await g.post("/solana/challenge",{publicKey:f},{credentials:"omit"})}catch(u){const r=I.handleApiError(u,"Failed to get challenge");throw i(r),r}finally{o(!1)}},[g]),c=a.useCallback(async(f,u,r)=>{if(!K.validateSolanaPublicKey(f)){const s={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw i(s),s}o(!0),i(null);try{const s=await g.post("/solana",{publicKey:f,signature:u,message:r});return e.callbacks?.onLoginSuccess?.(s.user,"solana"),n?.handleLoginSuccess(s.user,s.tokens),s}catch(s){const E=I.handleApiError(s,"Solana sign-in failed");throw i(E),E}finally{o(!1)}},[g,e.callbacks,n]),C=a.useCallback(()=>i(null),[]);return{requestChallenge:L,signIn:c,isLoading:S,error:W,clearError:C}}const J=[];function Q(e){return e.walletContext?l.jsx(D.WalletModalProvider,{children:l.jsx(U,{...e})}):l.jsx(T.WalletProvider,{wallets:J,localStorageKey:"cedros-walletName",children:l.jsx(D.WalletModalProvider,{children:l.jsx(U,{...e})})})}function U({onSuccess:e,onError:n,className:S="",variant:o="default",size:W="md",disabled:i=!1,hideIfNoWallet:g=!0,onLoadingChange:L,walletContext:c}){const{requestChallenge:C,signIn:f,isLoading:u}=_(),r=T.useWallet(),{visible:s,setVisible:E}=D.useWalletModal(),[h,m]=a.useState(!1),[z,j]=a.useState(!1),A=a.useRef(!1),q=a.useRef(!1),B=a.useRef(null),d=c?.connected??r.connected,y=c?.connecting??r.connecting,p=c?.publicKey??r.publicKey,b=c?.signMessage??r.signMessage,w=c?.wallet??r.wallet,F=c?.wallets??r.wallets,H=c?c.select:t=>r.select(t),N=c?.connect??r.connect,k=F.filter(t=>t.adapter.readyState==="Installed"||t.adapter.readyState==="Loadable"),P=a.useCallback(async()=>{if(!A.current){if(!p||!b){n?.(new Error("Wallet not ready"));return}A.current=!0;try{const t=p.toBase58(),v=await C(t),$=new TextEncoder().encode(v.message),x=await b($);if(!(x instanceof Uint8Array)||x.length===0)throw new Error("Wallet returned invalid signature");let R;try{R=btoa(String.fromCharCode(...x))}catch{throw new Error("Failed to encode signature")}await f(t,R,v.message),e?.()}catch(t){const v=t instanceof Error?t:new Error(String(t));n?.(v)}finally{A.current=!1,m(!1)}}},[p,b,C,f,e,n]);if(a.useEffect(()=>{z&&w&&!d&&!y&&(j(!1),N().catch(t=>{n?.(t instanceof Error?t:new Error(String(t))),m(!1)}))},[z,w,d,y,N,n]),a.useEffect(()=>{h&&d&&p&&b&&!A.current&&P().catch(()=>{})},[h,d,p,b,P]),a.useEffect(()=>{if(s)q.current=!0,B.current=w?.adapter.name??null;else if(q.current){q.current=!1;const t=(w?.adapter.name??null)!==B.current;h&&!d&&w&&t&&!y?j(!0):h&&!d&&m(!1)}},[s,h,d,w,y]),g&&k.length===0)return null;const V=async()=>{i||u||y||(d&&p&&b?(m(!0),await P()):k.length===1?(H(k[0].adapter.name),m(!0),j(!0)):(E(!0),m(!0)))},O={sm:"cedros-button-sm",md:"cedros-button-md",lg:"cedros-button-lg"},Y={default:"cedros-button-social",outline:"cedros-button-social-outline"},M=u||y||h&&!d;return a.useEffect(()=>{L?.(M)},[M,L]),l.jsxs("button",{type:"button",className:`cedros-button ${Y[o]} ${O[W]} ${S}`,onClick:V,disabled:i||M,"aria-label":"Continue with Solana",children:[M?l.jsx(G.LoadingSpinner,{size:"sm"}):l.jsxs("svg",{className:"cedros-button-icon",width:"18",height:"18",viewBox:"0 0 128 128",fill:"currentColor","aria-hidden":"true",children:[l.jsx("path",{d:"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z"}),l.jsx("path",{d:"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z"}),l.jsx("path",{d:"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z"})]}),l.jsx("span",{children:"Continue with Solana"})]})}function X(e){if(typeof window>"u")return!1;try{const n=require("@solana-mobile/wallet-standard-mobile"),S=e?.chains??["solana:mainnet"],o={appIdentity:{name:e?.name,uri:e?.uri,icon:e?.icon},chains:S};return typeof n.createDefaultAuthorizationCache=="function"&&(o.authorizationCache=n.createDefaultAuthorizationCache()),typeof n.createDefaultChainSelector=="function"&&(o.chainSelector=n.createDefaultChainSelector()),typeof n.createDefaultWalletNotFoundHandler=="function"&&(o.onWalletNotFound=n.createDefaultWalletNotFoundHandler()),n.registerMwa(o),!0}catch{return!1}}exports.SolanaLoginButton=Q;exports.registerMobileWallet=X;exports.useSolanaAuth=_;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobileWalletAdapter-qENF5Yeh.cjs","sources":["../src/hooks/useSolanaAuth.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { WalletProvider, useWallet } from '@solana/wallet-adapter-react';\nimport { WalletModalProvider, useWalletModal } from '@solana/wallet-adapter-react-ui';\nimport type { WalletName } from '@solana/wallet-adapter-base';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /** Called when the button's loading state changes (connecting, signing, etc.). */\n onLoadingChange?: (loading: boolean) => void;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * When provided, the component assumes a WalletProvider exists in the React tree and\n * uses the consumer's wallet context for wallet discovery and connection.\n * When omitted, the component provides its own WalletProvider with wallet-standard discovery.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/** Stable empty array to avoid re-renders in self-contained WalletProvider. */\nconst EMPTY_ADAPTERS: [] = [];\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Uses the standard wallet adapter modal for wallet selection, which provides\n * real brand icons and discovers all wallet-standard-compliant wallets.\n *\n * When `walletContext` is provided, assumes a WalletProvider exists in the tree.\n * Otherwise, wraps itself with WalletProvider for self-contained operation.\n */\nexport function SolanaLoginButton(props: SolanaLoginButtonProps) {\n if (props.walletContext) {\n // Consumer has their own WalletProvider; just add modal capability\n return (\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n );\n }\n\n // Self-contained: provide wallet-standard discovery + modal\n return (\n <WalletProvider wallets={EMPTY_ADAPTERS} localStorageKey=\"cedros-walletName\">\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n </WalletProvider>\n );\n}\n\nfunction SolanaLoginInner({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n onLoadingChange,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const adapterWallet = useWallet();\n const { visible: modalVisible, setVisible: setModalVisible } = useWalletModal();\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n const modalWasOpen = useRef(false);\n const walletOnModalOpen = useRef<string | null>(null);\n\n // Use walletContext if provided, otherwise use adapter's useWallet()\n const connected = walletContext?.connected ?? adapterWallet.connected;\n const connecting = walletContext?.connecting ?? adapterWallet.connecting;\n const publicKey = walletContext?.publicKey ?? adapterWallet.publicKey;\n const signMessage = walletContext?.signMessage ?? adapterWallet.signMessage;\n const wallet = walletContext?.wallet ?? adapterWallet.wallet;\n const wallets = walletContext?.wallets ?? adapterWallet.wallets;\n const select = walletContext\n ? walletContext.select\n : (name: string) => adapterWallet.select(name as WalletName);\n const connect = walletContext?.connect ?? adapterWallet.connect;\n\n // Get installed/ready wallets\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Execute the sign-in flow (challenge → sign → verify)\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n const challenge = await requestChallenge(pubKeyString);\n\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n await signIn(pubKeyString, signature, challenge.message);\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting) {\n setTriggerConnect(false);\n connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, connect, onError]);\n\n // Auto-execute sign-in when connected with pending login\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // When modal closes: connect if a NEW wallet was selected, else reset\n useEffect(() => {\n if (modalVisible) {\n modalWasOpen.current = true;\n walletOnModalOpen.current = wallet?.adapter.name ?? null;\n } else if (modalWasOpen.current) {\n modalWasOpen.current = false;\n const walletChanged = (wallet?.adapter.name ?? null) !== walletOnModalOpen.current;\n if (pendingLogin && !connected && wallet && walletChanged && !connecting) {\n // User selected a different wallet in the modal — trigger connection\n setTriggerConnect(true);\n } else if (pendingLogin && !connected) {\n // Modal dismissed without selecting a new wallet\n setPendingLogin(false);\n }\n }\n }, [modalVisible, pendingLogin, connected, wallet, connecting]);\n\n // Hide button if no wallets detected\n if (hideIfNoWallet && installedWallets.length === 0) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected — sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (installedWallets.length === 1) {\n // Single installed wallet — auto-select + connect\n select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else {\n // Multiple or zero wallets — open standard wallet modal\n // Always show modal even if a wallet was previously selected (via localStorage),\n // so each login attempt lets the user choose.\n setModalVisible(true);\n setPendingLogin(true);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n // Notify parent of loading state changes\n useEffect(() => {\n onLoadingChange?.(isLoading);\n }, [isLoading, onLoadingChange]);\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","EMPTY_ADAPTERS","SolanaLoginButton","props","WalletModalProvider","jsx","SolanaLoginInner","WalletProvider","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","onLoadingChange","walletContext","isAuthLoading","adapterWallet","useWallet","modalVisible","setModalVisible","useWalletModal","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","modalWasOpen","walletOnModalOpen","connected","connecting","signMessage","wallet","wallets","select","name","connect","installedWallets","w","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","walletChanged","handleClick","sizeClasses","variantClasses","jsxs","LoadingSpinner","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./useAuth-X6Ds6WW4.cjs"),o=require("./useCedrosLogin-C9MrcZvh.cjs"),e=require("./mobileWalletAdapter-CgBgAysP.cjs"),n=require("./LoadingSpinner-d6sSxgQN.cjs"),i=require("./ErrorMessage-CHbYbVi2.cjs");exports.CedrosLoginProvider=r.CedrosLoginProvider;exports.useAuth=r.useAuth;exports.useCedrosLogin=o.useCedrosLogin;exports.SolanaLoginButton=e.SolanaLoginButton;exports.registerMobileWallet=e.registerMobileWallet;exports.useSolanaAuth=e.useSolanaAuth;exports.LoadingSpinner=n.LoadingSpinner;exports.ErrorMessage=i.ErrorMessage;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./useAuth-X6Ds6WW4.cjs"),o=require("./useCedrosLogin-C9MrcZvh.cjs"),e=require("./mobileWalletAdapter-qENF5Yeh.cjs"),n=require("./LoadingSpinner-d6sSxgQN.cjs"),i=require("./ErrorMessage-CHbYbVi2.cjs");exports.CedrosLoginProvider=r.CedrosLoginProvider;exports.useAuth=r.useAuth;exports.useCedrosLogin=o.useCedrosLogin;exports.SolanaLoginButton=e.SolanaLoginButton;exports.registerMobileWallet=e.registerMobileWallet;exports.useSolanaAuth=e.useSolanaAuth;exports.LoadingSpinner=n.LoadingSpinner;exports.ErrorMessage=i.ErrorMessage;
@@ -1,6 +1,6 @@
1
1
  import { C as e, u as s } from "./useAuth-m5Hf89v8.js";
2
2
  import { u as t } from "./useCedrosLogin-_94MmGGq.js";
3
- import { S as u, r as i, u as g } from "./mobileWalletAdapter-orQcBp-b.js";
3
+ import { S as u, r as i, u as g } from "./mobileWalletAdapter-DMWmo9II.js";
4
4
  import { L as f } from "./LoadingSpinner-6vml-zwr.js";
5
5
  import { E as m } from "./ErrorMessage-CcEK0pYO.js";
6
6
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cedros/login-react",
3
- "version": "0.0.25",
3
+ "version": "0.0.26",
4
4
  "description": "React component library for authentication with email/password, Google OAuth, and Solana wallet sign-in",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1 +0,0 @@
1
- "use strict";const l=require("react/jsx-runtime"),a=require("react"),U=require("@solana/wallet-adapter-react"),D=require("@solana/wallet-adapter-react-ui"),I=require("./useCedrosLogin-C9MrcZvh.cjs"),K=require("./validation-BuGQrA-K.cjs"),O=require("./LoadingSpinner-d6sSxgQN.cjs");function T(){const{config:e,_internal:t}=I.useCedrosLogin(),[p,o]=a.useState(!1),[W,c]=a.useState(null),g=a.useMemo(()=>new I.ApiClient({baseUrl:e.serverUrl,timeoutMs:e.requestTimeout,retryAttempts:e.retryAttempts}),[e.serverUrl,e.requestTimeout,e.retryAttempts]),L=a.useCallback(async f=>{if(!K.validateSolanaPublicKey(f)){const u={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw c(u),u}o(!0),c(null);try{return await g.post("/solana/challenge",{publicKey:f},{credentials:"omit"})}catch(u){const r=I.handleApiError(u,"Failed to get challenge");throw c(r),r}finally{o(!1)}},[g]),i=a.useCallback(async(f,u,r)=>{if(!K.validateSolanaPublicKey(f)){const s={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw c(s),s}o(!0),c(null);try{const s=await g.post("/solana",{publicKey:f,signature:u,message:r});return e.callbacks?.onLoginSuccess?.(s.user,"solana"),t?.handleLoginSuccess(s.user,s.tokens),s}catch(s){const E=I.handleApiError(s,"Solana sign-in failed");throw c(E),E}finally{o(!1)}},[g,e.callbacks,t]),C=a.useCallback(()=>c(null),[]);return{requestChallenge:L,signIn:i,isLoading:p,error:W,clearError:C}}const G=[];function J(e){return e.walletContext?l.jsx(D.WalletModalProvider,{children:l.jsx(R,{...e})}):l.jsx(U.WalletProvider,{wallets:G,localStorageKey:"cedros-walletName",children:l.jsx(D.WalletModalProvider,{children:l.jsx(R,{...e})})})}function R({onSuccess:e,onError:t,className:p="",variant:o="default",size:W="md",disabled:c=!1,hideIfNoWallet:g=!0,onLoadingChange:L,walletContext:i}){const{requestChallenge:C,signIn:f,isLoading:u}=T(),r=U.useWallet(),{visible:s,setVisible:E}=D.useWalletModal(),[h,y]=a.useState(!1),[z,j]=a.useState(!1),A=a.useRef(!1),q=a.useRef(!1),d=i?.connected??r.connected,m=i?.connecting??r.connecting,b=i?.publicKey??r.publicKey,S=i?.signMessage??r.signMessage,w=i?.wallet??r.wallet,_=i?.wallets??r.wallets,F=i?i.select:n=>r.select(n),B=i?.connect??r.connect,k=_.filter(n=>n.adapter.readyState==="Installed"||n.adapter.readyState==="Loadable"),P=a.useCallback(async()=>{if(!A.current){if(!b||!S){t?.(new Error("Wallet not ready"));return}A.current=!0;try{const n=b.toBase58(),v=await C(n),$=new TextEncoder().encode(v.message),x=await S($);if(!(x instanceof Uint8Array)||x.length===0)throw new Error("Wallet returned invalid signature");let N;try{N=btoa(String.fromCharCode(...x))}catch{throw new Error("Failed to encode signature")}await f(n,N,v.message),e?.()}catch(n){const v=n instanceof Error?n:new Error(String(n));t?.(v)}finally{A.current=!1,y(!1)}}},[b,S,C,f,e,t]);if(a.useEffect(()=>{z&&w&&!d&&!m&&(j(!1),B().catch(n=>{t?.(n instanceof Error?n:new Error(String(n))),y(!1)}))},[z,w,d,m,B,t]),a.useEffect(()=>{h&&d&&b&&S&&!A.current&&P().catch(()=>{})},[h,d,b,S,P]),a.useEffect(()=>{s?q.current=!0:q.current&&(q.current=!1,h&&!d&&w&&!m?j(!0):h&&!d&&!w&&y(!1))},[s,h,d,w,m]),g&&k.length===0)return null;const H=async()=>{c||u||m||(d&&b&&S?(y(!0),await P()):k.length===1?(F(k[0].adapter.name),y(!0),j(!0)):(E(!0),y(!0)))},V={sm:"cedros-button-sm",md:"cedros-button-md",lg:"cedros-button-lg"},Y={default:"cedros-button-social",outline:"cedros-button-social-outline"},M=u||m||h&&!d;return a.useEffect(()=>{L?.(M)},[M,L]),l.jsxs("button",{type:"button",className:`cedros-button ${Y[o]} ${V[W]} ${p}`,onClick:H,disabled:c||M,"aria-label":"Continue with Solana",children:[M?l.jsx(O.LoadingSpinner,{size:"sm"}):l.jsxs("svg",{className:"cedros-button-icon",width:"18",height:"18",viewBox:"0 0 128 128",fill:"currentColor","aria-hidden":"true",children:[l.jsx("path",{d:"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z"}),l.jsx("path",{d:"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z"}),l.jsx("path",{d:"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z"})]}),l.jsx("span",{children:"Continue with Solana"})]})}function Q(e){if(typeof window>"u")return!1;try{const t=require("@solana-mobile/wallet-standard-mobile"),p=e?.chains??["solana:mainnet"],o={appIdentity:{name:e?.name,uri:e?.uri,icon:e?.icon},chains:p};return typeof t.createDefaultAuthorizationCache=="function"&&(o.authorizationCache=t.createDefaultAuthorizationCache()),typeof t.createDefaultChainSelector=="function"&&(o.chainSelector=t.createDefaultChainSelector()),typeof t.createDefaultWalletNotFoundHandler=="function"&&(o.onWalletNotFound=t.createDefaultWalletNotFoundHandler()),t.registerMwa(o),!0}catch{return!1}}exports.SolanaLoginButton=J;exports.registerMobileWallet=Q;exports.useSolanaAuth=T;
@@ -1 +0,0 @@
1
- {"version":3,"file":"mobileWalletAdapter-CgBgAysP.cjs","sources":["../src/hooks/useSolanaAuth.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { WalletProvider, useWallet } from '@solana/wallet-adapter-react';\nimport { WalletModalProvider, useWalletModal } from '@solana/wallet-adapter-react-ui';\nimport type { WalletName } from '@solana/wallet-adapter-base';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /** Called when the button's loading state changes (connecting, signing, etc.). */\n onLoadingChange?: (loading: boolean) => void;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * When provided, the component assumes a WalletProvider exists in the React tree and\n * uses the consumer's wallet context for wallet discovery and connection.\n * When omitted, the component provides its own WalletProvider with wallet-standard discovery.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/** Stable empty array to avoid re-renders in self-contained WalletProvider. */\nconst EMPTY_ADAPTERS: [] = [];\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Uses the standard wallet adapter modal for wallet selection, which provides\n * real brand icons and discovers all wallet-standard-compliant wallets.\n *\n * When `walletContext` is provided, assumes a WalletProvider exists in the tree.\n * Otherwise, wraps itself with WalletProvider for self-contained operation.\n */\nexport function SolanaLoginButton(props: SolanaLoginButtonProps) {\n if (props.walletContext) {\n // Consumer has their own WalletProvider; just add modal capability\n return (\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n );\n }\n\n // Self-contained: provide wallet-standard discovery + modal\n return (\n <WalletProvider wallets={EMPTY_ADAPTERS} localStorageKey=\"cedros-walletName\">\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n </WalletProvider>\n );\n}\n\nfunction SolanaLoginInner({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n onLoadingChange,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const adapterWallet = useWallet();\n const { visible: modalVisible, setVisible: setModalVisible } = useWalletModal();\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n const modalWasOpen = useRef(false);\n\n // Use walletContext if provided, otherwise use adapter's useWallet()\n const connected = walletContext?.connected ?? adapterWallet.connected;\n const connecting = walletContext?.connecting ?? adapterWallet.connecting;\n const publicKey = walletContext?.publicKey ?? adapterWallet.publicKey;\n const signMessage = walletContext?.signMessage ?? adapterWallet.signMessage;\n const wallet = walletContext?.wallet ?? adapterWallet.wallet;\n const wallets = walletContext?.wallets ?? adapterWallet.wallets;\n const select = walletContext\n ? walletContext.select\n : (name: string) => adapterWallet.select(name as WalletName);\n const connect = walletContext?.connect ?? adapterWallet.connect;\n\n // Get installed/ready wallets\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Execute the sign-in flow (challenge → sign → verify)\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n const challenge = await requestChallenge(pubKeyString);\n\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n await signIn(pubKeyString, signature, challenge.message);\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting) {\n setTriggerConnect(false);\n connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, connect, onError]);\n\n // Auto-execute sign-in when connected with pending login\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // When modal closes: connect if a wallet was selected, else reset\n useEffect(() => {\n if (modalVisible) {\n modalWasOpen.current = true;\n } else if (modalWasOpen.current) {\n modalWasOpen.current = false;\n if (pendingLogin && !connected && wallet && !connecting) {\n // Wallet was selected in modal — trigger connection\n setTriggerConnect(true);\n } else if (pendingLogin && !connected && !wallet) {\n // Modal dismissed without selecting a wallet\n setPendingLogin(false);\n }\n }\n }, [modalVisible, pendingLogin, connected, wallet, connecting]);\n\n // Hide button if no wallets detected\n if (hideIfNoWallet && installedWallets.length === 0) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected — sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (installedWallets.length === 1) {\n // Single installed wallet — auto-select + connect\n select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else {\n // Multiple or zero wallets — open standard wallet modal\n // Always show modal even if a wallet was previously selected (via localStorage),\n // so each login attempt lets the user choose.\n setModalVisible(true);\n setPendingLogin(true);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n // Notify parent of loading state changes\n useEffect(() => {\n onLoadingChange?.(isLoading);\n }, [isLoading, onLoadingChange]);\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","EMPTY_ADAPTERS","SolanaLoginButton","props","WalletModalProvider","jsx","SolanaLoginInner","WalletProvider","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","onLoadingChange","walletContext","isAuthLoading","adapterWallet","useWallet","modalVisible","setModalVisible","useWalletModal","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","modalWasOpen","connected","connecting","signMessage","wallet","wallets","select","name","connect","installedWallets","w","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","sizeClasses","variantClasses","jsxs","LoadingSpinner","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mobileWalletAdapter-orQcBp-b.js","sources":["../src/hooks/useSolanaAuth.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { WalletProvider, useWallet } from '@solana/wallet-adapter-react';\nimport { WalletModalProvider, useWalletModal } from '@solana/wallet-adapter-react-ui';\nimport type { WalletName } from '@solana/wallet-adapter-base';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /** Called when the button's loading state changes (connecting, signing, etc.). */\n onLoadingChange?: (loading: boolean) => void;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * When provided, the component assumes a WalletProvider exists in the React tree and\n * uses the consumer's wallet context for wallet discovery and connection.\n * When omitted, the component provides its own WalletProvider with wallet-standard discovery.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/** Stable empty array to avoid re-renders in self-contained WalletProvider. */\nconst EMPTY_ADAPTERS: [] = [];\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Uses the standard wallet adapter modal for wallet selection, which provides\n * real brand icons and discovers all wallet-standard-compliant wallets.\n *\n * When `walletContext` is provided, assumes a WalletProvider exists in the tree.\n * Otherwise, wraps itself with WalletProvider for self-contained operation.\n */\nexport function SolanaLoginButton(props: SolanaLoginButtonProps) {\n if (props.walletContext) {\n // Consumer has their own WalletProvider; just add modal capability\n return (\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n );\n }\n\n // Self-contained: provide wallet-standard discovery + modal\n return (\n <WalletProvider wallets={EMPTY_ADAPTERS} localStorageKey=\"cedros-walletName\">\n <WalletModalProvider>\n <SolanaLoginInner {...props} />\n </WalletModalProvider>\n </WalletProvider>\n );\n}\n\nfunction SolanaLoginInner({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n onLoadingChange,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const adapterWallet = useWallet();\n const { visible: modalVisible, setVisible: setModalVisible } = useWalletModal();\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n const modalWasOpen = useRef(false);\n\n // Use walletContext if provided, otherwise use adapter's useWallet()\n const connected = walletContext?.connected ?? adapterWallet.connected;\n const connecting = walletContext?.connecting ?? adapterWallet.connecting;\n const publicKey = walletContext?.publicKey ?? adapterWallet.publicKey;\n const signMessage = walletContext?.signMessage ?? adapterWallet.signMessage;\n const wallet = walletContext?.wallet ?? adapterWallet.wallet;\n const wallets = walletContext?.wallets ?? adapterWallet.wallets;\n const select = walletContext\n ? walletContext.select\n : (name: string) => adapterWallet.select(name as WalletName);\n const connect = walletContext?.connect ?? adapterWallet.connect;\n\n // Get installed/ready wallets\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Execute the sign-in flow (challenge → sign → verify)\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n const challenge = await requestChallenge(pubKeyString);\n\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n await signIn(pubKeyString, signature, challenge.message);\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting) {\n setTriggerConnect(false);\n connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, connect, onError]);\n\n // Auto-execute sign-in when connected with pending login\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // When modal closes: connect if a wallet was selected, else reset\n useEffect(() => {\n if (modalVisible) {\n modalWasOpen.current = true;\n } else if (modalWasOpen.current) {\n modalWasOpen.current = false;\n if (pendingLogin && !connected && wallet && !connecting) {\n // Wallet was selected in modal — trigger connection\n setTriggerConnect(true);\n } else if (pendingLogin && !connected && !wallet) {\n // Modal dismissed without selecting a wallet\n setPendingLogin(false);\n }\n }\n }, [modalVisible, pendingLogin, connected, wallet, connecting]);\n\n // Hide button if no wallets detected\n if (hideIfNoWallet && installedWallets.length === 0) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected — sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (installedWallets.length === 1) {\n // Single installed wallet — auto-select + connect\n select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else {\n // Multiple or zero wallets — open standard wallet modal\n // Always show modal even if a wallet was previously selected (via localStorage),\n // so each login attempt lets the user choose.\n setModalVisible(true);\n setPendingLogin(true);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n // Notify parent of loading state changes\n useEffect(() => {\n onLoadingChange?.(isLoading);\n }, [isLoading, onLoadingChange]);\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","EMPTY_ADAPTERS","SolanaLoginButton","props","WalletModalProvider","jsx","SolanaLoginInner","WalletProvider","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","onLoadingChange","walletContext","isAuthLoading","adapterWallet","useWallet","modalVisible","setModalVisible","useWalletModal","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","modalWasOpen","connected","connecting","signMessage","wallet","wallets","select","name","connect","installedWallets","w","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","sizeClasses","variantClasses","jsxs","LoadingSpinner","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}