oxform-core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/index.cjs +751 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +443 -0
  4. package/dist/index.d.ts +443 -0
  5. package/dist/index.js +747 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +20 -15
  8. package/export/index.ts +0 -7
  9. package/export/schema.ts +0 -1
  10. package/src/field-api.constants.ts +0 -15
  11. package/src/field-api.ts +0 -139
  12. package/src/form-api.ts +0 -84
  13. package/src/form-api.types.ts +0 -148
  14. package/src/form-array-field-api.ts +0 -233
  15. package/src/form-context-api.ts +0 -232
  16. package/src/form-field-api.ts +0 -174
  17. package/src/more-types.ts +0 -178
  18. package/src/tests/array/append.spec.ts +0 -138
  19. package/src/tests/array/insert.spec.ts +0 -182
  20. package/src/tests/array/move.spec.ts +0 -175
  21. package/src/tests/array/prepend.spec.ts +0 -138
  22. package/src/tests/array/remove.spec.ts +0 -174
  23. package/src/tests/array/swap.spec.ts +0 -152
  24. package/src/tests/array/update.spec.ts +0 -148
  25. package/src/tests/field/change.spec.ts +0 -226
  26. package/src/tests/field/reset.spec.ts +0 -617
  27. package/src/tests/field/set-errors.spec.ts +0 -254
  28. package/src/tests/field-api/field-api.spec.ts +0 -341
  29. package/src/tests/form-api/reset.spec.ts +0 -535
  30. package/src/tests/form-api/submit.spec.ts +0 -409
  31. package/src/types.ts +0 -5
  32. package/src/utils/get.ts +0 -5
  33. package/src/utils/testing/sleep.ts +0 -1
  34. package/src/utils/testing/tests.ts +0 -18
  35. package/src/utils/update.ts +0 -6
  36. package/src/utils/validate.ts +0 -8
  37. package/tsconfig.json +0 -3
  38. package/tsdown.config.ts +0 -10
  39. package/vitest.config.ts +0 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["array: any[]","validate","related: string[]","field","context: FormContextApi<Values>","updated: FormIssue[]"],"sources":["../src/field-api.constants.ts","../src/utils/get.ts","../src/utils/update.ts","../src/form-array-field-api.ts","../src/utils/validate.ts","../src/form-context-api.ts","../src/form-field-api.ts","../src/form-api.ts","../src/field-api.ts","../src/array-field-api.ts"],"sourcesContent":["import type { PersistedFieldMeta, PersistedFormStatus } from '#form-api.types';\n\nexport const defaultMeta = {\n blurred: false,\n touched: false,\n dirty: false,\n} satisfies PersistedFieldMeta;\n\nexport const defaultStatus = {\n submits: 0,\n submitting: false,\n validating: false,\n successful: false,\n dirty: false,\n} satisfies PersistedFormStatus;\n","export const get = (data: unknown, path: (string | number)[]) => {\n return path.reduce((acc, key) => {\n if (acc === undefined) return undefined;\n if (acc === null) return null;\n\n return (acc as any)[key];\n }, data);\n};\n","export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput;\nexport type Updater<TInput, TOutput = TInput> = TOutput | UpdaterFn<TInput, TOutput>;\n\nexport function update<TInput, TOutput = TInput>(updater: Updater<TInput, TOutput>, input: TInput): TOutput {\n return typeof updater === 'function' ? (updater as UpdaterFn<TInput, TOutput>)(input) : updater;\n}\n","import { defaultMeta } from '#field-api.constants';\nimport type { FormApi, FormArrayFields } from '#form-api';\nimport type { FieldChangeOptions } from '#form-api.types';\nimport type { FormContextApi } from '#form-context-api';\nimport type { FormFieldApi } from '#form-field-api';\nimport type { DeepValue, UnwrapOneLevelOfArray } from '#more-types';\nimport { get } from '#utils/get';\nimport { update, type Updater } from '#utils/update';\nimport { stringToPath } from 'remeda';\n\nexport class FormArrayFieldApi<Values> {\n private context: FormContextApi<Values>;\n private field: FormFieldApi<Values>;\n\n constructor({ field, context }: { field: FormFieldApi<Values>; context: FormContextApi<Values> }) {\n this.context = context;\n this.field = field;\n }\n\n public insert = <const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n index: number,\n value: Updater<UnwrapOneLevelOfArray<DeepValue<Values, Name>>>,\n options?: FieldChangeOptions,\n ) => {\n if (index < 0) index = 0;\n\n this.field.change(\n name,\n current => {\n const array = (current as any[]) ?? [];\n\n return [\n ...array.slice(0, index),\n ...(Array.from({ length: index - array.length }, () => undefined) as any[]),\n update(value, current as never),\n ...array.slice(index),\n ] as never;\n },\n options,\n );\n\n this.context.persisted.setState(current => {\n const fields = { ...current.fields };\n const value = get(current.values as never, stringToPath(name)) as any[] | undefined;\n const length = value?.length;\n\n if (length === undefined) return current;\n\n for (let i = index; i < length; i++) {\n const moving = current.fields[`${name}.${i}`];\n fields[`${name}.${i + 1}`] = moving ?? defaultMeta;\n }\n\n fields[`${name}.${index}`] = defaultMeta;\n\n return {\n ...current,\n fields,\n };\n });\n };\n\n public append = <const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n value: UnwrapOneLevelOfArray<DeepValue<Values, Name>>,\n options?: FieldChangeOptions,\n ) => {\n const current = this.field.get(name) as any[];\n return this.insert(name, current?.length ?? 0, value, options);\n };\n\n public prepend = <const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n value: UnwrapOneLevelOfArray<DeepValue<Values, Name>>,\n options?: FieldChangeOptions,\n ) => {\n return this.insert(name, 0, value, options);\n };\n\n public swap = <const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n from: number,\n to: number,\n options?: FieldChangeOptions,\n ) => {\n const start = from >= 0 ? from : 0;\n const end = to >= 0 ? to : 0;\n\n this.field.change(\n name,\n current => {\n const array = (current as any[]) ?? [];\n\n if (start === end) return array as never; // no-op\n\n const a = array[start];\n const b = array[end];\n\n return [...array.slice(0, start), b, ...array.slice(start + 1, end), a, ...array.slice(end + 1)] as never;\n },\n options,\n );\n\n this.context.persisted.setState(current => {\n const fields = { ...current.fields };\n\n fields[`${name}.${start}`] = current.fields[`${name}.${end}`] ?? defaultMeta;\n fields[`${name}.${end}`] = current.fields[`${name}.${start}`] ?? defaultMeta;\n\n return {\n ...current,\n fields,\n };\n });\n };\n\n public move<const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n _from: number,\n _to: number,\n options?: FieldChangeOptions,\n ) {\n const from = Math.max(_from, 0);\n const to = Math.max(_to, 0);\n const backwards = from > to;\n\n this.field.change(\n name,\n current => {\n const array: any[] = current ? [...(current as any[])] : [];\n\n if (from === to) return array as never;\n\n const moved = array[from];\n return array.toSpliced(backwards ? to : to + 1, 0, moved).toSpliced(backwards ? from + 1 : from, 1) as never;\n },\n options,\n );\n\n this.context.persisted.setState(current => {\n const fields = { ...current.fields };\n const value = get(current.values as never, stringToPath(name)) as any[] | undefined;\n const length = value?.length;\n\n const start = Math.min(from, to);\n const end = Math.max(from, to);\n\n if (length === undefined) return current;\n\n if (!backwards) {\n fields[`${name}.${to}`] = current.fields[`${name}.${from}`] ?? defaultMeta;\n }\n\n for (let i = backwards ? start + 1 : start; i < end; i++) {\n const shift = backwards ? -1 : 1;\n const moving = current.fields[`${name}.${i + shift}`];\n fields[`${name}.${i}`] = moving ?? defaultMeta;\n }\n\n return {\n ...current,\n fields,\n };\n });\n }\n\n public update = <const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n index: number,\n value: Updater<UnwrapOneLevelOfArray<DeepValue<Values, Name>>>,\n options?: FieldChangeOptions,\n ) => {\n this.field.change(\n name,\n current => {\n const array = (current as any[]) ?? [];\n const position = Math.max(Math.min(index, array.length - 1), 0);\n\n return [...array.slice(0, position), update(value, current as never), ...array.slice(position + 1)] as never;\n },\n options,\n );\n\n this.context.resetFieldMeta(`${name}.${index}`);\n this.context.setFieldMeta(`${name}.${index}`, {\n dirty: options?.should?.dirty !== false,\n touched: options?.should?.touch !== false,\n });\n };\n\n public remove<const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n index: number,\n options?: FieldChangeOptions,\n ) {\n let position = index;\n\n this.field.change(\n name,\n current => {\n const array = (current as any[]) ?? [];\n position = Math.max(Math.min(index, array.length - 1), 0);\n\n return [...array.slice(0, position), ...array.slice(position + 1)] as never;\n },\n { ...options, should: { ...options?.should, validate: false } },\n );\n\n this.context.persisted.setState(current => {\n const fields = { ...current.fields };\n const value = get(current.values as never, stringToPath(name)) as any[] | undefined;\n const length = value?.length ?? 0;\n\n for (let i = position; i < length; i++) {\n const moving = current.fields[`${name}.${i + 1}`];\n fields[`${name}.${i}`] = moving ?? defaultMeta;\n }\n\n delete fields[`${name}.${length}`];\n\n return {\n ...current,\n fields,\n };\n });\n\n const shouldValidate = options?.should?.validate !== false;\n if (shouldValidate) void this.context.validate(name, { type: 'change' });\n }\n\n public replace<const Name extends FormArrayFields<FormApi<Values>>>(\n name: Name,\n value: Updater<DeepValue<Values, Name>>,\n options?: FieldChangeOptions,\n ) {\n this.context.resetFieldMeta(name);\n this.field.change(name, value as never, options);\n }\n}\n","import type { StandardSchema } from '#types';\n\nexport const validate = async <Schema extends StandardSchema>(schema: Schema, input: unknown) => {\n let result = schema['~standard'].validate(input);\n if (result instanceof Promise) result = await result;\n\n return result as StandardSchema.Result<StandardSchema.InferOutput<Schema>>;\n};\n","import { defaultMeta, defaultStatus } from '#field-api.constants';\nimport type { FormApi, FormFields } from '#form-api';\nimport type {\n FieldMeta,\n FormBaseStore,\n FormIssue,\n FormOptions,\n FormStore,\n PersistedFieldMeta,\n PersistedFormStatus,\n ValidateOptions,\n} from '#form-api.types';\nimport { get } from '#utils/get';\nimport { validate } from '#utils/validate';\nimport { Derived, Store } from '@tanstack/store';\nimport { isDeepEqual, isFunction, mergeDeep, stringToPath } from 'remeda';\n\nexport class FormContextApi<Values> {\n public options!: FormOptions<Values>;\n public persisted: Store<FormBaseStore<Values>>;\n public store!: Derived<FormStore<Values>>;\n\n constructor(options: FormOptions<Values>) {\n const values = mergeDeep(options.defaultValues as never, options.values ?? {}) as Values;\n\n this.options = options;\n\n this.persisted = new Store<FormBaseStore<Values>>({\n values,\n fields: {},\n refs: {},\n status: defaultStatus,\n errors: {},\n });\n\n this.store = new Derived<FormStore<Values>>({\n deps: [this.persisted],\n fn: ({ currDepVals }) => {\n const persisted = currDepVals[0] as FormBaseStore<Values>;\n\n const invalid = Object.values(persisted.errors).some(issues => issues.length > 0);\n const dirty = Object.values(persisted.fields).some(meta => meta.dirty);\n const fields = Object.fromEntries(\n Object.entries(persisted.fields).map(([key, meta]) => {\n return [key, this.buildFieldMeta(key, meta, persisted.values as Values, persisted.errors)];\n }),\n );\n\n return {\n ...persisted,\n fields,\n status: {\n ...persisted.status,\n submitted: persisted.status.submits > 0,\n valid: !invalid,\n dirty: persisted.status.dirty || dirty,\n },\n };\n },\n });\n }\n\n public get status() {\n return this.store.state.status;\n }\n\n public get values() {\n return this.store.state.values;\n }\n\n private get validator() {\n const store = this.store.state;\n const validate = this.options.validate;\n\n return {\n change: isFunction(validate?.change) ? validate.change(store) : validate?.change,\n submit: isFunction(validate?.submit) ? validate.submit(store) : (validate?.submit ?? this.options.schema),\n blur: isFunction(validate?.blur) ? validate.blur(store) : validate?.blur,\n focus: isFunction(validate?.focus) ? validate.focus(store) : validate?.focus,\n };\n }\n\n public buildFieldMeta = (\n fieldName: string,\n persistedMeta: PersistedFieldMeta | undefined,\n values: Values,\n errors: Record<string, FormIssue[]>,\n ): FieldMeta => {\n const path = stringToPath(fieldName);\n const value = get(values as never, path);\n const defaultValue = get(this.options.defaultValues, path);\n const invalid = errors[fieldName]?.length > 0;\n const baseMeta = persistedMeta ?? defaultMeta;\n\n return {\n ...baseMeta,\n default: isDeepEqual(value, defaultValue),\n pristine: !baseMeta.dirty,\n valid: !invalid,\n } satisfies FieldMeta;\n };\n\n public setFieldMeta = (name: string, meta: Partial<PersistedFieldMeta>) => {\n this.persisted.setState(current => {\n return {\n ...current,\n fields: {\n ...current.fields,\n [name]: {\n ...defaultMeta,\n ...this.persisted.state.fields[name],\n ...meta,\n },\n },\n };\n });\n };\n\n public resetFieldMeta = (name: string) => {\n this.persisted.setState(current => {\n const fields = { ...current.fields };\n const all = Object.keys(current.fields);\n const affected = all.filter(key => key.startsWith(name));\n\n for (const key of affected) {\n delete fields[key];\n }\n\n return {\n ...current,\n fields,\n };\n });\n };\n\n public recomputeFieldMeta = (name: string) => {\n this.persisted.setState(current => {\n const related: string[] = this.options.related?.[name as never] ?? [];\n const all = Object.keys(current.fields);\n const affected = all.filter(key => key.startsWith(name) || related.includes(key));\n const updated = affected.reduce(\n (acc, key) => {\n return {\n ...acc,\n [key]: this.buildFieldMeta(key, current.fields[key], current.values as Values, current.errors),\n };\n },\n {} as Record<string, FieldMeta>,\n );\n\n return {\n ...current,\n fields: {\n ...current.fields,\n ...updated,\n },\n };\n });\n };\n\n public setStatus = (status: Partial<PersistedFormStatus>) => {\n this.persisted.setState(current => {\n return {\n ...current,\n status: {\n ...current.status,\n ...status,\n },\n };\n });\n };\n\n public validate = async (\n field?: FormFields<FormApi<Values>> | FormFields<FormApi<Values>>[],\n options?: ValidateOptions,\n ) => {\n const validator = options?.type ? this.validator[options.type] : this.options.schema;\n\n if (!validator) return [];\n\n this.setStatus({ validating: true });\n\n const { issues: allIssues } = await validate(validator, this.store.state.values);\n\n if (!allIssues) {\n this.persisted.setState(current => {\n return { ...current, errors: {}, status: { ...current.status, validating: false } };\n });\n\n return [];\n }\n\n const fields = field ? (Array.isArray(field) ? field : [field]) : undefined;\n const related: string[] = fields?.flatMap(field => this.options.related?.[field as never] ?? []) ?? [];\n const affected = [...(fields ?? []), ...related];\n\n const issues = allIssues.filter(issue => {\n const path = issue.path?.join('.') ?? 'root';\n return !fields || affected.some(key => path.startsWith(key));\n });\n\n const errors = issues.reduce((acc, issue) => {\n const path = issue.path?.join('.') ?? 'root';\n return {\n ...acc,\n [path]: [...(acc[path] ?? []), issue],\n };\n }, {} as any);\n\n this.persisted.setState(current => {\n const existing = { ...current.errors };\n\n for (const key of affected) {\n delete existing[key];\n }\n\n return {\n ...current,\n errors: {\n ...(fields ? existing : {}), // when validating a specific set of fields, keep the existing errors\n ...errors,\n },\n };\n });\n\n this.setStatus({ validating: false });\n\n return issues;\n };\n}\n","import { defaultMeta } from '#field-api.constants';\nimport type { FormApi, FormFields } from '#form-api';\nimport type { FieldChangeOptions, FormIssue, FormResetFieldOptions, FormSetErrorsOptions } from '#form-api.types';\nimport type { FormContextApi } from '#form-context-api';\nimport type { DeepValue } from '#more-types';\nimport { get } from '#utils/get';\nimport { update, type Updater } from '#utils/update';\nimport { batch } from '@tanstack/store';\nimport { setPath, stringToPath } from 'remeda';\n\nexport class FormFieldApi<Values> {\n constructor(private context: FormContextApi<Values>) {}\n\n /**\n * Changes the value of a specific field with optional control over side effects.\n * @param name - The name of the field to change\n * @param value - The new value to set for the field\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public change = <const Name extends FormFields<FormApi<Values>>>(\n name: Name,\n updater: Updater<DeepValue<Values, Name>>,\n options?: FieldChangeOptions,\n ) => {\n const shouldDirty = options?.should?.dirty !== false;\n const shouldTouch = options?.should?.touch !== false;\n const shouldValidate = options?.should?.validate !== false;\n\n this.context.persisted.setState(current => {\n const value = get(current.values as never, stringToPath(name)) as DeepValue<Values, Name>;\n\n const values = setPath(\n current.values as never,\n stringToPath(name) as never,\n update(updater, value) as never,\n ) as Values;\n\n return {\n ...current,\n values,\n };\n });\n\n if (shouldValidate) void this.context.validate(name, { type: 'change' });\n\n batch(() => {\n if (shouldDirty) this.context.setFieldMeta(name, { dirty: true });\n if (shouldTouch) this.context.setFieldMeta(name, { touched: true });\n });\n };\n\n public focus = <const Name extends FormFields<FormApi<Values>>>(name: Name) => {\n const ref = this.context.store.state.refs[name as never];\n\n if (ref) ref.focus();\n\n this.context.setFieldMeta(name as never, { touched: true });\n void this.context.validate(name as never, { type: 'focus' });\n };\n\n public blur = <const Name extends FormFields<FormApi<Values>>>(name: Name) => {\n const ref = this.context.store.state.refs[name as never];\n\n if (ref) ref.blur();\n\n this.context.setFieldMeta(name as never, { blurred: true });\n void this.context.validate(name as never, { type: 'blur' });\n };\n\n public get = <const Name extends FormFields<FormApi<Values>>>(name: Name) => {\n return get(this.context.store.state.values as never, stringToPath(name)) as DeepValue<Values, Name>;\n };\n\n public meta = <const Name extends FormFields<FormApi<Values>>>(name: Name) => {\n const meta = this.context.store.state.fields[name];\n\n if (meta) return meta;\n\n const updated = this.context.buildFieldMeta(\n name,\n undefined,\n this.context.store.state.values as Values,\n this.context.store.state.errors,\n );\n\n this.context.setFieldMeta(name, updated);\n\n return updated;\n };\n\n public register = <const Name extends FormFields<FormApi<Values>>>(name: Name) => {\n return (element: HTMLElement | null) => {\n if (!element) return;\n\n this.context.persisted.setState(current => {\n return {\n ...current,\n refs: {\n ...current.refs,\n [name]: element,\n },\n };\n });\n };\n };\n\n public errors = <const Name extends FormFields<FormApi<Values>>>(name: Name) => {\n return this.context.store.state.errors[name] ?? [];\n };\n\n public setErrors = <const Name extends FormFields<FormApi<Values>>>(\n name: Name,\n errors: FormIssue[],\n options?: FormSetErrorsOptions,\n ) => {\n this.context.persisted.setState(current => {\n const existing = current.errors[name] ?? [];\n let updated: FormIssue[];\n\n switch (options?.mode) {\n case 'append':\n updated = [...existing, ...errors];\n break;\n case 'keep':\n updated = existing.length > 0 ? existing : errors;\n break;\n case 'replace':\n default:\n updated = errors;\n break;\n }\n\n return {\n ...current,\n errors: {\n ...current.errors,\n [name]: updated,\n },\n };\n });\n };\n\n public reset = <const Name extends FormFields<FormApi<Values>>>(\n name: Name,\n options?: FormResetFieldOptions<DeepValue<Values, Name>>,\n ) => {\n const path = stringToPath(name as never);\n const defaultValue = get(this.context.options.defaultValues, path) as DeepValue<Values, Name>;\n const value = options?.value ?? defaultValue;\n\n this.context.persisted.setState(current => {\n const values = setPath(current.values as never, path as never, value as never);\n const fields = { ...current.fields };\n const refs = { ...current.refs };\n const errors = { ...current.errors };\n\n if (options?.meta) {\n const currentMeta = options?.keep?.meta ? (fields[name as string] ?? defaultMeta) : defaultMeta;\n fields[name as string] = {\n ...currentMeta,\n ...options.meta,\n };\n } else if (!options?.keep?.meta) delete fields[name as string];\n\n if (!options?.keep?.refs) delete refs[name as string];\n if (!options?.keep?.errors) delete errors[name as string];\n\n return {\n ...current,\n values,\n fields,\n refs,\n errors,\n };\n });\n };\n}\n","import { defaultStatus } from '#field-api.constants';\nimport type { FormOptions, FormResetOptions, FormSubmitErrorHandler, FormSubmitSuccessHandler } from '#form-api.types';\nimport { FormArrayFieldApi } from '#form-array-field-api';\nimport { FormContextApi } from '#form-context-api';\nimport { FormFieldApi } from '#form-field-api';\nimport type { DeepKeys, DeepKeysOfType, DeepValue } from '#more-types';\nimport type { ArrayLike, StandardSchema } from '#types';\n\nexport type AnyFormApi = FormApi<any>;\n\nexport type FormValues<Form extends AnyFormApi> = Form extends FormApi<infer Values> ? Values : never;\nexport type FormFields<Form extends AnyFormApi> = DeepKeys<FormValues<Form>>;\nexport type FormArrayFields<Form extends AnyFormApi> = DeepKeysOfType<FormValues<Form>, ArrayLike>;\nexport type FormFieldValue<Form extends AnyFormApi, Name extends FormFields<Form>> = DeepValue<FormValues<Form>, Name>;\n\nexport class FormApi<Values> {\n private context: FormContextApi<Values>;\n public field: FormFieldApi<Values>;\n public array: FormArrayFieldApi<Values>;\n\n constructor(options: FormOptions<Values>) {\n // todo: add form id to options\n\n this.context = new FormContextApi(options);\n this.field = new FormFieldApi(this.context);\n this.array = new FormArrayFieldApi({ field: this.field, context: this.context });\n }\n\n public '~mount' = () => {\n const unsubscribe = this.context.store.mount();\n\n return unsubscribe;\n };\n\n public '~update' = (options: FormOptions<Values>) => {\n this.context.options = options;\n };\n\n public store = () => {\n return this.context.store;\n };\n\n public status = () => {\n return this.context.store.state.status;\n };\n\n public values = () => {\n return this.context.store.state.values;\n };\n\n public options = () => {\n return this.context.options;\n };\n\n public get validate() {\n return this.context.validate;\n }\n\n public submit =\n (\n onSuccess: FormSubmitSuccessHandler<StandardSchema<Values>>,\n onError?: FormSubmitErrorHandler<StandardSchema<Values>>,\n ) =>\n async () => {\n this.context.setStatus({ submitting: true, dirty: true });\n\n const issues = await this.context.validate(undefined, { type: 'submit' });\n const valid = issues.length === 0;\n\n if (valid) {\n await onSuccess(this.context.store.state.values as never, this as never);\n } else {\n await onError?.(issues, this as never);\n }\n\n this.context.setStatus({\n submits: this.context.persisted.state.status.submits + 1,\n submitting: false,\n successful: valid,\n });\n };\n\n public reset = (options?: FormResetOptions<Values>) => {\n this.context.persisted.setState(current => {\n return {\n values: options?.values ?? this.context.options.defaultValues,\n fields: options?.keep?.fields ? current.fields : {},\n refs: options?.keep?.refs ? current.refs : {},\n errors: options?.keep?.errors ? current.errors : {},\n status: {\n ...defaultStatus,\n ...options?.status,\n },\n };\n });\n };\n}\n","import { type AnyFormApi, type FormFields, type FormFieldValue } from '#form-api';\nimport type {\n FieldChangeOptions,\n FieldMeta,\n FormIssue,\n FormResetFieldOptions,\n FormSetErrorsOptions,\n ValidateOptions,\n} from '#form-api.types';\nimport { get } from '#utils/get';\nimport type { Updater } from '#utils/update';\nimport { Derived } from '@tanstack/store';\nimport { isDeepEqual, stringToPath } from 'remeda';\n\nexport type FieldOptions<Form extends AnyFormApi, Name extends FormFields<Form>> = {\n form: Form;\n name: Name;\n};\n\nexport type FieldState<Value> = {\n value: Value;\n defaultValue: Value;\n meta: FieldMeta;\n errors: FormIssue[];\n};\n\nexport type AnyFieldApi = FieldApi<any>;\n\nexport class FieldApi<Value> {\n private _options: FieldOptions<AnyFormApi, string>;\n private form: AnyFormApi;\n private _store: Derived<FieldState<Value>>;\n\n constructor(options: FieldOptions<AnyFormApi, string>) {\n this._options = options;\n this.form = options.form;\n\n this._store = new Derived<FieldState<Value>>({\n deps: [this.form.store()],\n fn: ({ prevVal }) => {\n const previous = prevVal as FieldState<Value> | undefined;\n\n const value = this.form.field.get(this._options.name as never) as Value;\n const defaultValue = get(this.form.options().defaultValues as never, stringToPath(this._options.name)) as Value;\n const meta = this.form.field.meta(this._options.name as never);\n const errors = this.form.field.errors(this._options.name as never);\n\n const updated = {\n value,\n defaultValue,\n meta,\n errors,\n };\n\n if (previous && isDeepEqual(previous, updated)) return previous;\n\n return updated;\n },\n });\n }\n\n public '~mount' = () => {\n const unsubscribe = this._store.mount();\n\n return unsubscribe;\n };\n\n public '~update' = (options: FieldOptions<AnyFormApi, string>) => {\n // ref: https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1300\n this._options = options;\n };\n\n public options = () => {\n return this._options;\n };\n\n public store = () => {\n return this._store;\n };\n\n public state = () => {\n return this._store.state;\n };\n\n public focus = () => {\n return this.form.field.focus(this._options.name);\n };\n\n public blur = () => {\n return this.form.field.blur(this._options.name);\n };\n\n /**\n * Changes the value of this field with optional control over side effects.\n * @param value - The new value to set for the field\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public change = (value: Updater<Value>, options?: FieldChangeOptions) => {\n return this.form.field.change(this._options.name, value as never, options);\n };\n\n public register = () => this.form.field.register(this._options.name);\n\n /**\n * Validates this specific field using the specified validation type.\n * @param options - Optional validation options specifying the validation type ('change' | 'submit' | 'blur' | 'focus')\n * @returns Promise resolving to an array of validation issues for this field\n */\n public validate = (options?: ValidateOptions) => {\n return this.form.validate(this._options.name, options);\n };\n\n /**\n * Resets this field to its default value and optionally resets metadata and errors.\n * @param options - Reset options for controlling what gets reset and what gets kept\n */\n public reset = (options?: FormResetFieldOptions<Value>) => {\n return this.form.field.reset(this._options.name, options);\n };\n\n /**\n * Sets validation errors for this specific field.\n * @param errors - Array of validation errors to set\n * @param mode - How to handle existing errors: 'replace' (default), 'append', or 'keep'\n */\n public setErrors = (errors: FormIssue[], options?: FormSetErrorsOptions) => {\n return this.form.field.setErrors(this._options.name, errors, options);\n };\n}\n\nexport const createFieldApi = <Form extends AnyFormApi, const Name extends FormFields<Form>>(\n options: FieldOptions<Form, Name>,\n) => {\n type Value = FormFieldValue<Form, Name>;\n\n return new FieldApi(options) as FieldApi<Value>;\n};\n\n// const schema = z.object({ name: z.string(), nested: z.object({ deep: z.object({ deeper: z.string().array() }) }) });\n// const form = new FormApi({ schema });\n// const field = createFieldApi({ form, name: 'name' });\n","import { FieldApi, type FieldOptions } from '#field-api';\nimport { type AnyFormApi, type FormArrayFields, type FormFieldValue } from '#form-api';\nimport type {\n FieldChangeOptions,\n FormIssue,\n FormResetFieldOptions,\n FormSetErrorsOptions,\n ValidateOptions,\n} from '#form-api.types';\nimport type { UnwrapOneLevelOfArray } from '#more-types';\nimport type { ArrayLike } from '#types';\nimport type { Updater } from '#utils/update';\n\nexport type ArrayFieldOptions<Form extends AnyFormApi, Name extends FormArrayFields<Form>> = {\n form: Form;\n name: Name;\n};\n\nexport type AnyArrayFieldApi = ArrayFieldApi<any>;\n\nexport class ArrayFieldApi<Value extends ArrayLike> {\n private field: FieldApi<Value>;\n\n constructor(options: FieldOptions<AnyFormApi, string>) {\n this.field = new FieldApi<Value>(options);\n }\n\n private get _options() {\n return this.field.options();\n }\n\n public '~mount' = () => {\n const unsubscribe = this.field.store().mount();\n\n return unsubscribe;\n };\n\n public '~update' = (options: FieldOptions<AnyFormApi, string>) => {\n this.field['~update'](options);\n };\n\n public store = () => {\n return this.field.store();\n };\n\n public state = () => {\n return this.field.store().state;\n };\n\n /**\n * Appends a new item to the end of the array.\n * @param value - The value to append to the array\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public append = (value: UnwrapOneLevelOfArray<Value>, options?: FieldChangeOptions) => {\n return this._options.form.array.append(this._options.name as never, value as never, options);\n };\n\n /**\n * Prepends a new item to the beginning of the array.\n * @param value - The value to prepend to the array\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public prepend = (value: UnwrapOneLevelOfArray<Value>, options?: FieldChangeOptions) => {\n return this._options.form.array.prepend(this._options.name as never, value as never, options);\n };\n\n /**\n * Inserts a new item at the specified index in the array.\n * @param index - The index at which to insert the value\n * @param value - The value to insert into the array\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public insert = (index: number, value: Updater<UnwrapOneLevelOfArray<Value>>, options?: FieldChangeOptions) => {\n return this._options.form.array.insert(this._options.name as never, index, value as never, options);\n };\n\n /**\n * Updates an item at the specified index in the array.\n * @param index - The index of the item to update\n * @param value - The new value or updater function\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public update = (index: number, value: Updater<UnwrapOneLevelOfArray<Value>>, options?: FieldChangeOptions) => {\n return this._options.form.array.update(this._options.name as never, index, value as never, options);\n };\n\n /**\n * Removes an item at the specified index from the array.\n * @param index - The index of the item to remove\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public remove = (index: number, options?: FieldChangeOptions) => {\n return this._options.form.array.remove(this._options.name as never, index, options);\n };\n\n /**\n * Swaps two items in the array by their indices.\n * @param from - The index of the first item\n * @param to - The index of the second item\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public swap = (from: number, to: number, options?: FieldChangeOptions) => {\n return this._options.form.array.swap(this._options.name as never, from, to, options);\n };\n\n /**\n * Moves an item from one index to another in the array.\n * @param from - The index of the item to move\n * @param to - The target index\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public move = (from: number, to: number, options?: FieldChangeOptions) => {\n return this._options.form.array.move(this._options.name as never, from, to, options);\n };\n\n /**\n * Replaces the entire array with a new value.\n * @param value - The new array value or updater function\n * @param options - Optional configuration for controlling validation, dirty state, and touched state\n */\n public replace = (value: Updater<Value>, options?: FieldChangeOptions) => {\n return this._options.form.array.replace(this._options.name as never, value as never, options);\n };\n\n /**\n * Validates this specific array field using the specified validation type.\n * @param options - Optional validation options specifying the validation type ('change' | 'submit' | 'blur' | 'focus')\n * @returns Promise resolving to an array of validation issues for this field\n */\n public validate = (options?: ValidateOptions) => {\n return this._options.form.validate(this._options.name, options);\n };\n\n /**\n * Resets this array field to its default value and optionally resets metadata and errors.\n * @param options - Reset options for controlling what gets reset and what gets kept\n */\n public reset = (options?: FormResetFieldOptions<Value>) => {\n return this._options.form.field.reset(this._options.name, options);\n };\n\n /**\n * Sets validation errors for this specific array field.\n * @param errors - Array of validation errors to set\n * @param mode - How to handle existing errors: 'replace' (default), 'append', or 'keep'\n */\n public setErrors = (errors: FormIssue[], options?: FormSetErrorsOptions) => {\n return this._options.form.field.setErrors(this._options.name, errors, options);\n };\n}\n\nexport const createArrayFieldApi = <FormApi extends AnyFormApi, const Name extends FormArrayFields<FormApi>>(\n options: ArrayFieldOptions<FormApi, Name>,\n) => {\n return new ArrayFieldApi(options) as ArrayFieldApi<FormFieldValue<FormApi, Name>>;\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,40 +1,45 @@
1
1
  {
2
2
  "name": "oxform-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Oxform is a lean and flexible framework-agnostic form library.",
5
5
  "author": "Frantss <frantss.bongiovanni@gmail.com>",
6
6
  "license": "MIT",
7
7
  "sideEffects": false,
8
8
  "type": "module",
9
- "main": "./dist/index.js",
9
+ "main": "./dist/index.cjs",
10
10
  "imports": {
11
- "#*": [
12
- "./src/*.ts",
13
- "./src/**/*.ts"
14
- ]
11
+ "#*": "./src/*.ts"
15
12
  },
16
13
  "exports": {
17
- ".": "./dist/index.js",
18
- "./package.json": "./package.json",
19
- "./schema": "./dist/schema.js"
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ },
18
+ "./package.json": "./package.json"
20
19
  },
21
20
  "module": "./dist/index.js",
22
- "types": "./dist/index.d.ts",
21
+ "types": "./dist/index.d.cts",
22
+ "files": [
23
+ "dist"
24
+ ],
23
25
  "scripts": {
24
26
  "build": "tsdown --config-loader unrun",
25
27
  "test": "vitest",
26
- "lint:types": "tsc --noEmit"
28
+ "check:types": "tsc --noEmit",
29
+ "check:package": "publint --strict && attw --pack --no-emoji"
27
30
  },
28
31
  "dependencies": {
29
- "@standard-schema/spec": "catalog:oxform",
32
+ "@standard-schema/spec": "^1.1.0",
30
33
  "@tanstack/store": "^0.8.0",
31
34
  "remeda": "^2.32.0",
32
35
  "type-fest": "^5.3.1"
33
36
  },
34
37
  "devDependencies": {
35
- "tsdown": "catalog:oxform",
36
- "typescript": "catalog:oxform",
37
- "vitest": "catalog:oxform",
38
+ "@arethetypeswrong/cli": "^0.18.2",
39
+ "publint": "^0.3.16",
40
+ "tsdown": "^0.18.1",
41
+ "typescript": "^5.9.3",
42
+ "vitest": "^4.0.16",
38
43
  "zod": "^4.2.1"
39
44
  }
40
45
  }
package/export/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export { FieldApi } from '#field-api';
2
- export { FormApi } from '#form-api';
3
-
4
- export type * from '#field-api';
5
- export type * from '#form-api.types';
6
- export type * from '#more-types';
7
- export type { EventLike } from '#types';
package/export/schema.ts DELETED
@@ -1 +0,0 @@
1
- export type { StandardSchema } from '#types';
@@ -1,15 +0,0 @@
1
- import type { PersistedFieldMeta, PersistedFormStatus } from '#form-api.types';
2
-
3
- export const defaultMeta = {
4
- blurred: false,
5
- touched: false,
6
- dirty: false,
7
- } satisfies PersistedFieldMeta;
8
-
9
- export const defaultStatus = {
10
- submits: 0,
11
- submitting: false,
12
- validating: false,
13
- successful: false,
14
- dirty: false,
15
- } satisfies PersistedFormStatus;
package/src/field-api.ts DELETED
@@ -1,139 +0,0 @@
1
- import type { FormApi } from '#form-api';
2
- import type {
3
- FieldChangeOptions,
4
- FieldMeta,
5
- FormIssue,
6
- FormResetFieldOptions,
7
- FormSetErrorsOptions,
8
- ValidateOptions,
9
- } from '#form-api.types';
10
- import type { DeepKeys, DeepValue } from '#more-types';
11
- import type { EventLike, StandardSchema } from '#types';
12
- import { get } from '#utils/get';
13
- import type { Updater } from '#utils/update';
14
- import { Derived } from '@tanstack/store';
15
- import { stringToPath } from 'remeda';
16
-
17
- export type FieldOptions<
18
- Schema extends StandardSchema,
19
- Name extends DeepKeys<StandardSchema.InferInput<Schema>> = DeepKeys<StandardSchema.InferInput<Schema>>,
20
- > = {
21
- form: FormApi<Schema>;
22
- name: Name;
23
- };
24
-
25
- export type FieldStore<Value> = {
26
- value: Value;
27
- defaultValue: Value;
28
- meta: FieldMeta;
29
- errors: FormIssue[];
30
- };
31
-
32
- export type FieldProps<Value> = {
33
- defaultValue: Value;
34
- value: Value;
35
- ref: (element: HTMLElement | null) => void;
36
- onChange: (event: EventLike) => void;
37
- onBlur: (event: EventLike) => void;
38
- onFocus: (event: EventLike) => void;
39
- };
40
-
41
- export class FieldApi<
42
- Schema extends StandardSchema,
43
- Name extends DeepKeys<StandardSchema.InferInput<Schema>>,
44
- in out Value extends DeepValue<StandardSchema.InferInput<Schema>, Name>,
45
- > {
46
- public options: FieldOptions<Schema, Name>;
47
- public form: FormApi<Schema>;
48
- public store: Derived<FieldStore<Value>>;
49
-
50
- constructor(options: FieldOptions<Schema, Name>) {
51
- this.options = options;
52
- this.form = options.form;
53
-
54
- this.store = new Derived<FieldStore<Value>>({
55
- deps: [this.form.store],
56
- fn: () => {
57
- const value = this.form.field.get(this.options.name as never) as Value;
58
- const defaultValue = get(this.form.options.defaultValues as never, stringToPath(this.options.name)) as Value;
59
- const meta = this.form.field.meta(this.options.name as never);
60
- const errors = this.form.field.errors(this.options.name as never);
61
-
62
- return {
63
- value,
64
- defaultValue,
65
- meta,
66
- errors,
67
- };
68
- },
69
- });
70
- }
71
-
72
- public '~mount' = () => {
73
- const unsubscribe = this.store.mount();
74
-
75
- return unsubscribe;
76
- };
77
-
78
- public '~update' = (options: FieldOptions<Schema, Name>) => {
79
- // ref: https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1300
80
- this.options = options;
81
- };
82
-
83
- public get value() {
84
- return this.store.state.value;
85
- }
86
-
87
- public get defaultValue() {
88
- return this.store.state.defaultValue;
89
- }
90
-
91
- public get meta() {
92
- return this.store.state.meta;
93
- }
94
-
95
- public get errors() {
96
- return this.store.state.errors;
97
- }
98
-
99
- public focus = () => this.form.field.focus(this.options.name);
100
-
101
- public blur = () => this.form.field.blur(this.options.name);
102
-
103
- /**
104
- * Changes the value of this field with optional control over side effects.
105
- * @param value - The new value to set for the field
106
- * @param options - Optional configuration for controlling validation, dirty state, and touched state
107
- */
108
- public change = (value: Updater<Value>, options?: FieldChangeOptions) => {
109
- return this.form.field.change(this.options.name, value as never, options);
110
- };
111
-
112
- public register = () => this.form.field.register(this.options.name);
113
-
114
- /**
115
- * Validates this specific field using the specified validation type.
116
- * @param options - Optional validation options specifying the validation type ('change' | 'submit' | 'blur' | 'focus')
117
- * @returns Promise resolving to an array of validation issues for this field
118
- */
119
- public validate = (options?: ValidateOptions) => {
120
- return this.form.validate(this.options.name, options);
121
- };
122
-
123
- /**
124
- * Resets this field to its default value and optionally resets metadata and errors.
125
- * @param options - Reset options for controlling what gets reset and what gets kept
126
- */
127
- public reset = (options?: FormResetFieldOptions<Value>) => {
128
- return this.form.field.reset(this.options.name, options);
129
- };
130
-
131
- /**
132
- * Sets validation errors for this specific field.
133
- * @param errors - Array of validation errors to set
134
- * @param mode - How to handle existing errors: 'replace' (default), 'append', or 'keep'
135
- */
136
- public setErrors = (errors: FormIssue[], options?: FormSetErrorsOptions) => {
137
- return this.form.field.setErrors(this.options.name, errors, options);
138
- };
139
- }
package/src/form-api.ts DELETED
@@ -1,84 +0,0 @@
1
- import { defaultStatus } from '#field-api.constants';
2
- import type { FormOptions, FormResetOptions, FormSubmitErrorHandler, FormSubmitSuccessHandler } from '#form-api.types';
3
- import { FormArrayFieldApi } from '#form-array-field-api';
4
- import { FormContextApi } from '#form-context-api';
5
- import { FormFieldApi } from '#form-field-api';
6
- import type { StandardSchema } from '#types';
7
-
8
- export class FormApi<Schema extends StandardSchema> {
9
- private context: FormContextApi<Schema>;
10
- public field: FormFieldApi<Schema>;
11
- public array: FormArrayFieldApi<Schema>;
12
-
13
- constructor(options: FormOptions<Schema>) {
14
- this.context = new FormContextApi(options);
15
- this.field = new FormFieldApi(this.context);
16
- this.array = new FormArrayFieldApi({ field: this.field, context: this.context });
17
- }
18
-
19
- public '~mount' = () => {
20
- const unsubscribe = this.context.store.mount();
21
-
22
- return unsubscribe;
23
- };
24
-
25
- public '~update' = (options: FormOptions<NoInfer<Schema>>) => {
26
- this.context.options = options;
27
- };
28
-
29
- public get store() {
30
- return this.context.store;
31
- }
32
-
33
- public get status() {
34
- return this.context.store.state.status;
35
- }
36
-
37
- public get values() {
38
- return this.context.store.state.values;
39
- }
40
-
41
- public get options() {
42
- return this.context.options;
43
- }
44
-
45
- public get validate() {
46
- return this.context.validate;
47
- }
48
-
49
- public submit =
50
- (onSuccess: FormSubmitSuccessHandler<NoInfer<Schema>>, onError?: FormSubmitErrorHandler<NoInfer<Schema>>) =>
51
- async () => {
52
- this.context.setStatus({ submitting: true, dirty: true });
53
-
54
- const issues = await this.context.validate(undefined, { type: 'submit' });
55
- const valid = issues.length === 0;
56
-
57
- if (valid) {
58
- await onSuccess(this.context.store.state.values as never, this as never);
59
- } else {
60
- await onError?.(issues, this as never);
61
- }
62
-
63
- this.context.setStatus({
64
- submits: this.context.persisted.state.status.submits + 1,
65
- submitting: false,
66
- successful: valid,
67
- });
68
- };
69
-
70
- public reset = (options?: FormResetOptions<StandardSchema.InferInput<NoInfer<Schema>>>) => {
71
- this.context.persisted.setState(current => {
72
- return {
73
- values: options?.values ?? this.context.options.defaultValues,
74
- fields: options?.keep?.fields ? current.fields : {},
75
- refs: options?.keep?.refs ? current.refs : {},
76
- errors: options?.keep?.errors ? current.errors : {},
77
- status: {
78
- ...defaultStatus,
79
- ...options?.status,
80
- },
81
- };
82
- });
83
- };
84
- }
@@ -1,148 +0,0 @@
1
- import type { FormApi } from '#form-api';
2
- import type { DeepKeys } from '#more-types';
3
- import type { PartialDeep, Simplify, StandardSchema } from '#types';
4
-
5
- export type PersistedFormStatus = {
6
- submits: number;
7
- submitting: boolean;
8
- validating: boolean;
9
- successful: boolean;
10
- dirty: boolean;
11
- };
12
-
13
- export type FormStatus = Simplify<
14
- PersistedFormStatus & {
15
- submitted: boolean;
16
- valid: boolean;
17
- }
18
- >;
19
-
20
- export type PersistedFieldMeta = {
21
- blurred: boolean;
22
- touched: boolean;
23
- dirty: boolean;
24
- };
25
-
26
- export type FieldMeta = Simplify<
27
- PersistedFieldMeta & {
28
- touched: boolean;
29
- default: boolean;
30
- valid: boolean;
31
- pristine: boolean;
32
- }
33
- >;
34
-
35
- export type FormBaseStore<Schema extends StandardSchema> = {
36
- values: StandardSchema.InferInput<Schema>;
37
- fields: Record<string, PersistedFieldMeta>;
38
- refs: Record<string, HTMLElement | null>;
39
- status: PersistedFormStatus;
40
- errors: Record<string, FormIssue[]>;
41
- };
42
-
43
- export type FormStore<Schema extends StandardSchema> = {
44
- values: StandardSchema.InferInput<Schema>;
45
- fields: Record<string, FieldMeta>;
46
- refs: Record<string, HTMLElement | null>;
47
- status: FormStatus;
48
- errors: Record<string, FormIssue[]>;
49
- };
50
-
51
- export type FieldControl<Value> = {
52
- focus: () => void;
53
- blur: () => void;
54
- change: (value: Value) => void;
55
- register: (element: HTMLElement | null) => void;
56
- };
57
-
58
- type FormValidatorSchema<Schema extends StandardSchema> = NoInfer<
59
- StandardSchema<PartialDeep<StandardSchema.InferInput<Schema>>>
60
- >;
61
- type FormValidatorFunction<Schema extends StandardSchema> = (store: FormStore<Schema>) => FormValidatorSchema<Schema>;
62
- type FormValidator<Schema extends StandardSchema> = FormValidatorSchema<Schema> | FormValidatorFunction<Schema>;
63
-
64
- export type FormOptions<Schema extends StandardSchema> = {
65
- schema: Schema;
66
- values?: StandardSchema.InferInput<Schema>;
67
- defaultValues: StandardSchema.InferInput<Schema>;
68
- validate?: {
69
- change?: FormValidator<Schema>;
70
- submit?: FormValidator<Schema>;
71
- blur?: FormValidator<Schema>;
72
- focus?: FormValidator<Schema>;
73
- };
74
- related?: Record<DeepKeys<StandardSchema.InferInput<Schema>>, DeepKeys<StandardSchema.InferInput<Schema>>[]>;
75
- };
76
-
77
- export type FormIssue = StandardSchema.Issue;
78
-
79
- export type ValidationType = 'change' | 'submit' | 'blur' | 'focus';
80
-
81
- export type ValidateOptions = {
82
- type?: ValidationType;
83
- };
84
-
85
- export type FieldChangeOptions = {
86
- should?: {
87
- /** Whether to validate the field after changing its value. Defaults to true. */
88
- validate?: boolean;
89
- /** Whether to mark the field as dirty after changing its value. Defaults to true. */
90
- dirty?: boolean;
91
- /** Whether to mark the field as touched after changing its value. Defaults to true. */
92
- touch?: boolean;
93
- };
94
- };
95
-
96
- export type FieldResetMeta = {
97
- blurred?: boolean;
98
- touched?: boolean;
99
- dirty?: boolean;
100
- };
101
-
102
- export type FieldResetKeepOptions = {
103
- errors?: boolean;
104
- refs?: boolean;
105
- meta?: boolean;
106
- };
107
-
108
- export type FormResetFieldOptions<Value> = {
109
- value?: Value;
110
- meta?: FieldResetMeta;
111
- keep?: FieldResetKeepOptions;
112
- };
113
-
114
- export type FormResetKeepOptions = {
115
- /** Keep current field errors */
116
- errors?: boolean;
117
- /** Keep current references to html input elements */
118
- refs?: boolean;
119
- /** Keep current field metadata */
120
- fields?: boolean;
121
- };
122
-
123
- export type FormResetOptions<Values> = {
124
- values?: Values;
125
- status?: Partial<PersistedFormStatus>;
126
- keep?: FormResetKeepOptions;
127
- };
128
-
129
- export type FieldSetErrorsMode = 'replace' | 'append' | 'keep';
130
-
131
- export type FormSetErrorsOptions = {
132
- mode?: FieldSetErrorsMode;
133
- };
134
-
135
- export type FormSubmitSuccessHandler<Schema extends StandardSchema> = (
136
- data: StandardSchema.InferOutput<Schema>,
137
- form: FormApi<Schema>,
138
- ) => void | Promise<void>;
139
-
140
- export type FormSubmitErrorHandler<Schema extends StandardSchema> = (
141
- issues: FormIssue[],
142
- form: FormApi<Schema>,
143
- ) => void | Promise<void>;
144
-
145
- export type FormSubmitHandlers<Schema extends StandardSchema> = {
146
- onSuccess: FormSubmitSuccessHandler<Schema>;
147
- onError?: FormSubmitErrorHandler<Schema>;
148
- };