google-react-recaptcha-v3
Version:
React component for Google reCAPTCHA v3 with advanced features and optimizations
1 lines • 22.8 kB
Source Map (JSON)
{"version":3,"sources":["../libs/enums/TRANSACTIONAL_ACTIONS.ts","../libs/services/RecaptchaService.ts","../libs/constants/SCRIPT_ID.ts","../libs/utils/scriptLoader.ts","../libs/hooks/useReCaptcha.ts","../libs/ReCaptchaV3.tsx"],"names":["TRANSACTIONAL_ACTIONS","RecaptchaService","grecaptchaInstance","__publicField","config","error","_a","action","recaptchaService","createRecaptchaService","SCRIPT_ID","ScriptLoader","trustedTypesParam","options","timeout","onLoad","onError","resolve","script","timeoutId","startTime","checkReady","loadRecaptchaScript","waitForRecaptcha","useReCaptcha","siteKey","hl","trustedTypes","autoExecute","onVerify","isReady","setIsReady","useState","isLoading","setIsLoading","hasAutoExecutedRef","useRef","currentActionRef","currentSiteKeyRef","stableOnVerify","stableOnError","useEffect","execute","useCallback","_b","result","handleAutoExecute","validation","reset","isMounted","errorInstance","ReCaptchaV3","forwardRef","ref","useImperativeHandle","ReCaptchaV3_default"],"mappings":"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","file":"index.mjs","sourcesContent":["export enum TRANSACTIONAL_ACTIONS {\r\n LOGIN = \"login\",\r\n REGISTER = \"register\",\r\n PURCHASE = \"purchase\",\r\n PAYMENT = \"payment\",\r\n SUBMIT = \"submit\",\r\n CHECKOUT = \"checkout\",\r\n ORDER = \"order\",\r\n SIGNUP = \"signup\",\r\n SIGNIN = \"signin\",\r\n TRANSACTION = \"transaction\",\r\n}\r\n","import { TRANSACTIONAL_ACTIONS } from \"../enums/TRANSACTIONAL_ACTIONS\";\r\n\r\nexport interface RecaptchaConfig {\r\n siteKey: string;\r\n action: string;\r\n hl?: string;\r\n trustedTypes?: boolean;\r\n}\r\n\r\nexport interface RecaptchaExecutionResult {\r\n success: boolean;\r\n token?: string;\r\n error?: Error;\r\n}\r\n\r\nexport interface RecaptchaServiceInterface {\r\n execute(config: RecaptchaConfig): Promise<RecaptchaExecutionResult>;\r\n isReady(): boolean;\r\n reset(): void;\r\n validateAction(action: string): { isValid: boolean; warning?: string };\r\n}\r\n\r\nclass RecaptchaService implements RecaptchaServiceInterface {\r\n private grecaptcha: any = null;\r\n\r\n constructor(grecaptchaInstance?: any) {\r\n this.grecaptcha =\r\n grecaptchaInstance ||\r\n (typeof window !== \"undefined\" ? window.grecaptcha : null);\r\n }\r\n\r\n async execute(config: RecaptchaConfig): Promise<RecaptchaExecutionResult> {\r\n if (!this.isReady()) {\r\n return {\r\n success: false,\r\n error: new Error(\"reCAPTCHA not ready\"),\r\n };\r\n }\r\n\r\n try {\r\n const token = await this.grecaptcha.execute(config.siteKey, {\r\n action: config.action,\r\n });\r\n\r\n return {\r\n success: true,\r\n token,\r\n };\r\n } catch (error) {\r\n return {\r\n success: false,\r\n error:\r\n error instanceof Error ? error : new Error(\"Unknown reCAPTCHA error\"),\r\n };\r\n }\r\n }\r\n\r\n isReady(): boolean {\r\n return !!(this.grecaptcha && typeof this.grecaptcha.execute === \"function\");\r\n }\r\n\r\n reset(): void {\r\n if (this.grecaptcha?.reset) {\r\n this.grecaptcha.reset();\r\n }\r\n }\r\n\r\n validateAction(action: string): { isValid: boolean; warning?: string } {\r\n const isTransactional = Object.values(TRANSACTIONAL_ACTIONS).includes(\r\n action as TRANSACTIONAL_ACTIONS\r\n ) as boolean;\r\n\r\n if (isTransactional) {\r\n return {\r\n isValid: true,\r\n warning: `autoExecute no recomendado para acción transaccional: \"${action}\". Los tokens expiran en 2 minutos.`,\r\n };\r\n }\r\n\r\n return { isValid: true };\r\n }\r\n\r\n // Método para inyectar grecaptcha (útil para testing)\r\n setGrecaptcha(grecaptchaInstance: any): void {\r\n this.grecaptcha = grecaptchaInstance;\r\n }\r\n}\r\n\r\n// Singleton para uso global\r\nexport const recaptchaService = new RecaptchaService();\r\n\r\n// Factory para crear instancias (útil para testing)\r\nexport const createRecaptchaService = (\r\n grecaptchaInstance?: any\r\n): RecaptchaService => {\r\n return new RecaptchaService(grecaptchaInstance);\r\n};\r\n","export const SCRIPT_ID = \"recaptcha-v3-script\" as const;\r\n","import { POLLING_INTERVAL } from \"../constants/POLLING_INTERVAL\";\r\nimport { SCRIPT_ID } from \"../constants/SCRIPT_ID\";\r\n\r\nexport interface ScriptConfig {\r\n siteKey: string;\r\n hl?: string;\r\n trustedTypes?: boolean;\r\n}\r\n\r\nexport interface ScriptLoadResult {\r\n success: boolean;\r\n error?: Error;\r\n}\r\n\r\nexport interface ScriptLoaderOptions {\r\n timeout?: number;\r\n onLoad?: () => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport class ScriptLoader {\r\n private static buildScriptUrl(config: ScriptConfig): string {\r\n const trustedTypesParam = config.trustedTypes ? \"&trustedtypes=true\" : \"\";\r\n return `https://www.google.com/recaptcha/api.js?render=${config.siteKey}${\r\n config.hl ? `&hl=${config.hl}` : \"\"\r\n }${trustedTypesParam}`;\r\n }\r\n\r\n static isScriptLoaded(): boolean {\r\n return !!document.getElementById(SCRIPT_ID);\r\n }\r\n\r\n static async loadScript(\r\n config: ScriptConfig,\r\n options: ScriptLoaderOptions = {}\r\n ): Promise<ScriptLoadResult> {\r\n const { timeout = 5000, onLoad, onError } = options;\r\n\r\n return new Promise((resolve) => {\r\n if (this.isScriptLoaded()) {\r\n resolve({ success: true });\r\n return;\r\n }\r\n\r\n const script = document.createElement(\"script\");\r\n script.id = SCRIPT_ID;\r\n script.src = this.buildScriptUrl(config);\r\n script.async = true;\r\n script.defer = true;\r\n\r\n const timeoutId = setTimeout(() => {\r\n const error = new Error(`Script loading timeout after ${timeout}ms`);\r\n onError?.(error);\r\n resolve({ success: false, error });\r\n }, timeout);\r\n\r\n script.onload = () => {\r\n clearTimeout(timeoutId);\r\n onLoad?.();\r\n resolve({ success: true });\r\n };\r\n\r\n script.onerror = () => {\r\n clearTimeout(timeoutId);\r\n const error = new Error(\"Failed to load reCAPTCHA script\");\r\n onError?.(error);\r\n resolve({ success: false, error });\r\n };\r\n\r\n document.head.appendChild(script);\r\n });\r\n }\r\n\r\n static waitForGrecaptcha(timeout: number = 5000): Promise<boolean> {\r\n return new Promise((resolve) => {\r\n if (typeof window !== \"undefined\" && window.grecaptcha) {\r\n resolve(true);\r\n return;\r\n }\r\n\r\n const startTime = Date.now();\r\n const checkReady = setInterval(() => {\r\n if (typeof window !== \"undefined\" && window.grecaptcha) {\r\n clearInterval(checkReady);\r\n resolve(true);\r\n } else if (Date.now() - startTime > timeout) {\r\n clearInterval(checkReady);\r\n resolve(false);\r\n }\r\n }, POLLING_INTERVAL);\r\n });\r\n }\r\n}\r\n\r\n// Función utilitaria para uso directo\r\nexport const loadRecaptchaScript = (\r\n config: ScriptConfig,\r\n options?: ScriptLoaderOptions\r\n): Promise<ScriptLoadResult> => {\r\n return ScriptLoader.loadScript(config, options);\r\n};\r\n\r\n// Función utilitaria para esperar grecaptcha\r\nexport const waitForRecaptcha = (timeout?: number): Promise<boolean> => {\r\n return ScriptLoader.waitForGrecaptcha(timeout);\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport {\r\n RecaptchaConfig,\r\n recaptchaService,\r\n} from \"../services/RecaptchaService\";\r\nimport { loadRecaptchaScript, waitForRecaptcha } from \"../utils/scriptLoader\";\r\n\r\nexport interface UseRecaptchaOptions extends RecaptchaConfig {\r\n timeout?: number;\r\n autoExecute?: boolean;\r\n onVerify?: (token: string) => void;\r\n onError?: (error: Error) => void;\r\n}\r\n\r\nexport interface UseRecaptchaReturn {\r\n isReady: boolean;\r\n execute: () => Promise<string | null>;\r\n reset: () => void;\r\n isLoading: boolean;\r\n}\r\n\r\nexport function useReCaptcha(options: UseRecaptchaOptions): UseRecaptchaReturn {\r\n const {\r\n siteKey,\r\n action,\r\n hl,\r\n trustedTypes = false,\r\n timeout = 5000,\r\n autoExecute = false,\r\n onVerify,\r\n onError,\r\n } = options;\r\n\r\n const [isReady, setIsReady] = useState(false);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const hasAutoExecutedRef = useRef(false);\r\n const currentActionRef = useRef(action);\r\n const currentSiteKeyRef = useRef(siteKey);\r\n\r\n // Referencias estables para callbacks\r\n const stableOnVerify = useRef(onVerify);\r\n const stableOnError = useRef(onError);\r\n\r\n // Actualizar referencias sin causar re-renders\r\n stableOnVerify.current = onVerify;\r\n stableOnError.current = onError;\r\n\r\n // Reset del estado cuando cambian props críticas\r\n useEffect(() => {\r\n if (\r\n currentSiteKeyRef.current !== siteKey ||\r\n currentActionRef.current !== action\r\n ) {\r\n hasAutoExecutedRef.current = false;\r\n currentSiteKeyRef.current = siteKey;\r\n currentActionRef.current = action;\r\n }\r\n }, [siteKey, action]);\r\n\r\n // Función para ejecutar reCAPTCHA\r\n const execute = useCallback(async (): Promise<string | null> => {\r\n const config: RecaptchaConfig = { siteKey, action, hl, trustedTypes };\r\n const result = await recaptchaService.execute(config);\r\n\r\n if (result.success && result.token) {\r\n stableOnVerify.current?.(result.token);\r\n return result.token;\r\n } else {\r\n stableOnError.current?.(result.error || new Error(\"Unknown error\"));\r\n return null;\r\n }\r\n }, [siteKey, action, hl, trustedTypes]);\r\n\r\n // Auto-ejecución controlada\r\n const handleAutoExecute = useCallback(async () => {\r\n if (!autoExecute || hasAutoExecutedRef.current || !isReady) {\r\n return;\r\n }\r\n\r\n hasAutoExecutedRef.current = true;\r\n\r\n // Validar acción con advertencias\r\n const validation = recaptchaService.validateAction(action);\r\n if (validation.warning) {\r\n console.warn(`⚠️ ${validation.warning}`);\r\n }\r\n\r\n await execute();\r\n }, [autoExecute, isReady, action, execute]);\r\n\r\n // Reset function\r\n const reset = useCallback(() => {\r\n hasAutoExecutedRef.current = false;\r\n recaptchaService.reset();\r\n }, []);\r\n\r\n // Inicialización del script y grecaptcha\r\n useEffect(() => {\r\n let isMounted = true;\r\n\r\n const initializeRecaptcha = async () => {\r\n try {\r\n setIsLoading(true);\r\n\r\n // Cargar script si es necesario\r\n const scriptResult = await loadRecaptchaScript(\r\n { siteKey, hl, trustedTypes },\r\n {\r\n timeout,\r\n onError: (error) => stableOnError.current?.(error),\r\n }\r\n );\r\n\r\n if (!scriptResult.success) {\r\n return;\r\n }\r\n\r\n // Esperar a que grecaptcha esté disponible\r\n const grecaptchaReady = await waitForRecaptcha(timeout);\r\n\r\n if (!grecaptchaReady) {\r\n stableOnError.current?.(new Error(\"reCAPTCHA failed to initialize\"));\r\n return;\r\n }\r\n\r\n // Configurar el servicio con la instancia de grecaptcha\r\n if (typeof window !== \"undefined\" && window.grecaptcha) {\r\n recaptchaService.setGrecaptcha(window.grecaptcha);\r\n\r\n window.grecaptcha.ready(() => {\r\n if (isMounted) {\r\n setIsReady(true);\r\n setIsLoading(false);\r\n // Auto-ejecutar si está habilitado\r\n handleAutoExecute();\r\n }\r\n });\r\n }\r\n } catch (error) {\r\n if (isMounted) {\r\n const errorInstance =\r\n error instanceof Error ? error : new Error(\"Initialization failed\");\r\n stableOnError.current?.(errorInstance);\r\n setIsLoading(false);\r\n }\r\n }\r\n };\r\n\r\n initializeRecaptcha();\r\n\r\n return () => {\r\n isMounted = false;\r\n };\r\n }, [siteKey, hl, trustedTypes, timeout, handleAutoExecute]);\r\n\r\n return {\r\n isReady,\r\n execute,\r\n reset,\r\n isLoading,\r\n };\r\n}\r\n","import { forwardRef, useImperativeHandle } from \"react\";\r\nimport { useReCaptcha } from \"./hooks/useReCaptcha\";\r\nimport {\r\n RecaptchaV3Props,\r\n RecaptchaV3Ref,\r\n} from \"./interfaces/reCaptchaV3.interface\";\r\n\r\n/**\r\n * Componente ReCAPTCHA v3 con arquitectura limpia\r\n *\r\n * Este componente es una capa de presentación delgada que delega toda la lógica\r\n * al hook personalizado useRecaptcha, siguiendo principios de arquitectura limpia.\r\n *\r\n * @example\r\n * ```tsx\r\n * // Uso básico\r\n * <RecaptchaV3\r\n * siteKey=\"your-site-key\"\r\n * action=\"page_view\"\r\n * onVerify={(token) => console.log(token)}\r\n * />\r\n *\r\n * // Con auto-ejecución para analytics\r\n * <RecaptchaV3\r\n * siteKey=\"your-site-key\"\r\n * action=\"analytics\"\r\n * autoExecute={true}\r\n * onVerify={(token) => sendAnalytics(token)}\r\n * />\r\n *\r\n * // Con referencia para ejecución manual\r\n * const recaptchaRef = useRef<RecaptchaV3Ref>(null);\r\n *\r\n * const handleSubmit = async () => {\r\n * const token = await recaptchaRef.current?.execute();\r\n * if (token) {\r\n * // Enviar formulario con token\r\n * }\r\n * };\r\n *\r\n * <ReCaptchaV3\r\n * ref={recaptchaRef}\r\n * siteKey=\"your-site-key\"\r\n * action=\"form_submit\"\r\n * onVerify={(token) => setFormToken(token)}\r\n * />\r\n * ```\r\n */\r\nconst ReCaptchaV3 = forwardRef<RecaptchaV3Ref, RecaptchaV3Props>(\r\n (\r\n {\r\n siteKey,\r\n action,\r\n hl,\r\n onVerify,\r\n onError,\r\n trustedTypes = false,\r\n timeout = 5000,\r\n autoExecute = false,\r\n },\r\n ref\r\n ) => {\r\n // Toda la lógica delegada al hook personalizado\r\n const { isReady, execute, reset, isLoading } = useReCaptcha({\r\n siteKey,\r\n action,\r\n hl,\r\n trustedTypes,\r\n timeout,\r\n autoExecute,\r\n onVerify,\r\n onError,\r\n });\r\n\r\n // Exposición de la API pública a través del ref\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n execute,\r\n isReady: () => isReady,\r\n reset,\r\n isLoading: () => isLoading,\r\n }),\r\n [execute, isReady, reset, isLoading]\r\n );\r\n\r\n // Componente sin renderizado visual (headless)\r\n return null;\r\n }\r\n);\r\n\r\nReCaptchaV3.displayName = \"ReCaptchaV3\";\r\n\r\nexport default ReCaptchaV3;\r\n"]}