feat(ssr): use hcaptcha

This commit is contained in:
DIYgod 2025-05-25 12:37:58 +08:00
parent 22aec9207e
commit 4ba047ebc9
No known key found for this signature in database
9 changed files with 104 additions and 248 deletions

View File

@ -36,7 +36,6 @@
"dependencies": {
"@hcaptcha/react-hcaptcha": "1.12.0",
"cookie-es": "2.0.0",
"react-google-recaptcha": "3.1.0",
"react-google-recaptcha-v3": "1.10.1"
},
"devDependencies": {
@ -62,7 +61,6 @@
"@sentry/vite-plugin": "3.3.1",
"@types/html-minifier-terser": "7.0.2",
"@types/js-yaml": "4.0.9",
"@types/react-google-recaptcha": "2.1.9",
"@vitejs/plugin-legacy": "6.1.1",
"@vitejs/plugin-react": "4.4.1",
"async-es": "3.2.6",

View File

@ -1,5 +1,5 @@
import { UserAvatar } from "@client/components/ui/user-avatar"
import { loginHandler, oneTimeToken, twoFactor } from "@client/lib/auth"
import { loginHandler, oneTimeToken, signOut, twoFactor } from "@client/lib/auth"
import { queryClient } from "@client/lib/query-client"
import { useSession } from "@client/query/auth"
import { useAuthProviders } from "@client/query/users"
@ -18,39 +18,15 @@ import { Input } from "@follow/components/ui/input/index.js"
import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
import { DEEPLINK_SCHEME } from "@follow/shared/constants"
import { env } from "@follow/shared/env.ssr"
import HCaptcha from "@hcaptcha/react-hcaptcha"
import { zodResolver } from "@hookform/resolvers/zod"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import ReCAPTCHA from "react-google-recaptcha"
import { useForm } from "react-hook-form"
import { Trans, useTranslation } from "react-i18next"
import { Link, useLocation, useNavigate } from "react-router"
import { toast } from "sonner"
import { z } from "zod"
function closeRecaptcha(
recaptchaRef: React.RefObject<ReCAPTCHA | null>,
resetLoadingState: () => void,
) {
const handleClick = (e: MouseEvent) => {
const recaptchaIframeSelector =
'iframe[src*="recaptcha/api2"], iframe[src*="www.recaptcha.net"], iframe[src*="google.com/recaptcha"]'
const recaptchaChallengeIframe = document.querySelector(recaptchaIframeSelector)
if (
e.target instanceof Element &&
recaptchaChallengeIframe &&
!recaptchaChallengeIframe.contains(e.target) &&
!e.target.closest(".g-recaptcha")
) {
recaptchaRef.current?.reset()
resetLoadingState()
}
}
document.addEventListener("click", handleClick)
return () => document.removeEventListener("click", handleClick)
}
export function Login() {
const { status, refetch } = useSession()
@ -105,8 +81,19 @@ export function Login() {
case isAuthenticated: {
return (
<div className="mt-4 flex w-full flex-col items-center justify-center px-4">
<div className="relative flex items-center justify-center">
<div className="relative flex items-center justify-center gap-10">
<UserAvatar className="gap-4 px-10 py-4 text-2xl" />
<div className="absolute right-0">
<Button
variant="ghost"
onClick={async () => {
await signOut()
await refetch()
}}
>
<i className="i-mingcute-exit-line text-xl" />
</Button>
</div>
</div>
<p className="mt-4 text-center">
{t("redirect.successMessage", { app_name: APP_NAME })}
@ -237,17 +224,7 @@ function LoginWithPassword() {
const [needTwoFactor, setNeedTwoFactor] = useState(false)
const [isButtonLoading, setIsButtonLoading] = useState(false)
const recaptchaRef = useRef<ReCAPTCHA>(null)
const resetLoadingState = useCallback(() => {
setIsButtonLoading(false)
}, [])
useEffect(() => {
if (isButtonLoading) {
return closeRecaptcha(recaptchaRef, resetLoadingState)
}
}, [isButtonLoading, resetLoadingState])
const captchaRef = useRef<HCaptcha>(null)
async function onSubmit(values: z.infer<typeof formSchema>) {
setIsButtonLoading(true)
@ -263,8 +240,8 @@ function LoginWithPassword() {
return
}
const token = await recaptchaRef.current?.executeAsync()
if (!token) {
const response = await captchaRef.current?.execute({ async: true })
if (!response?.response) {
setIsButtonLoading(false)
return
}
@ -272,7 +249,7 @@ function LoginWithPassword() {
const res = await loginHandler("credential", "app", {
...values,
headers: {
"x-token": `r2:${token}`,
"x-token": `hc:${response?.response}`,
},
})
@ -350,7 +327,7 @@ function LoginWithPassword() {
)}
/>
)}
<ReCAPTCHA ref={recaptchaRef} sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY} size="invisible" />
<HCaptcha ref={captchaRef} sitekey={env.VITE_HCAPTCHA_SITE_KEY} size="invisible" />
<Button
type="submit"
buttonClassName="!mt-3 w-full"

View File

@ -17,40 +17,16 @@ import {
} from "@follow/components/ui/form/index.jsx"
import { Input } from "@follow/components/ui/input/index.js"
import { env } from "@follow/shared/env.ssr"
import HCaptcha from "@hcaptcha/react-hcaptcha"
import { zodResolver } from "@hookform/resolvers/zod"
import { useMutation } from "@tanstack/react-query"
import { useEffect, useRef } from "react"
import ReCAPTCHA from "react-google-recaptcha"
import { useRef } from "react"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router"
import { toast } from "sonner"
import { z } from "zod"
function closeRecaptcha(
recaptchaRef: React.RefObject<ReCAPTCHA | null>,
mutation?: { reset: () => void },
) {
const handleClick = (e: MouseEvent) => {
const recaptchaIframeSelector =
'iframe[src*="recaptcha/api2"], iframe[src*="www.recaptcha.net"], iframe[src*="google.com/recaptcha"]'
const recaptchaChallengeIframe = document.querySelector(recaptchaIframeSelector)
if (
e.target instanceof Element &&
(!recaptchaChallengeIframe || !recaptchaChallengeIframe.contains(e.target)) &&
!e.target.closest(".g-recaptcha") &&
recaptchaChallengeIframe
) {
recaptchaRef.current?.reset()
mutation?.reset()
}
}
document.addEventListener("click", handleClick)
return () => document.removeEventListener("click", handleClick)
}
const createEmailSchema = (t: any) =>
z.object({
email: z
@ -62,7 +38,7 @@ const createEmailSchema = (t: any) =>
export function Component() {
const { t } = useTranslation()
const navigate = useNavigate()
const recaptchaRef = useRef<ReCAPTCHA>(null)
const captchaRef = useRef<HCaptcha>(null)
const EmailSchema = createEmailSchema(t)
@ -78,7 +54,7 @@ export function Component() {
const { isValid } = form.formState
const updateMutation = useMutation({
mutationFn: async (values: z.infer<typeof EmailSchema>) => {
const token = await recaptchaRef.current?.executeAsync()
const response = await captchaRef.current?.execute({ async: true })
const res = await forgetPassword(
{
email: values.email,
@ -86,7 +62,7 @@ export function Component() {
},
{
headers: {
"x-token": `r2:${token}`,
"x-token": `hc:${response?.response}`,
},
},
)
@ -95,21 +71,13 @@ export function Component() {
}
},
onError: (error) => {
recaptchaRef.current?.reset()
toast.error(error.message)
},
onSuccess: () => {
toast.success(t("login.forget_password.success"))
},
onSettled: () => {
recaptchaRef.current?.reset()
},
})
useEffect(() => {
return closeRecaptcha(recaptchaRef, updateMutation)
}, [updateMutation])
function onSubmit(values: z.infer<typeof EmailSchema>) {
updateMutation.mutate(values)
}
@ -149,11 +117,7 @@ export function Component() {
</FormItem>
)}
/>
<ReCAPTCHA
ref={recaptchaRef}
sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY}
size="invisible"
/>
<HCaptcha ref={captchaRef} sitekey={env.VITE_HCAPTCHA_SITE_KEY} size="invisible" />
<div className="text-right">
<Button
disabled={!isValid || updateMutation.isPending}

View File

@ -14,39 +14,15 @@ import {
import { Input } from "@follow/components/ui/input/index.js"
import { env } from "@follow/shared/env.ssr"
import { tracker } from "@follow/tracker"
import HCaptcha from "@hcaptcha/react-hcaptcha"
import { zodResolver } from "@hookform/resolvers/zod"
import { useEffect, useRef, useState } from "react"
import ReCAPTCHA from "react-google-recaptcha"
import { useRef, useState } from "react"
import { useForm } from "react-hook-form"
import { Trans, useTranslation } from "react-i18next"
import { useNavigate } from "react-router"
import { toast } from "sonner"
import { z } from "zod"
function closeRecaptcha(
recaptchaRef: React.RefObject<ReCAPTCHA | null>,
setIsSubmitting: (value: boolean) => void,
) {
const handleClick = (e: MouseEvent) => {
const recaptchaIframeSelector =
'iframe[src*="recaptcha/api2"], iframe[src*="www.recaptcha.net"], iframe[src*="google.com/recaptcha"]'
const recaptchaChallengeIframe = document.querySelector(recaptchaIframeSelector)
if (
e.target instanceof Element &&
recaptchaChallengeIframe &&
!recaptchaChallengeIframe.contains(e.target) &&
!e.target.closest(".g-recaptcha")
) {
recaptchaRef.current?.reset()
setIsSubmitting(false)
}
}
document.addEventListener("click", handleClick)
return () => document.removeEventListener("click", handleClick)
}
export function Component() {
return (
<div className="flex h-full flex-col items-center justify-center gap-8">
@ -71,7 +47,7 @@ function RegisterForm() {
const { t } = useTranslation()
const [isSubmitting, setIsSubmitting] = useState(false)
const navigate = useNavigate()
const recaptchaRef = useRef<ReCAPTCHA>(null)
const captchaRef = useRef<HCaptcha>(null)
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
@ -86,19 +62,13 @@ function RegisterForm() {
const { data: authProviders } = useAuthProviders()
useEffect(() => {
if (isSubmitting) {
return closeRecaptcha(recaptchaRef, setIsSubmitting)
}
}, [isSubmitting])
async function onSubmit(values: z.infer<typeof formSchema>) {
setIsSubmitting(true)
try {
const token = await recaptchaRef.current?.executeAsync()
const response = await captchaRef.current?.execute({ async: true })
if (!token) {
if (!response?.response) {
return
}
@ -118,14 +88,11 @@ function RegisterForm() {
toast.error(context.error.message)
},
headers: {
"x-token": `r2:${token}`,
"x-token": `hc:${response?.response}`,
},
},
})
} finally {
if (recaptchaRef.current) {
recaptchaRef.current.reset()
}
setIsSubmitting(false)
}
}
@ -177,11 +144,7 @@ function RegisterForm() {
</FormItem>
)}
/>
<ReCAPTCHA
ref={recaptchaRef}
sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY}
size="invisible"
/>
<HCaptcha ref={captchaRef} sitekey={env.VITE_HCAPTCHA_SITE_KEY} size="invisible" />
<Button
isLoading={isSubmitting}
disabled={isSubmitting}

View File

@ -13,6 +13,7 @@
"@fastify/request-context": "6.2.0",
"@follow/tracker": "workspace:*",
"@fontsource/sn-pro": "5.2.5",
"@hcaptcha/react-hcaptcha": "1.12.0",
"@openpanel/web": "1.0.1",
"@radix-ui/react-avatar": "1.1.7",
"@resvg/resvg-js": "2.6.2",
@ -29,7 +30,6 @@
"ofetch": "1.4.1",
"rc-modal-sheet": "1.0.0",
"react-blurhash": "0.3.0",
"react-google-recaptcha": "3.1.0",
"react-hook-form": "7.56.1",
"react-hotkeys-hook": "5.0.1",
"react-i18next": "15.5.1",
@ -52,7 +52,6 @@
"@follow/types": "workspace:*",
"@follow/utils": "workspace:*",
"@types/html-minifier-terser": "7.0.2",
"@types/react-google-recaptcha": "2.1.9",
"chokidar": "4.0.3",
"code-inspector-plugin": "0.20.10",
"daisyui": "4.12.24",

View File

@ -5,7 +5,6 @@ export const DEFAULT_VALUES = {
INBOXES_EMAIL: "@follow.re",
OPENPANEL_CLIENT_ID: "4382168f-b8d2-40c1-9a26-133a312d072b",
OPENPANEL_API_URL: "https://openpanel.follow.is/api",
RECAPTCHA_V2_SITE_KEY: "6LdxQ-sqAAAAAN-_za3hUdFkJEO_cu2xHSpLKVan",
RECAPTCHA_V3_SITE_KEY: "6LdG-asqAAAAAEXr96565MKbRvxGEv31XEykRSHV",
HCAPTCHA_SITE_KEY: "f5324fa4-91a0-4c72-840e-001220a6ebda",
},

View File

@ -19,7 +19,6 @@ export const env = createEnv({
VITE_OPENPANEL_CLIENT_ID: z.string().optional(),
VITE_OPENPANEL_API_URL: z.string().url().optional(),
VITE_RECAPTCHA_V2_SITE_KEY: z.string().default(DEFAULT_VALUES.PROD.RECAPTCHA_V2_SITE_KEY),
VITE_RECAPTCHA_V3_SITE_KEY: z.string().default(DEFAULT_VALUES.PROD.RECAPTCHA_V3_SITE_KEY),
VITE_HCAPTCHA_SITE_KEY: z.string().default(DEFAULT_VALUES.PROD.HCAPTCHA_SITE_KEY),
},

View File

@ -26,8 +26,8 @@ export const env = createEnv({
VITE_WEB_PROD_URL: z.string().optional(),
VITE_WEB_DEV_URL: z.string().optional(),
VITE_RECAPTCHA_V2_SITE_KEY: z.string().default(DEFAULT_VALUES.PROD.RECAPTCHA_V2_SITE_KEY),
VITE_RECAPTCHA_V3_SITE_KEY: z.string().default(DEFAULT_VALUES.PROD.RECAPTCHA_V3_SITE_KEY),
VITE_HCAPTCHA_SITE_KEY: z.string().default(DEFAULT_VALUES.PROD.HCAPTCHA_SITE_KEY),
},
emptyStringAsUndefined: true,

View File

@ -4,12 +4,6 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
catalogs:
default:
typescript:
specifier: 5.8.3
version: 5.8.3
overrides:
'@electron/node-gyp': 10.2.0-electron.2
is-core-module: npm:@nolyfill/is-core-module@1.0.39
@ -169,9 +163,6 @@ importers:
cookie-es:
specifier: 2.0.0
version: 2.0.0
react-google-recaptcha:
specifier: 3.1.0
version: 3.1.0(react@19.0.0)
react-google-recaptcha-v3:
specifier: 1.10.1
version: 1.10.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -242,9 +233,6 @@ importers:
'@types/js-yaml':
specifier: 4.0.9
version: 4.0.9
'@types/react-google-recaptcha':
specifier: 2.1.9
version: 2.1.9
'@vitejs/plugin-legacy':
specifier: 6.1.1
version: 6.1.1(terser@5.39.0)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))
@ -1141,6 +1129,9 @@ importers:
'@fontsource/sn-pro':
specifier: 5.2.5
version: 5.2.5
'@hcaptcha/react-hcaptcha':
specifier: 1.12.0
version: 1.12.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@openpanel/web':
specifier: 1.0.1
version: 1.0.1
@ -1189,9 +1180,6 @@ importers:
react-blurhash:
specifier: 0.3.0
version: 0.3.0(blurhash@2.0.5)(react@19.0.0)
react-google-recaptcha:
specifier: 3.1.0
version: 3.1.0(react@19.0.0)
react-hook-form:
specifier: 7.56.1
version: 7.56.1(react@19.0.0)
@ -1200,7 +1188,7 @@ importers:
version: 5.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-i18next:
specifier: 15.5.1
version: 15.5.1(i18next@25.0.1(typescript@5.8.3))(react-dom@19.0.0(react@19.0.0))(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(typescript@5.8.3)
version: 15.5.1(i18next@25.0.1(typescript@5.8.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.3)
react-photo-view:
specifier: 1.2.7
version: 1.2.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -1253,9 +1241,6 @@ importers:
'@types/html-minifier-terser':
specifier: 7.0.2
version: 7.0.2
'@types/react-google-recaptcha':
specifier: 2.1.9
version: 2.1.9
chokidar:
specifier: 4.0.3
version: 4.0.3
@ -6424,9 +6409,6 @@ packages:
'@types/plist@3.0.5':
resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==}
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
'@types/qrcode@1.5.5':
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
@ -6435,17 +6417,11 @@ packages:
peerDependencies:
'@types/react': ^19.0.0
'@types/react-google-recaptcha@2.1.9':
resolution: {integrity: sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==}
'@types/react-reconciler@0.28.9':
resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==}
peerDependencies:
'@types/react': '*'
'@types/react@18.3.12':
resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
'@types/react@19.1.3':
resolution: {integrity: sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==}
@ -13006,11 +12982,6 @@ packages:
react: 19.0.0
react-dom: 19.0.0
react-async-script@1.2.0:
resolution: {integrity: sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==}
peerDependencies:
react: 19.0.0
react-blurhash@0.3.0:
resolution: {integrity: sha512-XlKr4Ns1iYFRnk6DkAblNbAwN/bTJvxTVoxMvmTcURdc5oLoXZwqAF9N3LZUh/HT+QFlq5n6IS6VsDGsviYAiQ==}
peerDependencies:
@ -13048,11 +13019,6 @@ packages:
react: 19.0.0
react-dom: 19.0.0
react-google-recaptcha@3.1.0:
resolution: {integrity: sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==}
peerDependencies:
react: 19.0.0
react-hook-form@7.56.1:
resolution: {integrity: sha512-qWAVokhSpshhcEuQDSANHx3jiAEFzu2HAaaQIzi/r9FNPm1ioAvuJSD4EuZzWd7Al7nTRKcKPnBKO7sRn+zavQ==}
engines: {node: '>=18.0.0'}
@ -15696,7 +15662,7 @@ snapshots:
'@babel/traverse': 7.27.0
'@babel/types': 7.27.0
convert-source-map: 2.0.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@ -15748,7 +15714,7 @@ snapshots:
'@babel/core': 7.26.10
'@babel/helper-compilation-targets': 7.27.0
'@babel/helper-plugin-utils': 7.26.5
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
lodash.debounce: 4.0.8
resolve: 1.22.10
transitivePeerDependencies:
@ -16524,7 +16490,7 @@ snapshots:
'@babel/parser': 7.27.0
'@babel/template': 7.27.0
'@babel/types': 7.27.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@ -16696,7 +16662,7 @@ snapshots:
'@electron/get': 3.1.0
chalk: 4.1.2
commander: 11.1.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
listr2: 7.0.2
log-symbols: 4.1.0
@ -16712,7 +16678,7 @@ snapshots:
'@electron/rebuild': 3.7.2
'@malept/cross-spawn-promise': 2.0.0
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
find-up: 5.0.0
fs-extra: 10.1.0
log-symbols: 4.1.0
@ -16739,7 +16705,7 @@ snapshots:
'@electron/rebuild': 3.7.2
'@malept/cross-spawn-promise': 2.0.0
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fast-glob: 3.3.3
filenamify: 4.3.0
find-up: 5.0.0
@ -16858,7 +16824,7 @@ snapshots:
'@octokit/rest': 18.12.0(encoding@0.1.13)
'@octokit/types': 6.41.0
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
log-symbols: 4.1.0
mime-types: 2.1.35
@ -16882,7 +16848,7 @@ snapshots:
'@electron-forge/core-utils': 7.8.0
'@electron-forge/shared-types': 7.8.0
'@malept/cross-spawn-promise': 2.0.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
username: 5.1.0
transitivePeerDependencies:
@ -16955,7 +16921,7 @@ snapshots:
'@electron/get@2.0.3':
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
env-paths: 2.2.1
fs-extra: 8.1.0
got: 11.8.6
@ -16969,7 +16935,7 @@ snapshots:
'@electron/get@3.1.0':
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
env-paths: 2.2.1
fs-extra: 8.1.0
got: 11.8.6
@ -16999,7 +16965,7 @@ snapshots:
'@electron/notarize@2.2.1':
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 9.1.0
promise-retry: 2.0.1
transitivePeerDependencies:
@ -17007,7 +16973,7 @@ snapshots:
'@electron/notarize@2.5.0':
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 9.1.0
promise-retry: 2.0.1
transitivePeerDependencies:
@ -17016,7 +16982,7 @@ snapshots:
'@electron/osx-sign@1.0.5':
dependencies:
compare-version: 0.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
isbinaryfile: 4.0.10
minimist: 1.2.8
@ -17027,7 +16993,7 @@ snapshots:
'@electron/osx-sign@1.3.3':
dependencies:
compare-version: 0.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
isbinaryfile: 4.0.10
minimist: 1.2.8
@ -17043,7 +17009,7 @@ snapshots:
'@electron/osx-sign': 1.3.3
'@electron/universal': 2.0.2
'@electron/windows-sign': 1.2.1
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
extract-zip: 2.0.1
filenamify: 4.3.0
fs-extra: 11.3.0
@ -17064,7 +17030,7 @@ snapshots:
'@electron/node-gyp': 10.2.0-electron.2
'@malept/cross-spawn-promise': 2.0.0
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
detect-libc: 2.0.3
fs-extra: 10.1.0
got: 11.8.6
@ -17083,7 +17049,7 @@ snapshots:
dependencies:
'@electron/asar': 3.4.1
'@malept/cross-spawn-promise': 1.1.1
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
dir-compare: 3.3.0
fs-extra: 9.1.0
minimatch: 3.1.2
@ -17095,7 +17061,7 @@ snapshots:
dependencies:
'@electron/asar': 3.4.1
'@malept/cross-spawn-promise': 2.0.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
dir-compare: 4.2.0
fs-extra: 11.3.0
minimatch: 9.0.5
@ -17106,7 +17072,7 @@ snapshots:
'@electron/windows-sign@1.2.1':
dependencies:
cross-dirname: 0.1.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 11.3.0
minimist: 1.2.8
postject: 1.0.0-alpha.6
@ -17635,7 +17601,7 @@ snapshots:
ci-info: 3.9.0
compression: 1.8.0
connect: 3.7.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
env-editor: 0.4.2
freeport-async: 2.0.0
getenv: 1.0.0
@ -17685,7 +17651,7 @@ snapshots:
'@expo/plist': 0.3.4
'@expo/sdk-runtime-versions': 1.0.0
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
getenv: 1.0.0
glob: 10.4.5
resolve-from: 5.0.0
@ -17835,7 +17801,7 @@ snapshots:
'@expo/env@1.0.5':
dependencies:
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
dotenv: 16.4.7
dotenv-expand: 11.0.7
getenv: 1.0.0
@ -17847,7 +17813,7 @@ snapshots:
'@expo/spawn-async': 1.7.2
arg: 5.0.2
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
find-up: 5.0.0
getenv: 1.0.0
minimatch: 9.0.5
@ -17915,7 +17881,7 @@ snapshots:
'@expo/json-file': 9.1.4
'@expo/spawn-async': 1.7.2
chalk: 4.1.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
dotenv: 16.4.7
dotenv-expand: 11.0.7
getenv: 1.0.0
@ -18042,7 +18008,7 @@ snapshots:
'@expo/image-utils': 0.7.4
'@expo/json-file': 9.1.4
'@react-native/normalize-colors': 0.79.1
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
resolve-from: 5.0.0
semver: 7.7.1
xml2js: 0.6.0
@ -19189,7 +19155,7 @@ snapshots:
'@malept/flatpak-bundler@0.4.0':
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 9.1.0
lodash: 4.17.21
tmp-promise: 3.0.3
@ -21493,8 +21459,6 @@ snapshots:
xmlbuilder: 15.1.1
optional: true
'@types/prop-types@15.7.14': {}
'@types/qrcode@1.5.5':
dependencies:
'@types/node': 22.15.3
@ -21503,19 +21467,10 @@ snapshots:
dependencies:
'@types/react': 19.1.3
'@types/react-google-recaptcha@2.1.9':
dependencies:
'@types/react': 18.3.12
'@types/react-reconciler@0.28.9(@types/react@19.1.3)':
dependencies:
'@types/react': 19.1.3
'@types/react@18.3.12':
dependencies:
'@types/prop-types': 15.7.14
csstype: 3.1.3
'@types/react@19.1.3':
dependencies:
csstype: 3.1.3
@ -22011,7 +21966,7 @@ snapshots:
agent-base@6.0.2:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
@ -22130,7 +22085,7 @@ snapshots:
builder-util: 24.13.1
builder-util-runtime: 9.2.4
chromium-pickle-js: 0.2.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3)
ejs: 3.1.10
electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3)
@ -22462,7 +22417,7 @@ snapshots:
babel-plugin-react-native-web: 0.19.13
babel-plugin-syntax-hermes-parser: 0.25.1
babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.26.10)
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
react-refresh: 0.14.2
resolve-from: 5.0.0
transitivePeerDependencies:
@ -22698,7 +22653,7 @@ snapshots:
builder-util-runtime@9.2.4:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
sax: 1.4.1
transitivePeerDependencies:
- supports-color
@ -22719,7 +22674,7 @@ snapshots:
builder-util-runtime: 9.2.4
chalk: 4.1.2
cross-spawn: 7.0.6
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
http-proxy-agent: 5.0.0
https-proxy-agent: 5.0.1
@ -23617,6 +23572,10 @@ snapshots:
dependencies:
ms: 2.1.3
debug@4.4.0:
dependencies:
ms: 2.1.3
debug@4.4.0(supports-color@8.1.1):
dependencies:
ms: 2.1.3
@ -24062,7 +24021,7 @@ snapshots:
electron-installer-dmg@5.0.1:
dependencies:
'@types/appdmg': 0.5.5
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
minimist: 1.2.8
optionalDependencies:
appdmg: 0.6.6
@ -24132,7 +24091,7 @@ snapshots:
dependencies:
chalk: 2.4.2
commander: 2.20.3
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 7.0.1
inquirer: 6.5.2
lodash.defaults: 4.2.0
@ -24146,7 +24105,7 @@ snapshots:
electron-winstaller@5.4.0:
dependencies:
'@electron/asar': 3.4.1
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 7.0.1
lodash: 4.17.21
temp: 0.9.4
@ -25364,7 +25323,7 @@ snapshots:
extract-zip@2.0.1:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
@ -25647,7 +25606,7 @@ snapshots:
flora-colossus@2.0.0:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fs-extra: 10.1.0
transitivePeerDependencies:
- supports-color
@ -25661,7 +25620,7 @@ snapshots:
follow-redirects@1.15.9(debug@4.4.0):
optionalDependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
font-list@1.5.1: {}
@ -25805,7 +25764,7 @@ snapshots:
galactus@1.0.0:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
flora-colossus: 2.0.0
fs-extra: 10.1.0
transitivePeerDependencies:
@ -26377,7 +26336,7 @@ snapshots:
dependencies:
'@tootallnate/once': 2.0.0
agent-base: 6.0.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
@ -26399,14 +26358,14 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.3
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
@ -27772,7 +27731,7 @@ snapshots:
metro-file-map@0.82.2:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
fb-watchman: 2.0.2
flow-enums-runtime: 0.0.6
graceful-fs: 4.2.11
@ -27868,7 +27827,7 @@ snapshots:
chalk: 4.1.2
ci-info: 2.0.0
connect: 3.7.0
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
error-stack-parser: 2.1.4
flow-enums-runtime: 0.0.6
graceful-fs: 4.2.11
@ -28967,7 +28926,7 @@ snapshots:
portfinder@1.0.37:
dependencies:
async: 3.2.6
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
@ -29393,12 +29352,6 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-async-script@1.2.0(react@19.0.0):
dependencies:
hoist-non-react-statics: 3.3.2
prop-types: 15.8.1
react: 19.0.0
react-blurhash@0.3.0(blurhash@2.0.5)(react@19.0.0):
dependencies:
blurhash: 2.0.5
@ -29437,12 +29390,6 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-google-recaptcha@3.1.0(react@19.0.0):
dependencies:
prop-types: 15.8.1
react: 19.0.0
react-async-script: 1.2.0(react@19.0.0)
react-hook-form@7.56.1(react@19.0.0):
dependencies:
react: 19.0.0
@ -29463,6 +29410,16 @@ snapshots:
react-native: 0.79.1(@babel/core@7.26.10)(@types/react@19.1.3)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)
typescript: 5.8.3
react-i18next@15.5.1(i18next@25.0.1(typescript@5.8.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.27.0
html-parse-stringify: 3.0.1
i18next: 25.0.1(typescript@5.8.3)
react: 19.0.0
optionalDependencies:
react-dom: 19.0.0(react@19.0.0)
typescript: 5.8.3
react-intersection-observer@9.16.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
react: 19.0.0
@ -29809,7 +29766,7 @@ snapshots:
read-binary-file-arch@1.0.6:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
@ -30618,7 +30575,7 @@ snapshots:
socks-proxy-agent@7.0.0:
dependencies:
agent-base: 6.0.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
socks: 2.8.4
transitivePeerDependencies:
- supports-color
@ -30933,7 +30890,7 @@ snapshots:
sumchecker@3.0.1:
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
transitivePeerDependencies:
- supports-color
@ -31341,7 +31298,7 @@ snapshots:
cac: 6.7.14
chokidar: 4.0.3
consola: 3.4.2
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
esbuild: 0.25.2
joycon: 3.1.1
picocolors: 1.1.1
@ -31364,7 +31321,7 @@ snapshots:
tsx@4.19.3:
dependencies:
esbuild: 0.25.2
esbuild: 0.25.3
get-tsconfig: 4.10.0
optionalDependencies:
fsevents: 2.3.3
@ -31839,7 +31796,7 @@ snapshots:
vite-plugin-mkcert@1.17.8(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1)):
dependencies:
axios: 1.8.4(debug@4.4.0)
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
picocolors: 1.1.1
vite: 6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1)
transitivePeerDependencies:
@ -31847,7 +31804,7 @@ snapshots:
vite-plugin-pwa@1.0.0(@vite-pwa/assets-generator@1.0.0)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.3.0):
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
pretty-bytes: 6.1.1
tinyglobby: 0.2.13
vite: 6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1)
@ -31860,7 +31817,7 @@ snapshots:
vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.3)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1)):
dependencies:
debug: 4.4.0(supports-color@8.1.1)
debug: 4.4.0
globrex: 0.1.2
tsconfck: 3.1.5(typescript@5.8.3)
optionalDependencies: