import type { Context } from "@wrnexus/core"; import type { ObjectSchema, ParseResult } from "@wrnexus/validation"; import type { CaptchaEngine, CaptchaProvider, CaptchaVerificationResult } from "./types.ts"; import { selfHostedProvider } from "./providers/self-hosted.ts"; export interface ParseWithCaptchaOptions { action: string; provider?: CaptchaProvider; engine?: CaptchaEngine; responseField?: string; bindHostname?: boolean; bindSession?: boolean; bindIp?: boolean; } export interface CaptchaParseResult> extends ParseResult { captcha: CaptchaVerificationResult; } export async function parseWithCaptcha>( schema: ObjectSchema, input: Record, ctx: Context, options: ParseWithCaptchaOptions, ): Promise> { const parsed = schema.parse(input) as ParseResult; const provider = options.provider ?? (options.engine ? selfHostedProvider(options.engine) : undefined); if (!provider) throw new TypeError("parseWithCaptcha requires provider or engine"); const field = options.responseField ?? provider.client.responseField; const token = input[field] ?? input.captchaToken ?? input.responseToken; const captcha = await provider.verify({ action: options.action, providerToken: token === undefined ? undefined : String(token), responseToken: token === undefined ? undefined : String(token), hostname: options.bindHostname === false ? undefined : ctx.url.hostname, sessionId: options.bindSession === false ? undefined : ctx.session.id(), ip: options.bindIp ? ctx.ip : undefined, }); const errors = { ...parsed.errors }; if (!captcha.success) errors[field] = captcha.message ?? "CAPTCHA verification failed"; return { ...parsed, ok: parsed.ok && captcha.success, errors, captcha, }; }