@anilkumarthakur/vue3-recaptcha
Version:
A Vue 3 plugin for Google reCAPTCHA v2 (checkbox & invisible) and v3 integration
1 lines • 54.4 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":["defineComponent","ref","computed","watch","onMounted","onBeforeUnmount","inject","createVNode","_createVNode","RECAPTCHA_INJECTION_KEY","loadRecaptchaScript","isRecaptchaLoaded","RecaptchaV2Checkbox","name","props","siteKey","type","String","default","undefined","theme","size","tabindex","Number","modelValue","emits","token","verify","expire","error","err","Error","load","setup","emit","expose","containerRef","widgetId","isLoaded","context","value","console","onVerify","onExpire","onError","renderWidget","container","key","version","Promise","resolve","check","window","setInterval","grecaptcha","render","clearInterval","sitekey","callback","error-callback","reset","currentWidgetId","getResponse","innerHTML","display","defineComponent","ref","computed","onMounted","onBeforeUnmount","inject","createVNode","_createVNode","RECAPTCHA_INJECTION_KEY","loadRecaptchaScript","isRecaptchaLoaded","RecaptchaV2Invisible","name","props","siteKey","type","String","default","undefined","badge","tabindex","Number","modelValue","emits","token","verify","expire","error","err","Error","load","setup","emit","expose","containerRef","widgetId","isLoaded","pendingResolve","pendingReject","context","value","console","onVerify","resolve","onExpire","onError","reject","renderWidget","container","key","version","Promise","check","window","setInterval","grecaptcha","render","clearInterval","sitekey","size","callback","error-callback","execute","currentWidgetId","timeoutHandle","setTimeout","rejectFn","clearTimeout","reset","getResponse","display","defineComponent","ref","computed","onMounted","onBeforeUnmount","inject","watch","createVNode","_createVNode","RECAPTCHA_INJECTION_KEY","loadRecaptchaScript","RecaptchaV3","name","props","siteKey","type","String","default","undefined","action","badge","hideBadge","Boolean","modelValue","emits","token","verify","error","err","Error","load","setup","emit","expose","isLoaded","isLoading","tokenExpiryTimer","context","value","console","updateBadgeVisibility","document","querySelector","style","visibility","version","onLoad","onError","loadError","execute","window","grecaptcha","key","actionName","clearTimeout","setTimeout","executeError","then","loadRecaptcha","display"],"sources":["../src/package/types/index.ts","../src/package/utils/script-loader.ts","../src/package/components/RecaptchaV2Checkbox.tsx","../src/package/components/RecaptchaV2Invisible.tsx","../src/package/components/RecaptchaV3.tsx","../src/package/composables/useRecaptchaV2.ts","../src/package/composables/useRecaptchaV3.ts","../src/package/index.ts"],"sourcesContent":["/**\n * Vue3 Recaptcha - Type Definitions\n * Supports Google reCAPTCHA v2 (checkbox, invisible) and v3\n */\n\nimport type { Ref, InjectionKey } from 'vue'\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\nexport type RecaptchaVersion = 'v2' | 'v3'\nexport type RecaptchaV2Type = 'checkbox' | 'invisible'\nexport type RecaptchaTheme = 'light' | 'dark'\nexport type RecaptchaSize = 'normal' | 'compact' | 'invisible'\nexport type RecaptchaBadgePosition = 'bottomright' | 'bottomleft' | 'inline'\n\n// ============================================================================\n// Plugin Options\n// ============================================================================\n\nexport interface RecaptchaPluginOptions {\n /**\n * Your reCAPTCHA site key from Google Console\n */\n siteKey: string\n\n /**\n * Version of reCAPTCHA to use\n * @default 'v3'\n */\n version?: RecaptchaVersion\n\n /**\n * Whether to load the script automatically\n * @default true\n */\n autoLoad?: boolean\n\n /**\n * Language code for reCAPTCHA widget\n * @see https://developers.google.com/recaptcha/docs/language\n */\n language?: string\n\n /**\n * Custom script URL (for enterprise or self-hosted)\n */\n scriptUrl?: string\n\n /**\n * Action name for reCAPTCHA v3 analytics\n * @default 'submit'\n */\n action?: string\n\n /**\n * Callback when script is loaded\n */\n onLoad?: () => void\n\n /**\n * Callback when script fails to load\n */\n onError?: (error: Error) => void\n}\n\n// ============================================================================\n// V2 Checkbox Props\n// ============================================================================\n\nexport interface RecaptchaV2CheckboxProps {\n /**\n * Site key (overrides plugin config)\n */\n siteKey?: string\n\n /**\n * Color theme of the widget\n * @default 'light'\n */\n theme?: RecaptchaTheme\n\n /**\n * Size of the widget\n * @default 'normal'\n */\n size?: 'normal' | 'compact'\n\n /**\n * Tabindex for accessibility\n * @default 0\n */\n tabindex?: number\n\n /**\n * v-model binding for token\n */\n modelValue?: string\n}\n\nexport interface RecaptchaV2CheckboxEmits {\n (e: 'update:modelValue', token: string): void\n (e: 'verify', token: string): void\n (e: 'expire'): void\n (e: 'error', error: Error): void\n (e: 'load'): void\n}\n\n// ============================================================================\n// V2 Invisible Props\n// ============================================================================\n\nexport interface RecaptchaV2InvisibleProps {\n /**\n * Site key (overrides plugin config)\n */\n siteKey?: string\n\n /**\n * Badge position\n * @default 'bottomright'\n */\n badge?: RecaptchaBadgePosition\n\n /**\n * Tabindex for accessibility\n * @default 0\n */\n tabindex?: number\n\n /**\n * v-model binding for token\n */\n modelValue?: string\n}\n\nexport interface RecaptchaV2InvisibleEmits {\n (e: 'update:modelValue', token: string): void\n (e: 'verify', token: string): void\n (e: 'expire'): void\n (e: 'error', error: Error): void\n (e: 'load'): void\n}\n\n// ============================================================================\n// V3 Props\n// ============================================================================\n\nexport interface RecaptchaV3Props {\n /**\n * Site key (overrides plugin config)\n */\n siteKey?: string\n\n /**\n * Action name for analytics (e.g., 'login', 'signup', 'submit')\n */\n action?: string\n\n /**\n * Badge position\n * @default 'bottomright'\n */\n badge?: RecaptchaBadgePosition\n\n /**\n * Whether to hide the badge\n * @default false\n */\n hideBadge?: boolean\n\n /**\n * v-model binding for token\n */\n modelValue?: string\n}\n\nexport interface RecaptchaV3Emits {\n (e: 'update:modelValue', token: string): void\n (e: 'verify', token: string): void\n (e: 'error', error: Error): void\n (e: 'load'): void\n}\n\n// ============================================================================\n// Composable Types\n// ============================================================================\n\nexport interface UseRecaptchaV2Options {\n siteKey?: string\n theme?: RecaptchaTheme\n size?: RecaptchaSize\n badge?: RecaptchaBadgePosition\n tabindex?: number\n}\n\nexport interface UseRecaptchaV3Options {\n siteKey?: string\n action?: string\n}\n\nexport interface UseRecaptchaV2Return {\n token: Readonly<Ref<string>>\n isReady: Readonly<Ref<boolean>>\n isLoading: Readonly<Ref<boolean>>\n error: Readonly<Ref<Error | null>>\n widgetId: Readonly<Ref<number | null>>\n render: (container: HTMLElement | string) => Promise<number>\n execute: () => Promise<string>\n reset: () => void\n getResponse: () => string\n}\n\nexport interface UseRecaptchaV3Return {\n token: Readonly<Ref<string>>\n isReady: Readonly<Ref<boolean>>\n isLoading: Readonly<Ref<boolean>>\n error: Readonly<Ref<Error | null>>\n execute: (action?: string) => Promise<string>\n}\n\n// ============================================================================\n// Google reCAPTCHA Global Types\n// ============================================================================\n\nexport interface GrecaptchaV2RenderParameters {\n sitekey: string\n theme?: RecaptchaTheme\n size?: RecaptchaSize\n tabindex?: number\n callback?: (token: string) => void\n 'expired-callback'?: () => void\n 'error-callback'?: () => void\n}\n\nexport interface GrecaptchaV2 {\n render: (container: HTMLElement | string, parameters: GrecaptchaV2RenderParameters) => number\n execute: (widgetId?: number) => void\n reset: (widgetId?: number) => void\n getResponse: (widgetId?: number) => string\n}\n\nexport interface GrecaptchaV3 {\n ready: (callback: () => void) => void\n execute: (siteKey: string, options: { action: string }) => Promise<string>\n}\n\n/**\n * Combined grecaptcha interface with properly overloaded execute method.\n * Covers both v2 (widget-based) and v3 (score-based) API surfaces.\n */\nexport interface Grecaptcha {\n render: (container: HTMLElement | string, parameters: GrecaptchaV2RenderParameters) => number\n execute(widgetId?: number): void\n execute(siteKey: string, options: { action: string }): Promise<string>\n reset: (widgetId?: number) => void\n getResponse: (widgetId?: number) => string\n ready: (callback: () => void) => void\n}\n\ndeclare global {\n interface Window {\n grecaptcha: Grecaptcha\n ___grecaptcha_cfg?: {\n explicit?: boolean\n }\n }\n}\n\n// ============================================================================\n// Component Instance Types (for typing template refs)\n// ============================================================================\n\nexport interface RecaptchaV2CheckboxInstance {\n reset: () => void\n getResponse: () => string\n widgetId: Ref<number | null>\n}\n\nexport interface RecaptchaV2InvisibleInstance {\n execute: () => Promise<string>\n reset: () => void\n getResponse: () => string\n widgetId: Ref<number | null>\n}\n\nexport interface RecaptchaV3Instance {\n load: () => Promise<void>\n /** @deprecated Use `load` instead */\n loadRecaptcha: () => Promise<void>\n execute: (action?: string) => Promise<string>\n isLoaded: Ref<boolean>\n isLoading: Ref<boolean>\n error: Ref<Error | null>\n}\n\n// ============================================================================\n// Injection Keys\n// ============================================================================\n\nexport const RECAPTCHA_INJECTION_KEY: InjectionKey<RecaptchaContext> = Symbol('vue3-recaptcha')\n\nexport interface RecaptchaContext {\n siteKey: string\n version: RecaptchaVersion\n isLoaded: Ref<boolean>\n loadScript: () => Promise<void>\n}\n","/**\n * Script Loader Utility\n * Handles loading Google reCAPTCHA scripts\n */\n\nimport type { RecaptchaVersion } from '../types'\n\nexport interface ScriptLoaderOptions {\n siteKey: string\n version: RecaptchaVersion\n language?: string\n scriptUrl?: string\n onLoad?: () => void\n onError?: (error: Error) => void\n}\n\nconst SCRIPT_ID = 'vue3-recaptcha-script'\nconst RECAPTCHA_SCRIPT_URL = 'https://www.google.com/recaptcha/api.js'\n\nlet loadPromise: Promise<void> | null = null\nlet isLoaded = false\n\n/**\n * Check if reCAPTCHA script is already loaded and ready\n */\nexport function isRecaptchaLoaded(): boolean {\n return isLoaded && typeof window !== 'undefined' && typeof window.grecaptcha !== 'undefined'\n}\n\n/**\n * Get existing script element by id\n */\nexport function getExistingScript(): HTMLScriptElement | null {\n return document.querySelector<HTMLScriptElement>(`#${SCRIPT_ID}`)\n}\n\n/**\n * Remove existing script and reset load state\n */\nexport function removeScript(): void {\n const existingScript = getExistingScript()\n if (existingScript) {\n existingScript.remove()\n }\n loadPromise = null\n isLoaded = false\n}\n\n/**\n * Build the reCAPTCHA script URL from options\n */\nexport function buildScriptUrl(options: ScriptLoaderOptions): string {\n const { siteKey, version, language, scriptUrl } = options\n\n if (scriptUrl) {\n return scriptUrl\n }\n\n const params = new URLSearchParams()\n\n if (version === 'v3') {\n params.set('render', siteKey)\n } else {\n params.set('render', 'explicit')\n if (typeof window !== 'undefined') {\n window.___grecaptcha_cfg = window.___grecaptcha_cfg ?? {}\n window.___grecaptcha_cfg.explicit = true\n }\n }\n\n if (language) {\n params.set('hl', language)\n }\n\n return `${RECAPTCHA_SCRIPT_URL}?${params.toString()}`\n}\n\n/**\n * Load the reCAPTCHA script. Returns a shared promise if loading is already in progress.\n */\nexport function loadRecaptchaScript(options: ScriptLoaderOptions): Promise<void> {\n if (loadPromise) {\n return loadPromise\n }\n\n if (isRecaptchaLoaded()) {\n return Promise.resolve()\n }\n\n loadPromise = new Promise<void>((resolve, reject) => {\n const existingScript = getExistingScript()\n\n if (existingScript) {\n if (isRecaptchaLoaded()) {\n resolve()\n return\n }\n\n existingScript.addEventListener('load', () => {\n isLoaded = true\n options.onLoad?.()\n resolve()\n })\n\n existingScript.addEventListener('error', () => {\n const error = new Error('Failed to load reCAPTCHA script')\n options.onError?.(error)\n reject(error)\n })\n\n return\n }\n\n const script = document.createElement('script')\n script.id = SCRIPT_ID\n script.src = buildScriptUrl(options)\n script.async = true\n script.defer = true\n\n script.onload = () => {\n if (options.version === 'v3') {\n window.grecaptcha.ready(() => {\n isLoaded = true\n options.onLoad?.()\n resolve()\n })\n } else {\n isLoaded = true\n options.onLoad?.()\n resolve()\n }\n }\n\n script.onerror = () => {\n const error = new Error('Failed to load reCAPTCHA script')\n loadPromise = null\n options.onError?.(error)\n reject(error)\n }\n\n document.head.appendChild(script)\n })\n\n return loadPromise\n}\n\n/**\n * Wait for grecaptcha to become available, with a 10-second timeout.\n */\nexport function waitForRecaptcha(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (isRecaptchaLoaded()) {\n resolve()\n return\n }\n\n const timeout = window.setTimeout(() => {\n window.clearInterval(checkInterval)\n reject(new Error('Timeout waiting for reCAPTCHA to load'))\n }, 10000)\n\n const checkInterval = window.setInterval(() => {\n if (typeof window.grecaptcha !== 'undefined') {\n window.clearInterval(checkInterval)\n window.clearTimeout(timeout)\n\n if (typeof window.grecaptcha.ready === 'function') {\n window.grecaptcha.ready(() => {\n isLoaded = true\n resolve()\n })\n } else {\n isLoaded = true\n resolve()\n }\n }\n }, 100)\n })\n}\n","/**\n * RecaptchaV2Checkbox Component\n * Renders Google reCAPTCHA v2 checkbox widget\n */\n\nimport { defineComponent, ref, computed, watch, onMounted, onBeforeUnmount, inject } from 'vue'\nimport type { PropType } from 'vue'\nimport type { RecaptchaTheme, RecaptchaContext } from '../types'\nimport { RECAPTCHA_INJECTION_KEY } from '../types'\nimport { loadRecaptchaScript, isRecaptchaLoaded } from '../utils/script-loader'\n\nexport const RecaptchaV2Checkbox = defineComponent({\n name: 'RecaptchaV2Checkbox',\n\n props: {\n siteKey: {\n type: String,\n default: undefined\n },\n theme: {\n type: String as PropType<RecaptchaTheme>,\n default: 'light' as RecaptchaTheme\n },\n size: {\n type: String as PropType<'normal' | 'compact'>,\n default: 'normal' as 'normal' | 'compact'\n },\n tabindex: {\n type: Number,\n default: 0\n },\n modelValue: {\n type: String,\n default: ''\n }\n },\n\n emits: {\n 'update:modelValue': (token: string) => typeof token === 'string',\n verify: (token: string) => typeof token === 'string',\n expire: null,\n error: (err: Error) => err instanceof Error,\n load: null\n },\n\n setup(props, { emit, expose }) {\n const containerRef = ref<HTMLElement | null>(null)\n const widgetId = ref<number | null>(null)\n const isLoaded = ref(false)\n\n const context = inject<RecaptchaContext | undefined>(RECAPTCHA_INJECTION_KEY, undefined)\n const siteKey = computed<string>(() => props.siteKey ?? context?.siteKey ?? '')\n\n if (!siteKey.value) {\n console.error('[vue3-recaptcha] Site key is required for RecaptchaV2Checkbox')\n }\n\n function onVerify(token: string): void {\n emit('update:modelValue', token)\n emit('verify', token)\n }\n\n function onExpire(): void {\n emit('update:modelValue', '')\n emit('expire')\n }\n\n function onError(err: Error): void {\n emit('update:modelValue', '')\n emit('error', err)\n }\n\n async function renderWidget(): Promise<void> {\n const container = containerRef.value\n const key = siteKey.value\n if (!container || !key) return\n\n try {\n if (!isRecaptchaLoaded()) {\n await loadRecaptchaScript({ siteKey: key, version: 'v2' })\n }\n\n await new Promise<void>((resolve) => {\n const check = window.setInterval(() => {\n if (typeof window.grecaptcha?.render === 'function') {\n window.clearInterval(check)\n resolve()\n }\n }, 50)\n })\n\n widgetId.value = window.grecaptcha.render(container, {\n sitekey: key,\n theme: props.theme,\n size: props.size,\n tabindex: props.tabindex,\n callback: onVerify,\n 'expired-callback': onExpire,\n 'error-callback': () => {\n onError(new Error('reCAPTCHA verification error'))\n }\n })\n\n isLoaded.value = true\n emit('load')\n } catch (err) {\n onError(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n function reset(): void {\n const currentWidgetId = widgetId.value\n if (currentWidgetId !== null) {\n window.grecaptcha.reset(currentWidgetId)\n emit('update:modelValue', '')\n }\n }\n\n function getResponse(): string {\n const currentWidgetId = widgetId.value\n if (currentWidgetId !== null) {\n return window.grecaptcha.getResponse(currentWidgetId)\n }\n return ''\n }\n\n watch(\n () => [props.theme, props.size] as const,\n () => {\n const container = containerRef.value\n if (isLoaded.value && container) {\n container.innerHTML = ''\n widgetId.value = null\n isLoaded.value = false\n void renderWidget()\n }\n }\n )\n\n onMounted(() => {\n void renderWidget()\n })\n\n onBeforeUnmount(() => {\n widgetId.value = null\n })\n\n expose({ reset, getResponse, widgetId })\n\n return () => (\n <div\n ref={containerRef}\n class=\"vue3-recaptcha-v2-checkbox\"\n style={{ display: 'inline-block' }}\n />\n )\n }\n})\n\nexport default RecaptchaV2Checkbox\n","/**\n * RecaptchaV2Invisible Component\n * Renders Google reCAPTCHA v2 invisible widget\n */\n\nimport { defineComponent, ref, computed, onMounted, onBeforeUnmount, inject } from 'vue'\nimport type { PropType } from 'vue'\nimport type { RecaptchaBadgePosition, RecaptchaContext } from '../types'\nimport { RECAPTCHA_INJECTION_KEY } from '../types'\nimport { loadRecaptchaScript, isRecaptchaLoaded } from '../utils/script-loader'\n\nexport const RecaptchaV2Invisible = defineComponent({\n name: 'RecaptchaV2Invisible',\n\n props: {\n siteKey: {\n type: String,\n default: undefined\n },\n badge: {\n type: String as PropType<RecaptchaBadgePosition>,\n default: 'bottomright' as RecaptchaBadgePosition\n },\n tabindex: {\n type: Number,\n default: 0\n },\n modelValue: {\n type: String,\n default: ''\n }\n },\n\n emits: {\n 'update:modelValue': (token: string) => typeof token === 'string',\n verify: (token: string) => typeof token === 'string',\n expire: null,\n error: (err: Error) => err instanceof Error,\n load: null\n },\n\n setup(props, { emit, expose }) {\n const containerRef = ref<HTMLElement | null>(null)\n const widgetId = ref<number | null>(null)\n const isLoaded = ref(false)\n\n const pendingResolve = ref<((token: string) => void) | null>(null)\n const pendingReject = ref<((error: Error) => void) | null>(null)\n\n const context = inject<RecaptchaContext | undefined>(RECAPTCHA_INJECTION_KEY, undefined)\n const siteKey = computed<string>(() => props.siteKey ?? context?.siteKey ?? '')\n\n if (!siteKey.value) {\n console.error('[vue3-recaptcha] Site key is required for RecaptchaV2Invisible')\n }\n\n function onVerify(token: string): void {\n emit('update:modelValue', token)\n emit('verify', token)\n\n const resolve = pendingResolve.value\n if (resolve) {\n pendingResolve.value = null\n pendingReject.value = null\n resolve(token)\n }\n }\n\n function onExpire(): void {\n emit('update:modelValue', '')\n emit('expire')\n }\n\n function onError(err: Error): void {\n emit('update:modelValue', '')\n emit('error', err)\n\n const reject = pendingReject.value\n if (reject) {\n pendingResolve.value = null\n pendingReject.value = null\n reject(err)\n }\n }\n\n async function renderWidget(): Promise<void> {\n const container = containerRef.value\n const key = siteKey.value\n if (!container || !key) return\n\n try {\n if (!isRecaptchaLoaded()) {\n await loadRecaptchaScript({ siteKey: key, version: 'v2' })\n }\n\n await new Promise<void>((resolve) => {\n const check = window.setInterval(() => {\n if (typeof window.grecaptcha?.render === 'function') {\n window.clearInterval(check)\n resolve()\n }\n }, 50)\n })\n\n widgetId.value = window.grecaptcha.render(container, {\n sitekey: key,\n size: 'invisible',\n tabindex: props.tabindex,\n callback: onVerify,\n 'expired-callback': onExpire,\n 'error-callback': () => {\n onError(new Error('reCAPTCHA verification error'))\n }\n })\n\n isLoaded.value = true\n emit('load')\n } catch (err) {\n onError(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n async function execute(): Promise<string> {\n if (!isLoaded.value) {\n await renderWidget()\n }\n\n const currentWidgetId = widgetId.value\n if (currentWidgetId === null) {\n throw new Error('[vue3-recaptcha] Widget not initialized')\n }\n\n return new Promise<string>((resolve, reject) => {\n const timeoutHandle = window.setTimeout(() => {\n const rejectFn = pendingReject.value\n pendingResolve.value = null\n pendingReject.value = null\n rejectFn?.(new Error('[vue3-recaptcha] Verification timeout after 60 seconds'))\n }, 60000)\n\n pendingResolve.value = (token: string) => {\n window.clearTimeout(timeoutHandle)\n resolve(token)\n }\n\n pendingReject.value = (err: Error) => {\n window.clearTimeout(timeoutHandle)\n reject(err)\n }\n\n try {\n window.grecaptcha.execute(currentWidgetId)\n } catch (err) {\n window.clearTimeout(timeoutHandle)\n pendingResolve.value = null\n pendingReject.value = null\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n }\n\n function reset(): void {\n const currentWidgetId = widgetId.value\n if (currentWidgetId !== null) {\n window.grecaptcha.reset(currentWidgetId)\n emit('update:modelValue', '')\n pendingResolve.value = null\n pendingReject.value = null\n }\n }\n\n function getResponse(): string {\n const currentWidgetId = widgetId.value\n if (currentWidgetId !== null) {\n return window.grecaptcha.getResponse(currentWidgetId)\n }\n return ''\n }\n\n onMounted(() => {\n void renderWidget()\n })\n\n onBeforeUnmount(() => {\n widgetId.value = null\n pendingResolve.value = null\n pendingReject.value = null\n })\n\n expose({ execute, reset, getResponse, widgetId })\n\n return () => (\n <div ref={containerRef} class=\"vue3-recaptcha-v2-invisible\" style={{ display: 'none' }} />\n )\n }\n})\n\nexport default RecaptchaV2Invisible\n","/**\n * RecaptchaV3 Component\n * Provides Google reCAPTCHA v3 functionality\n * V3 is invisible and provides a score-based verification\n */\n\nimport { defineComponent, ref, computed, onMounted, onBeforeUnmount, inject, watch } from 'vue'\nimport type { PropType } from 'vue'\nimport type { RecaptchaBadgePosition, RecaptchaContext } from '../types'\nimport { RECAPTCHA_INJECTION_KEY } from '../types'\nimport { loadRecaptchaScript } from '../utils/script-loader'\n\nexport const RecaptchaV3 = defineComponent({\n name: 'RecaptchaV3',\n\n props: {\n siteKey: {\n type: String,\n default: undefined\n },\n action: {\n type: String,\n default: 'submit'\n },\n badge: {\n type: String as PropType<RecaptchaBadgePosition>,\n default: 'bottomright' as RecaptchaBadgePosition\n },\n hideBadge: {\n type: Boolean,\n default: false\n },\n modelValue: {\n type: String,\n default: ''\n }\n },\n\n emits: {\n 'update:modelValue': (token: string) => typeof token === 'string',\n verify: (token: string) => typeof token === 'string',\n error: (err: Error) => err instanceof Error,\n load: null\n },\n\n setup(props, { emit, expose }) {\n const isLoaded = ref(false)\n const isLoading = ref(false)\n const error = ref<Error | null>(null)\n let tokenExpiryTimer: number | undefined\n\n const context = inject<RecaptchaContext | undefined>(RECAPTCHA_INJECTION_KEY, undefined)\n const siteKey = computed<string>(() => props.siteKey ?? context?.siteKey ?? '')\n\n if (!siteKey.value) {\n console.error('[vue3-recaptcha] Site key is required for RecaptchaV3')\n }\n\n function updateBadgeVisibility(): void {\n if (typeof document === 'undefined') return\n const badge = document.querySelector<HTMLElement>('.grecaptcha-badge')\n if (badge) {\n badge.style.visibility = props.hideBadge ? 'hidden' : 'visible'\n }\n }\n\n async function load(): Promise<void> {\n if (isLoaded.value || isLoading.value) return\n\n isLoading.value = true\n error.value = null\n\n try {\n await loadRecaptchaScript({\n siteKey: siteKey.value,\n version: 'v3',\n onLoad: () => {\n isLoaded.value = true\n emit('load')\n },\n onError: (err) => {\n error.value = err\n emit('error', err)\n }\n })\n\n isLoaded.value = true\n } catch (err) {\n const loadError = err instanceof Error ? err : new Error(String(err))\n error.value = loadError\n emit('error', loadError)\n } finally {\n isLoading.value = false\n }\n }\n\n async function execute(action?: string): Promise<string> {\n if (!isLoaded.value) {\n await load()\n }\n\n if (typeof window.grecaptcha === 'undefined') {\n const err = new Error('[vue3-recaptcha] grecaptcha is not available')\n emit('error', err)\n throw err\n }\n\n const key = siteKey.value\n const actionName = action ?? props.action ?? 'submit'\n\n try {\n const token = await window.grecaptcha.execute(key, { action: actionName })\n\n emit('update:modelValue', token)\n emit('verify', token)\n\n if (tokenExpiryTimer !== undefined) {\n clearTimeout(tokenExpiryTimer as number)\n }\n\n // V3 tokens expire after 2 minutes — clear slightly before\n tokenExpiryTimer = setTimeout(() => {\n emit('update:modelValue', '')\n }, 110000) as unknown as number\n\n return token\n } catch (err) {\n const executeError = err instanceof Error ? err : new Error(String(err))\n error.value = executeError\n emit('error', executeError)\n emit('update:modelValue', '')\n throw executeError\n }\n }\n\n watch(() => props.hideBadge, updateBadgeVisibility)\n\n onMounted(() => {\n void load().then(() => {\n updateBadgeVisibility()\n })\n })\n\n onBeforeUnmount(() => {\n if (tokenExpiryTimer !== undefined) {\n clearTimeout(tokenExpiryTimer as number)\n }\n })\n\n expose({\n load,\n loadRecaptcha: load,\n execute,\n isLoaded,\n isLoading,\n error\n })\n\n return () => <div class=\"vue3-recaptcha-v3\" style={{ display: 'none' }} />\n }\n})\n\nexport default RecaptchaV3\n","/**\n * useRecaptchaV2 Composable\n * Provides reactive reCAPTCHA v2 functionality for Vue 3\n */\n\nimport { ref, readonly, inject, onUnmounted } from 'vue'\nimport type { UseRecaptchaV2Options, UseRecaptchaV2Return, RecaptchaContext } from '../types'\nimport { RECAPTCHA_INJECTION_KEY } from '../types'\nimport { loadRecaptchaScript, isRecaptchaLoaded } from '../utils/script-loader'\n\nexport function useRecaptchaV2(options: UseRecaptchaV2Options = {}): UseRecaptchaV2Return {\n const context = inject<RecaptchaContext | undefined>(RECAPTCHA_INJECTION_KEY, undefined)\n\n const resolvedSiteKey = options.siteKey ?? context?.siteKey\n if (!resolvedSiteKey) {\n throw new Error(\n '[vue3-recaptcha] Site key is required. Provide it via plugin options or composable options.'\n )\n }\n const siteKey: string = resolvedSiteKey\n\n const token = ref('')\n const isReady = ref(false)\n const isLoading = ref(false)\n const error = ref<Error | null>(null)\n const widgetId = ref<number | null>(null)\n\n /**\n * Load the reCAPTCHA script if not already loaded\n */\n async function ensureLoaded(): Promise<void> {\n if (isRecaptchaLoaded()) {\n isReady.value = true\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n await loadRecaptchaScript({\n siteKey,\n version: 'v2',\n onError: (err) => {\n error.value = err\n }\n })\n isReady.value = true\n } catch (err) {\n error.value = err instanceof Error ? err : new Error(String(err))\n throw error.value\n } finally {\n isLoading.value = false\n }\n }\n\n /**\n * Render the reCAPTCHA widget in a container\n */\n async function render(container: HTMLElement | string): Promise<number> {\n await ensureLoaded()\n\n if (widgetId.value !== null) {\n reset()\n }\n\n return new Promise<number>((resolve, reject) => {\n try {\n const id = window.grecaptcha.render(container, {\n sitekey: siteKey,\n theme: options.theme,\n size: options.size,\n tabindex: options.tabindex,\n callback: (responseToken: string) => {\n token.value = responseToken\n },\n 'expired-callback': () => {\n token.value = ''\n },\n 'error-callback': () => {\n error.value = new Error('reCAPTCHA verification error')\n token.value = ''\n }\n })\n\n widgetId.value = id\n resolve(id)\n } catch (err) {\n const renderError = err instanceof Error ? err : new Error(String(err))\n error.value = renderError\n reject(renderError)\n }\n })\n }\n\n /**\n * Execute invisible reCAPTCHA (for invisible size mode)\n */\n async function execute(): Promise<string> {\n await ensureLoaded()\n\n const currentWidgetId = widgetId.value\n if (currentWidgetId === null) {\n throw new Error('[vue3-recaptcha] Widget not rendered. Call render() first.')\n }\n\n return new Promise<string>((resolve, reject) => {\n const previousToken = token.value\n\n const intervalId = setInterval(() => {\n if (token.value && token.value !== previousToken) {\n clearInterval(intervalId as unknown as number)\n clearTimeout(timeoutId as unknown as number)\n resolve(token.value)\n }\n }, 100)\n\n const timeoutId = setTimeout(() => {\n clearInterval(intervalId as unknown as number)\n reject(new Error('[vue3-recaptcha] Execute timeout after 30 seconds'))\n }, 30000)\n\n try {\n window.grecaptcha.execute(currentWidgetId)\n } catch (err) {\n clearInterval(intervalId as unknown as number)\n clearTimeout(timeoutId as unknown as number)\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n }\n\n /**\n * Reset the widget\n */\n function reset(): void {\n const currentWidgetId = widgetId.value\n if (currentWidgetId !== null) {\n window.grecaptcha.reset(currentWidgetId)\n token.value = ''\n }\n }\n\n /**\n * Get the current response token from the widget\n */\n function getResponse(): string {\n const currentWidgetId = widgetId.value\n if (currentWidgetId !== null) {\n return window.grecaptcha.getResponse(currentWidgetId)\n }\n return token.value\n }\n\n onUnmounted(() => {\n widgetId.value = null\n token.value = ''\n })\n\n return {\n token: readonly(token),\n isReady: readonly(isReady),\n isLoading: readonly(isLoading),\n error: readonly(error),\n widgetId: readonly(widgetId),\n render,\n execute,\n reset,\n getResponse\n }\n}\n","/**\n * useRecaptchaV3 Composable\n * Provides reactive reCAPTCHA v3 functionality for Vue 3\n */\n\nimport { ref, readonly, inject, onUnmounted } from 'vue'\nimport type { UseRecaptchaV3Options, UseRecaptchaV3Return, RecaptchaContext } from '../types'\nimport { RECAPTCHA_INJECTION_KEY } from '../types'\nimport { loadRecaptchaScript, isRecaptchaLoaded } from '../utils/script-loader'\n\nexport function useRecaptchaV3(options: UseRecaptchaV3Options = {}): UseRecaptchaV3Return {\n const context = inject<RecaptchaContext | undefined>(RECAPTCHA_INJECTION_KEY, undefined)\n\n const resolvedSiteKey = options.siteKey ?? context?.siteKey\n if (!resolvedSiteKey) {\n throw new Error(\n '[vue3-recaptcha] Site key is required. Provide it via plugin options or composable options.'\n )\n }\n const siteKey: string = resolvedSiteKey\n\n const token = ref('')\n const isReady = ref(false)\n const isLoading = ref(false)\n const error = ref<Error | null>(null)\n\n let tokenRefreshTimer: number | undefined\n\n /**\n * Load the reCAPTCHA script if not already loaded\n */\n async function ensureLoaded(): Promise<void> {\n if (isRecaptchaLoaded()) {\n isReady.value = true\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n await loadRecaptchaScript({\n siteKey,\n version: 'v3',\n onError: (err) => {\n error.value = err\n }\n })\n isReady.value = true\n } catch (err) {\n error.value = err instanceof Error ? err : new Error(String(err))\n throw error.value\n } finally {\n isLoading.value = false\n }\n }\n\n /**\n * Execute reCAPTCHA v3 and get a token\n * @param action - Action name for analytics (overrides default)\n */\n async function execute(action?: string): Promise<string> {\n await ensureLoaded()\n\n const actionName = action ?? options.action ?? 'submit'\n\n isLoading.value = true\n error.value = null\n\n try {\n const responseToken = await window.grecaptcha.execute(siteKey, { action: actionName })\n token.value = responseToken\n\n if (tokenRefreshTimer !== undefined) {\n clearTimeout(tokenRefreshTimer as number)\n }\n\n // V3 tokens expire after 2 minutes — clear slightly before expiry\n tokenRefreshTimer = setTimeout(() => {\n token.value = ''\n }, 110000) as unknown as number\n\n return responseToken\n } catch (err) {\n const executeError = err instanceof Error ? err : new Error(String(err))\n error.value = executeError\n token.value = ''\n throw executeError\n } finally {\n isLoading.value = false\n }\n }\n\n onUnmounted(() => {\n if (tokenRefreshTimer !== undefined) {\n clearTimeout(tokenRefreshTimer as number)\n }\n token.value = ''\n })\n\n return {\n token: readonly(token),\n isReady: readonly(isReady),\n isLoading: readonly(isLoading),\n error: readonly(error),\n execute\n }\n}\n","/**\n * Vue3 Recaptcha\n * A Vue 3 plugin for Google reCAPTCHA v2 and v3\n *\n * @packageDocumentation\n */\n\nimport type { App, Plugin } from 'vue'\nimport { ref } from 'vue'\nimport type { RecaptchaPluginOptions, RecaptchaContext, RecaptchaVersion } from './types'\nimport { RECAPTCHA_INJECTION_KEY } from './types'\nimport { loadRecaptchaScript } from './utils/script-loader'\n\n// Components\nimport { RecaptchaV2Checkbox } from './components/RecaptchaV2Checkbox'\nimport { RecaptchaV2Invisible } from './components/RecaptchaV2Invisible'\nimport { RecaptchaV3 } from './components/RecaptchaV3'\n\n// Composables\nimport { useRecaptchaV2 } from './composables/useRecaptchaV2'\nimport { useRecaptchaV3 } from './composables/useRecaptchaV3'\n\n/**\n * Vue 3 Recaptcha Plugin\n *\n * @example\n * ```ts\n * import { createApp } from 'vue'\n * import { VueRecaptchaPlugin } from '@anilkumarthakur/vue3-recaptcha'\n *\n * const app = createApp(App)\n * app.use(VueRecaptchaPlugin, {\n * siteKey: 'your-site-key',\n * version: 'v3'\n * })\n * ```\n */\nexport const VueRecaptchaPlugin: Plugin = {\n install(app: App, options: RecaptchaPluginOptions) {\n if (!options?.siteKey) {\n console.error('[vue3-recaptcha] Site key is required')\n return\n }\n\n const version: RecaptchaVersion = options.version ?? 'v3'\n const isLoaded = ref(false)\n\n app.component('RecaptchaV2Checkbox', RecaptchaV2Checkbox)\n app.component('RecaptchaV2Invisible', RecaptchaV2Invisible)\n app.component('RecaptchaV2', RecaptchaV2Checkbox)\n app.component('RecaptchaV3', RecaptchaV3)\n app.component('Recaptcha', RecaptchaV3)\n\n const loadScript = async (): Promise<void> => {\n await loadRecaptchaScript({\n siteKey: options.siteKey,\n version,\n language: options.language,\n scriptUrl: options.scriptUrl,\n onLoad: () => {\n isLoaded.value = true\n options.onLoad?.()\n }\n // onError is intentionally omitted — errors propagate as rejections\n // and are handled once in the .catch below\n })\n isLoaded.value = true\n }\n\n const context: RecaptchaContext = {\n siteKey: options.siteKey,\n version,\n isLoaded,\n loadScript\n }\n\n app.provide(RECAPTCHA_INJECTION_KEY, context)\n\n if (options.autoLoad !== false) {\n loadScript().catch((err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err))\n console.error('[vue3-recaptcha] Failed to auto-load script:', error)\n options.onError?.(error)\n })\n }\n }\n}\n\n// Legacy alias for backward compatibility\nexport const recaptchaPlugin = VueRecaptchaPlugin\n\n// ============================================================================\n// Export Components\n// ============================================================================\n\nexport { RecaptchaV2Checkbox, RecaptchaV2Invisible, RecaptchaV3 }\n\nexport { RecaptchaV2Checkbox as RecaptchaV2 }\nexport { RecaptchaV3 as Recaptcha }\n\n// ============================================================================\n// Export Composables\n// ============================================================================\n\nexport { useRecaptchaV2, useRecaptchaV3 }\n\n// ============================================================================\n// Export Types\n// ============================================================================\n\nexport type {\n RecaptchaPluginOptions,\n RecaptchaContext,\n RecaptchaVersion,\n RecaptchaV2CheckboxProps,\n RecaptchaV2CheckboxEmits,\n RecaptchaV2InvisibleProps,\n RecaptchaV2InvisibleEmits,\n RecaptchaV3Props,\n RecaptchaV3Emits,\n UseRecaptchaV2Options,\n UseRecaptchaV2Return,\n UseRecaptchaV3Options,\n UseRecaptchaV3Return,\n RecaptchaTheme,\n RecaptchaSize,\n RecaptchaBadgePosition,\n Grecaptcha,\n GrecaptchaV2,\n GrecaptchaV3,\n GrecaptchaV2RenderParameters,\n RecaptchaV2CheckboxInstance,\n RecaptchaV2InvisibleInstance,\n RecaptchaV3Instance\n} from './types'\n\nexport { RECAPTCHA_INJECTION_KEY } from './types'\n\n// ============================================================================\n// Export Utilities\n// ============================================================================\n\nexport {\n loadRecaptchaScript,\n isRecaptchaLoaded,\n removeScript,\n waitForRecaptcha\n} from './utils/script-loader'\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default VueRecaptchaPlugin\n"],"mappings":"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