import { isGlobalFormValidationError, isStandardSchemaValidator, standardSchemaValidators, } from '@tanstack/react-form' import { getRequestHeader } from '@tanstack/react-start/server' import { decode } from 'decode-formdata' import { createServerFn } from '@tanstack/react-start' import { ServerValidateError } from './error' import { setInternalTanStackCookie } from './utils' import type { FormAsyncValidateOrFn, FormOptions, FormValidateAsyncFn, FormValidateOrFn, ServerFormState, UnwrapFormAsyncValidateOrFn, } from '@tanstack/react-form' import type { FormDataInfo } from 'decode-formdata' interface CreateServerValidateOptions< TFormData, TOnMount extends undefined | FormValidateOrFn, TOnChange extends undefined | FormValidateOrFn, TOnChangeAsync extends undefined | FormAsyncValidateOrFn, TOnBlur extends undefined | FormValidateOrFn, TOnBlurAsync extends undefined | FormAsyncValidateOrFn, TOnSubmit extends undefined | FormValidateOrFn, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TOnDynamic extends undefined | FormValidateOrFn, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TOnServer extends undefined | FormAsyncValidateOrFn, TSubmitMeta, > extends FormOptions< TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta > { onServerValidate: TOnServer } const serverFn = createServerFn({ method: 'POST' }) .validator( (data: { formData: unknown; info?: unknown; defaultOpts: unknown }) => { return data }, ) .handler(async ({ data }) => { const { formData, info, defaultOpts } = data as { formData: FormData info?: FormDataInfo defaultOpts: CreateServerValidateOptions< any, any, any, any, any, any, any, any, any, any, any, any > } const { onServerValidate } = defaultOpts const runValidator = async ({ value, validationSource, }: { value: any validationSource: 'form' }) => { if (isStandardSchemaValidator(onServerValidate)) { return await standardSchemaValidators.validateAsync( { value, validationSource }, onServerValidate, ) } return (onServerValidate as FormValidateAsyncFn)({ value, signal: undefined as never, formApi: undefined as never, }) } const referer = getRequestHeader('referer')! const decodedData = (info ? decode(formData, info) : decode(formData)) as never as any const onServerError = (await runValidator({ value: decodedData, validationSource: 'form', })) as UnwrapFormAsyncValidateOrFn | undefined if (!onServerError) return decodedData const onServerErrorVal = ( isGlobalFormValidationError(onServerError) ? onServerError.form : onServerError ) as UnwrapFormAsyncValidateOrFn const formState: ServerFormState = { errorMap: { onServer: onServerError, }, values: decodedData, errors: onServerErrorVal ? [onServerErrorVal] : [], } setInternalTanStackCookie(formState) throw new ServerValidateError({ response: new Response('ok', { headers: { Location: referer, }, status: 302, }), formState: formState, }) }) export const createServerValidate = < TFormData, TOnMount extends undefined | FormValidateOrFn, TOnChange extends undefined | FormValidateOrFn, TOnChangeAsync extends undefined | FormAsyncValidateOrFn, TOnBlur extends undefined | FormValidateOrFn, TOnBlurAsync extends undefined | FormAsyncValidateOrFn, TOnSubmit extends undefined | FormValidateOrFn, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TOnDynamic extends undefined | FormValidateOrFn, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TOnServer extends undefined | FormAsyncValidateOrFn, TSubmitMeta, >( defaultOpts: CreateServerValidateOptions< TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta >, ) => (formData: FormData, info?: FormDataInfo) => serverFn({ data: { defaultOpts, formData, info } })