@cedros/login-react 0.0.30 → 0.0.31
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/GoogleLoginButton-BwiL1o6U.cjs +1 -0
- package/dist/GoogleLoginButton-BwiL1o6U.cjs.map +1 -0
- package/dist/{GoogleLoginButton-DEbiQngr.js → GoogleLoginButton-CFlqxvU-.js} +94 -107
- package/dist/GoogleLoginButton-CFlqxvU-.js.map +1 -0
- package/dist/SolanaLoginButton-DHsiug1Y.js +195 -0
- package/dist/SolanaLoginButton-DHsiug1Y.js.map +1 -0
- package/dist/SolanaLoginButton-DNEcSSF_.cjs +1 -0
- package/dist/SolanaLoginButton-DNEcSSF_.cjs.map +1 -0
- package/dist/google-only.cjs +1 -1
- package/dist/google-only.js +1 -1
- package/dist/index.cjs +12 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +665 -670
- package/dist/index.js.map +1 -1
- package/dist/solana-only.cjs +1 -1
- package/dist/solana-only.d.ts +2 -2
- package/dist/solana-only.js +1 -1
- package/package.json +1 -1
- package/dist/GoogleLoginButton-CjBO3Rf1.cjs +0 -1
- package/dist/GoogleLoginButton-CjBO3Rf1.cjs.map +0 -1
- package/dist/GoogleLoginButton-DEbiQngr.js.map +0 -1
- package/dist/SolanaLoginButton-DAV3r4oB.cjs +0 -1
- package/dist/SolanaLoginButton-DAV3r4oB.cjs.map +0 -1
- package/dist/SolanaLoginButton-DFOoLqoj.js +0 -192
- package/dist/SolanaLoginButton-DFOoLqoj.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const c=require("react/jsx-runtime"),n=require("react"),E=require("./useCedrosLogin-DtJorrE7.cjs"),L=require("./LoadingSpinner-d6sSxgQN.cjs"),S={loading:!1,loaded:!1,error:null,callbacks:[],load(){return typeof window>"u"||typeof document>"u"?Promise.reject(new Error("Google Sign-In script loader cannot run in SSR")):this.loaded?Promise.resolve():this.loading?new Promise((e,a)=>{this.callbacks.push({resolve:e,reject:a})}):(this.loading=!0,new Promise((e,a)=>{this.callbacks.push({resolve:e,reject:a});const g=document.getElementById("google-gsi-script");if(g){window.google?.accounts?.id?(this.loaded=!0,this.loading=!1,this.callbacks.forEach(o=>o.resolve()),this.callbacks=[]):g.addEventListener("load",()=>{this.loaded=!0,this.loading=!1,this.callbacks.forEach(o=>o.resolve()),this.callbacks=[]});return}const s=document.createElement("script");s.src="https://accounts.google.com/gsi/client",s.async=!0,s.defer=!0,s.id="google-gsi-script",s.onload=()=>{this.loaded=!0,this.loading=!1,this.callbacks.forEach(o=>o.resolve()),this.callbacks=[]},s.onerror=()=>{this.loading=!1,s.remove();const o=new Error("Failed to load Google Sign-In script");this.callbacks.forEach(h=>h.reject(o)),this.callbacks=[]},document.head.appendChild(s)}))},_reset(){this.loading=!1,this.loaded=!1,this.error=null,this.callbacks=[]}};function w(){const{config:e,_internal:a}=E.useCedrosLogin(),[g,s]=n.useState(!1),[o,h]=n.useState(!1),[k,r]=n.useState(null),[p,f]=n.useState(null),u=n.useRef(null),m=n.useRef(e),d=n.useRef(null),b=n.useMemo(()=>new E.ApiClient({baseUrl:e.serverUrl,timeoutMs:e.requestTimeout,retryAttempts:e.retryAttempts}),[e.serverUrl,e.requestTimeout,e.retryAttempts]);n.useEffect(()=>{m.current=e},[e]);const C=n.useCallback(async t=>{const l=u.current;if(l){if(t.error){const i={code:"SERVER_ERROR",message:t.error==="access_denied"?"Google sign-in was cancelled.":"Unable to sign in with Google. Please try again."};r(i),s(!1),u.current=null,l.reject(i);return}try{const i=await b.post("/google",{accessToken:t.access_token});m.current.callbacks?.onLoginSuccess?.(i.user,"google"),a?.handleLoginSuccess(i.user,i.tokens),s(!1),l.resolve(i)}catch(i){const R=E.handleApiError(i,"Unable to sign in with Google. Please try again.");R.code==="ACCOUNT_LINK_REQUIRED"&&f(t.access_token??null),r(R),s(!1),l.reject(R)}finally{u.current=null}}},[b,a]);n.useEffect(()=>{if(!e.googleClientId)return;let t=!0;return S.load().then(()=>{if(!t)return;const l=window.google?.accounts?.oauth2?.initTokenClient({client_id:e.googleClientId,scope:"openid email profile",callback:C});l&&(d.current=l,h(!0))}).catch(()=>{t&&r({code:"SERVER_ERROR",message:"Unable to load Google sign-in. Please refresh and try again."})}),()=>{t=!1,d.current=null}},[e.googleClientId,C]);const I=n.useCallback(async()=>{if(!e.googleClientId){const t={code:"VALIDATION_ERROR",message:"Google Client ID not configured"};throw r(t),t}if(!o){const t={code:"VALIDATION_ERROR",message:"Google sign-in is not ready yet. Please wait a moment and try again."};throw r(t),t}if(u.current){const t={code:"VALIDATION_ERROR",message:"Google sign-in is already in progress."};throw r(t),t}return s(!0),r(null),new Promise((t,l)=>{u.current={resolve:t,reject:l},d.current?.requestAccessToken()})},[e.googleClientId,o]),y=n.useCallback(()=>r(null),[]),A=n.useCallback(()=>f(null),[]);return{signIn:I,isLoading:g,isInitialized:o,error:k,clearError:y,pendingLinkIdToken:p,clearPendingLink:A}}function v({onSuccess:e,onError:a,className:g="",variant:s="default",size:o="md",disabled:h=!1}){const{signIn:k,isLoading:r,isInitialized:p}=w(),f=async()=>{try{await k(),e?.()}catch(d){const b=d instanceof Error?d:new Error(String(d));a?.(b)}},u={sm:"cedros-button-sm",md:"cedros-button-md",lg:"cedros-button-lg"},m={default:"cedros-button-social",outline:"cedros-button-social-outline"};return c.jsxs("button",{type:"button",className:`cedros-button ${m[s]} ${u[o]} ${g}`,onClick:f,disabled:h||!p||r,"aria-label":"Sign in with Google",children:[r?c.jsx(L.LoadingSpinner,{size:"sm"}):c.jsxs("svg",{className:"cedros-button-icon",width:"18",height:"18",viewBox:"0 0 18 18",fill:"none","aria-hidden":"true",children:[c.jsx("path",{d:"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z",fill:"#4285F4"}),c.jsx("path",{d:"M9.003 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.26c-.806.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.96v2.332A8.997 8.997 0 0 0 9.003 18z",fill:"#34A853"}),c.jsx("path",{d:"M3.964 10.712A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.96A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.96 4.042l3.004-2.33z",fill:"#FBBC05"}),c.jsx("path",{d:"M9.003 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.464.891 11.428 0 9.002 0A8.997 8.997 0 0 0 .96 4.958l3.005 2.332c.708-2.127 2.692-3.71 5.036-3.71z",fill:"#EA4335"})]}),c.jsx("span",{children:"Continue with Google"})]})}exports.GoogleLoginButton=v;exports.useGoogleAuth=w;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleLoginButton-BwiL1o6U.cjs","sources":["../src/hooks/useGoogleAuth.ts","../src/components/google/GoogleLoginButton.tsx"],"sourcesContent":["import { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport type { AuthResponse, AuthError } from '../types';\n\n/**\n * Module-level singleton for Google script loading (P-01)\n *\n * Prevents race conditions when multiple components mount simultaneously.\n * Uses a promise queue pattern to ensure the script is only loaded once.\n *\n * ## SSR Limitations (F-08)\n *\n * This singleton persists across the module lifecycle. In SSR environments:\n * - Module state persists between requests (potential cross-request leakage)\n * - Google Sign-In requires browser APIs (document, window)\n * - The hook guards against SSR with `googleClientId` checks\n *\n * For SSR frameworks (Next.js, Remix), ensure this hook is only used\n * in client-side components.\n *\n * ## Test Isolation\n *\n * For test isolation, call `scriptLoader._reset()` in test setup/teardown.\n *\n * @internal\n */\nconst scriptLoader = {\n loading: false,\n loaded: false,\n error: null as Error | null,\n callbacks: [] as Array<{ resolve: () => void; reject: (err: Error) => void }>,\n\n load(): Promise<void> {\n // SSR guard: avoid touching browser globals when running in Node/SSR.\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.reject(new Error('Google Sign-In script loader cannot run in SSR'));\n }\n\n // Already loaded\n if (this.loaded) {\n return Promise.resolve();\n }\n\n // Loading in progress - queue callback\n if (this.loading) {\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n });\n }\n\n // Start loading\n this.loading = true;\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n\n // Check if script already exists (from previous session or SSR)\n const existingScript = document.getElementById('google-gsi-script');\n if (existingScript) {\n if (window.google?.accounts?.id) {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n } else {\n existingScript.addEventListener('load', () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n });\n }\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://accounts.google.com/gsi/client';\n script.async = true;\n script.defer = true;\n script.id = 'google-gsi-script';\n\n script.onload = () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n };\n\n script.onerror = () => {\n this.loading = false;\n // M-02: Remove failed script from DOM to allow retry on next load() call.\n // Without removal, retry finds existingScript and waits for a load event\n // that will never fire on an already-failed script.\n script.remove();\n const error = new Error('Failed to load Google Sign-In script');\n this.callbacks.forEach((cb) => cb.reject(error));\n this.callbacks = [];\n };\n\n document.head.appendChild(script);\n });\n },\n\n /**\n * Reset singleton state for test isolation (F-08)\n * @internal - Only use in test setup/teardown\n */\n _reset(): void {\n this.loading = false;\n this.loaded = false;\n this.error = null;\n this.callbacks = [];\n },\n};\n\n/** @internal */\nexport const _internalGoogleScriptLoader = scriptLoader;\n\nexport interface UseGoogleAuthReturn {\n signIn: () => Promise<AuthResponse>;\n isLoading: boolean;\n isInitialized: boolean;\n error: AuthError | null;\n clearError: () => void;\n /** ID token saved when ACCOUNT_LINK_REQUIRED is returned. Pass to POST /auth/link-oauth with the user's password. */\n pendingLinkIdToken: string | null;\n /** Clear the pending link state */\n clearPendingLink: () => void;\n}\n\ninterface PromiseCallbacks {\n resolve: (value: AuthResponse) => void;\n reject: (error: AuthError) => void;\n}\n\n/**\n * Hook for Google OAuth authentication.\n *\n * @example\n * ```tsx\n * function GoogleButton() {\n * const { signIn, isLoading, isInitialized, error } = useGoogleAuth();\n *\n * return (\n * <button onClick={signIn} disabled={!isInitialized || isLoading}>\n * {isLoading ? 'Signing in...' : 'Sign in with Google'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useGoogleAuth(): UseGoogleAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [isInitialized, setIsInitialized] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const [pendingLinkIdToken, setPendingLinkIdToken] = useState<string | null>(null);\n\n const promiseCallbacksRef = useRef<PromiseCallbacks | null>(null);\n const configRef = useRef(config);\n const tokenClientRef = useRef<GoogleTokenClient | 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 // Keep config ref in sync\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Handle access token from OAuth popup (initTokenClient flow)\n const handleTokenResponse = useCallback(\n async (response: GoogleTokenResponse) => {\n const callbacks = promiseCallbacksRef.current;\n if (!callbacks) return;\n\n if (response.error) {\n const err: AuthError = {\n code: 'SERVER_ERROR',\n message: response.error === 'access_denied'\n ? 'Google sign-in was cancelled.'\n : 'Unable to sign in with Google. Please try again.',\n };\n setError(err);\n setIsLoading(false);\n promiseCallbacksRef.current = null;\n callbacks.reject(err);\n return;\n }\n\n try {\n const data = await apiClient.post<AuthResponse>('/google', {\n accessToken: response.access_token,\n });\n configRef.current.callbacks?.onLoginSuccess?.(data.user, 'google');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n setIsLoading(false);\n callbacks.resolve(data);\n } catch (err) {\n const authError = handleApiError(err, 'Unable to sign in with Google. Please try again.');\n if (authError.code === 'ACCOUNT_LINK_REQUIRED') {\n setPendingLinkIdToken(response.access_token ?? null);\n }\n setError(authError);\n setIsLoading(false);\n callbacks.reject(authError);\n } finally {\n promiseCallbacksRef.current = null;\n }\n },\n [apiClient, _internal]\n );\n\n // P-01: Initialize Google OAuth token client using singleton loader.\n // Uses initTokenClient (OAuth popup) instead of One Tap prompt() which\n // has exponential cooldown after dismissal and is blocked by Brave.\n useEffect(() => {\n if (!config.googleClientId) {\n return;\n }\n\n let isMounted = true;\n\n scriptLoader\n .load()\n .then(() => {\n if (!isMounted) return;\n\n const client = window.google?.accounts?.oauth2?.initTokenClient({\n client_id: config.googleClientId!,\n scope: 'openid email profile',\n callback: handleTokenResponse,\n });\n\n if (client) {\n tokenClientRef.current = client;\n setIsInitialized(true);\n }\n })\n .catch(() => {\n if (isMounted) {\n setError({\n code: 'SERVER_ERROR',\n message: 'Unable to load Google sign-in. Please refresh and try again.',\n });\n }\n });\n\n return () => {\n isMounted = false;\n tokenClientRef.current = null;\n };\n }, [config.googleClientId, handleTokenResponse]);\n\n const signIn = useCallback(async (): Promise<AuthResponse> => {\n if (!config.googleClientId) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google Client ID not configured',\n };\n setError(err);\n throw err;\n }\n\n if (!isInitialized) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is not ready yet. Please wait a moment and try again.',\n };\n setError(err);\n throw err;\n }\n\n if (promiseCallbacksRef.current) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is already in progress.',\n };\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n return new Promise<AuthResponse>((resolve, reject) => {\n promiseCallbacksRef.current = { resolve, reject };\n\n // Open the Google OAuth popup via initTokenClient.\n // This avoids One Tap's exponential cooldown after dismissal and works\n // in browsers that block One Tap (e.g., Brave).\n tokenClientRef.current?.requestAccessToken();\n });\n }, [config.googleClientId, isInitialized]);\n\n const clearError = useCallback(() => setError(null), []);\n const clearPendingLink = useCallback(() => setPendingLinkIdToken(null), []);\n\n return {\n signIn,\n isLoading,\n isInitialized,\n error,\n clearError,\n pendingLinkIdToken,\n clearPendingLink,\n };\n}\n\n/** Response from Google's initTokenClient callback */\ninterface GoogleTokenResponse {\n access_token?: string;\n error?: string;\n error_description?: string;\n}\n\n/** Token client returned by google.accounts.oauth2.initTokenClient */\ninterface GoogleTokenClient {\n requestAccessToken: () => void;\n}\n\n// Type declaration for Google Identity Services\ndeclare global {\n interface Window {\n google?: {\n accounts?: {\n /** Used only to detect if the GIS script has loaded */\n id?: object;\n oauth2?: {\n initTokenClient: (config: {\n client_id: string;\n scope: string;\n callback: (response: GoogleTokenResponse) => void;\n }) => GoogleTokenClient;\n };\n };\n };\n }\n}\n","import { useGoogleAuth } from '../../hooks/useGoogleAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface GoogleLoginButtonProps {\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\n/**\n * Google OAuth login button\n */\nexport function GoogleLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n}: GoogleLoginButtonProps) {\n const { signIn, isLoading, isInitialized } = useGoogleAuth();\n\n const handleClick = async () => {\n try {\n await signIn();\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\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 return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || !isInitialized || isLoading}\n aria-label=\"Sign in with Google\"\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 18 18\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M9.003 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.26c-.806.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.96v2.332A8.997 8.997 0 0 0 9.003 18z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M3.964 10.712A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.96A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.96 4.042l3.004-2.33z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M9.003 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.464.891 11.428 0 9.002 0A8.997 8.997 0 0 0 .96 4.958l3.005 2.332c.708-2.127 2.692-3.71 5.036-3.71z\"\n fill=\"#EA4335\"\n />\n </svg>\n )}\n <span>Continue with Google</span>\n </button>\n );\n}\n"],"names":["scriptLoader","resolve","reject","existingScript","cb","script","error","useGoogleAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","isInitialized","setIsInitialized","setError","pendingLinkIdToken","setPendingLinkIdToken","promiseCallbacksRef","useRef","configRef","tokenClientRef","apiClient","useMemo","ApiClient","useEffect","handleTokenResponse","useCallback","response","callbacks","err","data","authError","handleApiError","isMounted","client","signIn","clearError","clearPendingLink","GoogleLoginButton","onSuccess","onError","className","variant","size","disabled","handleClick","sizeClasses","variantClasses","jsxs","jsx","LoadingSpinner"],"mappings":"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"}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { u as
|
|
4
|
-
import { L as
|
|
5
|
-
const
|
|
1
|
+
import { jsxs as C, jsx as u } from "react/jsx-runtime";
|
|
2
|
+
import { useState as m, useRef as w, useMemo as G, useEffect as y, useCallback as p } from "react";
|
|
3
|
+
import { u as S, A as _, h as T } from "./useCedrosLogin-CFfID-0i.js";
|
|
4
|
+
import { L as P } from "./LoadingSpinner-6vml-zwr.js";
|
|
5
|
+
const z = {
|
|
6
6
|
loading: !1,
|
|
7
7
|
loaded: !1,
|
|
8
8
|
error: null,
|
|
9
9
|
callbacks: [],
|
|
10
10
|
load() {
|
|
11
|
-
return typeof window > "u" || typeof document > "u" ? Promise.reject(new Error("Google Sign-In script loader cannot run in SSR")) : this.loaded ? Promise.resolve() : this.loading ? new Promise((e,
|
|
12
|
-
this.callbacks.push({ resolve: e, reject:
|
|
13
|
-
}) : (this.loading = !0, new Promise((e,
|
|
14
|
-
this.callbacks.push({ resolve: e, reject:
|
|
11
|
+
return typeof window > "u" || typeof document > "u" ? Promise.reject(new Error("Google Sign-In script loader cannot run in SSR")) : this.loaded ? Promise.resolve() : this.loading ? new Promise((e, i) => {
|
|
12
|
+
this.callbacks.push({ resolve: e, reject: i });
|
|
13
|
+
}) : (this.loading = !0, new Promise((e, i) => {
|
|
14
|
+
this.callbacks.push({ resolve: e, reject: i });
|
|
15
15
|
const d = document.getElementById("google-gsi-script");
|
|
16
16
|
if (d) {
|
|
17
|
-
window.google?.accounts?.id ? (this.loaded = !0, this.loading = !1, this.callbacks.forEach((
|
|
18
|
-
this.loaded = !0, this.loading = !1, this.callbacks.forEach((
|
|
17
|
+
window.google?.accounts?.id ? (this.loaded = !0, this.loading = !1, this.callbacks.forEach((o) => o.resolve()), this.callbacks = []) : d.addEventListener("load", () => {
|
|
18
|
+
this.loaded = !0, this.loading = !1, this.callbacks.forEach((o) => o.resolve()), this.callbacks = [];
|
|
19
19
|
});
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
this.loaded = !0, this.loading = !1, this.callbacks.forEach((
|
|
25
|
-
},
|
|
26
|
-
this.loading = !1,
|
|
27
|
-
const
|
|
28
|
-
this.callbacks.forEach((
|
|
29
|
-
}, document.head.appendChild(
|
|
22
|
+
const n = document.createElement("script");
|
|
23
|
+
n.src = "https://accounts.google.com/gsi/client", n.async = !0, n.defer = !0, n.id = "google-gsi-script", n.onload = () => {
|
|
24
|
+
this.loaded = !0, this.loading = !1, this.callbacks.forEach((o) => o.resolve()), this.callbacks = [];
|
|
25
|
+
}, n.onerror = () => {
|
|
26
|
+
this.loading = !1, n.remove();
|
|
27
|
+
const o = new Error("Failed to load Google Sign-In script");
|
|
28
|
+
this.callbacks.forEach((g) => g.reject(o)), this.callbacks = [];
|
|
29
|
+
}, document.head.appendChild(n);
|
|
30
30
|
}));
|
|
31
31
|
},
|
|
32
32
|
/**
|
|
@@ -37,148 +37,135 @@ const O = {
|
|
|
37
37
|
this.loading = !1, this.loaded = !1, this.error = null, this.callbacks = [];
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
|
-
function
|
|
41
|
-
const { config: e, _internal:
|
|
42
|
-
() => new
|
|
40
|
+
function O() {
|
|
41
|
+
const { config: e, _internal: i } = S(), [d, n] = m(!1), [o, g] = m(!1), [b, s] = m(null), [k, h] = m(null), a = w(null), E = w(e), c = w(null), f = G(
|
|
42
|
+
() => new _({
|
|
43
43
|
baseUrl: e.serverUrl,
|
|
44
44
|
timeoutMs: e.requestTimeout,
|
|
45
45
|
retryAttempts: e.retryAttempts
|
|
46
46
|
}),
|
|
47
47
|
[e.serverUrl, e.requestTimeout, e.retryAttempts]
|
|
48
48
|
);
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
y(() => {
|
|
50
|
+
E.current = e;
|
|
51
51
|
}, [e]);
|
|
52
|
-
const
|
|
53
|
-
async (
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
52
|
+
const I = p(
|
|
53
|
+
async (t) => {
|
|
54
|
+
const r = a.current;
|
|
55
|
+
if (r) {
|
|
56
|
+
if (t.error) {
|
|
57
|
+
const l = {
|
|
58
|
+
code: "SERVER_ERROR",
|
|
59
|
+
message: t.error === "access_denied" ? "Google sign-in was cancelled." : "Unable to sign in with Google. Please try again."
|
|
60
|
+
};
|
|
61
|
+
s(l), n(!1), a.current = null, r.reject(l);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
56
64
|
try {
|
|
57
|
-
const
|
|
58
|
-
|
|
65
|
+
const l = await f.post("/google", {
|
|
66
|
+
accessToken: t.access_token
|
|
59
67
|
});
|
|
60
|
-
|
|
61
|
-
} catch (
|
|
62
|
-
const
|
|
63
|
-
|
|
68
|
+
E.current.callbacks?.onLoginSuccess?.(l.user, "google"), i?.handleLoginSuccess(l.user, l.tokens), n(!1), r.resolve(l);
|
|
69
|
+
} catch (l) {
|
|
70
|
+
const R = T(l, "Unable to sign in with Google. Please try again.");
|
|
71
|
+
R.code === "ACCOUNT_LINK_REQUIRED" && h(t.access_token ?? null), s(R), n(!1), r.reject(R);
|
|
64
72
|
} finally {
|
|
65
73
|
a.current = null;
|
|
66
74
|
}
|
|
75
|
+
}
|
|
67
76
|
},
|
|
68
|
-
[
|
|
77
|
+
[f, i]
|
|
69
78
|
);
|
|
70
|
-
|
|
79
|
+
y(() => {
|
|
71
80
|
if (!e.googleClientId)
|
|
72
81
|
return;
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
let t = !0;
|
|
83
|
+
return z.load().then(() => {
|
|
84
|
+
if (!t) return;
|
|
85
|
+
const r = window.google?.accounts?.oauth2?.initTokenClient({
|
|
76
86
|
client_id: e.googleClientId,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
82
|
-
return O.load().then(() => {
|
|
83
|
-
s && i();
|
|
87
|
+
scope: "openid email profile",
|
|
88
|
+
callback: I
|
|
89
|
+
});
|
|
90
|
+
r && (c.current = r, g(!0));
|
|
84
91
|
}).catch(() => {
|
|
85
|
-
|
|
92
|
+
t && s({
|
|
86
93
|
code: "SERVER_ERROR",
|
|
87
94
|
message: "Unable to load Google sign-in. Please refresh and try again."
|
|
88
95
|
});
|
|
89
96
|
}), () => {
|
|
90
|
-
|
|
97
|
+
t = !1, c.current = null;
|
|
91
98
|
};
|
|
92
|
-
}, [e.googleClientId,
|
|
93
|
-
const
|
|
99
|
+
}, [e.googleClientId, I]);
|
|
100
|
+
const A = p(async () => {
|
|
94
101
|
if (!e.googleClientId) {
|
|
95
|
-
const
|
|
102
|
+
const t = {
|
|
96
103
|
code: "VALIDATION_ERROR",
|
|
97
104
|
message: "Google Client ID not configured"
|
|
98
105
|
};
|
|
99
|
-
throw
|
|
106
|
+
throw s(t), t;
|
|
100
107
|
}
|
|
101
|
-
if (!
|
|
102
|
-
const
|
|
108
|
+
if (!o) {
|
|
109
|
+
const t = {
|
|
103
110
|
code: "VALIDATION_ERROR",
|
|
104
111
|
message: "Google sign-in is not ready yet. Please wait a moment and try again."
|
|
105
112
|
};
|
|
106
|
-
throw
|
|
113
|
+
throw s(t), t;
|
|
107
114
|
}
|
|
108
115
|
if (a.current) {
|
|
109
|
-
const
|
|
116
|
+
const t = {
|
|
110
117
|
code: "VALIDATION_ERROR",
|
|
111
118
|
message: "Google sign-in is already in progress."
|
|
112
119
|
};
|
|
113
|
-
throw
|
|
120
|
+
throw s(t), t;
|
|
114
121
|
}
|
|
115
|
-
return
|
|
116
|
-
a.current = { resolve:
|
|
117
|
-
if (r.isNotDisplayed()) {
|
|
118
|
-
const l = {
|
|
119
|
-
code: "SERVER_ERROR",
|
|
120
|
-
message: "Google sign-in popup was blocked. Please allow popups for this site and try again."
|
|
121
|
-
};
|
|
122
|
-
n(l), o(!1), a.current = null, i(l);
|
|
123
|
-
} else if (r.isSkippedMoment()) {
|
|
124
|
-
const l = {
|
|
125
|
-
code: "SERVER_ERROR",
|
|
126
|
-
message: "Google sign-in was cancelled."
|
|
127
|
-
};
|
|
128
|
-
n(l), o(!1), a.current = null, i(l);
|
|
129
|
-
} else if (r.isDismissedMoment()) {
|
|
130
|
-
const l = {
|
|
131
|
-
code: "SERVER_ERROR",
|
|
132
|
-
message: "Google sign-in was cancelled."
|
|
133
|
-
};
|
|
134
|
-
n(l), o(!1), a.current = null, i(l);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
122
|
+
return n(!0), s(null), new Promise((t, r) => {
|
|
123
|
+
a.current = { resolve: t, reject: r }, c.current?.requestAccessToken();
|
|
137
124
|
});
|
|
138
|
-
}, [e.googleClientId,
|
|
125
|
+
}, [e.googleClientId, o]), L = p(() => s(null), []), v = p(() => h(null), []);
|
|
139
126
|
return {
|
|
140
|
-
signIn:
|
|
127
|
+
signIn: A,
|
|
141
128
|
isLoading: d,
|
|
142
|
-
isInitialized:
|
|
143
|
-
error:
|
|
144
|
-
clearError:
|
|
145
|
-
pendingLinkIdToken:
|
|
146
|
-
clearPendingLink:
|
|
129
|
+
isInitialized: o,
|
|
130
|
+
error: b,
|
|
131
|
+
clearError: L,
|
|
132
|
+
pendingLinkIdToken: k,
|
|
133
|
+
clearPendingLink: v
|
|
147
134
|
};
|
|
148
135
|
}
|
|
149
|
-
function
|
|
136
|
+
function V({
|
|
150
137
|
onSuccess: e,
|
|
151
|
-
onError:
|
|
138
|
+
onError: i,
|
|
152
139
|
className: d = "",
|
|
153
|
-
variant:
|
|
154
|
-
size:
|
|
155
|
-
disabled:
|
|
140
|
+
variant: n = "default",
|
|
141
|
+
size: o = "md",
|
|
142
|
+
disabled: g = !1
|
|
156
143
|
}) {
|
|
157
|
-
const { signIn:
|
|
144
|
+
const { signIn: b, isLoading: s, isInitialized: k } = O(), h = async () => {
|
|
158
145
|
try {
|
|
159
|
-
await
|
|
160
|
-
} catch (
|
|
161
|
-
const
|
|
162
|
-
|
|
146
|
+
await b(), e?.();
|
|
147
|
+
} catch (c) {
|
|
148
|
+
const f = c instanceof Error ? c : new Error(String(c));
|
|
149
|
+
i?.(f);
|
|
163
150
|
}
|
|
164
151
|
}, a = {
|
|
165
152
|
sm: "cedros-button-sm",
|
|
166
153
|
md: "cedros-button-md",
|
|
167
154
|
lg: "cedros-button-lg"
|
|
168
155
|
};
|
|
169
|
-
return /* @__PURE__ */
|
|
156
|
+
return /* @__PURE__ */ C(
|
|
170
157
|
"button",
|
|
171
158
|
{
|
|
172
159
|
type: "button",
|
|
173
160
|
className: `cedros-button ${{
|
|
174
161
|
default: "cedros-button-social",
|
|
175
162
|
outline: "cedros-button-social-outline"
|
|
176
|
-
}[
|
|
163
|
+
}[n]} ${a[o]} ${d}`,
|
|
177
164
|
onClick: h,
|
|
178
|
-
disabled:
|
|
165
|
+
disabled: g || !k || s,
|
|
179
166
|
"aria-label": "Sign in with Google",
|
|
180
167
|
children: [
|
|
181
|
-
|
|
168
|
+
s ? /* @__PURE__ */ u(P, { size: "sm" }) : /* @__PURE__ */ C(
|
|
182
169
|
"svg",
|
|
183
170
|
{
|
|
184
171
|
className: "cedros-button-icon",
|
|
@@ -188,28 +175,28 @@ function D({
|
|
|
188
175
|
fill: "none",
|
|
189
176
|
"aria-hidden": "true",
|
|
190
177
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ u(
|
|
192
179
|
"path",
|
|
193
180
|
{
|
|
194
181
|
d: "M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z",
|
|
195
182
|
fill: "#4285F4"
|
|
196
183
|
}
|
|
197
184
|
),
|
|
198
|
-
/* @__PURE__ */
|
|
185
|
+
/* @__PURE__ */ u(
|
|
199
186
|
"path",
|
|
200
187
|
{
|
|
201
188
|
d: "M9.003 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.26c-.806.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.96v2.332A8.997 8.997 0 0 0 9.003 18z",
|
|
202
189
|
fill: "#34A853"
|
|
203
190
|
}
|
|
204
191
|
),
|
|
205
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ u(
|
|
206
193
|
"path",
|
|
207
194
|
{
|
|
208
195
|
d: "M3.964 10.712A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.96A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.96 4.042l3.004-2.33z",
|
|
209
196
|
fill: "#FBBC05"
|
|
210
197
|
}
|
|
211
198
|
),
|
|
212
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ u(
|
|
213
200
|
"path",
|
|
214
201
|
{
|
|
215
202
|
d: "M9.003 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.464.891 11.428 0 9.002 0A8.997 8.997 0 0 0 .96 4.958l3.005 2.332c.708-2.127 2.692-3.71 5.036-3.71z",
|
|
@@ -219,12 +206,12 @@ function D({
|
|
|
219
206
|
]
|
|
220
207
|
}
|
|
221
208
|
),
|
|
222
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ u("span", { children: "Continue with Google" })
|
|
223
210
|
]
|
|
224
211
|
}
|
|
225
212
|
);
|
|
226
213
|
}
|
|
227
214
|
export {
|
|
228
|
-
|
|
229
|
-
|
|
215
|
+
V as G,
|
|
216
|
+
O as u
|
|
230
217
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleLoginButton-CFlqxvU-.js","sources":["../src/hooks/useGoogleAuth.ts","../src/components/google/GoogleLoginButton.tsx"],"sourcesContent":["import { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport type { AuthResponse, AuthError } from '../types';\n\n/**\n * Module-level singleton for Google script loading (P-01)\n *\n * Prevents race conditions when multiple components mount simultaneously.\n * Uses a promise queue pattern to ensure the script is only loaded once.\n *\n * ## SSR Limitations (F-08)\n *\n * This singleton persists across the module lifecycle. In SSR environments:\n * - Module state persists between requests (potential cross-request leakage)\n * - Google Sign-In requires browser APIs (document, window)\n * - The hook guards against SSR with `googleClientId` checks\n *\n * For SSR frameworks (Next.js, Remix), ensure this hook is only used\n * in client-side components.\n *\n * ## Test Isolation\n *\n * For test isolation, call `scriptLoader._reset()` in test setup/teardown.\n *\n * @internal\n */\nconst scriptLoader = {\n loading: false,\n loaded: false,\n error: null as Error | null,\n callbacks: [] as Array<{ resolve: () => void; reject: (err: Error) => void }>,\n\n load(): Promise<void> {\n // SSR guard: avoid touching browser globals when running in Node/SSR.\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.reject(new Error('Google Sign-In script loader cannot run in SSR'));\n }\n\n // Already loaded\n if (this.loaded) {\n return Promise.resolve();\n }\n\n // Loading in progress - queue callback\n if (this.loading) {\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n });\n }\n\n // Start loading\n this.loading = true;\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n\n // Check if script already exists (from previous session or SSR)\n const existingScript = document.getElementById('google-gsi-script');\n if (existingScript) {\n if (window.google?.accounts?.id) {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n } else {\n existingScript.addEventListener('load', () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n });\n }\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://accounts.google.com/gsi/client';\n script.async = true;\n script.defer = true;\n script.id = 'google-gsi-script';\n\n script.onload = () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n };\n\n script.onerror = () => {\n this.loading = false;\n // M-02: Remove failed script from DOM to allow retry on next load() call.\n // Without removal, retry finds existingScript and waits for a load event\n // that will never fire on an already-failed script.\n script.remove();\n const error = new Error('Failed to load Google Sign-In script');\n this.callbacks.forEach((cb) => cb.reject(error));\n this.callbacks = [];\n };\n\n document.head.appendChild(script);\n });\n },\n\n /**\n * Reset singleton state for test isolation (F-08)\n * @internal - Only use in test setup/teardown\n */\n _reset(): void {\n this.loading = false;\n this.loaded = false;\n this.error = null;\n this.callbacks = [];\n },\n};\n\n/** @internal */\nexport const _internalGoogleScriptLoader = scriptLoader;\n\nexport interface UseGoogleAuthReturn {\n signIn: () => Promise<AuthResponse>;\n isLoading: boolean;\n isInitialized: boolean;\n error: AuthError | null;\n clearError: () => void;\n /** ID token saved when ACCOUNT_LINK_REQUIRED is returned. Pass to POST /auth/link-oauth with the user's password. */\n pendingLinkIdToken: string | null;\n /** Clear the pending link state */\n clearPendingLink: () => void;\n}\n\ninterface PromiseCallbacks {\n resolve: (value: AuthResponse) => void;\n reject: (error: AuthError) => void;\n}\n\n/**\n * Hook for Google OAuth authentication.\n *\n * @example\n * ```tsx\n * function GoogleButton() {\n * const { signIn, isLoading, isInitialized, error } = useGoogleAuth();\n *\n * return (\n * <button onClick={signIn} disabled={!isInitialized || isLoading}>\n * {isLoading ? 'Signing in...' : 'Sign in with Google'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useGoogleAuth(): UseGoogleAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [isInitialized, setIsInitialized] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const [pendingLinkIdToken, setPendingLinkIdToken] = useState<string | null>(null);\n\n const promiseCallbacksRef = useRef<PromiseCallbacks | null>(null);\n const configRef = useRef(config);\n const tokenClientRef = useRef<GoogleTokenClient | 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 // Keep config ref in sync\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Handle access token from OAuth popup (initTokenClient flow)\n const handleTokenResponse = useCallback(\n async (response: GoogleTokenResponse) => {\n const callbacks = promiseCallbacksRef.current;\n if (!callbacks) return;\n\n if (response.error) {\n const err: AuthError = {\n code: 'SERVER_ERROR',\n message: response.error === 'access_denied'\n ? 'Google sign-in was cancelled.'\n : 'Unable to sign in with Google. Please try again.',\n };\n setError(err);\n setIsLoading(false);\n promiseCallbacksRef.current = null;\n callbacks.reject(err);\n return;\n }\n\n try {\n const data = await apiClient.post<AuthResponse>('/google', {\n accessToken: response.access_token,\n });\n configRef.current.callbacks?.onLoginSuccess?.(data.user, 'google');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n setIsLoading(false);\n callbacks.resolve(data);\n } catch (err) {\n const authError = handleApiError(err, 'Unable to sign in with Google. Please try again.');\n if (authError.code === 'ACCOUNT_LINK_REQUIRED') {\n setPendingLinkIdToken(response.access_token ?? null);\n }\n setError(authError);\n setIsLoading(false);\n callbacks.reject(authError);\n } finally {\n promiseCallbacksRef.current = null;\n }\n },\n [apiClient, _internal]\n );\n\n // P-01: Initialize Google OAuth token client using singleton loader.\n // Uses initTokenClient (OAuth popup) instead of One Tap prompt() which\n // has exponential cooldown after dismissal and is blocked by Brave.\n useEffect(() => {\n if (!config.googleClientId) {\n return;\n }\n\n let isMounted = true;\n\n scriptLoader\n .load()\n .then(() => {\n if (!isMounted) return;\n\n const client = window.google?.accounts?.oauth2?.initTokenClient({\n client_id: config.googleClientId!,\n scope: 'openid email profile',\n callback: handleTokenResponse,\n });\n\n if (client) {\n tokenClientRef.current = client;\n setIsInitialized(true);\n }\n })\n .catch(() => {\n if (isMounted) {\n setError({\n code: 'SERVER_ERROR',\n message: 'Unable to load Google sign-in. Please refresh and try again.',\n });\n }\n });\n\n return () => {\n isMounted = false;\n tokenClientRef.current = null;\n };\n }, [config.googleClientId, handleTokenResponse]);\n\n const signIn = useCallback(async (): Promise<AuthResponse> => {\n if (!config.googleClientId) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google Client ID not configured',\n };\n setError(err);\n throw err;\n }\n\n if (!isInitialized) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is not ready yet. Please wait a moment and try again.',\n };\n setError(err);\n throw err;\n }\n\n if (promiseCallbacksRef.current) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is already in progress.',\n };\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n return new Promise<AuthResponse>((resolve, reject) => {\n promiseCallbacksRef.current = { resolve, reject };\n\n // Open the Google OAuth popup via initTokenClient.\n // This avoids One Tap's exponential cooldown after dismissal and works\n // in browsers that block One Tap (e.g., Brave).\n tokenClientRef.current?.requestAccessToken();\n });\n }, [config.googleClientId, isInitialized]);\n\n const clearError = useCallback(() => setError(null), []);\n const clearPendingLink = useCallback(() => setPendingLinkIdToken(null), []);\n\n return {\n signIn,\n isLoading,\n isInitialized,\n error,\n clearError,\n pendingLinkIdToken,\n clearPendingLink,\n };\n}\n\n/** Response from Google's initTokenClient callback */\ninterface GoogleTokenResponse {\n access_token?: string;\n error?: string;\n error_description?: string;\n}\n\n/** Token client returned by google.accounts.oauth2.initTokenClient */\ninterface GoogleTokenClient {\n requestAccessToken: () => void;\n}\n\n// Type declaration for Google Identity Services\ndeclare global {\n interface Window {\n google?: {\n accounts?: {\n /** Used only to detect if the GIS script has loaded */\n id?: object;\n oauth2?: {\n initTokenClient: (config: {\n client_id: string;\n scope: string;\n callback: (response: GoogleTokenResponse) => void;\n }) => GoogleTokenClient;\n };\n };\n };\n }\n}\n","import { useGoogleAuth } from '../../hooks/useGoogleAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface GoogleLoginButtonProps {\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\n/**\n * Google OAuth login button\n */\nexport function GoogleLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n}: GoogleLoginButtonProps) {\n const { signIn, isLoading, isInitialized } = useGoogleAuth();\n\n const handleClick = async () => {\n try {\n await signIn();\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\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 return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || !isInitialized || isLoading}\n aria-label=\"Sign in with Google\"\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 18 18\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M9.003 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.26c-.806.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.96v2.332A8.997 8.997 0 0 0 9.003 18z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M3.964 10.712A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.96A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.96 4.042l3.004-2.33z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M9.003 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.464.891 11.428 0 9.002 0A8.997 8.997 0 0 0 .96 4.958l3.005 2.332c.708-2.127 2.692-3.71 5.036-3.71z\"\n fill=\"#EA4335\"\n />\n </svg>\n )}\n <span>Continue with Google</span>\n </button>\n );\n}\n"],"names":["scriptLoader","resolve","reject","existingScript","cb","script","error","useGoogleAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","isInitialized","setIsInitialized","setError","pendingLinkIdToken","setPendingLinkIdToken","promiseCallbacksRef","useRef","configRef","tokenClientRef","apiClient","useMemo","ApiClient","useEffect","handleTokenResponse","useCallback","response","callbacks","err","data","authError","handleApiError","isMounted","client","signIn","clearError","clearPendingLink","GoogleLoginButton","onSuccess","onError","className","variant","size","disabled","handleClick","sizeClasses","jsxs","jsx","LoadingSpinner"],"mappings":"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"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { jsx as i, jsxs as V } from "react/jsx-runtime";
|
|
2
|
+
import { useState as P, 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 q, useWalletModal as ee } from "@solana/wallet-adapter-react-ui";
|
|
5
|
+
import { u as te, A as ne, h as H } from "./useCedrosLogin-CFfID-0i.js";
|
|
6
|
+
import { a as j } from "./validation-B8kMV3BL.js";
|
|
7
|
+
import { L as ae } from "./LoadingSpinner-6vml-zwr.js";
|
|
8
|
+
function re() {
|
|
9
|
+
const { config: e, _internal: a } = te(), [b, d] = P(!1), [k, o] = P(null), f = X(
|
|
10
|
+
() => new ne({
|
|
11
|
+
baseUrl: e.serverUrl,
|
|
12
|
+
timeoutMs: e.requestTimeout,
|
|
13
|
+
retryAttempts: e.retryAttempts
|
|
14
|
+
}),
|
|
15
|
+
[e.serverUrl, e.requestTimeout, e.retryAttempts]
|
|
16
|
+
), S = W(
|
|
17
|
+
async (u) => {
|
|
18
|
+
if (!j(u)) {
|
|
19
|
+
const l = {
|
|
20
|
+
code: "INVALID_PUBLIC_KEY",
|
|
21
|
+
message: "Invalid Solana public key format"
|
|
22
|
+
};
|
|
23
|
+
throw o(l), l;
|
|
24
|
+
}
|
|
25
|
+
d(!0), o(null);
|
|
26
|
+
try {
|
|
27
|
+
return await f.post(
|
|
28
|
+
"/solana/challenge",
|
|
29
|
+
{ publicKey: u },
|
|
30
|
+
{ credentials: "omit" }
|
|
31
|
+
);
|
|
32
|
+
} catch (l) {
|
|
33
|
+
const n = H(l, "Unable to start wallet verification. Please try again.");
|
|
34
|
+
throw o(n), n;
|
|
35
|
+
} finally {
|
|
36
|
+
d(!1);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
[f]
|
|
40
|
+
), s = W(
|
|
41
|
+
async (u, l, n) => {
|
|
42
|
+
if (!j(u)) {
|
|
43
|
+
const r = {
|
|
44
|
+
code: "INVALID_PUBLIC_KEY",
|
|
45
|
+
message: "Invalid Solana public key format"
|
|
46
|
+
};
|
|
47
|
+
throw o(r), r;
|
|
48
|
+
}
|
|
49
|
+
d(!0), o(null);
|
|
50
|
+
try {
|
|
51
|
+
const r = await f.post("/solana", {
|
|
52
|
+
publicKey: u,
|
|
53
|
+
signature: l,
|
|
54
|
+
message: n
|
|
55
|
+
});
|
|
56
|
+
return e.callbacks?.onLoginSuccess?.(r.user, "solana"), a?.handleLoginSuccess(r.user, r.tokens), r;
|
|
57
|
+
} catch (r) {
|
|
58
|
+
const v = H(r, "Unable to sign in with your wallet. Please try again.");
|
|
59
|
+
throw o(v), v;
|
|
60
|
+
} finally {
|
|
61
|
+
d(!1);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[f, e.callbacks, a]
|
|
65
|
+
), L = W(() => o(null), []);
|
|
66
|
+
return {
|
|
67
|
+
requestChallenge: S,
|
|
68
|
+
signIn: s,
|
|
69
|
+
isLoading: b,
|
|
70
|
+
error: k,
|
|
71
|
+
clearError: L
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function se(e) {
|
|
75
|
+
if (typeof window > "u")
|
|
76
|
+
return !1;
|
|
77
|
+
try {
|
|
78
|
+
const a = await import("@solana-mobile/wallet-standard-mobile"), b = e?.chains ?? ["solana:mainnet"];
|
|
79
|
+
return a.registerMwa({
|
|
80
|
+
appIdentity: {
|
|
81
|
+
name: e?.name,
|
|
82
|
+
uri: e?.uri,
|
|
83
|
+
icon: e?.icon
|
|
84
|
+
},
|
|
85
|
+
chains: b,
|
|
86
|
+
authorizationCache: a.createDefaultAuthorizationCache(),
|
|
87
|
+
chainSelector: a.createDefaultChainSelector(),
|
|
88
|
+
onWalletNotFound: a.createDefaultWalletNotFoundHandler()
|
|
89
|
+
}), !0;
|
|
90
|
+
} catch {
|
|
91
|
+
return !1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
typeof navigator < "u" && /Android/i.test(navigator.userAgent) && se();
|
|
95
|
+
const oe = [];
|
|
96
|
+
function he(e) {
|
|
97
|
+
return e.walletContext ? /* @__PURE__ */ i(q, { children: /* @__PURE__ */ i(F, { ...e }) }) : /* @__PURE__ */ i(Z, { wallets: oe, localStorageKey: "cedros-walletName", children: /* @__PURE__ */ i(q, { children: /* @__PURE__ */ i(F, { ...e }) }) });
|
|
98
|
+
}
|
|
99
|
+
function F({
|
|
100
|
+
onSuccess: e,
|
|
101
|
+
onError: a,
|
|
102
|
+
className: b = "",
|
|
103
|
+
variant: d = "default",
|
|
104
|
+
size: k = "md",
|
|
105
|
+
disabled: o = !1,
|
|
106
|
+
hideIfNoWallet: f = !0,
|
|
107
|
+
onLoadingChange: S,
|
|
108
|
+
walletContext: s
|
|
109
|
+
}) {
|
|
110
|
+
const { requestChallenge: L, signIn: u, isLoading: l } = re(), n = x(), { visible: r, setVisible: v } = ee(), [g, h] = P(!1), [T, U] = P(!1), A = K(!1), z = K(!1), E = K(!1), c = s?.connected ?? n.connected, m = s?.connecting ?? n.connecting, y = s?.publicKey ?? n.publicKey, p = s?.signMessage ?? n.signMessage, w = s?.wallet ?? n.wallet, Y = s?.wallets ?? n.wallets, $ = s ? s.select : (t) => n.select(t), _ = s?.connect ?? n.connect, B = Y.filter(
|
|
111
|
+
(t) => t.adapter.readyState === "Installed" || t.adapter.readyState === "Loadable"
|
|
112
|
+
), N = W(async () => {
|
|
113
|
+
if (!A.current) {
|
|
114
|
+
if (!y || !p) {
|
|
115
|
+
a?.(new Error("Wallet not ready"));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
A.current = !0;
|
|
119
|
+
try {
|
|
120
|
+
const t = y.toBase58(), C = await L(t), Q = new TextEncoder().encode(C.message), D = await p(Q);
|
|
121
|
+
if (!(D instanceof Uint8Array) || D.length === 0)
|
|
122
|
+
throw new Error("Wallet returned invalid signature");
|
|
123
|
+
let R;
|
|
124
|
+
try {
|
|
125
|
+
R = btoa(String.fromCharCode(...D));
|
|
126
|
+
} catch {
|
|
127
|
+
throw new Error("Failed to encode signature");
|
|
128
|
+
}
|
|
129
|
+
await u(t, R, C.message), E.current = !1, e?.();
|
|
130
|
+
} catch (t) {
|
|
131
|
+
const C = t instanceof Error ? t : new Error(String(t));
|
|
132
|
+
E.current = !0, a?.(C);
|
|
133
|
+
} finally {
|
|
134
|
+
A.current = !1, h(!1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}, [y, p, L, u, e, a]);
|
|
138
|
+
if (M(() => {
|
|
139
|
+
T && w && !c && !m && (U(!1), _().catch((t) => {
|
|
140
|
+
a?.(t instanceof Error ? t : new Error(String(t))), h(!1);
|
|
141
|
+
}));
|
|
142
|
+
}, [T, w, c, m, _, a]), M(() => {
|
|
143
|
+
g && c && y && p && !A.current && N().catch(() => {
|
|
144
|
+
});
|
|
145
|
+
}, [g, c, y, p, N]), M(() => {
|
|
146
|
+
r ? z.current = !0 : z.current && (z.current = !1, g && !c && w && !m ? U(!0) : g && !c && h(!1));
|
|
147
|
+
}, [r, g, c, w, m]), f && B.length === 0)
|
|
148
|
+
return null;
|
|
149
|
+
const O = async () => {
|
|
150
|
+
o || l || m || (c && y && p && !E.current ? (h(!0), await N()) : B.length === 1 && !w ? ($(B[0].adapter.name), h(!0), U(!0)) : (E.current = !1, w && n.select(null), v(!0), h(!0)));
|
|
151
|
+
}, G = {
|
|
152
|
+
sm: "cedros-button-sm",
|
|
153
|
+
md: "cedros-button-md",
|
|
154
|
+
lg: "cedros-button-lg"
|
|
155
|
+
}, J = {
|
|
156
|
+
default: "cedros-button-social",
|
|
157
|
+
outline: "cedros-button-social-outline"
|
|
158
|
+
}, I = l || m || g && !c;
|
|
159
|
+
return M(() => {
|
|
160
|
+
S?.(I);
|
|
161
|
+
}, [I, S]), /* @__PURE__ */ V(
|
|
162
|
+
"button",
|
|
163
|
+
{
|
|
164
|
+
type: "button",
|
|
165
|
+
className: `cedros-button ${J[d]} ${G[k]} ${b}`,
|
|
166
|
+
onClick: O,
|
|
167
|
+
disabled: o || I,
|
|
168
|
+
"aria-label": "Continue with Solana",
|
|
169
|
+
children: [
|
|
170
|
+
I ? /* @__PURE__ */ i(ae, { size: "sm" }) : /* @__PURE__ */ V(
|
|
171
|
+
"svg",
|
|
172
|
+
{
|
|
173
|
+
className: "cedros-button-icon",
|
|
174
|
+
width: "18",
|
|
175
|
+
height: "18",
|
|
176
|
+
viewBox: "0 0 128 128",
|
|
177
|
+
fill: "currentColor",
|
|
178
|
+
"aria-hidden": "true",
|
|
179
|
+
children: [
|
|
180
|
+
/* @__PURE__ */ i("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" }),
|
|
181
|
+
/* @__PURE__ */ i("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" }),
|
|
182
|
+
/* @__PURE__ */ i("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" })
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
),
|
|
186
|
+
/* @__PURE__ */ i("span", { children: "Continue with Solana" })
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
export {
|
|
192
|
+
he as S,
|
|
193
|
+
se as r,
|
|
194
|
+
re as u
|
|
195
|
+
};
|