@tanstack/form-core
Advanced tools
+1
-1
| { | ||
| "name": "@tanstack/form-core", | ||
| "version": "0.2.0", | ||
| "version": "0.3.0", | ||
| "description": "Powerful, type-safe, framework agnostic forms.", | ||
@@ -5,0 +5,0 @@ "author": "tannerlinsley", |
+53
-29
@@ -1,6 +0,6 @@ | ||
| import type { DeepKeys, DeepValue, Updater } from './utils' | ||
| import type { FormApi, ValidationError } from './FormApi' | ||
| import { type DeepKeys, type DeepValue, type Updater } from './utils' | ||
| import type { FormApi, ValidationError, ValidationErrorMap } from './FormApi' | ||
| import { Store } from '@tanstack/store' | ||
| export type ValidationCause = 'change' | 'blur' | 'submit' | ||
| export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount' | ||
@@ -55,4 +55,5 @@ type ValidateFn<TData, TFormData> = ( | ||
| isTouched: boolean | ||
| touchedError?: ValidationError | ||
| error?: ValidationError | ||
| touchedErrors: ValidationError[] | ||
| errors: ValidationError[] | ||
| errorMap: ValidationErrorMap | ||
| isValidating: boolean | ||
@@ -114,2 +115,5 @@ } | ||
| isTouched: false, | ||
| touchedErrors: [], | ||
| errors: [], | ||
| errorMap: {}, | ||
| ...opts.defaultMeta, | ||
@@ -122,5 +126,5 @@ }, | ||
| state.meta.touchedError = state.meta.isTouched | ||
| ? state.meta.error | ||
| : undefined | ||
| state.meta.touchedErrors = state.meta.isTouched | ||
| ? state.meta.errors | ||
| : [] | ||
@@ -209,2 +213,5 @@ this.prevState = state | ||
| isTouched: false, | ||
| touchedErrors: [], | ||
| errors: [], | ||
| errorMap: {}, | ||
| ...this.options.defaultMeta, | ||
@@ -246,3 +253,2 @@ } as FieldMeta) | ||
| cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur | ||
| if (!validate) return | ||
@@ -255,12 +261,16 @@ | ||
| const error = normalizeError(validate(value as never, this as never)) | ||
| if (this.state.meta.error !== error) { | ||
| const errorMapKey = getErrorMapKey(cause) | ||
| if (error && this.state.meta.errorMap[errorMapKey] !== error) { | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| error, | ||
| errors: [...prev.errors, error], | ||
| errorMap: { | ||
| ...prev.errorMap, | ||
| [getErrorMapKey(cause)]: error, | ||
| }, | ||
| })) | ||
| } | ||
| // If a sync error is encountered, cancel any async validation | ||
| if (this.state.meta.error) { | ||
| // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation | ||
| if (this.state.meta.errorMap[errorMapKey]) { | ||
| this.cancelValidateAsync() | ||
@@ -302,5 +312,3 @@ } | ||
| : onBlurAsync | ||
| if (!validate) return | ||
| if (!validate) return [] | ||
| const debounceMs = | ||
@@ -338,5 +346,5 @@ cause === 'submit' | ||
| if (checkLatest()) { | ||
| const prevErrors = this.getMeta().errors | ||
| try { | ||
| const rawError = await validate(value as never, this as never) | ||
| if (checkLatest()) { | ||
@@ -347,9 +355,13 @@ const error = normalizeError(rawError) | ||
| isValidating: false, | ||
| error, | ||
| errors: [...prev.errors, error], | ||
| errorMap: { | ||
| ...prev.errorMap, | ||
| [getErrorMapKey(cause)]: error, | ||
| }, | ||
| })) | ||
| this.getInfo().validationResolve?.(error) | ||
| this.getInfo().validationResolve?.([...prevErrors, error]) | ||
| } | ||
| } catch (error) { | ||
| if (checkLatest()) { | ||
| this.getInfo().validationReject?.(error) | ||
| this.getInfo().validationReject?.([...prevErrors, error]) | ||
| throw error | ||
@@ -366,3 +378,3 @@ } | ||
| // Always return the latest validation promise to the caller | ||
| return this.getInfo().validationPromise | ||
| return this.getInfo().validationPromise ?? [] | ||
| } | ||
@@ -373,16 +385,15 @@ | ||
| value?: typeof this._tdata, | ||
| ): ValidationError | Promise<ValidationError> => { | ||
| ): ValidationError[] | Promise<ValidationError[]> => { | ||
| // If the field is pristine and validatePristine is false, do not validate | ||
| if (!this.state.meta.isTouched) return | ||
| if (!this.state.meta.isTouched) return [] | ||
| // Attempt to sync validate first | ||
| this.validateSync(value, cause) | ||
| // If there is an error, return it, do not attempt async validation | ||
| if (this.state.meta.error) { | ||
| const errorMapKey = getErrorMapKey(cause) | ||
| // If there is an error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation | ||
| if (this.getMeta().errorMap[errorMapKey]) { | ||
| if (!this.options.asyncAlways) { | ||
| return this.state.meta.error | ||
| return this.state.meta.errors | ||
| } | ||
| } | ||
| // No error? Attempt async validation | ||
@@ -417,1 +428,14 @@ return this.validateAsync(value, cause) | ||
| } | ||
| function getErrorMapKey(cause: ValidationCause) { | ||
| switch (cause) { | ||
| case 'submit': | ||
| return 'onSubmit' | ||
| case 'change': | ||
| return 'onChange' | ||
| case 'blur': | ||
| return 'onBlur' | ||
| case 'mount': | ||
| return 'onMount' | ||
| } | ||
| } |
+14
-7
| import { Store } from '@tanstack/store' | ||
| // | ||
| import type { DeepKeys, DeepValue, Updater } from './utils' | ||
| import { functionalUpdate, getBy, setBy } from './utils' | ||
| import { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils' | ||
| import type { FieldApi, FieldMeta, ValidationCause } from './FieldApi' | ||
@@ -40,5 +40,5 @@ | ||
| validationAsyncCount?: number | ||
| validationPromise?: Promise<ValidationError> | ||
| validationResolve?: (error: ValidationError) => void | ||
| validationReject?: (error: unknown) => void | ||
| validationPromise?: Promise<ValidationError[]> | ||
| validationResolve?: (errors: ValidationError[]) => void | ||
| validationReject?: (errors: unknown) => void | ||
| } | ||
@@ -48,2 +48,8 @@ | ||
| export type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}` | ||
| export type ValidationErrorMap = { | ||
| [K in ValidationErrorMapKeys]?: ValidationError | ||
| } | ||
| export type FormState<TData> = { | ||
@@ -122,3 +128,5 @@ values: TData | ||
| const isFieldsValid = !fieldMetaValues.some((field) => field?.error) | ||
| const isFieldsValid = !fieldMetaValues.some((field) => | ||
| isNonEmptyArray(field?.errors), | ||
| ) | ||
@@ -198,4 +206,3 @@ const isTouched = fieldMetaValues.some((field) => field?.isTouched) | ||
| validateAllFields = async (cause: ValidationCause) => { | ||
| const fieldValidationPromises: Promise<ValidationError>[] = [] as any | ||
| const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any | ||
| this.store.batch(() => { | ||
@@ -202,0 +209,0 @@ void (Object.values(this.fieldInfo) as FieldInfo<any>[]).forEach( |
@@ -33,2 +33,5 @@ import { expect } from 'vitest' | ||
| isValidating: false, | ||
| touchedErrors: [], | ||
| errors: [], | ||
| errorMap: {}, | ||
| }) | ||
@@ -48,2 +51,5 @@ }) | ||
| isValidating: false, | ||
| touchedErrors: [], | ||
| errors: [], | ||
| errorMap: {}, | ||
| }) | ||
@@ -198,5 +204,8 @@ }) | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onChange: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -225,6 +234,9 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
| await vi.runAllTimersAsync() | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onChange: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -255,3 +267,3 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
@@ -262,3 +274,6 @@ field.setValue('other') | ||
| expect(sleepMock).toHaveBeenCalledTimes(1) | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onChange: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -289,3 +304,3 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
@@ -296,3 +311,6 @@ field.setValue('other') | ||
| expect(sleepMock).toHaveBeenCalledTimes(1) | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onChange: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -320,3 +338,6 @@ | ||
| field.validate('blur') | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onBlur: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -345,7 +366,10 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
| field.validate('blur') | ||
| await vi.runAllTimersAsync() | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onBlur: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -376,3 +400,3 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
@@ -384,3 +408,6 @@ field.validate('blur') | ||
| expect(sleepMock).toHaveBeenCalledTimes(1) | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onBlur: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -411,3 +438,3 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
@@ -419,3 +446,6 @@ field.validate('blur') | ||
| expect(sleepMock).toHaveBeenCalledTimes(1) | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onBlur: 'Please enter a different value', | ||
| }) | ||
| }) | ||
@@ -444,9 +474,46 @@ | ||
| expect(field.getMeta().error).toBeUndefined() | ||
| expect(field.getMeta().errors.length).toBe(0) | ||
| field.setValue('other', { touch: true }) | ||
| field.validate('submit') | ||
| await vi.runAllTimersAsync() | ||
| expect(field.getMeta().error).toBe('Please enter a different value') | ||
| expect(field.getMeta().errors).toContain('Please enter a different value') | ||
| expect(field.getMeta().errorMap).toMatchObject({ | ||
| onSubmit: 'Please enter a different value', | ||
| }) | ||
| }) | ||
| it('should contain multiple errors when running validation onBlur and onChange', () => { | ||
| const form = new FormApi({ | ||
| defaultValues: { | ||
| name: 'other', | ||
| }, | ||
| }) | ||
| const field = new FieldApi({ | ||
| form, | ||
| name: 'name', | ||
| onBlur: (value) => { | ||
| if (value === 'other') return 'Please enter a different value' | ||
| return | ||
| }, | ||
| onChange: (value) => { | ||
| if (value === 'other') return 'Please enter a different value' | ||
| return | ||
| }, | ||
| }) | ||
| field.mount() | ||
| field.setValue('other', { touch: true }) | ||
| field.validate('blur') | ||
| expect(field.getMeta().errors).toStrictEqual([ | ||
| 'Please enter a different value', | ||
| 'Please enter a different value', | ||
| ]) | ||
| expect(field.getMeta().errorMap).toEqual({ | ||
| onBlur: 'Please enter a different value', | ||
| onChange: 'Please enter a different value', | ||
| }) | ||
| }) | ||
| it('should handle default value on field using state.value', async () => { | ||
@@ -453,0 +520,0 @@ interface Form { |
+4
-0
@@ -104,2 +104,6 @@ export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput | ||
| export function isNonEmptyArray(obj: any) { | ||
| return !(Array.isArray(obj) && obj.length === 0) | ||
| } | ||
| export type RequiredByKey<T, K extends keyof T> = Omit<T, K> & | ||
@@ -106,0 +110,0 @@ Required<Pick<T, K>> |
| var __accessCheck = (obj, member, msg) => { | ||
| if (!member.has(obj)) | ||
| throw TypeError("Cannot " + msg); | ||
| }; | ||
| var __privateGet = (obj, member, getter) => { | ||
| __accessCheck(obj, member, "read from private field"); | ||
| return getter ? getter.call(obj) : member.get(obj); | ||
| }; | ||
| var __privateAdd = (obj, member, value) => { | ||
| if (member.has(obj)) | ||
| throw TypeError("Cannot add the same private member more than once"); | ||
| member instanceof WeakSet ? member.add(obj) : member.set(obj, value); | ||
| }; | ||
| export { | ||
| __privateGet, | ||
| __privateAdd | ||
| }; | ||
| //# sourceMappingURL=chunk-4QZDOMDG.js.map |
| {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| var __accessCheck = (obj, member, msg) => { | ||
| if (!member.has(obj)) | ||
| throw TypeError("Cannot " + msg); | ||
| }; | ||
| var __privateGet = (obj, member, getter) => { | ||
| __accessCheck(obj, member, "read from private field"); | ||
| return getter ? getter.call(obj) : member.get(obj); | ||
| }; | ||
| var __privateAdd = (obj, member, value) => { | ||
| if (member.has(obj)) | ||
| throw TypeError("Cannot add the same private member more than once"); | ||
| member instanceof WeakSet ? member.add(obj) : member.set(obj, value); | ||
| }; | ||
| // src/FieldApi.ts | ||
| var FieldApi_exports = {}; | ||
| __export(FieldApi_exports, { | ||
| FieldApi: () => FieldApi | ||
| }); | ||
| module.exports = __toCommonJS(FieldApi_exports); | ||
| var import_store = require("@tanstack/store"); | ||
| var uid = 0; | ||
| var _leaseValidateAsync; | ||
| var _FieldApi = class _FieldApi { | ||
| constructor(opts) { | ||
| this.options = {}; | ||
| this.mount = () => { | ||
| var _a, _b; | ||
| const info = this.getInfo(); | ||
| info.instances[this.uid] = this; | ||
| const unsubscribe = this.form.store.subscribe(() => { | ||
| this.store.batch(() => { | ||
| const nextValue = this.getValue(); | ||
| const nextMeta = this.getMeta(); | ||
| if (nextValue !== this.state.value) { | ||
| this.store.setState((prev) => ({ ...prev, value: nextValue })); | ||
| } | ||
| if (nextMeta !== this.state.meta) { | ||
| this.store.setState((prev) => ({ ...prev, meta: nextMeta })); | ||
| } | ||
| }); | ||
| }); | ||
| this.update(this.options); | ||
| (_b = (_a = this.options).onMount) == null ? void 0 : _b.call(_a, this); | ||
| return () => { | ||
| unsubscribe(); | ||
| delete info.instances[this.uid]; | ||
| if (!Object.keys(info.instances).length) { | ||
| delete this.form.fieldInfo[this.name]; | ||
| } | ||
| }; | ||
| }; | ||
| this.update = (opts) => { | ||
| var _a; | ||
| if (this.state.value === void 0) { | ||
| const formDefault = (_a = opts.form.options.defaultValues) == null ? void 0 : _a[opts.name]; | ||
| if (opts.defaultValue !== void 0) { | ||
| this.setValue(opts.defaultValue); | ||
| } else if (formDefault !== void 0) { | ||
| this.setValue(formDefault); | ||
| } | ||
| } | ||
| if (this._getMeta() === void 0) { | ||
| this.setMeta(this.state.meta); | ||
| } | ||
| this.options = opts; | ||
| }; | ||
| this.getValue = () => { | ||
| return this.form.getFieldValue(this.name); | ||
| }; | ||
| this.setValue = (updater, options) => { | ||
| this.form.setFieldValue(this.name, updater, options); | ||
| this.validate("change", this.state.value); | ||
| }; | ||
| this._getMeta = () => this.form.getFieldMeta(this.name); | ||
| this.getMeta = () => this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...this.options.defaultMeta | ||
| }; | ||
| this.setMeta = (updater) => this.form.setFieldMeta(this.name, updater); | ||
| this.getInfo = () => this.form.getFieldInfo(this.name); | ||
| this.pushValue = (value) => this.form.pushFieldValue(this.name, value); | ||
| this.insertValue = (index, value) => this.form.insertFieldValue(this.name, index, value); | ||
| this.removeValue = (index) => this.form.removeFieldValue(this.name, index); | ||
| this.swapValues = (aIndex, bIndex) => this.form.swapFieldValues(this.name, aIndex, bIndex); | ||
| this.getSubField = (name) => new _FieldApi({ | ||
| name: `${this.name}.${name}`, | ||
| form: this.form | ||
| }); | ||
| this.validateSync = (value = this.state.value, cause) => { | ||
| const { onChange, onBlur } = this.options; | ||
| const validate = cause === "submit" ? void 0 : cause === "change" ? onChange : onBlur; | ||
| if (!validate) | ||
| return; | ||
| const validationCount = (this.getInfo().validationCount || 0) + 1; | ||
| this.getInfo().validationCount = validationCount; | ||
| const error = normalizeError(validate(value, this)); | ||
| if (this.state.meta.error !== error) { | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| error | ||
| })); | ||
| } | ||
| if (this.state.meta.error) { | ||
| this.cancelValidateAsync(); | ||
| } | ||
| }; | ||
| __privateAdd(this, _leaseValidateAsync, () => { | ||
| const count = (this.getInfo().validationAsyncCount || 0) + 1; | ||
| this.getInfo().validationAsyncCount = count; | ||
| return count; | ||
| }); | ||
| this.cancelValidateAsync = () => { | ||
| __privateGet(this, _leaseValidateAsync).call(this); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false | ||
| })); | ||
| }; | ||
| this.validateAsync = async (value = this.state.value, cause) => { | ||
| var _a, _b, _c, _d; | ||
| const { | ||
| onChangeAsync, | ||
| onBlurAsync, | ||
| onSubmitAsync, | ||
| asyncDebounceMs, | ||
| onBlurAsyncDebounceMs, | ||
| onChangeAsyncDebounceMs | ||
| } = this.options; | ||
| const validate = cause === "change" ? onChangeAsync : cause === "submit" ? onSubmitAsync : onBlurAsync; | ||
| if (!validate) | ||
| return; | ||
| const debounceMs = cause === "submit" ? 0 : (cause === "change" ? onChangeAsyncDebounceMs : onBlurAsyncDebounceMs) ?? asyncDebounceMs ?? 0; | ||
| if (this.state.meta.isValidating !== true) | ||
| this.setMeta((prev) => ({ ...prev, isValidating: true })); | ||
| const validationAsyncCount = __privateGet(this, _leaseValidateAsync).call(this); | ||
| const checkLatest = () => validationAsyncCount === this.getInfo().validationAsyncCount; | ||
| if (!this.getInfo().validationPromise) { | ||
| this.getInfo().validationPromise = new Promise((resolve, reject) => { | ||
| this.getInfo().validationResolve = resolve; | ||
| this.getInfo().validationReject = reject; | ||
| }); | ||
| } | ||
| if (debounceMs > 0) { | ||
| await new Promise((r) => setTimeout(r, debounceMs)); | ||
| } | ||
| if (checkLatest()) { | ||
| try { | ||
| const rawError = await validate(value, this); | ||
| if (checkLatest()) { | ||
| const error = normalizeError(rawError); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false, | ||
| error | ||
| })); | ||
| (_b = (_a = this.getInfo()).validationResolve) == null ? void 0 : _b.call(_a, error); | ||
| } | ||
| } catch (error) { | ||
| if (checkLatest()) { | ||
| (_d = (_c = this.getInfo()).validationReject) == null ? void 0 : _d.call(_c, error); | ||
| throw error; | ||
| } | ||
| } finally { | ||
| if (checkLatest()) { | ||
| this.setMeta((prev) => ({ ...prev, isValidating: false })); | ||
| delete this.getInfo().validationPromise; | ||
| } | ||
| } | ||
| } | ||
| return this.getInfo().validationPromise; | ||
| }; | ||
| this.validate = (cause, value) => { | ||
| if (!this.state.meta.isTouched) | ||
| return; | ||
| this.validateSync(value, cause); | ||
| if (this.state.meta.error) { | ||
| if (!this.options.asyncAlways) { | ||
| return this.state.meta.error; | ||
| } | ||
| } | ||
| return this.validateAsync(value, cause); | ||
| }; | ||
| this.handleChange = (updater) => { | ||
| this.setValue(updater, { touch: true }); | ||
| }; | ||
| this.handleBlur = () => { | ||
| const prevTouched = this.state.meta.isTouched; | ||
| if (!prevTouched) { | ||
| this.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| this.validate("change"); | ||
| } | ||
| this.validate("blur"); | ||
| }; | ||
| this.form = opts.form; | ||
| this.uid = uid++; | ||
| this.name = opts.name; | ||
| this.store = new import_store.Store( | ||
| { | ||
| value: this.getValue(), | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| meta: this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...opts.defaultMeta | ||
| } | ||
| }, | ||
| { | ||
| onUpdate: () => { | ||
| const state = this.store.state; | ||
| state.meta.touchedError = state.meta.isTouched ? state.meta.error : void 0; | ||
| this.prevState = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.prevState = this.state; | ||
| this.options = opts; | ||
| } | ||
| }; | ||
| _leaseValidateAsync = new WeakMap(); | ||
| var FieldApi = _FieldApi; | ||
| function normalizeError(rawError) { | ||
| if (rawError) { | ||
| if (typeof rawError !== "string") { | ||
| return "Invalid Form Values"; | ||
| } | ||
| return rawError; | ||
| } | ||
| return void 0; | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| FieldApi | ||
| }); | ||
| //# sourceMappingURL=FieldApi.cjs.map |
| {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import type { DeepKeys, DeepValue, Updater } from './utils'\nimport type { FormApi, ValidationError } from './FormApi'\nimport { Store } from '@tanstack/store'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit'\n\ntype ValidateFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError\n\ntype ValidateAsyncFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError | Promise<ValidationError>\n\nexport interface FieldOptions<\n _TData,\n TFormData,\n /**\n * This allows us to restrict the name to only be a valid field name while\n * also assigning it to a generic\n */\n TName = unknown extends TFormData ? string : DeepKeys<TFormData>,\n /**\n * If TData is unknown, we can use the TName generic to determine the type\n */\n TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n onMount?: (formApi: FieldApi<TData, TFormData>) => void\n onChange?: ValidateFn<TData, TFormData>\n onChangeAsync?: ValidateAsyncFn<TData, TFormData>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateFn<TData, TFormData>\n onBlurAsync?: ValidateAsyncFn<TData, TFormData>\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: ValidateAsyncFn<TData, TFormData>\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport type FieldApiOptions<TData, TFormData> = FieldOptions<\n TData,\n TFormData\n> & {\n form: FormApi<TFormData>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedError?: ValidationError\n error?: ValidationError\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\n/**\n * TData may not be known at the time of FieldApi construction, so we need to\n * use a conditional type to determine if TData is known or not.\n *\n * If TData is not known, we use the TFormData type to determine the type of\n * the field value based on the field name.\n */\ntype GetTData<Name, TData, TFormData> = unknown extends TData\n ? DeepValue<TFormData, Name>\n : TData\n\nexport class FieldApi<TData, TFormData> {\n uid: number\n form: FormApi<TFormData>\n name!: DeepKeys<TFormData>\n /**\n * This is a hack that allows us to use `GetTData` without calling it everywhere\n *\n * Unfortunately this hack appears to be needed alongside the `TName` hack\n * further up in this file. This properly types all of the internal methods,\n * while the `TName` hack types the options properly\n */\n _tdata!: GetTData<typeof this.name, TData, TFormData>\n store!: Store<FieldState<typeof this._tdata>>\n state!: FieldState<typeof this._tdata>\n prevState!: FieldState<typeof this._tdata>\n options: FieldOptions<typeof this._tdata, TFormData> = {} as any\n\n constructor(opts: FieldApiOptions<TData, TFormData>) {\n this.form = opts.form\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as any\n\n this.store = new Store<FieldState<typeof this._tdata>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.touchedError = state.meta.isTouched\n ? state.meta.error\n : undefined\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (opts: FieldApiOptions<typeof this._tdata, TFormData>) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TFormData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): typeof this._tdata => {\n return this.form.getFieldValue(this.name)\n }\n\n setValue = (\n updater: Updater<typeof this._tdata>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <TName extends DeepKeys<typeof this._tdata>>(name: TName) =>\n new FieldApi<DeepValue<typeof this._tdata, TName>, TFormData>({\n name: `${this.name}.${name}` as never,\n form: this.form,\n })\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n const error = normalizeError(validate(value as never, this as never))\n\n if (this.state.meta.error !== error) {\n this.setMeta((prev) => ({\n ...prev,\n error,\n }))\n }\n\n // If a sync error is encountered, cancel any async validation\n if (this.state.meta.error) {\n this.cancelValidateAsync()\n }\n }\n\n #leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.#leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n\n if (!validate) return\n\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true)\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.#leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n try {\n const rawError = await validate(value as never, this as never)\n\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n error,\n }))\n this.getInfo().validationResolve?.(error)\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.(error)\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise\n }\n\n validate = (\n cause: ValidationCause,\n value?: typeof this._tdata,\n ): ValidationError | Promise<ValidationError> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is an error, return it, do not attempt async validation\n if (this.state.meta.error) {\n if (!this.options.asyncAlways) {\n return this.state.meta.error\n }\n }\n\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<typeof this._tdata>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n"],"mappings":"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","names":[]} |
| export { FieldApi, FieldApiOptions, FieldMeta, FieldOptions, FieldState, ValidationCause } from './index.cjs'; | ||
| import './utils.cjs'; | ||
| import '@tanstack/store'; |
| export { FieldApi, FieldApiOptions, FieldMeta, FieldOptions, FieldState, ValidationCause } from './index.js'; | ||
| import './utils.js'; | ||
| import '@tanstack/store'; |
| import { | ||
| __privateAdd, | ||
| __privateGet | ||
| } from "./chunk-4QZDOMDG.js"; | ||
| // src/FieldApi.ts | ||
| import { Store } from "@tanstack/store"; | ||
| var uid = 0; | ||
| var _leaseValidateAsync; | ||
| var _FieldApi = class _FieldApi { | ||
| constructor(opts) { | ||
| this.options = {}; | ||
| this.mount = () => { | ||
| var _a, _b; | ||
| const info = this.getInfo(); | ||
| info.instances[this.uid] = this; | ||
| const unsubscribe = this.form.store.subscribe(() => { | ||
| this.store.batch(() => { | ||
| const nextValue = this.getValue(); | ||
| const nextMeta = this.getMeta(); | ||
| if (nextValue !== this.state.value) { | ||
| this.store.setState((prev) => ({ ...prev, value: nextValue })); | ||
| } | ||
| if (nextMeta !== this.state.meta) { | ||
| this.store.setState((prev) => ({ ...prev, meta: nextMeta })); | ||
| } | ||
| }); | ||
| }); | ||
| this.update(this.options); | ||
| (_b = (_a = this.options).onMount) == null ? void 0 : _b.call(_a, this); | ||
| return () => { | ||
| unsubscribe(); | ||
| delete info.instances[this.uid]; | ||
| if (!Object.keys(info.instances).length) { | ||
| delete this.form.fieldInfo[this.name]; | ||
| } | ||
| }; | ||
| }; | ||
| this.update = (opts) => { | ||
| var _a; | ||
| if (this.state.value === void 0) { | ||
| const formDefault = (_a = opts.form.options.defaultValues) == null ? void 0 : _a[opts.name]; | ||
| if (opts.defaultValue !== void 0) { | ||
| this.setValue(opts.defaultValue); | ||
| } else if (formDefault !== void 0) { | ||
| this.setValue(formDefault); | ||
| } | ||
| } | ||
| if (this._getMeta() === void 0) { | ||
| this.setMeta(this.state.meta); | ||
| } | ||
| this.options = opts; | ||
| }; | ||
| this.getValue = () => { | ||
| return this.form.getFieldValue(this.name); | ||
| }; | ||
| this.setValue = (updater, options) => { | ||
| this.form.setFieldValue(this.name, updater, options); | ||
| this.validate("change", this.state.value); | ||
| }; | ||
| this._getMeta = () => this.form.getFieldMeta(this.name); | ||
| this.getMeta = () => this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...this.options.defaultMeta | ||
| }; | ||
| this.setMeta = (updater) => this.form.setFieldMeta(this.name, updater); | ||
| this.getInfo = () => this.form.getFieldInfo(this.name); | ||
| this.pushValue = (value) => this.form.pushFieldValue(this.name, value); | ||
| this.insertValue = (index, value) => this.form.insertFieldValue(this.name, index, value); | ||
| this.removeValue = (index) => this.form.removeFieldValue(this.name, index); | ||
| this.swapValues = (aIndex, bIndex) => this.form.swapFieldValues(this.name, aIndex, bIndex); | ||
| this.getSubField = (name) => new _FieldApi({ | ||
| name: `${this.name}.${name}`, | ||
| form: this.form | ||
| }); | ||
| this.validateSync = (value = this.state.value, cause) => { | ||
| const { onChange, onBlur } = this.options; | ||
| const validate = cause === "submit" ? void 0 : cause === "change" ? onChange : onBlur; | ||
| if (!validate) | ||
| return; | ||
| const validationCount = (this.getInfo().validationCount || 0) + 1; | ||
| this.getInfo().validationCount = validationCount; | ||
| const error = normalizeError(validate(value, this)); | ||
| if (this.state.meta.error !== error) { | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| error | ||
| })); | ||
| } | ||
| if (this.state.meta.error) { | ||
| this.cancelValidateAsync(); | ||
| } | ||
| }; | ||
| __privateAdd(this, _leaseValidateAsync, () => { | ||
| const count = (this.getInfo().validationAsyncCount || 0) + 1; | ||
| this.getInfo().validationAsyncCount = count; | ||
| return count; | ||
| }); | ||
| this.cancelValidateAsync = () => { | ||
| __privateGet(this, _leaseValidateAsync).call(this); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false | ||
| })); | ||
| }; | ||
| this.validateAsync = async (value = this.state.value, cause) => { | ||
| var _a, _b, _c, _d; | ||
| const { | ||
| onChangeAsync, | ||
| onBlurAsync, | ||
| onSubmitAsync, | ||
| asyncDebounceMs, | ||
| onBlurAsyncDebounceMs, | ||
| onChangeAsyncDebounceMs | ||
| } = this.options; | ||
| const validate = cause === "change" ? onChangeAsync : cause === "submit" ? onSubmitAsync : onBlurAsync; | ||
| if (!validate) | ||
| return; | ||
| const debounceMs = cause === "submit" ? 0 : (cause === "change" ? onChangeAsyncDebounceMs : onBlurAsyncDebounceMs) ?? asyncDebounceMs ?? 0; | ||
| if (this.state.meta.isValidating !== true) | ||
| this.setMeta((prev) => ({ ...prev, isValidating: true })); | ||
| const validationAsyncCount = __privateGet(this, _leaseValidateAsync).call(this); | ||
| const checkLatest = () => validationAsyncCount === this.getInfo().validationAsyncCount; | ||
| if (!this.getInfo().validationPromise) { | ||
| this.getInfo().validationPromise = new Promise((resolve, reject) => { | ||
| this.getInfo().validationResolve = resolve; | ||
| this.getInfo().validationReject = reject; | ||
| }); | ||
| } | ||
| if (debounceMs > 0) { | ||
| await new Promise((r) => setTimeout(r, debounceMs)); | ||
| } | ||
| if (checkLatest()) { | ||
| try { | ||
| const rawError = await validate(value, this); | ||
| if (checkLatest()) { | ||
| const error = normalizeError(rawError); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false, | ||
| error | ||
| })); | ||
| (_b = (_a = this.getInfo()).validationResolve) == null ? void 0 : _b.call(_a, error); | ||
| } | ||
| } catch (error) { | ||
| if (checkLatest()) { | ||
| (_d = (_c = this.getInfo()).validationReject) == null ? void 0 : _d.call(_c, error); | ||
| throw error; | ||
| } | ||
| } finally { | ||
| if (checkLatest()) { | ||
| this.setMeta((prev) => ({ ...prev, isValidating: false })); | ||
| delete this.getInfo().validationPromise; | ||
| } | ||
| } | ||
| } | ||
| return this.getInfo().validationPromise; | ||
| }; | ||
| this.validate = (cause, value) => { | ||
| if (!this.state.meta.isTouched) | ||
| return; | ||
| this.validateSync(value, cause); | ||
| if (this.state.meta.error) { | ||
| if (!this.options.asyncAlways) { | ||
| return this.state.meta.error; | ||
| } | ||
| } | ||
| return this.validateAsync(value, cause); | ||
| }; | ||
| this.handleChange = (updater) => { | ||
| this.setValue(updater, { touch: true }); | ||
| }; | ||
| this.handleBlur = () => { | ||
| const prevTouched = this.state.meta.isTouched; | ||
| if (!prevTouched) { | ||
| this.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| this.validate("change"); | ||
| } | ||
| this.validate("blur"); | ||
| }; | ||
| this.form = opts.form; | ||
| this.uid = uid++; | ||
| this.name = opts.name; | ||
| this.store = new Store( | ||
| { | ||
| value: this.getValue(), | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| meta: this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...opts.defaultMeta | ||
| } | ||
| }, | ||
| { | ||
| onUpdate: () => { | ||
| const state = this.store.state; | ||
| state.meta.touchedError = state.meta.isTouched ? state.meta.error : void 0; | ||
| this.prevState = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.prevState = this.state; | ||
| this.options = opts; | ||
| } | ||
| }; | ||
| _leaseValidateAsync = new WeakMap(); | ||
| var FieldApi = _FieldApi; | ||
| function normalizeError(rawError) { | ||
| if (rawError) { | ||
| if (typeof rawError !== "string") { | ||
| return "Invalid Form Values"; | ||
| } | ||
| return rawError; | ||
| } | ||
| return void 0; | ||
| } | ||
| export { | ||
| FieldApi | ||
| }; | ||
| //# sourceMappingURL=FieldApi.js.map |
| {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import type { DeepKeys, DeepValue, Updater } from './utils'\nimport type { FormApi, ValidationError } from './FormApi'\nimport { Store } from '@tanstack/store'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit'\n\ntype ValidateFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError\n\ntype ValidateAsyncFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError | Promise<ValidationError>\n\nexport interface FieldOptions<\n _TData,\n TFormData,\n /**\n * This allows us to restrict the name to only be a valid field name while\n * also assigning it to a generic\n */\n TName = unknown extends TFormData ? string : DeepKeys<TFormData>,\n /**\n * If TData is unknown, we can use the TName generic to determine the type\n */\n TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n onMount?: (formApi: FieldApi<TData, TFormData>) => void\n onChange?: ValidateFn<TData, TFormData>\n onChangeAsync?: ValidateAsyncFn<TData, TFormData>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateFn<TData, TFormData>\n onBlurAsync?: ValidateAsyncFn<TData, TFormData>\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: ValidateAsyncFn<TData, TFormData>\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport type FieldApiOptions<TData, TFormData> = FieldOptions<\n TData,\n TFormData\n> & {\n form: FormApi<TFormData>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedError?: ValidationError\n error?: ValidationError\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\n/**\n * TData may not be known at the time of FieldApi construction, so we need to\n * use a conditional type to determine if TData is known or not.\n *\n * If TData is not known, we use the TFormData type to determine the type of\n * the field value based on the field name.\n */\ntype GetTData<Name, TData, TFormData> = unknown extends TData\n ? DeepValue<TFormData, Name>\n : TData\n\nexport class FieldApi<TData, TFormData> {\n uid: number\n form: FormApi<TFormData>\n name!: DeepKeys<TFormData>\n /**\n * This is a hack that allows us to use `GetTData` without calling it everywhere\n *\n * Unfortunately this hack appears to be needed alongside the `TName` hack\n * further up in this file. This properly types all of the internal methods,\n * while the `TName` hack types the options properly\n */\n _tdata!: GetTData<typeof this.name, TData, TFormData>\n store!: Store<FieldState<typeof this._tdata>>\n state!: FieldState<typeof this._tdata>\n prevState!: FieldState<typeof this._tdata>\n options: FieldOptions<typeof this._tdata, TFormData> = {} as any\n\n constructor(opts: FieldApiOptions<TData, TFormData>) {\n this.form = opts.form\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as any\n\n this.store = new Store<FieldState<typeof this._tdata>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.touchedError = state.meta.isTouched\n ? state.meta.error\n : undefined\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (opts: FieldApiOptions<typeof this._tdata, TFormData>) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TFormData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): typeof this._tdata => {\n return this.form.getFieldValue(this.name)\n }\n\n setValue = (\n updater: Updater<typeof this._tdata>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <TName extends DeepKeys<typeof this._tdata>>(name: TName) =>\n new FieldApi<DeepValue<typeof this._tdata, TName>, TFormData>({\n name: `${this.name}.${name}` as never,\n form: this.form,\n })\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n const error = normalizeError(validate(value as never, this as never))\n\n if (this.state.meta.error !== error) {\n this.setMeta((prev) => ({\n ...prev,\n error,\n }))\n }\n\n // If a sync error is encountered, cancel any async validation\n if (this.state.meta.error) {\n this.cancelValidateAsync()\n }\n }\n\n #leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.#leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n\n if (!validate) return\n\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true)\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.#leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n try {\n const rawError = await validate(value as never, this as never)\n\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n error,\n }))\n this.getInfo().validationResolve?.(error)\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.(error)\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise\n }\n\n validate = (\n cause: ValidationCause,\n value?: typeof this._tdata,\n ): ValidationError | Promise<ValidationError> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is an error, return it, do not attempt async validation\n if (this.state.meta.error) {\n if (!this.options.asyncAlways) {\n return this.state.meta.error\n }\n }\n\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<typeof this._tdata>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n"],"mappings":";;;;;;AAEA,SAAS,aAAa;AAyDtB,IAAI,MAAM;AA3DV;AA6EO,IAAM,YAAN,MAAM,UAA2B;AAAA,EAiBtC,YAAY,MAAyC;AAFrD,mBAAuD,CAAC;AA0CxD,iBAAQ,MAAM;AAtIhB;AAuII,YAAM,OAAO,KAAK,QAAQ;AAC1B,WAAK,UAAU,KAAK,GAAG,IAAI;AAE3B,YAAM,cAAc,KAAK,KAAK,MAAM,UAAU,MAAM;AAClD,aAAK,MAAM,MAAM,MAAM;AACrB,gBAAM,YAAY,KAAK,SAAS;AAChC,gBAAM,WAAW,KAAK,QAAQ;AAE9B,cAAI,cAAc,KAAK,MAAM,OAAO;AAClC,iBAAK,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,UAAU,EAAE;AAAA,UAC/D;AAEA,cAAI,aAAa,KAAK,MAAM,MAAM;AAChC,iBAAK,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,MAAM,SAAS,EAAE;AAAA,UAC7D;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,WAAK,OAAO,KAAK,OAAgB;AACjC,uBAAK,SAAQ,YAAb,4BAAuB;AAEvB,aAAO,MAAM;AACX,oBAAY;AACZ,eAAO,KAAK,UAAU,KAAK,GAAG;AAC9B,YAAI,CAAC,OAAO,KAAK,KAAK,SAAS,EAAE,QAAQ;AACvC,iBAAO,KAAK,KAAK,UAAU,KAAK,IAAI;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAEA,kBAAS,CAAC,SAAyD;AArKrE;AAwKI,UAAI,KAAK,MAAM,UAAU,QAAW;AAClC,cAAM,eACJ,UAAK,KAAK,QAAQ,kBAAlB,mBAAkC,KAAK;AAEzC,YAAI,KAAK,iBAAiB,QAAW;AACnC,eAAK,SAAS,KAAK,YAAqB;AAAA,QAC1C,WAAW,gBAAgB,QAAW;AACpC,eAAK,SAAS,WAAoB;AAAA,QACpC;AAAA,MACF;AAGA,UAAI,KAAK,SAAS,MAAM,QAAW;AACjC,aAAK,QAAQ,KAAK,MAAM,IAAI;AAAA,MAC9B;AAEA,WAAK,UAAU;AAAA,IACjB;AAEA,oBAAW,MAA0B;AACnC,aAAO,KAAK,KAAK,cAAc,KAAK,IAAI;AAAA,IAC1C;AAEA,oBAAW,CACT,SACA,YACG;AACH,WAAK,KAAK,cAAc,KAAK,MAAM,SAAkB,OAAO;AAC5D,WAAK,SAAS,UAAU,KAAK,MAAM,KAAK;AAAA,IAC1C;AAEA,oBAAW,MAAM,KAAK,KAAK,aAAa,KAAK,IAAI;AACjD,mBAAU,MACR,KAAK,SAAS,KACb;AAAA,MACC,cAAc;AAAA,MACd,WAAW;AAAA,MACX,GAAG,KAAK,QAAQ;AAAA,IAClB;AAEF,mBAAU,CAAC,YACT,KAAK,KAAK,aAAa,KAAK,MAAM,OAAO;AAE3C,mBAAU,MAAM,KAAK,KAAK,aAAa,KAAK,IAAI;AAEhD,qBAAY,CACV,UAGG,KAAK,KAAK,eAAe,KAAK,MAAM,KAAY;AAErD,uBAAc,CACZ,OACA,UAGG,KAAK,KAAK,iBAAiB,KAAK,MAAM,OAAO,KAAY;AAE9D,uBAAc,CAAC,UAAkB,KAAK,KAAK,iBAAiB,KAAK,MAAM,KAAK;AAE5E,sBAAa,CAAC,QAAgB,WAC5B,KAAK,KAAK,gBAAgB,KAAK,MAAM,QAAQ,MAAM;AAErD,uBAAc,CAA6C,SACzD,IAAI,UAA0D;AAAA,MAC5D,MAAM,GAAG,KAAK,IAAI,IAAI,IAAI;AAAA,MAC1B,MAAM,KAAK;AAAA,IACb,CAAC;AAEH,wBAAe,CAAC,QAAQ,KAAK,MAAM,OAAO,UAA2B;AACnE,YAAM,EAAE,UAAU,OAAO,IAAI,KAAK;AAClC,YAAM,WACJ,UAAU,WAAW,SAAY,UAAU,WAAW,WAAW;AAEnE,UAAI,CAAC;AAAU;AAIf,YAAM,mBAAmB,KAAK,QAAQ,EAAE,mBAAmB,KAAK;AAChE,WAAK,QAAQ,EAAE,kBAAkB;AACjC,YAAM,QAAQ,eAAe,SAAS,OAAgB,IAAa,CAAC;AAEpE,UAAI,KAAK,MAAM,KAAK,UAAU,OAAO;AACnC,aAAK,QAAQ,CAAC,UAAU;AAAA,UACtB,GAAG;AAAA,UACH;AAAA,QACF,EAAE;AAAA,MACJ;AAGA,UAAI,KAAK,MAAM,KAAK,OAAO;AACzB,aAAK,oBAAoB;AAAA,MAC3B;AAAA,IACF;AAEA,4CAAsB,MAAM;AAC1B,YAAM,SAAS,KAAK,QAAQ,EAAE,wBAAwB,KAAK;AAC3D,WAAK,QAAQ,EAAE,uBAAuB;AACtC,aAAO;AAAA,IACT;AAEA,+BAAsB,MAAM;AAE1B,yBAAK,qBAAL;AAEA,WAAK,QAAQ,CAAC,UAAU;AAAA,QACtB,GAAG;AAAA,QACH,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ;AAEA,yBAAgB,OAAO,QAAQ,KAAK,MAAM,OAAO,UAA2B;AAvR9E;AAwRI,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,IAAI,KAAK;AAET,YAAM,WACJ,UAAU,WACN,gBACA,UAAU,WACV,gBACA;AAEN,UAAI,CAAC;AAAU;AAEf,YAAM,aACJ,UAAU,WACN,KACC,UAAU,WACP,0BACA,0BACJ,mBACA;AAEN,UAAI,KAAK,MAAM,KAAK,iBAAiB;AACnC,aAAK,QAAQ,CAAC,UAAU,EAAE,GAAG,MAAM,cAAc,KAAK,EAAE;AAI1D,YAAM,uBAAuB,mBAAK,qBAAL;AAE7B,YAAM,cAAc,MAClB,yBAAyB,KAAK,QAAQ,EAAE;AAE1C,UAAI,CAAC,KAAK,QAAQ,EAAE,mBAAmB;AACrC,aAAK,QAAQ,EAAE,oBAAoB,IAAI,QAAQ,CAAC,SAAS,WAAW;AAClE,eAAK,QAAQ,EAAE,oBAAoB;AACnC,eAAK,QAAQ,EAAE,mBAAmB;AAAA,QACpC,CAAC;AAAA,MACH;AAEA,UAAI,aAAa,GAAG;AAClB,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;AAAA,MACpD;AAGA,UAAI,YAAY,GAAG;AACjB,YAAI;AACF,gBAAM,WAAW,MAAM,SAAS,OAAgB,IAAa;AAE7D,cAAI,YAAY,GAAG;AACjB,kBAAM,QAAQ,eAAe,QAAQ;AACrC,iBAAK,QAAQ,CAAC,UAAU;AAAA,cACtB,GAAG;AAAA,cACH,cAAc;AAAA,cACd;AAAA,YACF,EAAE;AACF,6BAAK,QAAQ,GAAE,sBAAf,4BAAmC;AAAA,UACrC;AAAA,QACF,SAAS,OAAO;AACd,cAAI,YAAY,GAAG;AACjB,6BAAK,QAAQ,GAAE,qBAAf,4BAAkC;AAClC,kBAAM;AAAA,UACR;AAAA,QACF,UAAE;AACA,cAAI,YAAY,GAAG;AACjB,iBAAK,QAAQ,CAAC,UAAU,EAAE,GAAG,MAAM,cAAc,MAAM,EAAE;AACzD,mBAAO,KAAK,QAAQ,EAAE;AAAA,UACxB;AAAA,QACF;AAAA,MACF;AAGA,aAAO,KAAK,QAAQ,EAAE;AAAA,IACxB;AAEA,oBAAW,CACT,OACA,UAC+C;AAE/C,UAAI,CAAC,KAAK,MAAM,KAAK;AAAW;AAGhC,WAAK,aAAa,OAAO,KAAK;AAG9B,UAAI,KAAK,MAAM,KAAK,OAAO;AACzB,YAAI,CAAC,KAAK,QAAQ,aAAa;AAC7B,iBAAO,KAAK,MAAM,KAAK;AAAA,QACzB;AAAA,MACF;AAGA,aAAO,KAAK,cAAc,OAAO,KAAK;AAAA,IACxC;AAEA,wBAAe,CAAC,YAAyC;AACvD,WAAK,SAAS,SAAS,EAAE,OAAO,KAAK,CAAC;AAAA,IACxC;AAEA,sBAAa,MAAM;AACjB,YAAM,cAAc,KAAK,MAAM,KAAK;AACpC,UAAI,CAAC,aAAa;AAChB,aAAK,QAAQ,CAAC,UAAU,EAAE,GAAG,MAAM,WAAW,KAAK,EAAE;AACrD,aAAK,SAAS,QAAQ;AAAA,MACxB;AACA,WAAK,SAAS,MAAM;AAAA,IACtB;AAxSE,SAAK,OAAO,KAAK;AACjB,SAAK,MAAM;AAOX,SAAK,OAAO,KAAK;AAEjB,SAAK,QAAQ,IAAI;AAAA,MACf;AAAA,QACE,OAAO,KAAK,SAAS;AAAA;AAAA,QAErB,MAAM,KAAK,SAAS,KAAK;AAAA,UACvB,cAAc;AAAA,UACd,WAAW;AAAA,UACX,GAAG,KAAK;AAAA,QACV;AAAA,MACF;AAAA,MACA;AAAA,QACE,UAAU,MAAM;AACd,gBAAM,QAAQ,KAAK,MAAM;AAEzB,gBAAM,KAAK,eAAe,MAAM,KAAK,YACjC,MAAM,KAAK,QACX;AAEJ,eAAK,YAAY;AACjB,eAAK,QAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,SAAK,QAAQ,KAAK,MAAM;AACxB,SAAK,YAAY,KAAK;AACtB,SAAK,UAAU;AAAA,EACjB;AAoQF;AAjIE;AA1LK,IAAM,WAAN;AA6TP,SAAS,eAAe,UAA4B;AAClD,MAAI,UAAU;AACZ,QAAI,OAAO,aAAa,UAAU;AAChC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/FormApi.ts | ||
| var FormApi_exports = {}; | ||
| __export(FormApi_exports, { | ||
| FormApi: () => FormApi | ||
| }); | ||
| module.exports = __toCommonJS(FormApi_exports); | ||
| var import_store = require("@tanstack/store"); | ||
| var import_utils = require("./utils.cjs"); | ||
| function getDefaultFormState(defaultState) { | ||
| return { | ||
| values: defaultState.values ?? {}, | ||
| fieldMeta: defaultState.fieldMeta ?? {}, | ||
| canSubmit: defaultState.canSubmit ?? true, | ||
| isFieldsValid: defaultState.isFieldsValid ?? false, | ||
| isFieldsValidating: defaultState.isFieldsValidating ?? false, | ||
| isFormValid: defaultState.isFormValid ?? false, | ||
| isFormValidating: defaultState.isFormValidating ?? false, | ||
| isSubmitted: defaultState.isSubmitted ?? false, | ||
| isSubmitting: defaultState.isSubmitting ?? false, | ||
| isTouched: defaultState.isTouched ?? false, | ||
| isValid: defaultState.isValid ?? false, | ||
| isValidating: defaultState.isValidating ?? false, | ||
| submissionAttempts: defaultState.submissionAttempts ?? 0, | ||
| formValidationCount: defaultState.formValidationCount ?? 0 | ||
| }; | ||
| } | ||
| var FormApi = class { | ||
| constructor(opts) { | ||
| // // This carries the context for nested fields | ||
| this.options = {}; | ||
| this.fieldInfo = {}; | ||
| this.validationMeta = {}; | ||
| this.update = (options) => { | ||
| if (!options) | ||
| return; | ||
| this.store.batch(() => { | ||
| const shouldUpdateValues = options.defaultValues && options.defaultValues !== this.options.defaultValues && !this.state.isTouched; | ||
| const shouldUpdateState = options.defaultState !== this.options.defaultState && !this.state.isTouched; | ||
| this.store.setState( | ||
| () => getDefaultFormState( | ||
| Object.assign( | ||
| {}, | ||
| this.state, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateState ? options.defaultState : {}, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateValues ? { | ||
| values: options.defaultValues | ||
| } : {} | ||
| ) | ||
| ) | ||
| ); | ||
| }); | ||
| this.options = options; | ||
| }; | ||
| this.reset = () => this.store.setState( | ||
| () => { | ||
| var _a; | ||
| return getDefaultFormState({ | ||
| ...this.options.defaultState, | ||
| values: this.options.defaultValues ?? ((_a = this.options.defaultState) == null ? void 0 : _a.values) | ||
| }); | ||
| } | ||
| ); | ||
| this.validateAllFields = async (cause) => { | ||
| const fieldValidationPromises = []; | ||
| this.store.batch(() => { | ||
| void Object.values(this.fieldInfo).forEach( | ||
| (field) => { | ||
| Object.values(field.instances).forEach((instance) => { | ||
| if (!instance.state.meta.isTouched) { | ||
| instance.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| fieldValidationPromises.push( | ||
| Promise.resolve().then(() => instance.validate(cause)) | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| ); | ||
| }); | ||
| return Promise.all(fieldValidationPromises); | ||
| }; | ||
| this.handleSubmit = async () => { | ||
| var _a, _b, _c, _d, _e, _f; | ||
| this.store.setState((old) => ({ | ||
| ...old, | ||
| // Submission attempts mark the form as not submitted | ||
| isSubmitted: false, | ||
| // Count submission attempts | ||
| submissionAttempts: old.submissionAttempts + 1 | ||
| })); | ||
| if (!this.state.canSubmit) | ||
| return; | ||
| this.store.setState((d) => ({ ...d, isSubmitting: true })); | ||
| const done = () => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitting: false })); | ||
| }; | ||
| await this.validateAllFields("submit"); | ||
| if (!this.state.isFieldsValid) { | ||
| done(); | ||
| (_b = (_a = this.options).onSubmitInvalid) == null ? void 0 : _b.call(_a, this.state.values, this); | ||
| return; | ||
| } | ||
| if (!this.state.isValid) { | ||
| done(); | ||
| (_d = (_c = this.options).onSubmitInvalid) == null ? void 0 : _d.call(_c, this.state.values, this); | ||
| return; | ||
| } | ||
| try { | ||
| await ((_f = (_e = this.options).onSubmit) == null ? void 0 : _f.call(_e, this.state.values, this)); | ||
| this.store.batch(() => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitted: true })); | ||
| done(); | ||
| }); | ||
| } catch (err) { | ||
| done(); | ||
| throw err; | ||
| } | ||
| }; | ||
| this.getFieldValue = (field) => (0, import_utils.getBy)(this.state.values, field); | ||
| this.getFieldMeta = (field) => { | ||
| return this.state.fieldMeta[field]; | ||
| }; | ||
| this.getFieldInfo = (field) => { | ||
| var _a; | ||
| return (_a = this.fieldInfo)[field] || (_a[field] = { | ||
| instances: {} | ||
| }); | ||
| }; | ||
| this.setFieldMeta = (field, updater) => { | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| fieldMeta: { | ||
| ...prev.fieldMeta, | ||
| [field]: (0, import_utils.functionalUpdate)(updater, prev.fieldMeta[field]) | ||
| } | ||
| }; | ||
| }); | ||
| }; | ||
| this.setFieldValue = (field, updater, opts) => { | ||
| const touch = opts == null ? void 0 : opts.touch; | ||
| this.store.batch(() => { | ||
| if (touch) { | ||
| this.setFieldMeta(field, (prev) => ({ | ||
| ...prev, | ||
| isTouched: true | ||
| })); | ||
| } | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| values: (0, import_utils.setBy)(prev.values, field, updater) | ||
| }; | ||
| }); | ||
| }); | ||
| }; | ||
| this.pushFieldValue = (field, value, opts) => { | ||
| return this.setFieldValue( | ||
| field, | ||
| (prev) => [...Array.isArray(prev) ? prev : [], value], | ||
| opts | ||
| ); | ||
| }; | ||
| this.insertFieldValue = (field, index, value, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.map( | ||
| (d, i) => i === index ? value : d | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.removeFieldValue = (field, index, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.filter( | ||
| (_d, i) => i !== index | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.swapFieldValues = (field, index1, index2) => { | ||
| this.setFieldValue(field, (prev) => { | ||
| const prev1 = prev[index1]; | ||
| const prev2 = prev[index2]; | ||
| return (0, import_utils.setBy)((0, import_utils.setBy)(prev, `${index1}`, prev2), `${index2}`, prev1); | ||
| }); | ||
| }; | ||
| var _a; | ||
| this.store = new import_store.Store( | ||
| getDefaultFormState({ | ||
| ...opts == null ? void 0 : opts.defaultState, | ||
| values: (opts == null ? void 0 : opts.defaultValues) ?? ((_a = opts == null ? void 0 : opts.defaultState) == null ? void 0 : _a.values), | ||
| isFormValid: true | ||
| }), | ||
| { | ||
| onUpdate: () => { | ||
| let { state } = this.store; | ||
| const fieldMetaValues = Object.values(state.fieldMeta); | ||
| const isFieldsValidating = fieldMetaValues.some( | ||
| (field) => field == null ? void 0 : field.isValidating | ||
| ); | ||
| const isFieldsValid = !fieldMetaValues.some((field) => field == null ? void 0 : field.error); | ||
| const isTouched = fieldMetaValues.some((field) => field == null ? void 0 : field.isTouched); | ||
| const isValidating = isFieldsValidating || state.isFormValidating; | ||
| const isFormValid = !state.formError; | ||
| const isValid = isFieldsValid && isFormValid; | ||
| const canSubmit = state.submissionAttempts === 0 && !isTouched || !isValidating && !state.isSubmitting && isValid; | ||
| state = { | ||
| ...state, | ||
| isFieldsValidating, | ||
| isFieldsValid, | ||
| isFormValid, | ||
| isValid, | ||
| canSubmit, | ||
| isTouched | ||
| }; | ||
| this.store.state = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.update(opts || {}); | ||
| } | ||
| }; | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| FormApi | ||
| }); | ||
| //# sourceMappingURL=FormApi.cjs.map |
| {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\n//\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\n\nexport type FormOptions<TData> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onMountAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onMountAsyncDebounceMs?: number\n onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onChangeAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onChangeAsyncDebounceMs?: number\n onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onBlurAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>\n onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void\n}\n\nexport type FieldInfo<TFormData> = {\n instances: Record<string, FieldApi<any, TFormData>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError>\n validationResolve?: (error: ValidationError) => void\n validationReject?: (error: unknown) => void\n}\n\nexport type ValidationError = undefined | false | null | string\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>> = {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...opts?.defaultState,\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some((field) => field?.error)\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...this.options.defaultState,\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError>[] = [] as any\n\n this.store.batch(() => {\n void (Object.values(this.fieldInfo) as FieldInfo<any>[]).forEach(\n (field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n },\n )\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(field: TField) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]} |
| import '@tanstack/store'; | ||
| import './utils.cjs'; | ||
| export { FieldInfo, FormApi, FormOptions, FormState, ValidationError, ValidationMeta } from './index.cjs'; |
| import '@tanstack/store'; | ||
| import './utils.js'; | ||
| export { FieldInfo, FormApi, FormOptions, FormState, ValidationError, ValidationMeta } from './index.js'; |
| import "./chunk-4QZDOMDG.js"; | ||
| // src/FormApi.ts | ||
| import { Store } from "@tanstack/store"; | ||
| import { functionalUpdate, getBy, setBy } from "./utils.js"; | ||
| function getDefaultFormState(defaultState) { | ||
| return { | ||
| values: defaultState.values ?? {}, | ||
| fieldMeta: defaultState.fieldMeta ?? {}, | ||
| canSubmit: defaultState.canSubmit ?? true, | ||
| isFieldsValid: defaultState.isFieldsValid ?? false, | ||
| isFieldsValidating: defaultState.isFieldsValidating ?? false, | ||
| isFormValid: defaultState.isFormValid ?? false, | ||
| isFormValidating: defaultState.isFormValidating ?? false, | ||
| isSubmitted: defaultState.isSubmitted ?? false, | ||
| isSubmitting: defaultState.isSubmitting ?? false, | ||
| isTouched: defaultState.isTouched ?? false, | ||
| isValid: defaultState.isValid ?? false, | ||
| isValidating: defaultState.isValidating ?? false, | ||
| submissionAttempts: defaultState.submissionAttempts ?? 0, | ||
| formValidationCount: defaultState.formValidationCount ?? 0 | ||
| }; | ||
| } | ||
| var FormApi = class { | ||
| constructor(opts) { | ||
| // // This carries the context for nested fields | ||
| this.options = {}; | ||
| this.fieldInfo = {}; | ||
| this.validationMeta = {}; | ||
| this.update = (options) => { | ||
| if (!options) | ||
| return; | ||
| this.store.batch(() => { | ||
| const shouldUpdateValues = options.defaultValues && options.defaultValues !== this.options.defaultValues && !this.state.isTouched; | ||
| const shouldUpdateState = options.defaultState !== this.options.defaultState && !this.state.isTouched; | ||
| this.store.setState( | ||
| () => getDefaultFormState( | ||
| Object.assign( | ||
| {}, | ||
| this.state, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateState ? options.defaultState : {}, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateValues ? { | ||
| values: options.defaultValues | ||
| } : {} | ||
| ) | ||
| ) | ||
| ); | ||
| }); | ||
| this.options = options; | ||
| }; | ||
| this.reset = () => this.store.setState( | ||
| () => { | ||
| var _a; | ||
| return getDefaultFormState({ | ||
| ...this.options.defaultState, | ||
| values: this.options.defaultValues ?? ((_a = this.options.defaultState) == null ? void 0 : _a.values) | ||
| }); | ||
| } | ||
| ); | ||
| this.validateAllFields = async (cause) => { | ||
| const fieldValidationPromises = []; | ||
| this.store.batch(() => { | ||
| void Object.values(this.fieldInfo).forEach( | ||
| (field) => { | ||
| Object.values(field.instances).forEach((instance) => { | ||
| if (!instance.state.meta.isTouched) { | ||
| instance.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| fieldValidationPromises.push( | ||
| Promise.resolve().then(() => instance.validate(cause)) | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| ); | ||
| }); | ||
| return Promise.all(fieldValidationPromises); | ||
| }; | ||
| this.handleSubmit = async () => { | ||
| var _a, _b, _c, _d, _e, _f; | ||
| this.store.setState((old) => ({ | ||
| ...old, | ||
| // Submission attempts mark the form as not submitted | ||
| isSubmitted: false, | ||
| // Count submission attempts | ||
| submissionAttempts: old.submissionAttempts + 1 | ||
| })); | ||
| if (!this.state.canSubmit) | ||
| return; | ||
| this.store.setState((d) => ({ ...d, isSubmitting: true })); | ||
| const done = () => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitting: false })); | ||
| }; | ||
| await this.validateAllFields("submit"); | ||
| if (!this.state.isFieldsValid) { | ||
| done(); | ||
| (_b = (_a = this.options).onSubmitInvalid) == null ? void 0 : _b.call(_a, this.state.values, this); | ||
| return; | ||
| } | ||
| if (!this.state.isValid) { | ||
| done(); | ||
| (_d = (_c = this.options).onSubmitInvalid) == null ? void 0 : _d.call(_c, this.state.values, this); | ||
| return; | ||
| } | ||
| try { | ||
| await ((_f = (_e = this.options).onSubmit) == null ? void 0 : _f.call(_e, this.state.values, this)); | ||
| this.store.batch(() => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitted: true })); | ||
| done(); | ||
| }); | ||
| } catch (err) { | ||
| done(); | ||
| throw err; | ||
| } | ||
| }; | ||
| this.getFieldValue = (field) => getBy(this.state.values, field); | ||
| this.getFieldMeta = (field) => { | ||
| return this.state.fieldMeta[field]; | ||
| }; | ||
| this.getFieldInfo = (field) => { | ||
| var _a; | ||
| return (_a = this.fieldInfo)[field] || (_a[field] = { | ||
| instances: {} | ||
| }); | ||
| }; | ||
| this.setFieldMeta = (field, updater) => { | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| fieldMeta: { | ||
| ...prev.fieldMeta, | ||
| [field]: functionalUpdate(updater, prev.fieldMeta[field]) | ||
| } | ||
| }; | ||
| }); | ||
| }; | ||
| this.setFieldValue = (field, updater, opts) => { | ||
| const touch = opts == null ? void 0 : opts.touch; | ||
| this.store.batch(() => { | ||
| if (touch) { | ||
| this.setFieldMeta(field, (prev) => ({ | ||
| ...prev, | ||
| isTouched: true | ||
| })); | ||
| } | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| values: setBy(prev.values, field, updater) | ||
| }; | ||
| }); | ||
| }); | ||
| }; | ||
| this.pushFieldValue = (field, value, opts) => { | ||
| return this.setFieldValue( | ||
| field, | ||
| (prev) => [...Array.isArray(prev) ? prev : [], value], | ||
| opts | ||
| ); | ||
| }; | ||
| this.insertFieldValue = (field, index, value, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.map( | ||
| (d, i) => i === index ? value : d | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.removeFieldValue = (field, index, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.filter( | ||
| (_d, i) => i !== index | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.swapFieldValues = (field, index1, index2) => { | ||
| this.setFieldValue(field, (prev) => { | ||
| const prev1 = prev[index1]; | ||
| const prev2 = prev[index2]; | ||
| return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1); | ||
| }); | ||
| }; | ||
| var _a; | ||
| this.store = new Store( | ||
| getDefaultFormState({ | ||
| ...opts == null ? void 0 : opts.defaultState, | ||
| values: (opts == null ? void 0 : opts.defaultValues) ?? ((_a = opts == null ? void 0 : opts.defaultState) == null ? void 0 : _a.values), | ||
| isFormValid: true | ||
| }), | ||
| { | ||
| onUpdate: () => { | ||
| let { state } = this.store; | ||
| const fieldMetaValues = Object.values(state.fieldMeta); | ||
| const isFieldsValidating = fieldMetaValues.some( | ||
| (field) => field == null ? void 0 : field.isValidating | ||
| ); | ||
| const isFieldsValid = !fieldMetaValues.some((field) => field == null ? void 0 : field.error); | ||
| const isTouched = fieldMetaValues.some((field) => field == null ? void 0 : field.isTouched); | ||
| const isValidating = isFieldsValidating || state.isFormValidating; | ||
| const isFormValid = !state.formError; | ||
| const isValid = isFieldsValid && isFormValid; | ||
| const canSubmit = state.submissionAttempts === 0 && !isTouched || !isValidating && !state.isSubmitting && isValid; | ||
| state = { | ||
| ...state, | ||
| isFieldsValidating, | ||
| isFieldsValid, | ||
| isFormValid, | ||
| isValid, | ||
| canSubmit, | ||
| isTouched | ||
| }; | ||
| this.store.state = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.update(opts || {}); | ||
| } | ||
| }; | ||
| export { | ||
| FormApi | ||
| }; | ||
| //# sourceMappingURL=FormApi.js.map |
| {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\n//\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\n\nexport type FormOptions<TData> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onMountAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onMountAsyncDebounceMs?: number\n onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onChangeAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onChangeAsyncDebounceMs?: number\n onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onBlurAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>\n onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void\n}\n\nexport type FieldInfo<TFormData> = {\n instances: Record<string, FieldApi<any, TFormData>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError>\n validationResolve?: (error: ValidationError) => void\n validationReject?: (error: unknown) => void\n}\n\nexport type ValidationError = undefined | false | null | string\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>> = {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...opts?.defaultState,\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some((field) => field?.error)\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...this.options.defaultState,\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError>[] = [] as any\n\n this.store.batch(() => {\n void (Object.values(this.fieldInfo) as FieldInfo<any>[]).forEach(\n (field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n },\n )\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(field: TField) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":";;;AAAA,SAAS,aAAa;AAGtB,SAAS,kBAAkB,OAAO,aAAa;AAgE/C,SAAS,oBACP,cACkB;AAClB,SAAO;AAAA,IACL,QAAQ,aAAa,UAAW,CAAC;AAAA,IACjC,WAAW,aAAa,aAAc,CAAC;AAAA,IACvC,WAAW,aAAa,aAAa;AAAA,IACrC,eAAe,aAAa,iBAAiB;AAAA,IAC7C,oBAAoB,aAAa,sBAAsB;AAAA,IACvD,aAAa,aAAa,eAAe;AAAA,IACzC,kBAAkB,aAAa,oBAAoB;AAAA,IACnD,aAAa,aAAa,eAAe;AAAA,IACzC,cAAc,aAAa,gBAAgB;AAAA,IAC3C,WAAW,aAAa,aAAa;AAAA,IACrC,SAAS,aAAa,WAAW;AAAA,IACjC,cAAc,aAAa,gBAAgB;AAAA,IAC3C,oBAAoB,aAAa,sBAAsB;AAAA,IACvD,qBAAqB,aAAa,uBAAuB;AAAA,EAC3D;AACF;AAEO,IAAM,UAAN,MAAyB;AAAA,EAW9B,YAAY,MAA+B;AAT3C;AAAA,mBAAkC,CAAC;AAKnC,qBAA+D,CAAC;AAEhE,0BAAiC,CAAC;AAsDlC,kBAAS,CAAC,YAAqC;AAC7C,UAAI,CAAC;AAAS;AAEd,WAAK,MAAM,MAAM,MAAM;AACrB,cAAM,qBACJ,QAAQ,iBACR,QAAQ,kBAAkB,KAAK,QAAQ,iBACvC,CAAC,KAAK,MAAM;AAEd,cAAM,oBACJ,QAAQ,iBAAiB,KAAK,QAAQ,gBACtC,CAAC,KAAK,MAAM;AAEd,aAAK,MAAM;AAAA,UAAS,MAClB;AAAA,YACE,OAAO;AAAA,cACL,CAAC;AAAA,cACD,KAAK;AAAA;AAAA,cAEL,oBAAoB,QAAQ,eAAe,CAAC;AAAA;AAAA,cAE5C,qBACI;AAAA,gBACE,QAAQ,QAAQ;AAAA,cAClB,IACA,CAAC;AAAA,YACP;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,WAAK,UAAU;AAAA,IACjB;AAEA,iBAAQ,MACN,KAAK,MAAM;AAAA,MAAS,MAAG;AA1L3B;AA2LM,mCAAoB;AAAA,UAClB,GAAG,KAAK,QAAQ;AAAA,UAChB,QAAQ,KAAK,QAAQ,mBAAiB,UAAK,QAAQ,iBAAb,mBAA2B;AAAA,QACnE,CAAC;AAAA;AAAA,IACH;AAEF,6BAAoB,OAAO,UAA2B;AACpD,YAAM,0BAAsD,CAAC;AAE7D,WAAK,MAAM,MAAM,MAAM;AACrB,aAAM,OAAO,OAAO,KAAK,SAAS,EAAuB;AAAA,UACvD,CAAC,UAAU;AACT,mBAAO,OAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,aAAa;AAEnD,kBAAI,CAAC,SAAS,MAAM,KAAK,WAAW;AAElC,yBAAS,QAAQ,CAAC,UAAU,EAAE,GAAG,MAAM,WAAW,KAAK,EAAE;AAEzD,wCAAwB;AAAA,kBACtB,QAAQ,QAAQ,EAAE,KAAK,MAAM,SAAS,SAAS,KAAK,CAAC;AAAA,gBACvD;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAED,aAAO,QAAQ,IAAI,uBAAuB;AAAA,IAC5C;AAEA,wBAAe,YAAY;AAzN7B;AA+NI,WAAK,MAAM,SAAS,CAAC,SAAS;AAAA,QAC5B,GAAG;AAAA;AAAA,QAEH,aAAa;AAAA;AAAA,QAEb,oBAAoB,IAAI,qBAAqB;AAAA,MAC/C,EAAE;AAGF,UAAI,CAAC,KAAK,MAAM;AAAW;AAE3B,WAAK,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,cAAc,KAAK,EAAE;AAEzD,YAAM,OAAO,MAAM;AACjB,aAAK,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,cAAc,MAAM,EAAE;AAAA,MAClE;AAGA,YAAM,KAAK,kBAAkB,QAAQ;AAGrC,UAAI,CAAC,KAAK,MAAM,eAAe;AAC7B,aAAK;AACL,yBAAK,SAAQ,oBAAb,4BAA+B,KAAK,MAAM,QAAQ;AAClD;AAAA,MACF;AAKA,UAAI,CAAC,KAAK,MAAM,SAAS;AACvB,aAAK;AACL,yBAAK,SAAQ,oBAAb,4BAA+B,KAAK,MAAM,QAAQ;AAClD;AAAA,MACF;AAEA,UAAI;AAEF,gBAAM,gBAAK,SAAQ,aAAb,4BAAwB,KAAK,MAAM,QAAQ;AAEjD,aAAK,MAAM,MAAM,MAAM;AACrB,eAAK,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,aAAa,KAAK,EAAE;AAC9D,eAAK;AAAA,QACP,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,aAAK;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAEA,yBAAgB,CACd,UACiC,MAAM,KAAK,MAAM,QAAQ,KAAK;AAEjE,wBAAe,CACb,UAC0B;AAC1B,aAAO,KAAK,MAAM,UAAU,KAAK;AAAA,IACnC;AAEA,wBAAe,CAAqC,UAAkB;AA3RxE;AA6RI,cAAQ,UAAK,WAAL,uBAA0B;AAAA,QAChC,WAAW,CAAC;AAAA,MACd;AAAA,IACF;AAEA,wBAAe,CACb,OACA,YACG;AACH,WAAK,MAAM,SAAS,CAAC,SAAS;AAC5B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,WAAW;AAAA,YACT,GAAG,KAAK;AAAA,YACR,CAAC,KAAK,GAAG,iBAAiB,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA,UAC1D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,yBAAgB,CACd,OACA,SACA,SACG;AACH,YAAM,QAAQ,6BAAM;AAEpB,WAAK,MAAM,MAAM,MAAM;AACrB,YAAI,OAAO;AACT,eAAK,aAAa,OAAO,CAAC,UAAU;AAAA,YAClC,GAAG;AAAA,YACH,WAAW;AAAA,UACb,EAAE;AAAA,QACJ;AAEA,aAAK,MAAM,SAAS,CAAC,SAAS;AAC5B,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO;AAAA,UAC3C;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,0BAAiB,CACf,OACA,OACA,SACG;AACH,aAAO,KAAK;AAAA,QACV;AAAA,QACA,CAAC,SAAS,CAAC,GAAI,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAAI,KAAK;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAEA,4BAAmB,CACjB,OACA,OACA,OACA,SACG;AACH,WAAK;AAAA,QACH;AAAA,QACA,CAAC,SAAS;AACR,iBAAQ,KAAwC;AAAA,YAAI,CAAC,GAAG,MACtD,MAAM,QAAQ,QAAQ;AAAA,UACxB;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,4BAAmB,CACjB,OACA,OACA,SACG;AACH,WAAK;AAAA,QACH;AAAA,QACA,CAAC,SAAS;AACR,iBAAQ,KAAwC;AAAA,YAC9C,CAAC,IAAI,MAAM,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,2BAAkB,CAChB,OACA,QACA,WACG;AACH,WAAK,cAAc,OAAO,CAAC,SAAc;AACvC,cAAM,QAAQ,KAAK,MAAM;AACzB,cAAM,QAAQ,KAAK,MAAM;AACzB,eAAO,MAAM,MAAM,MAAM,GAAG,MAAM,IAAI,KAAK,GAAG,GAAG,MAAM,IAAI,KAAK;AAAA,MAClE,CAAC;AAAA,IACH;AAhYF;AAoGI,SAAK,QAAQ,IAAI;AAAA,MACf,oBAAoB;AAAA,QAClB,GAAG,6BAAM;AAAA,QACT,SAAQ,6BAAM,oBAAiB,kCAAM,iBAAN,mBAAoB;AAAA,QACnD,aAAa;AAAA,MACf,CAAC;AAAA,MACD;AAAA,QACE,UAAU,MAAM;AACd,cAAI,EAAE,MAAM,IAAI,KAAK;AAErB,gBAAM,kBAAkB,OAAO,OAAO,MAAM,SAAS;AAKrD,gBAAM,qBAAqB,gBAAgB;AAAA,YACzC,CAAC,UAAU,+BAAO;AAAA,UACpB;AAEA,gBAAM,gBAAgB,CAAC,gBAAgB,KAAK,CAAC,UAAU,+BAAO,KAAK;AAEnE,gBAAM,YAAY,gBAAgB,KAAK,CAAC,UAAU,+BAAO,SAAS;AAElE,gBAAM,eAAe,sBAAsB,MAAM;AACjD,gBAAM,cAAc,CAAC,MAAM;AAC3B,gBAAM,UAAU,iBAAiB;AACjC,gBAAM,YACH,MAAM,uBAAuB,KAAK,CAAC,aACnC,CAAC,gBAAgB,CAAC,MAAM,gBAAgB;AAE3C,kBAAQ;AAAA,YACN,GAAG;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAEA,eAAK,MAAM,QAAQ;AACnB,eAAK,QAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,SAAK,QAAQ,KAAK,MAAM;AAExB,SAAK,OAAO,QAAQ,CAAC,CAAC;AAAA,EACxB;AA4OF;","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/index.ts | ||
| var src_exports = {}; | ||
| module.exports = __toCommonJS(src_exports); | ||
| __reExport(src_exports, require("./FormApi.cjs"), module.exports); | ||
| __reExport(src_exports, require("./FieldApi.cjs"), module.exports); | ||
| __reExport(src_exports, require("./utils.cjs"), module.exports); | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| ...require("./FormApi.cjs"), | ||
| ...require("./FieldApi.cjs"), | ||
| ...require("./utils.cjs") | ||
| }); | ||
| //# sourceMappingURL=index.cjs.map |
| {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from './FormApi'\nexport * from './FieldApi'\nexport * from './utils'\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,wBAAc,0BAAd;AACA,wBAAc,2BADd;AAEA,wBAAc,wBAFd;","names":[]} |
| import { Store } from '@tanstack/store'; | ||
| import { DeepKeys, DeepValue, Updater } from './utils.cjs'; | ||
| export { Narrow, Pretty, RequiredByKey, UpdaterFn, functionalUpdate, getBy, setBy } from './utils.cjs'; | ||
| type FormOptions<TData> = { | ||
| defaultValues?: TData; | ||
| defaultState?: Partial<FormState<TData>>; | ||
| asyncDebounceMs?: number; | ||
| onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onMountAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onMountAsyncDebounceMs?: number; | ||
| onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onChangeAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onBlurAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>; | ||
| onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void; | ||
| }; | ||
| type FieldInfo<TFormData> = { | ||
| instances: Record<string, FieldApi<any, TFormData>>; | ||
| } & ValidationMeta; | ||
| type ValidationMeta = { | ||
| validationCount?: number; | ||
| validationAsyncCount?: number; | ||
| validationPromise?: Promise<ValidationError>; | ||
| validationResolve?: (error: ValidationError) => void; | ||
| validationReject?: (error: unknown) => void; | ||
| }; | ||
| type ValidationError = undefined | false | null | string; | ||
| type FormState<TData> = { | ||
| values: TData; | ||
| isFormValidating: boolean; | ||
| formValidationCount: number; | ||
| isFormValid: boolean; | ||
| formError?: ValidationError; | ||
| fieldMeta: Record<DeepKeys<TData>, FieldMeta>; | ||
| isFieldsValidating: boolean; | ||
| isFieldsValid: boolean; | ||
| isSubmitting: boolean; | ||
| isTouched: boolean; | ||
| isSubmitted: boolean; | ||
| isValidating: boolean; | ||
| isValid: boolean; | ||
| canSubmit: boolean; | ||
| submissionAttempts: number; | ||
| }; | ||
| declare class FormApi<TFormData> { | ||
| options: FormOptions<TFormData>; | ||
| store: Store<FormState<TFormData>>; | ||
| state: FormState<TFormData>; | ||
| fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>>; | ||
| fieldName?: string; | ||
| validationMeta: ValidationMeta; | ||
| constructor(opts?: FormOptions<TFormData>); | ||
| update: (options?: FormOptions<TFormData>) => void; | ||
| reset: () => void; | ||
| validateAllFields: (cause: ValidationCause) => Promise<ValidationError[]>; | ||
| handleSubmit: () => Promise<void>; | ||
| getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>; | ||
| getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined; | ||
| getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[TField]; | ||
| setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void; | ||
| setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| insertFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| removeFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| swapFieldValues: <TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number) => void; | ||
| } | ||
| type ValidationCause = 'change' | 'blur' | 'submit'; | ||
| type ValidateFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError; | ||
| type ValidateAsyncFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>; | ||
| interface FieldOptions<_TData, TFormData, | ||
| /** | ||
| * This allows us to restrict the name to only be a valid field name while | ||
| * also assigning it to a generic | ||
| */ | ||
| TName = unknown extends TFormData ? string : DeepKeys<TFormData>, | ||
| /** | ||
| * If TData is unknown, we can use the TName generic to determine the type | ||
| */ | ||
| TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> { | ||
| name: TName; | ||
| index?: TData extends any[] ? number : never; | ||
| defaultValue?: TData; | ||
| asyncDebounceMs?: number; | ||
| asyncAlways?: boolean; | ||
| onMount?: (formApi: FieldApi<TData, TFormData>) => void; | ||
| onChange?: ValidateFn<TData, TFormData>; | ||
| onChangeAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: ValidateFn<TData, TFormData>; | ||
| onBlurAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmitAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| defaultMeta?: Partial<FieldMeta>; | ||
| } | ||
| type FieldApiOptions<TData, TFormData> = FieldOptions<TData, TFormData> & { | ||
| form: FormApi<TFormData>; | ||
| }; | ||
| type FieldMeta = { | ||
| isTouched: boolean; | ||
| touchedError?: ValidationError; | ||
| error?: ValidationError; | ||
| isValidating: boolean; | ||
| }; | ||
| type FieldState<TData> = { | ||
| value: TData; | ||
| meta: FieldMeta; | ||
| }; | ||
| /** | ||
| * TData may not be known at the time of FieldApi construction, so we need to | ||
| * use a conditional type to determine if TData is known or not. | ||
| * | ||
| * If TData is not known, we use the TFormData type to determine the type of | ||
| * the field value based on the field name. | ||
| */ | ||
| type GetTData<Name, TData, TFormData> = unknown extends TData ? DeepValue<TFormData, Name> : TData; | ||
| declare class FieldApi<TData, TFormData> { | ||
| #private; | ||
| uid: number; | ||
| form: FormApi<TFormData>; | ||
| name: DeepKeys<TFormData>; | ||
| /** | ||
| * This is a hack that allows us to use `GetTData` without calling it everywhere | ||
| * | ||
| * Unfortunately this hack appears to be needed alongside the `TName` hack | ||
| * further up in this file. This properly types all of the internal methods, | ||
| * while the `TName` hack types the options properly | ||
| */ | ||
| _tdata: GetTData<typeof this$1.name, TData, TFormData>; | ||
| store: Store<FieldState<typeof this$1._tdata>>; | ||
| state: FieldState<typeof this$1._tdata>; | ||
| prevState: FieldState<typeof this$1._tdata>; | ||
| options: FieldOptions<typeof this$1._tdata, TFormData>; | ||
| constructor(opts: FieldApiOptions<TData, TFormData>); | ||
| mount: () => () => void; | ||
| update: (opts: FieldApiOptions<typeof this$1._tdata, TFormData>) => void; | ||
| getValue: () => typeof this$1._tdata; | ||
| setValue: (updater: Updater<typeof this$1._tdata>, options?: { | ||
| touch?: boolean; | ||
| notify?: boolean; | ||
| }) => void; | ||
| _getMeta: () => FieldMeta | undefined; | ||
| getMeta: () => FieldMeta; | ||
| setMeta: (updater: Updater<FieldMeta>) => void; | ||
| getInfo: () => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[DeepKeys<TFormData>]; | ||
| pushValue: (value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| insertValue: (index: number, value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| removeValue: (index: number) => void; | ||
| swapValues: (aIndex: number, bIndex: number) => void; | ||
| getSubField: <TName extends DeepKeys<GetTData<DeepKeys<TFormData>, TData, TFormData>>>(name: TName) => FieldApi<DeepValue<GetTData<DeepKeys<TFormData>, TData, TFormData>, TName>, TFormData>; | ||
| validateSync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => void; | ||
| cancelValidateAsync: () => void; | ||
| validateAsync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => Promise<ValidationError>; | ||
| validate: (cause: ValidationCause, value?: typeof this$1._tdata) => ValidationError | Promise<ValidationError>; | ||
| handleChange: (updater: Updater<typeof this$1._tdata>) => void; | ||
| handleBlur: () => void; | ||
| } | ||
| export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, Updater, ValidationCause, ValidationError, ValidationMeta }; |
| import { Store } from '@tanstack/store'; | ||
| import { DeepKeys, DeepValue, Updater } from './utils.js'; | ||
| export { Narrow, Pretty, RequiredByKey, UpdaterFn, functionalUpdate, getBy, setBy } from './utils.js'; | ||
| type FormOptions<TData> = { | ||
| defaultValues?: TData; | ||
| defaultState?: Partial<FormState<TData>>; | ||
| asyncDebounceMs?: number; | ||
| onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onMountAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onMountAsyncDebounceMs?: number; | ||
| onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onChangeAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onBlurAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>; | ||
| onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void; | ||
| }; | ||
| type FieldInfo<TFormData> = { | ||
| instances: Record<string, FieldApi<any, TFormData>>; | ||
| } & ValidationMeta; | ||
| type ValidationMeta = { | ||
| validationCount?: number; | ||
| validationAsyncCount?: number; | ||
| validationPromise?: Promise<ValidationError>; | ||
| validationResolve?: (error: ValidationError) => void; | ||
| validationReject?: (error: unknown) => void; | ||
| }; | ||
| type ValidationError = undefined | false | null | string; | ||
| type FormState<TData> = { | ||
| values: TData; | ||
| isFormValidating: boolean; | ||
| formValidationCount: number; | ||
| isFormValid: boolean; | ||
| formError?: ValidationError; | ||
| fieldMeta: Record<DeepKeys<TData>, FieldMeta>; | ||
| isFieldsValidating: boolean; | ||
| isFieldsValid: boolean; | ||
| isSubmitting: boolean; | ||
| isTouched: boolean; | ||
| isSubmitted: boolean; | ||
| isValidating: boolean; | ||
| isValid: boolean; | ||
| canSubmit: boolean; | ||
| submissionAttempts: number; | ||
| }; | ||
| declare class FormApi<TFormData> { | ||
| options: FormOptions<TFormData>; | ||
| store: Store<FormState<TFormData>>; | ||
| state: FormState<TFormData>; | ||
| fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>>; | ||
| fieldName?: string; | ||
| validationMeta: ValidationMeta; | ||
| constructor(opts?: FormOptions<TFormData>); | ||
| update: (options?: FormOptions<TFormData>) => void; | ||
| reset: () => void; | ||
| validateAllFields: (cause: ValidationCause) => Promise<ValidationError[]>; | ||
| handleSubmit: () => Promise<void>; | ||
| getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>; | ||
| getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined; | ||
| getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[TField]; | ||
| setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void; | ||
| setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| insertFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| removeFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| swapFieldValues: <TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number) => void; | ||
| } | ||
| type ValidationCause = 'change' | 'blur' | 'submit'; | ||
| type ValidateFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError; | ||
| type ValidateAsyncFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>; | ||
| interface FieldOptions<_TData, TFormData, | ||
| /** | ||
| * This allows us to restrict the name to only be a valid field name while | ||
| * also assigning it to a generic | ||
| */ | ||
| TName = unknown extends TFormData ? string : DeepKeys<TFormData>, | ||
| /** | ||
| * If TData is unknown, we can use the TName generic to determine the type | ||
| */ | ||
| TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> { | ||
| name: TName; | ||
| index?: TData extends any[] ? number : never; | ||
| defaultValue?: TData; | ||
| asyncDebounceMs?: number; | ||
| asyncAlways?: boolean; | ||
| onMount?: (formApi: FieldApi<TData, TFormData>) => void; | ||
| onChange?: ValidateFn<TData, TFormData>; | ||
| onChangeAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: ValidateFn<TData, TFormData>; | ||
| onBlurAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmitAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| defaultMeta?: Partial<FieldMeta>; | ||
| } | ||
| type FieldApiOptions<TData, TFormData> = FieldOptions<TData, TFormData> & { | ||
| form: FormApi<TFormData>; | ||
| }; | ||
| type FieldMeta = { | ||
| isTouched: boolean; | ||
| touchedError?: ValidationError; | ||
| error?: ValidationError; | ||
| isValidating: boolean; | ||
| }; | ||
| type FieldState<TData> = { | ||
| value: TData; | ||
| meta: FieldMeta; | ||
| }; | ||
| /** | ||
| * TData may not be known at the time of FieldApi construction, so we need to | ||
| * use a conditional type to determine if TData is known or not. | ||
| * | ||
| * If TData is not known, we use the TFormData type to determine the type of | ||
| * the field value based on the field name. | ||
| */ | ||
| type GetTData<Name, TData, TFormData> = unknown extends TData ? DeepValue<TFormData, Name> : TData; | ||
| declare class FieldApi<TData, TFormData> { | ||
| #private; | ||
| uid: number; | ||
| form: FormApi<TFormData>; | ||
| name: DeepKeys<TFormData>; | ||
| /** | ||
| * This is a hack that allows us to use `GetTData` without calling it everywhere | ||
| * | ||
| * Unfortunately this hack appears to be needed alongside the `TName` hack | ||
| * further up in this file. This properly types all of the internal methods, | ||
| * while the `TName` hack types the options properly | ||
| */ | ||
| _tdata: GetTData<typeof this$1.name, TData, TFormData>; | ||
| store: Store<FieldState<typeof this$1._tdata>>; | ||
| state: FieldState<typeof this$1._tdata>; | ||
| prevState: FieldState<typeof this$1._tdata>; | ||
| options: FieldOptions<typeof this$1._tdata, TFormData>; | ||
| constructor(opts: FieldApiOptions<TData, TFormData>); | ||
| mount: () => () => void; | ||
| update: (opts: FieldApiOptions<typeof this$1._tdata, TFormData>) => void; | ||
| getValue: () => typeof this$1._tdata; | ||
| setValue: (updater: Updater<typeof this$1._tdata>, options?: { | ||
| touch?: boolean; | ||
| notify?: boolean; | ||
| }) => void; | ||
| _getMeta: () => FieldMeta | undefined; | ||
| getMeta: () => FieldMeta; | ||
| setMeta: (updater: Updater<FieldMeta>) => void; | ||
| getInfo: () => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[DeepKeys<TFormData>]; | ||
| pushValue: (value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| insertValue: (index: number, value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| removeValue: (index: number) => void; | ||
| swapValues: (aIndex: number, bIndex: number) => void; | ||
| getSubField: <TName extends DeepKeys<GetTData<DeepKeys<TFormData>, TData, TFormData>>>(name: TName) => FieldApi<DeepValue<GetTData<DeepKeys<TFormData>, TData, TFormData>, TName>, TFormData>; | ||
| validateSync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => void; | ||
| cancelValidateAsync: () => void; | ||
| validateAsync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => Promise<ValidationError>; | ||
| validate: (cause: ValidationCause, value?: typeof this$1._tdata) => ValidationError | Promise<ValidationError>; | ||
| handleChange: (updater: Updater<typeof this$1._tdata>) => void; | ||
| handleBlur: () => void; | ||
| } | ||
| export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, Updater, ValidationCause, ValidationError, ValidationMeta }; |
| // src/index.ts | ||
| export * from "./FormApi.js"; | ||
| export * from "./FieldApi.js"; | ||
| export * from "./utils.js"; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from './FormApi'\nexport * from './FieldApi'\nexport * from './utils'\n"],"mappings":";AAAA,cAAc;AACd,cAAc;AACd,cAAc;","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/utils.ts | ||
| var utils_exports = {}; | ||
| __export(utils_exports, { | ||
| functionalUpdate: () => functionalUpdate, | ||
| getBy: () => getBy, | ||
| setBy: () => setBy | ||
| }); | ||
| module.exports = __toCommonJS(utils_exports); | ||
| function functionalUpdate(updater, input) { | ||
| return typeof updater === "function" ? updater(input) : updater; | ||
| } | ||
| function getBy(obj, path) { | ||
| const pathArray = makePathArray(path); | ||
| const pathObj = pathArray; | ||
| return pathObj.reduce((current, pathPart) => { | ||
| if (typeof current !== "undefined") { | ||
| return current[pathPart]; | ||
| } | ||
| return void 0; | ||
| }, obj); | ||
| } | ||
| function setBy(obj, _path, updater) { | ||
| const path = makePathArray(_path); | ||
| function doSet(parent) { | ||
| if (!path.length) { | ||
| return functionalUpdate(updater, parent); | ||
| } | ||
| const key = path.shift(); | ||
| if (typeof key === "string") { | ||
| if (typeof parent === "object") { | ||
| return { | ||
| ...parent, | ||
| [key]: doSet(parent[key]) | ||
| }; | ||
| } | ||
| return { | ||
| [key]: doSet() | ||
| }; | ||
| } | ||
| if (typeof key === "number") { | ||
| if (Array.isArray(parent)) { | ||
| const prefix = parent.slice(0, key); | ||
| return [ | ||
| ...prefix.length ? prefix : new Array(key), | ||
| doSet(parent[key]), | ||
| ...parent.slice(key + 1) | ||
| ]; | ||
| } | ||
| return [...new Array(key), doSet()]; | ||
| } | ||
| throw new Error("Uh oh!"); | ||
| } | ||
| return doSet(obj); | ||
| } | ||
| var reFindNumbers0 = /^(\d*)$/gm; | ||
| var reFindNumbers1 = /\.(\d*)\./gm; | ||
| var reFindNumbers2 = /^(\d*)\./gm; | ||
| var reFindNumbers3 = /\.(\d*$)/gm; | ||
| var reFindMultiplePeriods = /\.{2,}/gm; | ||
| var intPrefix = "__int__"; | ||
| var intReplace = `${intPrefix}$1`; | ||
| function makePathArray(str) { | ||
| if (typeof str !== "string") { | ||
| throw new Error("Path must be a string."); | ||
| } | ||
| return str.replace("[", ".").replace("]", "").replace(reFindNumbers0, intReplace).replace(reFindNumbers1, `.${intReplace}.`).replace(reFindNumbers2, `${intReplace}.`).replace(reFindNumbers3, `.${intReplace}`).replace(reFindMultiplePeriods, ".").split(".").map((d) => { | ||
| if (d.indexOf(intPrefix) === 0) { | ||
| return parseInt(d.substring(intPrefix.length), 10); | ||
| } | ||
| return d; | ||
| }); | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| functionalUpdate, | ||
| getBy, | ||
| setBy | ||
| }); | ||
| //# sourceMappingURL=utils.cjs.map |
| {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T> = unknown extends T\n ? keyof T\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>\n : T extends any[]\n ? DeepKeys<T[number]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T>\n : never\n\ntype DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;","names":[]} |
| type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput; | ||
| type Updater<TInput, TOutput = TInput> = TOutput | UpdaterFn<TInput, TOutput>; | ||
| declare function functionalUpdate<TInput, TOutput = TInput>(updater: Updater<TInput, TOutput>, input: TInput): TOutput; | ||
| /** | ||
| * Get a value from an object using a path, including dot notation. | ||
| */ | ||
| declare function getBy(obj: any, path: any): any; | ||
| /** | ||
| * Set a value on an object using a path, including dot notation. | ||
| */ | ||
| declare function setBy(obj: any, _path: any, updater: Updater<any>): any; | ||
| type RequiredByKey<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; | ||
| type ComputeRange<N extends number, Result extends Array<unknown> = []> = Result['length'] extends N ? Result : ComputeRange<N, [...Result, Result['length']]>; | ||
| type Index40 = ComputeRange<40>[number]; | ||
| type IsTuple<T> = T extends readonly any[] & { | ||
| length: infer Length; | ||
| } ? Length extends Index40 ? T : never : never; | ||
| type AllowedIndexes<Tuple extends ReadonlyArray<any>, Keys extends number = never> = Tuple extends readonly [] ? Keys : Tuple extends readonly [infer _, ...infer Tail] ? AllowedIndexes<Tail, Keys | Tail['length']> : Keys; | ||
| type DeepKeys<T> = unknown extends T ? keyof T : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>> : T extends any[] ? DeepKeys<T[number]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T> : never; | ||
| type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}` : never; | ||
| type DeepValue<T, TProp> = T extends Record<string | number, any> ? TProp extends `${infer TBranch}.${infer TDeepProp}` ? DeepValue<T[TBranch], TDeepProp> : T[TProp & string] : never; | ||
| type Narrowable = string | number | bigint | boolean; | ||
| type NarrowRaw<A> = (A extends [] ? [] : never) | (A extends Narrowable ? A : never) | { | ||
| [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>; | ||
| }; | ||
| type Narrow<A> = Try<A, [], NarrowRaw<A>>; | ||
| type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch; | ||
| type Pretty<T> = { | ||
| [K in keyof T]: T[K]; | ||
| } & {}; | ||
| export { DeepKeys, DeepValue, Narrow, Pretty, RequiredByKey, Updater, UpdaterFn, functionalUpdate, getBy, setBy }; |
| type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput; | ||
| type Updater<TInput, TOutput = TInput> = TOutput | UpdaterFn<TInput, TOutput>; | ||
| declare function functionalUpdate<TInput, TOutput = TInput>(updater: Updater<TInput, TOutput>, input: TInput): TOutput; | ||
| /** | ||
| * Get a value from an object using a path, including dot notation. | ||
| */ | ||
| declare function getBy(obj: any, path: any): any; | ||
| /** | ||
| * Set a value on an object using a path, including dot notation. | ||
| */ | ||
| declare function setBy(obj: any, _path: any, updater: Updater<any>): any; | ||
| type RequiredByKey<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; | ||
| type ComputeRange<N extends number, Result extends Array<unknown> = []> = Result['length'] extends N ? Result : ComputeRange<N, [...Result, Result['length']]>; | ||
| type Index40 = ComputeRange<40>[number]; | ||
| type IsTuple<T> = T extends readonly any[] & { | ||
| length: infer Length; | ||
| } ? Length extends Index40 ? T : never : never; | ||
| type AllowedIndexes<Tuple extends ReadonlyArray<any>, Keys extends number = never> = Tuple extends readonly [] ? Keys : Tuple extends readonly [infer _, ...infer Tail] ? AllowedIndexes<Tail, Keys | Tail['length']> : Keys; | ||
| type DeepKeys<T> = unknown extends T ? keyof T : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>> : T extends any[] ? DeepKeys<T[number]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T> : never; | ||
| type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}` : never; | ||
| type DeepValue<T, TProp> = T extends Record<string | number, any> ? TProp extends `${infer TBranch}.${infer TDeepProp}` ? DeepValue<T[TBranch], TDeepProp> : T[TProp & string] : never; | ||
| type Narrowable = string | number | bigint | boolean; | ||
| type NarrowRaw<A> = (A extends [] ? [] : never) | (A extends Narrowable ? A : never) | { | ||
| [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>; | ||
| }; | ||
| type Narrow<A> = Try<A, [], NarrowRaw<A>>; | ||
| type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch; | ||
| type Pretty<T> = { | ||
| [K in keyof T]: T[K]; | ||
| } & {}; | ||
| export { DeepKeys, DeepValue, Narrow, Pretty, RequiredByKey, Updater, UpdaterFn, functionalUpdate, getBy, setBy }; |
| import "./chunk-4QZDOMDG.js"; | ||
| // src/utils.ts | ||
| function functionalUpdate(updater, input) { | ||
| return typeof updater === "function" ? updater(input) : updater; | ||
| } | ||
| function getBy(obj, path) { | ||
| const pathArray = makePathArray(path); | ||
| const pathObj = pathArray; | ||
| return pathObj.reduce((current, pathPart) => { | ||
| if (typeof current !== "undefined") { | ||
| return current[pathPart]; | ||
| } | ||
| return void 0; | ||
| }, obj); | ||
| } | ||
| function setBy(obj, _path, updater) { | ||
| const path = makePathArray(_path); | ||
| function doSet(parent) { | ||
| if (!path.length) { | ||
| return functionalUpdate(updater, parent); | ||
| } | ||
| const key = path.shift(); | ||
| if (typeof key === "string") { | ||
| if (typeof parent === "object") { | ||
| return { | ||
| ...parent, | ||
| [key]: doSet(parent[key]) | ||
| }; | ||
| } | ||
| return { | ||
| [key]: doSet() | ||
| }; | ||
| } | ||
| if (typeof key === "number") { | ||
| if (Array.isArray(parent)) { | ||
| const prefix = parent.slice(0, key); | ||
| return [ | ||
| ...prefix.length ? prefix : new Array(key), | ||
| doSet(parent[key]), | ||
| ...parent.slice(key + 1) | ||
| ]; | ||
| } | ||
| return [...new Array(key), doSet()]; | ||
| } | ||
| throw new Error("Uh oh!"); | ||
| } | ||
| return doSet(obj); | ||
| } | ||
| var reFindNumbers0 = /^(\d*)$/gm; | ||
| var reFindNumbers1 = /\.(\d*)\./gm; | ||
| var reFindNumbers2 = /^(\d*)\./gm; | ||
| var reFindNumbers3 = /\.(\d*$)/gm; | ||
| var reFindMultiplePeriods = /\.{2,}/gm; | ||
| var intPrefix = "__int__"; | ||
| var intReplace = `${intPrefix}$1`; | ||
| function makePathArray(str) { | ||
| if (typeof str !== "string") { | ||
| throw new Error("Path must be a string."); | ||
| } | ||
| return str.replace("[", ".").replace("]", "").replace(reFindNumbers0, intReplace).replace(reFindNumbers1, `.${intReplace}.`).replace(reFindNumbers2, `${intReplace}.`).replace(reFindNumbers3, `.${intReplace}`).replace(reFindMultiplePeriods, ".").split(".").map((d) => { | ||
| if (d.indexOf(intPrefix) === 0) { | ||
| return parseInt(d.substring(intPrefix.length), 10); | ||
| } | ||
| return d; | ||
| }); | ||
| } | ||
| export { | ||
| functionalUpdate, | ||
| getBy, | ||
| setBy | ||
| }; | ||
| //# sourceMappingURL=utils.js.map |
| {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T> = unknown extends T\n ? keyof T\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>\n : T extends any[]\n ? DeepKeys<T[number]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T>\n : never\n\ntype DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";;;AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/FieldApi.ts | ||
| var FieldApi_exports = {}; | ||
| __export(FieldApi_exports, { | ||
| FieldApi: () => FieldApi | ||
| }); | ||
| module.exports = __toCommonJS(FieldApi_exports); | ||
| var import_store = require("@tanstack/store"); | ||
| var uid = 0; | ||
| var FieldApi = class _FieldApi { | ||
| constructor(opts) { | ||
| this.options = {}; | ||
| this.mount = () => { | ||
| const info = this.getInfo(); | ||
| info.instances[this.uid] = this; | ||
| const unsubscribe = this.form.store.subscribe(() => { | ||
| this.store.batch(() => { | ||
| const nextValue = this.getValue(); | ||
| const nextMeta = this.getMeta(); | ||
| if (nextValue !== this.state.value) { | ||
| this.store.setState((prev) => ({ ...prev, value: nextValue })); | ||
| } | ||
| if (nextMeta !== this.state.meta) { | ||
| this.store.setState((prev) => ({ ...prev, meta: nextMeta })); | ||
| } | ||
| }); | ||
| }); | ||
| this.update(this.options); | ||
| this.options.onMount?.(this); | ||
| return () => { | ||
| unsubscribe(); | ||
| delete info.instances[this.uid]; | ||
| if (!Object.keys(info.instances).length) { | ||
| delete this.form.fieldInfo[this.name]; | ||
| } | ||
| }; | ||
| }; | ||
| this.update = (opts) => { | ||
| if (this.state.value === void 0) { | ||
| const formDefault = opts.form.options.defaultValues?.[opts.name]; | ||
| if (opts.defaultValue !== void 0) { | ||
| this.setValue(opts.defaultValue); | ||
| } else if (formDefault !== void 0) { | ||
| this.setValue(formDefault); | ||
| } | ||
| } | ||
| if (this._getMeta() === void 0) { | ||
| this.setMeta(this.state.meta); | ||
| } | ||
| this.options = opts; | ||
| }; | ||
| this.getValue = () => { | ||
| return this.form.getFieldValue(this.name); | ||
| }; | ||
| this.setValue = (updater, options) => { | ||
| this.form.setFieldValue(this.name, updater, options); | ||
| this.validate("change", this.state.value); | ||
| }; | ||
| this._getMeta = () => this.form.getFieldMeta(this.name); | ||
| this.getMeta = () => this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...this.options.defaultMeta | ||
| }; | ||
| this.setMeta = (updater) => this.form.setFieldMeta(this.name, updater); | ||
| this.getInfo = () => this.form.getFieldInfo(this.name); | ||
| this.pushValue = (value) => this.form.pushFieldValue(this.name, value); | ||
| this.insertValue = (index, value) => this.form.insertFieldValue(this.name, index, value); | ||
| this.removeValue = (index) => this.form.removeFieldValue(this.name, index); | ||
| this.swapValues = (aIndex, bIndex) => this.form.swapFieldValues(this.name, aIndex, bIndex); | ||
| this.getSubField = (name) => new _FieldApi({ | ||
| name: `${this.name}.${name}`, | ||
| form: this.form | ||
| }); | ||
| this.validateSync = (value = this.state.value, cause) => { | ||
| const { onChange, onBlur } = this.options; | ||
| const validate = cause === "submit" ? void 0 : cause === "change" ? onChange : onBlur; | ||
| if (!validate) | ||
| return; | ||
| const validationCount = (this.getInfo().validationCount || 0) + 1; | ||
| this.getInfo().validationCount = validationCount; | ||
| const error = normalizeError(validate(value, this)); | ||
| if (this.state.meta.error !== error) { | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| error | ||
| })); | ||
| } | ||
| if (this.state.meta.error) { | ||
| this.cancelValidateAsync(); | ||
| } | ||
| }; | ||
| this.#leaseValidateAsync = () => { | ||
| const count = (this.getInfo().validationAsyncCount || 0) + 1; | ||
| this.getInfo().validationAsyncCount = count; | ||
| return count; | ||
| }; | ||
| this.cancelValidateAsync = () => { | ||
| this.#leaseValidateAsync(); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false | ||
| })); | ||
| }; | ||
| this.validateAsync = async (value = this.state.value, cause) => { | ||
| const { | ||
| onChangeAsync, | ||
| onBlurAsync, | ||
| onSubmitAsync, | ||
| asyncDebounceMs, | ||
| onBlurAsyncDebounceMs, | ||
| onChangeAsyncDebounceMs | ||
| } = this.options; | ||
| const validate = cause === "change" ? onChangeAsync : cause === "submit" ? onSubmitAsync : onBlurAsync; | ||
| if (!validate) | ||
| return; | ||
| const debounceMs = cause === "submit" ? 0 : (cause === "change" ? onChangeAsyncDebounceMs : onBlurAsyncDebounceMs) ?? asyncDebounceMs ?? 0; | ||
| if (this.state.meta.isValidating !== true) | ||
| this.setMeta((prev) => ({ ...prev, isValidating: true })); | ||
| const validationAsyncCount = this.#leaseValidateAsync(); | ||
| const checkLatest = () => validationAsyncCount === this.getInfo().validationAsyncCount; | ||
| if (!this.getInfo().validationPromise) { | ||
| this.getInfo().validationPromise = new Promise((resolve, reject) => { | ||
| this.getInfo().validationResolve = resolve; | ||
| this.getInfo().validationReject = reject; | ||
| }); | ||
| } | ||
| if (debounceMs > 0) { | ||
| await new Promise((r) => setTimeout(r, debounceMs)); | ||
| } | ||
| if (checkLatest()) { | ||
| try { | ||
| const rawError = await validate(value, this); | ||
| if (checkLatest()) { | ||
| const error = normalizeError(rawError); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false, | ||
| error | ||
| })); | ||
| this.getInfo().validationResolve?.(error); | ||
| } | ||
| } catch (error) { | ||
| if (checkLatest()) { | ||
| this.getInfo().validationReject?.(error); | ||
| throw error; | ||
| } | ||
| } finally { | ||
| if (checkLatest()) { | ||
| this.setMeta((prev) => ({ ...prev, isValidating: false })); | ||
| delete this.getInfo().validationPromise; | ||
| } | ||
| } | ||
| } | ||
| return this.getInfo().validationPromise; | ||
| }; | ||
| this.validate = (cause, value) => { | ||
| if (!this.state.meta.isTouched) | ||
| return; | ||
| this.validateSync(value, cause); | ||
| if (this.state.meta.error) { | ||
| if (!this.options.asyncAlways) { | ||
| return this.state.meta.error; | ||
| } | ||
| } | ||
| return this.validateAsync(value, cause); | ||
| }; | ||
| this.handleChange = (updater) => { | ||
| this.setValue(updater, { touch: true }); | ||
| }; | ||
| this.handleBlur = () => { | ||
| const prevTouched = this.state.meta.isTouched; | ||
| if (!prevTouched) { | ||
| this.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| this.validate("change"); | ||
| } | ||
| this.validate("blur"); | ||
| }; | ||
| this.form = opts.form; | ||
| this.uid = uid++; | ||
| this.name = opts.name; | ||
| this.store = new import_store.Store( | ||
| { | ||
| value: this.getValue(), | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| meta: this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...opts.defaultMeta | ||
| } | ||
| }, | ||
| { | ||
| onUpdate: () => { | ||
| const state = this.store.state; | ||
| state.meta.touchedError = state.meta.isTouched ? state.meta.error : void 0; | ||
| this.prevState = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.prevState = this.state; | ||
| this.options = opts; | ||
| } | ||
| #leaseValidateAsync; | ||
| }; | ||
| function normalizeError(rawError) { | ||
| if (rawError) { | ||
| if (typeof rawError !== "string") { | ||
| return "Invalid Form Values"; | ||
| } | ||
| return rawError; | ||
| } | ||
| return void 0; | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| FieldApi | ||
| }); | ||
| //# sourceMappingURL=FieldApi.cjs.map |
| {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import type { DeepKeys, DeepValue, Updater } from './utils'\nimport type { FormApi, ValidationError } from './FormApi'\nimport { Store } from '@tanstack/store'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit'\n\ntype ValidateFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError\n\ntype ValidateAsyncFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError | Promise<ValidationError>\n\nexport interface FieldOptions<\n _TData,\n TFormData,\n /**\n * This allows us to restrict the name to only be a valid field name while\n * also assigning it to a generic\n */\n TName = unknown extends TFormData ? string : DeepKeys<TFormData>,\n /**\n * If TData is unknown, we can use the TName generic to determine the type\n */\n TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n onMount?: (formApi: FieldApi<TData, TFormData>) => void\n onChange?: ValidateFn<TData, TFormData>\n onChangeAsync?: ValidateAsyncFn<TData, TFormData>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateFn<TData, TFormData>\n onBlurAsync?: ValidateAsyncFn<TData, TFormData>\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: ValidateAsyncFn<TData, TFormData>\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport type FieldApiOptions<TData, TFormData> = FieldOptions<\n TData,\n TFormData\n> & {\n form: FormApi<TFormData>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedError?: ValidationError\n error?: ValidationError\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\n/**\n * TData may not be known at the time of FieldApi construction, so we need to\n * use a conditional type to determine if TData is known or not.\n *\n * If TData is not known, we use the TFormData type to determine the type of\n * the field value based on the field name.\n */\ntype GetTData<Name, TData, TFormData> = unknown extends TData\n ? DeepValue<TFormData, Name>\n : TData\n\nexport class FieldApi<TData, TFormData> {\n uid: number\n form: FormApi<TFormData>\n name!: DeepKeys<TFormData>\n /**\n * This is a hack that allows us to use `GetTData` without calling it everywhere\n *\n * Unfortunately this hack appears to be needed alongside the `TName` hack\n * further up in this file. This properly types all of the internal methods,\n * while the `TName` hack types the options properly\n */\n _tdata!: GetTData<typeof this.name, TData, TFormData>\n store!: Store<FieldState<typeof this._tdata>>\n state!: FieldState<typeof this._tdata>\n prevState!: FieldState<typeof this._tdata>\n options: FieldOptions<typeof this._tdata, TFormData> = {} as any\n\n constructor(opts: FieldApiOptions<TData, TFormData>) {\n this.form = opts.form\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as any\n\n this.store = new Store<FieldState<typeof this._tdata>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.touchedError = state.meta.isTouched\n ? state.meta.error\n : undefined\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (opts: FieldApiOptions<typeof this._tdata, TFormData>) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TFormData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): typeof this._tdata => {\n return this.form.getFieldValue(this.name)\n }\n\n setValue = (\n updater: Updater<typeof this._tdata>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <TName extends DeepKeys<typeof this._tdata>>(name: TName) =>\n new FieldApi<DeepValue<typeof this._tdata, TName>, TFormData>({\n name: `${this.name}.${name}` as never,\n form: this.form,\n })\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n const error = normalizeError(validate(value as never, this as never))\n\n if (this.state.meta.error !== error) {\n this.setMeta((prev) => ({\n ...prev,\n error,\n }))\n }\n\n // If a sync error is encountered, cancel any async validation\n if (this.state.meta.error) {\n this.cancelValidateAsync()\n }\n }\n\n #leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.#leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n\n if (!validate) return\n\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true)\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.#leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n try {\n const rawError = await validate(value as never, this as never)\n\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n error,\n }))\n this.getInfo().validationResolve?.(error)\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.(error)\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise\n }\n\n validate = (\n cause: ValidationCause,\n value?: typeof this._tdata,\n ): ValidationError | Promise<ValidationError> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is an error, return it, do not attempt async validation\n if (this.state.meta.error) {\n if (!this.options.asyncAlways) {\n return this.state.meta.error\n }\n }\n\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<typeof this._tdata>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n"],"mappings":"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","names":[]} |
| export { FieldApi, FieldApiOptions, FieldMeta, FieldOptions, FieldState, ValidationCause } from './index.cjs'; | ||
| import './utils.cjs'; | ||
| import '@tanstack/store'; |
| export { FieldApi, FieldApiOptions, FieldMeta, FieldOptions, FieldState, ValidationCause } from './index.js'; | ||
| import './utils.js'; | ||
| import '@tanstack/store'; |
| // src/FieldApi.ts | ||
| import { Store } from "@tanstack/store"; | ||
| var uid = 0; | ||
| var FieldApi = class _FieldApi { | ||
| constructor(opts) { | ||
| this.options = {}; | ||
| this.mount = () => { | ||
| const info = this.getInfo(); | ||
| info.instances[this.uid] = this; | ||
| const unsubscribe = this.form.store.subscribe(() => { | ||
| this.store.batch(() => { | ||
| const nextValue = this.getValue(); | ||
| const nextMeta = this.getMeta(); | ||
| if (nextValue !== this.state.value) { | ||
| this.store.setState((prev) => ({ ...prev, value: nextValue })); | ||
| } | ||
| if (nextMeta !== this.state.meta) { | ||
| this.store.setState((prev) => ({ ...prev, meta: nextMeta })); | ||
| } | ||
| }); | ||
| }); | ||
| this.update(this.options); | ||
| this.options.onMount?.(this); | ||
| return () => { | ||
| unsubscribe(); | ||
| delete info.instances[this.uid]; | ||
| if (!Object.keys(info.instances).length) { | ||
| delete this.form.fieldInfo[this.name]; | ||
| } | ||
| }; | ||
| }; | ||
| this.update = (opts) => { | ||
| if (this.state.value === void 0) { | ||
| const formDefault = opts.form.options.defaultValues?.[opts.name]; | ||
| if (opts.defaultValue !== void 0) { | ||
| this.setValue(opts.defaultValue); | ||
| } else if (formDefault !== void 0) { | ||
| this.setValue(formDefault); | ||
| } | ||
| } | ||
| if (this._getMeta() === void 0) { | ||
| this.setMeta(this.state.meta); | ||
| } | ||
| this.options = opts; | ||
| }; | ||
| this.getValue = () => { | ||
| return this.form.getFieldValue(this.name); | ||
| }; | ||
| this.setValue = (updater, options) => { | ||
| this.form.setFieldValue(this.name, updater, options); | ||
| this.validate("change", this.state.value); | ||
| }; | ||
| this._getMeta = () => this.form.getFieldMeta(this.name); | ||
| this.getMeta = () => this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...this.options.defaultMeta | ||
| }; | ||
| this.setMeta = (updater) => this.form.setFieldMeta(this.name, updater); | ||
| this.getInfo = () => this.form.getFieldInfo(this.name); | ||
| this.pushValue = (value) => this.form.pushFieldValue(this.name, value); | ||
| this.insertValue = (index, value) => this.form.insertFieldValue(this.name, index, value); | ||
| this.removeValue = (index) => this.form.removeFieldValue(this.name, index); | ||
| this.swapValues = (aIndex, bIndex) => this.form.swapFieldValues(this.name, aIndex, bIndex); | ||
| this.getSubField = (name) => new _FieldApi({ | ||
| name: `${this.name}.${name}`, | ||
| form: this.form | ||
| }); | ||
| this.validateSync = (value = this.state.value, cause) => { | ||
| const { onChange, onBlur } = this.options; | ||
| const validate = cause === "submit" ? void 0 : cause === "change" ? onChange : onBlur; | ||
| if (!validate) | ||
| return; | ||
| const validationCount = (this.getInfo().validationCount || 0) + 1; | ||
| this.getInfo().validationCount = validationCount; | ||
| const error = normalizeError(validate(value, this)); | ||
| if (this.state.meta.error !== error) { | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| error | ||
| })); | ||
| } | ||
| if (this.state.meta.error) { | ||
| this.cancelValidateAsync(); | ||
| } | ||
| }; | ||
| this.#leaseValidateAsync = () => { | ||
| const count = (this.getInfo().validationAsyncCount || 0) + 1; | ||
| this.getInfo().validationAsyncCount = count; | ||
| return count; | ||
| }; | ||
| this.cancelValidateAsync = () => { | ||
| this.#leaseValidateAsync(); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false | ||
| })); | ||
| }; | ||
| this.validateAsync = async (value = this.state.value, cause) => { | ||
| const { | ||
| onChangeAsync, | ||
| onBlurAsync, | ||
| onSubmitAsync, | ||
| asyncDebounceMs, | ||
| onBlurAsyncDebounceMs, | ||
| onChangeAsyncDebounceMs | ||
| } = this.options; | ||
| const validate = cause === "change" ? onChangeAsync : cause === "submit" ? onSubmitAsync : onBlurAsync; | ||
| if (!validate) | ||
| return; | ||
| const debounceMs = cause === "submit" ? 0 : (cause === "change" ? onChangeAsyncDebounceMs : onBlurAsyncDebounceMs) ?? asyncDebounceMs ?? 0; | ||
| if (this.state.meta.isValidating !== true) | ||
| this.setMeta((prev) => ({ ...prev, isValidating: true })); | ||
| const validationAsyncCount = this.#leaseValidateAsync(); | ||
| const checkLatest = () => validationAsyncCount === this.getInfo().validationAsyncCount; | ||
| if (!this.getInfo().validationPromise) { | ||
| this.getInfo().validationPromise = new Promise((resolve, reject) => { | ||
| this.getInfo().validationResolve = resolve; | ||
| this.getInfo().validationReject = reject; | ||
| }); | ||
| } | ||
| if (debounceMs > 0) { | ||
| await new Promise((r) => setTimeout(r, debounceMs)); | ||
| } | ||
| if (checkLatest()) { | ||
| try { | ||
| const rawError = await validate(value, this); | ||
| if (checkLatest()) { | ||
| const error = normalizeError(rawError); | ||
| this.setMeta((prev) => ({ | ||
| ...prev, | ||
| isValidating: false, | ||
| error | ||
| })); | ||
| this.getInfo().validationResolve?.(error); | ||
| } | ||
| } catch (error) { | ||
| if (checkLatest()) { | ||
| this.getInfo().validationReject?.(error); | ||
| throw error; | ||
| } | ||
| } finally { | ||
| if (checkLatest()) { | ||
| this.setMeta((prev) => ({ ...prev, isValidating: false })); | ||
| delete this.getInfo().validationPromise; | ||
| } | ||
| } | ||
| } | ||
| return this.getInfo().validationPromise; | ||
| }; | ||
| this.validate = (cause, value) => { | ||
| if (!this.state.meta.isTouched) | ||
| return; | ||
| this.validateSync(value, cause); | ||
| if (this.state.meta.error) { | ||
| if (!this.options.asyncAlways) { | ||
| return this.state.meta.error; | ||
| } | ||
| } | ||
| return this.validateAsync(value, cause); | ||
| }; | ||
| this.handleChange = (updater) => { | ||
| this.setValue(updater, { touch: true }); | ||
| }; | ||
| this.handleBlur = () => { | ||
| const prevTouched = this.state.meta.isTouched; | ||
| if (!prevTouched) { | ||
| this.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| this.validate("change"); | ||
| } | ||
| this.validate("blur"); | ||
| }; | ||
| this.form = opts.form; | ||
| this.uid = uid++; | ||
| this.name = opts.name; | ||
| this.store = new Store( | ||
| { | ||
| value: this.getValue(), | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| meta: this._getMeta() ?? { | ||
| isValidating: false, | ||
| isTouched: false, | ||
| ...opts.defaultMeta | ||
| } | ||
| }, | ||
| { | ||
| onUpdate: () => { | ||
| const state = this.store.state; | ||
| state.meta.touchedError = state.meta.isTouched ? state.meta.error : void 0; | ||
| this.prevState = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.prevState = this.state; | ||
| this.options = opts; | ||
| } | ||
| #leaseValidateAsync; | ||
| }; | ||
| function normalizeError(rawError) { | ||
| if (rawError) { | ||
| if (typeof rawError !== "string") { | ||
| return "Invalid Form Values"; | ||
| } | ||
| return rawError; | ||
| } | ||
| return void 0; | ||
| } | ||
| export { | ||
| FieldApi | ||
| }; | ||
| //# sourceMappingURL=FieldApi.js.map |
| {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import type { DeepKeys, DeepValue, Updater } from './utils'\nimport type { FormApi, ValidationError } from './FormApi'\nimport { Store } from '@tanstack/store'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit'\n\ntype ValidateFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError\n\ntype ValidateAsyncFn<TData, TFormData> = (\n value: TData,\n fieldApi: FieldApi<TData, TFormData>,\n) => ValidationError | Promise<ValidationError>\n\nexport interface FieldOptions<\n _TData,\n TFormData,\n /**\n * This allows us to restrict the name to only be a valid field name while\n * also assigning it to a generic\n */\n TName = unknown extends TFormData ? string : DeepKeys<TFormData>,\n /**\n * If TData is unknown, we can use the TName generic to determine the type\n */\n TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n onMount?: (formApi: FieldApi<TData, TFormData>) => void\n onChange?: ValidateFn<TData, TFormData>\n onChangeAsync?: ValidateAsyncFn<TData, TFormData>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateFn<TData, TFormData>\n onBlurAsync?: ValidateAsyncFn<TData, TFormData>\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: ValidateAsyncFn<TData, TFormData>\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport type FieldApiOptions<TData, TFormData> = FieldOptions<\n TData,\n TFormData\n> & {\n form: FormApi<TFormData>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedError?: ValidationError\n error?: ValidationError\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\n/**\n * TData may not be known at the time of FieldApi construction, so we need to\n * use a conditional type to determine if TData is known or not.\n *\n * If TData is not known, we use the TFormData type to determine the type of\n * the field value based on the field name.\n */\ntype GetTData<Name, TData, TFormData> = unknown extends TData\n ? DeepValue<TFormData, Name>\n : TData\n\nexport class FieldApi<TData, TFormData> {\n uid: number\n form: FormApi<TFormData>\n name!: DeepKeys<TFormData>\n /**\n * This is a hack that allows us to use `GetTData` without calling it everywhere\n *\n * Unfortunately this hack appears to be needed alongside the `TName` hack\n * further up in this file. This properly types all of the internal methods,\n * while the `TName` hack types the options properly\n */\n _tdata!: GetTData<typeof this.name, TData, TFormData>\n store!: Store<FieldState<typeof this._tdata>>\n state!: FieldState<typeof this._tdata>\n prevState!: FieldState<typeof this._tdata>\n options: FieldOptions<typeof this._tdata, TFormData> = {} as any\n\n constructor(opts: FieldApiOptions<TData, TFormData>) {\n this.form = opts.form\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as any\n\n this.store = new Store<FieldState<typeof this._tdata>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.touchedError = state.meta.isTouched\n ? state.meta.error\n : undefined\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (opts: FieldApiOptions<typeof this._tdata, TFormData>) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TFormData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): typeof this._tdata => {\n return this.form.getFieldValue(this.name)\n }\n\n setValue = (\n updater: Updater<typeof this._tdata>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: typeof this._tdata extends any[]\n ? (typeof this._tdata)[number]\n : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <TName extends DeepKeys<typeof this._tdata>>(name: TName) =>\n new FieldApi<DeepValue<typeof this._tdata, TName>, TFormData>({\n name: `${this.name}.${name}` as never,\n form: this.form,\n })\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n const error = normalizeError(validate(value as never, this as never))\n\n if (this.state.meta.error !== error) {\n this.setMeta((prev) => ({\n ...prev,\n error,\n }))\n }\n\n // If a sync error is encountered, cancel any async validation\n if (this.state.meta.error) {\n this.cancelValidateAsync()\n }\n }\n\n #leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.#leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n\n if (!validate) return\n\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true)\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.#leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n try {\n const rawError = await validate(value as never, this as never)\n\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n error,\n }))\n this.getInfo().validationResolve?.(error)\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.(error)\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise\n }\n\n validate = (\n cause: ValidationCause,\n value?: typeof this._tdata,\n ): ValidationError | Promise<ValidationError> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is an error, return it, do not attempt async validation\n if (this.state.meta.error) {\n if (!this.options.asyncAlways) {\n return this.state.meta.error\n }\n }\n\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<typeof this._tdata>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n"],"mappings":"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","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/FormApi.ts | ||
| var FormApi_exports = {}; | ||
| __export(FormApi_exports, { | ||
| FormApi: () => FormApi | ||
| }); | ||
| module.exports = __toCommonJS(FormApi_exports); | ||
| var import_store = require("@tanstack/store"); | ||
| var import_utils = require("./utils.cjs"); | ||
| function getDefaultFormState(defaultState) { | ||
| return { | ||
| values: defaultState.values ?? {}, | ||
| fieldMeta: defaultState.fieldMeta ?? {}, | ||
| canSubmit: defaultState.canSubmit ?? true, | ||
| isFieldsValid: defaultState.isFieldsValid ?? false, | ||
| isFieldsValidating: defaultState.isFieldsValidating ?? false, | ||
| isFormValid: defaultState.isFormValid ?? false, | ||
| isFormValidating: defaultState.isFormValidating ?? false, | ||
| isSubmitted: defaultState.isSubmitted ?? false, | ||
| isSubmitting: defaultState.isSubmitting ?? false, | ||
| isTouched: defaultState.isTouched ?? false, | ||
| isValid: defaultState.isValid ?? false, | ||
| isValidating: defaultState.isValidating ?? false, | ||
| submissionAttempts: defaultState.submissionAttempts ?? 0, | ||
| formValidationCount: defaultState.formValidationCount ?? 0 | ||
| }; | ||
| } | ||
| var FormApi = class { | ||
| constructor(opts) { | ||
| // // This carries the context for nested fields | ||
| this.options = {}; | ||
| this.fieldInfo = {}; | ||
| this.validationMeta = {}; | ||
| this.update = (options) => { | ||
| if (!options) | ||
| return; | ||
| this.store.batch(() => { | ||
| const shouldUpdateValues = options.defaultValues && options.defaultValues !== this.options.defaultValues && !this.state.isTouched; | ||
| const shouldUpdateState = options.defaultState !== this.options.defaultState && !this.state.isTouched; | ||
| this.store.setState( | ||
| () => getDefaultFormState( | ||
| Object.assign( | ||
| {}, | ||
| this.state, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateState ? options.defaultState : {}, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateValues ? { | ||
| values: options.defaultValues | ||
| } : {} | ||
| ) | ||
| ) | ||
| ); | ||
| }); | ||
| this.options = options; | ||
| }; | ||
| this.reset = () => this.store.setState( | ||
| () => getDefaultFormState({ | ||
| ...this.options.defaultState, | ||
| values: this.options.defaultValues ?? this.options.defaultState?.values | ||
| }) | ||
| ); | ||
| this.validateAllFields = async (cause) => { | ||
| const fieldValidationPromises = []; | ||
| this.store.batch(() => { | ||
| void Object.values(this.fieldInfo).forEach( | ||
| (field) => { | ||
| Object.values(field.instances).forEach((instance) => { | ||
| if (!instance.state.meta.isTouched) { | ||
| instance.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| fieldValidationPromises.push( | ||
| Promise.resolve().then(() => instance.validate(cause)) | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| ); | ||
| }); | ||
| return Promise.all(fieldValidationPromises); | ||
| }; | ||
| this.handleSubmit = async () => { | ||
| this.store.setState((old) => ({ | ||
| ...old, | ||
| // Submission attempts mark the form as not submitted | ||
| isSubmitted: false, | ||
| // Count submission attempts | ||
| submissionAttempts: old.submissionAttempts + 1 | ||
| })); | ||
| if (!this.state.canSubmit) | ||
| return; | ||
| this.store.setState((d) => ({ ...d, isSubmitting: true })); | ||
| const done = () => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitting: false })); | ||
| }; | ||
| await this.validateAllFields("submit"); | ||
| if (!this.state.isFieldsValid) { | ||
| done(); | ||
| this.options.onSubmitInvalid?.(this.state.values, this); | ||
| return; | ||
| } | ||
| if (!this.state.isValid) { | ||
| done(); | ||
| this.options.onSubmitInvalid?.(this.state.values, this); | ||
| return; | ||
| } | ||
| try { | ||
| await this.options.onSubmit?.(this.state.values, this); | ||
| this.store.batch(() => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitted: true })); | ||
| done(); | ||
| }); | ||
| } catch (err) { | ||
| done(); | ||
| throw err; | ||
| } | ||
| }; | ||
| this.getFieldValue = (field) => (0, import_utils.getBy)(this.state.values, field); | ||
| this.getFieldMeta = (field) => { | ||
| return this.state.fieldMeta[field]; | ||
| }; | ||
| this.getFieldInfo = (field) => { | ||
| return this.fieldInfo[field] ||= { | ||
| instances: {} | ||
| }; | ||
| }; | ||
| this.setFieldMeta = (field, updater) => { | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| fieldMeta: { | ||
| ...prev.fieldMeta, | ||
| [field]: (0, import_utils.functionalUpdate)(updater, prev.fieldMeta[field]) | ||
| } | ||
| }; | ||
| }); | ||
| }; | ||
| this.setFieldValue = (field, updater, opts) => { | ||
| const touch = opts?.touch; | ||
| this.store.batch(() => { | ||
| if (touch) { | ||
| this.setFieldMeta(field, (prev) => ({ | ||
| ...prev, | ||
| isTouched: true | ||
| })); | ||
| } | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| values: (0, import_utils.setBy)(prev.values, field, updater) | ||
| }; | ||
| }); | ||
| }); | ||
| }; | ||
| this.pushFieldValue = (field, value, opts) => { | ||
| return this.setFieldValue( | ||
| field, | ||
| (prev) => [...Array.isArray(prev) ? prev : [], value], | ||
| opts | ||
| ); | ||
| }; | ||
| this.insertFieldValue = (field, index, value, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.map( | ||
| (d, i) => i === index ? value : d | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.removeFieldValue = (field, index, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.filter( | ||
| (_d, i) => i !== index | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.swapFieldValues = (field, index1, index2) => { | ||
| this.setFieldValue(field, (prev) => { | ||
| const prev1 = prev[index1]; | ||
| const prev2 = prev[index2]; | ||
| return (0, import_utils.setBy)((0, import_utils.setBy)(prev, `${index1}`, prev2), `${index2}`, prev1); | ||
| }); | ||
| }; | ||
| this.store = new import_store.Store( | ||
| getDefaultFormState({ | ||
| ...opts?.defaultState, | ||
| values: opts?.defaultValues ?? opts?.defaultState?.values, | ||
| isFormValid: true | ||
| }), | ||
| { | ||
| onUpdate: () => { | ||
| let { state } = this.store; | ||
| const fieldMetaValues = Object.values(state.fieldMeta); | ||
| const isFieldsValidating = fieldMetaValues.some( | ||
| (field) => field?.isValidating | ||
| ); | ||
| const isFieldsValid = !fieldMetaValues.some((field) => field?.error); | ||
| const isTouched = fieldMetaValues.some((field) => field?.isTouched); | ||
| const isValidating = isFieldsValidating || state.isFormValidating; | ||
| const isFormValid = !state.formError; | ||
| const isValid = isFieldsValid && isFormValid; | ||
| const canSubmit = state.submissionAttempts === 0 && !isTouched || !isValidating && !state.isSubmitting && isValid; | ||
| state = { | ||
| ...state, | ||
| isFieldsValidating, | ||
| isFieldsValid, | ||
| isFormValid, | ||
| isValid, | ||
| canSubmit, | ||
| isTouched | ||
| }; | ||
| this.store.state = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.update(opts || {}); | ||
| } | ||
| }; | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| FormApi | ||
| }); | ||
| //# sourceMappingURL=FormApi.cjs.map |
| {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\n//\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\n\nexport type FormOptions<TData> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onMountAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onMountAsyncDebounceMs?: number\n onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onChangeAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onChangeAsyncDebounceMs?: number\n onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onBlurAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>\n onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void\n}\n\nexport type FieldInfo<TFormData> = {\n instances: Record<string, FieldApi<any, TFormData>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError>\n validationResolve?: (error: ValidationError) => void\n validationReject?: (error: unknown) => void\n}\n\nexport type ValidationError = undefined | false | null | string\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>> = {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...opts?.defaultState,\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some((field) => field?.error)\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...this.options.defaultState,\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError>[] = [] as any\n\n this.store.batch(() => {\n void (Object.values(this.fieldInfo) as FieldInfo<any>[]).forEach(\n (field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n },\n )\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(field: TField) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]} |
| import '@tanstack/store'; | ||
| import './utils.cjs'; | ||
| export { FieldInfo, FormApi, FormOptions, FormState, ValidationError, ValidationMeta } from './index.cjs'; |
| import '@tanstack/store'; | ||
| import './utils.js'; | ||
| export { FieldInfo, FormApi, FormOptions, FormState, ValidationError, ValidationMeta } from './index.js'; |
| // src/FormApi.ts | ||
| import { Store } from "@tanstack/store"; | ||
| import { functionalUpdate, getBy, setBy } from "./utils.js"; | ||
| function getDefaultFormState(defaultState) { | ||
| return { | ||
| values: defaultState.values ?? {}, | ||
| fieldMeta: defaultState.fieldMeta ?? {}, | ||
| canSubmit: defaultState.canSubmit ?? true, | ||
| isFieldsValid: defaultState.isFieldsValid ?? false, | ||
| isFieldsValidating: defaultState.isFieldsValidating ?? false, | ||
| isFormValid: defaultState.isFormValid ?? false, | ||
| isFormValidating: defaultState.isFormValidating ?? false, | ||
| isSubmitted: defaultState.isSubmitted ?? false, | ||
| isSubmitting: defaultState.isSubmitting ?? false, | ||
| isTouched: defaultState.isTouched ?? false, | ||
| isValid: defaultState.isValid ?? false, | ||
| isValidating: defaultState.isValidating ?? false, | ||
| submissionAttempts: defaultState.submissionAttempts ?? 0, | ||
| formValidationCount: defaultState.formValidationCount ?? 0 | ||
| }; | ||
| } | ||
| var FormApi = class { | ||
| constructor(opts) { | ||
| // // This carries the context for nested fields | ||
| this.options = {}; | ||
| this.fieldInfo = {}; | ||
| this.validationMeta = {}; | ||
| this.update = (options) => { | ||
| if (!options) | ||
| return; | ||
| this.store.batch(() => { | ||
| const shouldUpdateValues = options.defaultValues && options.defaultValues !== this.options.defaultValues && !this.state.isTouched; | ||
| const shouldUpdateState = options.defaultState !== this.options.defaultState && !this.state.isTouched; | ||
| this.store.setState( | ||
| () => getDefaultFormState( | ||
| Object.assign( | ||
| {}, | ||
| this.state, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateState ? options.defaultState : {}, | ||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| shouldUpdateValues ? { | ||
| values: options.defaultValues | ||
| } : {} | ||
| ) | ||
| ) | ||
| ); | ||
| }); | ||
| this.options = options; | ||
| }; | ||
| this.reset = () => this.store.setState( | ||
| () => getDefaultFormState({ | ||
| ...this.options.defaultState, | ||
| values: this.options.defaultValues ?? this.options.defaultState?.values | ||
| }) | ||
| ); | ||
| this.validateAllFields = async (cause) => { | ||
| const fieldValidationPromises = []; | ||
| this.store.batch(() => { | ||
| void Object.values(this.fieldInfo).forEach( | ||
| (field) => { | ||
| Object.values(field.instances).forEach((instance) => { | ||
| if (!instance.state.meta.isTouched) { | ||
| instance.setMeta((prev) => ({ ...prev, isTouched: true })); | ||
| fieldValidationPromises.push( | ||
| Promise.resolve().then(() => instance.validate(cause)) | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| ); | ||
| }); | ||
| return Promise.all(fieldValidationPromises); | ||
| }; | ||
| this.handleSubmit = async () => { | ||
| this.store.setState((old) => ({ | ||
| ...old, | ||
| // Submission attempts mark the form as not submitted | ||
| isSubmitted: false, | ||
| // Count submission attempts | ||
| submissionAttempts: old.submissionAttempts + 1 | ||
| })); | ||
| if (!this.state.canSubmit) | ||
| return; | ||
| this.store.setState((d) => ({ ...d, isSubmitting: true })); | ||
| const done = () => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitting: false })); | ||
| }; | ||
| await this.validateAllFields("submit"); | ||
| if (!this.state.isFieldsValid) { | ||
| done(); | ||
| this.options.onSubmitInvalid?.(this.state.values, this); | ||
| return; | ||
| } | ||
| if (!this.state.isValid) { | ||
| done(); | ||
| this.options.onSubmitInvalid?.(this.state.values, this); | ||
| return; | ||
| } | ||
| try { | ||
| await this.options.onSubmit?.(this.state.values, this); | ||
| this.store.batch(() => { | ||
| this.store.setState((prev) => ({ ...prev, isSubmitted: true })); | ||
| done(); | ||
| }); | ||
| } catch (err) { | ||
| done(); | ||
| throw err; | ||
| } | ||
| }; | ||
| this.getFieldValue = (field) => getBy(this.state.values, field); | ||
| this.getFieldMeta = (field) => { | ||
| return this.state.fieldMeta[field]; | ||
| }; | ||
| this.getFieldInfo = (field) => { | ||
| return this.fieldInfo[field] ||= { | ||
| instances: {} | ||
| }; | ||
| }; | ||
| this.setFieldMeta = (field, updater) => { | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| fieldMeta: { | ||
| ...prev.fieldMeta, | ||
| [field]: functionalUpdate(updater, prev.fieldMeta[field]) | ||
| } | ||
| }; | ||
| }); | ||
| }; | ||
| this.setFieldValue = (field, updater, opts) => { | ||
| const touch = opts?.touch; | ||
| this.store.batch(() => { | ||
| if (touch) { | ||
| this.setFieldMeta(field, (prev) => ({ | ||
| ...prev, | ||
| isTouched: true | ||
| })); | ||
| } | ||
| this.store.setState((prev) => { | ||
| return { | ||
| ...prev, | ||
| values: setBy(prev.values, field, updater) | ||
| }; | ||
| }); | ||
| }); | ||
| }; | ||
| this.pushFieldValue = (field, value, opts) => { | ||
| return this.setFieldValue( | ||
| field, | ||
| (prev) => [...Array.isArray(prev) ? prev : [], value], | ||
| opts | ||
| ); | ||
| }; | ||
| this.insertFieldValue = (field, index, value, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.map( | ||
| (d, i) => i === index ? value : d | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.removeFieldValue = (field, index, opts) => { | ||
| this.setFieldValue( | ||
| field, | ||
| (prev) => { | ||
| return prev.filter( | ||
| (_d, i) => i !== index | ||
| ); | ||
| }, | ||
| opts | ||
| ); | ||
| }; | ||
| this.swapFieldValues = (field, index1, index2) => { | ||
| this.setFieldValue(field, (prev) => { | ||
| const prev1 = prev[index1]; | ||
| const prev2 = prev[index2]; | ||
| return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1); | ||
| }); | ||
| }; | ||
| this.store = new Store( | ||
| getDefaultFormState({ | ||
| ...opts?.defaultState, | ||
| values: opts?.defaultValues ?? opts?.defaultState?.values, | ||
| isFormValid: true | ||
| }), | ||
| { | ||
| onUpdate: () => { | ||
| let { state } = this.store; | ||
| const fieldMetaValues = Object.values(state.fieldMeta); | ||
| const isFieldsValidating = fieldMetaValues.some( | ||
| (field) => field?.isValidating | ||
| ); | ||
| const isFieldsValid = !fieldMetaValues.some((field) => field?.error); | ||
| const isTouched = fieldMetaValues.some((field) => field?.isTouched); | ||
| const isValidating = isFieldsValidating || state.isFormValidating; | ||
| const isFormValid = !state.formError; | ||
| const isValid = isFieldsValid && isFormValid; | ||
| const canSubmit = state.submissionAttempts === 0 && !isTouched || !isValidating && !state.isSubmitting && isValid; | ||
| state = { | ||
| ...state, | ||
| isFieldsValidating, | ||
| isFieldsValid, | ||
| isFormValid, | ||
| isValid, | ||
| canSubmit, | ||
| isTouched | ||
| }; | ||
| this.store.state = state; | ||
| this.state = state; | ||
| } | ||
| } | ||
| ); | ||
| this.state = this.store.state; | ||
| this.update(opts || {}); | ||
| } | ||
| }; | ||
| export { | ||
| FormApi | ||
| }; | ||
| //# sourceMappingURL=FormApi.js.map |
| {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\n//\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\n\nexport type FormOptions<TData> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onMountAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onMountAsyncDebounceMs?: number\n onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onChangeAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onChangeAsyncDebounceMs?: number\n onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError\n onBlurAsync?: (\n values: TData,\n formApi: FormApi<TData>,\n ) => ValidationError | Promise<ValidationError>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>\n onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void\n}\n\nexport type FieldInfo<TFormData> = {\n instances: Record<string, FieldApi<any, TFormData>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError>\n validationResolve?: (error: ValidationError) => void\n validationReject?: (error: unknown) => void\n}\n\nexport type ValidationError = undefined | false | null | string\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>> = {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...opts?.defaultState,\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some((field) => field?.error)\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...this.options.defaultState,\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError>[] = [] as any\n\n this.store.batch(() => {\n void (Object.values(this.fieldInfo) as FieldInfo<any>[]).forEach(\n (field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n },\n )\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(field: TField) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField>[number],\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/index.ts | ||
| var src_exports = {}; | ||
| module.exports = __toCommonJS(src_exports); | ||
| __reExport(src_exports, require("./FormApi.cjs"), module.exports); | ||
| __reExport(src_exports, require("./FieldApi.cjs"), module.exports); | ||
| __reExport(src_exports, require("./utils.cjs"), module.exports); | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| ...require("./FormApi.cjs"), | ||
| ...require("./FieldApi.cjs"), | ||
| ...require("./utils.cjs") | ||
| }); | ||
| //# sourceMappingURL=index.cjs.map |
| {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from './FormApi'\nexport * from './FieldApi'\nexport * from './utils'\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,wBAAc,0BAAd;AACA,wBAAc,2BADd;AAEA,wBAAc,wBAFd;","names":[]} |
| import { Store } from '@tanstack/store'; | ||
| import { DeepKeys, DeepValue, Updater } from './utils.cjs'; | ||
| export { Narrow, Pretty, RequiredByKey, UpdaterFn, functionalUpdate, getBy, setBy } from './utils.cjs'; | ||
| type FormOptions<TData> = { | ||
| defaultValues?: TData; | ||
| defaultState?: Partial<FormState<TData>>; | ||
| asyncDebounceMs?: number; | ||
| onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onMountAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onMountAsyncDebounceMs?: number; | ||
| onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onChangeAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onBlurAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>; | ||
| onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void; | ||
| }; | ||
| type FieldInfo<TFormData> = { | ||
| instances: Record<string, FieldApi<any, TFormData>>; | ||
| } & ValidationMeta; | ||
| type ValidationMeta = { | ||
| validationCount?: number; | ||
| validationAsyncCount?: number; | ||
| validationPromise?: Promise<ValidationError>; | ||
| validationResolve?: (error: ValidationError) => void; | ||
| validationReject?: (error: unknown) => void; | ||
| }; | ||
| type ValidationError = undefined | false | null | string; | ||
| type FormState<TData> = { | ||
| values: TData; | ||
| isFormValidating: boolean; | ||
| formValidationCount: number; | ||
| isFormValid: boolean; | ||
| formError?: ValidationError; | ||
| fieldMeta: Record<DeepKeys<TData>, FieldMeta>; | ||
| isFieldsValidating: boolean; | ||
| isFieldsValid: boolean; | ||
| isSubmitting: boolean; | ||
| isTouched: boolean; | ||
| isSubmitted: boolean; | ||
| isValidating: boolean; | ||
| isValid: boolean; | ||
| canSubmit: boolean; | ||
| submissionAttempts: number; | ||
| }; | ||
| declare class FormApi<TFormData> { | ||
| options: FormOptions<TFormData>; | ||
| store: Store<FormState<TFormData>>; | ||
| state: FormState<TFormData>; | ||
| fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>>; | ||
| fieldName?: string; | ||
| validationMeta: ValidationMeta; | ||
| constructor(opts?: FormOptions<TFormData>); | ||
| update: (options?: FormOptions<TFormData>) => void; | ||
| reset: () => void; | ||
| validateAllFields: (cause: ValidationCause) => Promise<ValidationError[]>; | ||
| handleSubmit: () => Promise<void>; | ||
| getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>; | ||
| getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined; | ||
| getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[TField]; | ||
| setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void; | ||
| setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| insertFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| removeFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| swapFieldValues: <TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number) => void; | ||
| } | ||
| type ValidationCause = 'change' | 'blur' | 'submit'; | ||
| type ValidateFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError; | ||
| type ValidateAsyncFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>; | ||
| interface FieldOptions<_TData, TFormData, | ||
| /** | ||
| * This allows us to restrict the name to only be a valid field name while | ||
| * also assigning it to a generic | ||
| */ | ||
| TName = unknown extends TFormData ? string : DeepKeys<TFormData>, | ||
| /** | ||
| * If TData is unknown, we can use the TName generic to determine the type | ||
| */ | ||
| TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> { | ||
| name: TName; | ||
| index?: TData extends any[] ? number : never; | ||
| defaultValue?: TData; | ||
| asyncDebounceMs?: number; | ||
| asyncAlways?: boolean; | ||
| onMount?: (formApi: FieldApi<TData, TFormData>) => void; | ||
| onChange?: ValidateFn<TData, TFormData>; | ||
| onChangeAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: ValidateFn<TData, TFormData>; | ||
| onBlurAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmitAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| defaultMeta?: Partial<FieldMeta>; | ||
| } | ||
| type FieldApiOptions<TData, TFormData> = FieldOptions<TData, TFormData> & { | ||
| form: FormApi<TFormData>; | ||
| }; | ||
| type FieldMeta = { | ||
| isTouched: boolean; | ||
| touchedError?: ValidationError; | ||
| error?: ValidationError; | ||
| isValidating: boolean; | ||
| }; | ||
| type FieldState<TData> = { | ||
| value: TData; | ||
| meta: FieldMeta; | ||
| }; | ||
| /** | ||
| * TData may not be known at the time of FieldApi construction, so we need to | ||
| * use a conditional type to determine if TData is known or not. | ||
| * | ||
| * If TData is not known, we use the TFormData type to determine the type of | ||
| * the field value based on the field name. | ||
| */ | ||
| type GetTData<Name, TData, TFormData> = unknown extends TData ? DeepValue<TFormData, Name> : TData; | ||
| declare class FieldApi<TData, TFormData> { | ||
| #private; | ||
| uid: number; | ||
| form: FormApi<TFormData>; | ||
| name: DeepKeys<TFormData>; | ||
| /** | ||
| * This is a hack that allows us to use `GetTData` without calling it everywhere | ||
| * | ||
| * Unfortunately this hack appears to be needed alongside the `TName` hack | ||
| * further up in this file. This properly types all of the internal methods, | ||
| * while the `TName` hack types the options properly | ||
| */ | ||
| _tdata: GetTData<typeof this$1.name, TData, TFormData>; | ||
| store: Store<FieldState<typeof this$1._tdata>>; | ||
| state: FieldState<typeof this$1._tdata>; | ||
| prevState: FieldState<typeof this$1._tdata>; | ||
| options: FieldOptions<typeof this$1._tdata, TFormData>; | ||
| constructor(opts: FieldApiOptions<TData, TFormData>); | ||
| mount: () => () => void; | ||
| update: (opts: FieldApiOptions<typeof this$1._tdata, TFormData>) => void; | ||
| getValue: () => typeof this$1._tdata; | ||
| setValue: (updater: Updater<typeof this$1._tdata>, options?: { | ||
| touch?: boolean; | ||
| notify?: boolean; | ||
| }) => void; | ||
| _getMeta: () => FieldMeta | undefined; | ||
| getMeta: () => FieldMeta; | ||
| setMeta: (updater: Updater<FieldMeta>) => void; | ||
| getInfo: () => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[DeepKeys<TFormData>]; | ||
| pushValue: (value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| insertValue: (index: number, value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| removeValue: (index: number) => void; | ||
| swapValues: (aIndex: number, bIndex: number) => void; | ||
| getSubField: <TName extends DeepKeys<GetTData<DeepKeys<TFormData>, TData, TFormData>>>(name: TName) => FieldApi<DeepValue<GetTData<DeepKeys<TFormData>, TData, TFormData>, TName>, TFormData>; | ||
| validateSync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => void; | ||
| cancelValidateAsync: () => void; | ||
| validateAsync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => Promise<ValidationError>; | ||
| validate: (cause: ValidationCause, value?: typeof this$1._tdata) => ValidationError | Promise<ValidationError>; | ||
| handleChange: (updater: Updater<typeof this$1._tdata>) => void; | ||
| handleBlur: () => void; | ||
| } | ||
| export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, Updater, ValidationCause, ValidationError, ValidationMeta }; |
| import { Store } from '@tanstack/store'; | ||
| import { DeepKeys, DeepValue, Updater } from './utils.js'; | ||
| export { Narrow, Pretty, RequiredByKey, UpdaterFn, functionalUpdate, getBy, setBy } from './utils.js'; | ||
| type FormOptions<TData> = { | ||
| defaultValues?: TData; | ||
| defaultState?: Partial<FormState<TData>>; | ||
| asyncDebounceMs?: number; | ||
| onMount?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onMountAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onMountAsyncDebounceMs?: number; | ||
| onChange?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onChangeAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: (values: TData, formApi: FormApi<TData>) => ValidationError; | ||
| onBlurAsync?: (values: TData, formApi: FormApi<TData>) => ValidationError | Promise<ValidationError>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmit?: (values: TData, formApi: FormApi<TData>) => any | Promise<any>; | ||
| onSubmitInvalid?: (values: TData, formApi: FormApi<TData>) => void; | ||
| }; | ||
| type FieldInfo<TFormData> = { | ||
| instances: Record<string, FieldApi<any, TFormData>>; | ||
| } & ValidationMeta; | ||
| type ValidationMeta = { | ||
| validationCount?: number; | ||
| validationAsyncCount?: number; | ||
| validationPromise?: Promise<ValidationError>; | ||
| validationResolve?: (error: ValidationError) => void; | ||
| validationReject?: (error: unknown) => void; | ||
| }; | ||
| type ValidationError = undefined | false | null | string; | ||
| type FormState<TData> = { | ||
| values: TData; | ||
| isFormValidating: boolean; | ||
| formValidationCount: number; | ||
| isFormValid: boolean; | ||
| formError?: ValidationError; | ||
| fieldMeta: Record<DeepKeys<TData>, FieldMeta>; | ||
| isFieldsValidating: boolean; | ||
| isFieldsValid: boolean; | ||
| isSubmitting: boolean; | ||
| isTouched: boolean; | ||
| isSubmitted: boolean; | ||
| isValidating: boolean; | ||
| isValid: boolean; | ||
| canSubmit: boolean; | ||
| submissionAttempts: number; | ||
| }; | ||
| declare class FormApi<TFormData> { | ||
| options: FormOptions<TFormData>; | ||
| store: Store<FormState<TFormData>>; | ||
| state: FormState<TFormData>; | ||
| fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>>; | ||
| fieldName?: string; | ||
| validationMeta: ValidationMeta; | ||
| constructor(opts?: FormOptions<TFormData>); | ||
| update: (options?: FormOptions<TFormData>) => void; | ||
| reset: () => void; | ||
| validateAllFields: (cause: ValidationCause) => Promise<ValidationError[]>; | ||
| handleSubmit: () => Promise<void>; | ||
| getFieldValue: <TField extends DeepKeys<TFormData>>(field: TField) => DeepValue<TFormData, TField>; | ||
| getFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField) => FieldMeta | undefined; | ||
| getFieldInfo: <TField extends DeepKeys<TFormData>>(field: TField) => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[TField]; | ||
| setFieldMeta: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<FieldMeta>) => void; | ||
| setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| insertFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, value: DeepValue<TFormData, TField>[number], opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| removeFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, index: number, opts?: { | ||
| touch?: boolean; | ||
| }) => void; | ||
| swapFieldValues: <TField extends DeepKeys<TFormData>>(field: TField, index1: number, index2: number) => void; | ||
| } | ||
| type ValidationCause = 'change' | 'blur' | 'submit'; | ||
| type ValidateFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError; | ||
| type ValidateAsyncFn<TData, TFormData> = (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>; | ||
| interface FieldOptions<_TData, TFormData, | ||
| /** | ||
| * This allows us to restrict the name to only be a valid field name while | ||
| * also assigning it to a generic | ||
| */ | ||
| TName = unknown extends TFormData ? string : DeepKeys<TFormData>, | ||
| /** | ||
| * If TData is unknown, we can use the TName generic to determine the type | ||
| */ | ||
| TData = unknown extends _TData ? DeepValue<TFormData, TName> : _TData> { | ||
| name: TName; | ||
| index?: TData extends any[] ? number : never; | ||
| defaultValue?: TData; | ||
| asyncDebounceMs?: number; | ||
| asyncAlways?: boolean; | ||
| onMount?: (formApi: FieldApi<TData, TFormData>) => void; | ||
| onChange?: ValidateFn<TData, TFormData>; | ||
| onChangeAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onChangeAsyncDebounceMs?: number; | ||
| onBlur?: ValidateFn<TData, TFormData>; | ||
| onBlurAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| onBlurAsyncDebounceMs?: number; | ||
| onSubmitAsync?: ValidateAsyncFn<TData, TFormData>; | ||
| defaultMeta?: Partial<FieldMeta>; | ||
| } | ||
| type FieldApiOptions<TData, TFormData> = FieldOptions<TData, TFormData> & { | ||
| form: FormApi<TFormData>; | ||
| }; | ||
| type FieldMeta = { | ||
| isTouched: boolean; | ||
| touchedError?: ValidationError; | ||
| error?: ValidationError; | ||
| isValidating: boolean; | ||
| }; | ||
| type FieldState<TData> = { | ||
| value: TData; | ||
| meta: FieldMeta; | ||
| }; | ||
| /** | ||
| * TData may not be known at the time of FieldApi construction, so we need to | ||
| * use a conditional type to determine if TData is known or not. | ||
| * | ||
| * If TData is not known, we use the TFormData type to determine the type of | ||
| * the field value based on the field name. | ||
| */ | ||
| type GetTData<Name, TData, TFormData> = unknown extends TData ? DeepValue<TFormData, Name> : TData; | ||
| declare class FieldApi<TData, TFormData> { | ||
| #private; | ||
| uid: number; | ||
| form: FormApi<TFormData>; | ||
| name: DeepKeys<TFormData>; | ||
| /** | ||
| * This is a hack that allows us to use `GetTData` without calling it everywhere | ||
| * | ||
| * Unfortunately this hack appears to be needed alongside the `TName` hack | ||
| * further up in this file. This properly types all of the internal methods, | ||
| * while the `TName` hack types the options properly | ||
| */ | ||
| _tdata: GetTData<typeof this$1.name, TData, TFormData>; | ||
| store: Store<FieldState<typeof this$1._tdata>>; | ||
| state: FieldState<typeof this$1._tdata>; | ||
| prevState: FieldState<typeof this$1._tdata>; | ||
| options: FieldOptions<typeof this$1._tdata, TFormData>; | ||
| constructor(opts: FieldApiOptions<TData, TFormData>); | ||
| mount: () => () => void; | ||
| update: (opts: FieldApiOptions<typeof this$1._tdata, TFormData>) => void; | ||
| getValue: () => typeof this$1._tdata; | ||
| setValue: (updater: Updater<typeof this$1._tdata>, options?: { | ||
| touch?: boolean; | ||
| notify?: boolean; | ||
| }) => void; | ||
| _getMeta: () => FieldMeta | undefined; | ||
| getMeta: () => FieldMeta; | ||
| setMeta: (updater: Updater<FieldMeta>) => void; | ||
| getInfo: () => Record<DeepKeys<TFormData>, FieldInfo<TFormData>>[DeepKeys<TFormData>]; | ||
| pushValue: (value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| insertValue: (index: number, value: typeof this$1._tdata extends any[] ? (typeof this$1._tdata)[number] : never) => void; | ||
| removeValue: (index: number) => void; | ||
| swapValues: (aIndex: number, bIndex: number) => void; | ||
| getSubField: <TName extends DeepKeys<GetTData<DeepKeys<TFormData>, TData, TFormData>>>(name: TName) => FieldApi<DeepValue<GetTData<DeepKeys<TFormData>, TData, TFormData>, TName>, TFormData>; | ||
| validateSync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => void; | ||
| cancelValidateAsync: () => void; | ||
| validateAsync: (value: GetTData<DeepKeys<TFormData>, TData, TFormData> | undefined, cause: ValidationCause) => Promise<ValidationError>; | ||
| validate: (cause: ValidationCause, value?: typeof this$1._tdata) => ValidationError | Promise<ValidationError>; | ||
| handleChange: (updater: Updater<typeof this$1._tdata>) => void; | ||
| handleBlur: () => void; | ||
| } | ||
| export { DeepKeys, DeepValue, FieldApi, FieldApiOptions, FieldInfo, FieldMeta, FieldOptions, FieldState, FormApi, FormOptions, FormState, Updater, ValidationCause, ValidationError, ValidationMeta }; |
| // src/index.ts | ||
| export * from "./FormApi.js"; | ||
| export * from "./FieldApi.js"; | ||
| export * from "./utils.js"; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from './FormApi'\nexport * from './FieldApi'\nexport * from './utils'\n"],"mappings":";AAAA,cAAc;AACd,cAAc;AACd,cAAc;","names":[]} |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/utils.ts | ||
| var utils_exports = {}; | ||
| __export(utils_exports, { | ||
| functionalUpdate: () => functionalUpdate, | ||
| getBy: () => getBy, | ||
| setBy: () => setBy | ||
| }); | ||
| module.exports = __toCommonJS(utils_exports); | ||
| function functionalUpdate(updater, input) { | ||
| return typeof updater === "function" ? updater(input) : updater; | ||
| } | ||
| function getBy(obj, path) { | ||
| const pathArray = makePathArray(path); | ||
| const pathObj = pathArray; | ||
| return pathObj.reduce((current, pathPart) => { | ||
| if (typeof current !== "undefined") { | ||
| return current[pathPart]; | ||
| } | ||
| return void 0; | ||
| }, obj); | ||
| } | ||
| function setBy(obj, _path, updater) { | ||
| const path = makePathArray(_path); | ||
| function doSet(parent) { | ||
| if (!path.length) { | ||
| return functionalUpdate(updater, parent); | ||
| } | ||
| const key = path.shift(); | ||
| if (typeof key === "string") { | ||
| if (typeof parent === "object") { | ||
| return { | ||
| ...parent, | ||
| [key]: doSet(parent[key]) | ||
| }; | ||
| } | ||
| return { | ||
| [key]: doSet() | ||
| }; | ||
| } | ||
| if (typeof key === "number") { | ||
| if (Array.isArray(parent)) { | ||
| const prefix = parent.slice(0, key); | ||
| return [ | ||
| ...prefix.length ? prefix : new Array(key), | ||
| doSet(parent[key]), | ||
| ...parent.slice(key + 1) | ||
| ]; | ||
| } | ||
| return [...new Array(key), doSet()]; | ||
| } | ||
| throw new Error("Uh oh!"); | ||
| } | ||
| return doSet(obj); | ||
| } | ||
| var reFindNumbers0 = /^(\d*)$/gm; | ||
| var reFindNumbers1 = /\.(\d*)\./gm; | ||
| var reFindNumbers2 = /^(\d*)\./gm; | ||
| var reFindNumbers3 = /\.(\d*$)/gm; | ||
| var reFindMultiplePeriods = /\.{2,}/gm; | ||
| var intPrefix = "__int__"; | ||
| var intReplace = `${intPrefix}$1`; | ||
| function makePathArray(str) { | ||
| if (typeof str !== "string") { | ||
| throw new Error("Path must be a string."); | ||
| } | ||
| return str.replace("[", ".").replace("]", "").replace(reFindNumbers0, intReplace).replace(reFindNumbers1, `.${intReplace}.`).replace(reFindNumbers2, `${intReplace}.`).replace(reFindNumbers3, `.${intReplace}`).replace(reFindMultiplePeriods, ".").split(".").map((d) => { | ||
| if (d.indexOf(intPrefix) === 0) { | ||
| return parseInt(d.substring(intPrefix.length), 10); | ||
| } | ||
| return d; | ||
| }); | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| functionalUpdate, | ||
| getBy, | ||
| setBy | ||
| }); | ||
| //# sourceMappingURL=utils.cjs.map |
| {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T> = unknown extends T\n ? keyof T\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>\n : T extends any[]\n ? DeepKeys<T[number]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T>\n : never\n\ntype DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;","names":[]} |
| type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput; | ||
| type Updater<TInput, TOutput = TInput> = TOutput | UpdaterFn<TInput, TOutput>; | ||
| declare function functionalUpdate<TInput, TOutput = TInput>(updater: Updater<TInput, TOutput>, input: TInput): TOutput; | ||
| /** | ||
| * Get a value from an object using a path, including dot notation. | ||
| */ | ||
| declare function getBy(obj: any, path: any): any; | ||
| /** | ||
| * Set a value on an object using a path, including dot notation. | ||
| */ | ||
| declare function setBy(obj: any, _path: any, updater: Updater<any>): any; | ||
| type RequiredByKey<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; | ||
| type ComputeRange<N extends number, Result extends Array<unknown> = []> = Result['length'] extends N ? Result : ComputeRange<N, [...Result, Result['length']]>; | ||
| type Index40 = ComputeRange<40>[number]; | ||
| type IsTuple<T> = T extends readonly any[] & { | ||
| length: infer Length; | ||
| } ? Length extends Index40 ? T : never : never; | ||
| type AllowedIndexes<Tuple extends ReadonlyArray<any>, Keys extends number = never> = Tuple extends readonly [] ? Keys : Tuple extends readonly [infer _, ...infer Tail] ? AllowedIndexes<Tail, Keys | Tail['length']> : Keys; | ||
| type DeepKeys<T> = unknown extends T ? keyof T : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>> : T extends any[] ? DeepKeys<T[number]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T> : never; | ||
| type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}` : never; | ||
| type DeepValue<T, TProp> = T extends Record<string | number, any> ? TProp extends `${infer TBranch}.${infer TDeepProp}` ? DeepValue<T[TBranch], TDeepProp> : T[TProp & string] : never; | ||
| type Narrowable = string | number | bigint | boolean; | ||
| type NarrowRaw<A> = (A extends [] ? [] : never) | (A extends Narrowable ? A : never) | { | ||
| [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>; | ||
| }; | ||
| type Narrow<A> = Try<A, [], NarrowRaw<A>>; | ||
| type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch; | ||
| type Pretty<T> = { | ||
| [K in keyof T]: T[K]; | ||
| } & {}; | ||
| export { DeepKeys, DeepValue, Narrow, Pretty, RequiredByKey, Updater, UpdaterFn, functionalUpdate, getBy, setBy }; |
| type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput; | ||
| type Updater<TInput, TOutput = TInput> = TOutput | UpdaterFn<TInput, TOutput>; | ||
| declare function functionalUpdate<TInput, TOutput = TInput>(updater: Updater<TInput, TOutput>, input: TInput): TOutput; | ||
| /** | ||
| * Get a value from an object using a path, including dot notation. | ||
| */ | ||
| declare function getBy(obj: any, path: any): any; | ||
| /** | ||
| * Set a value on an object using a path, including dot notation. | ||
| */ | ||
| declare function setBy(obj: any, _path: any, updater: Updater<any>): any; | ||
| type RequiredByKey<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>; | ||
| type ComputeRange<N extends number, Result extends Array<unknown> = []> = Result['length'] extends N ? Result : ComputeRange<N, [...Result, Result['length']]>; | ||
| type Index40 = ComputeRange<40>[number]; | ||
| type IsTuple<T> = T extends readonly any[] & { | ||
| length: infer Length; | ||
| } ? Length extends Index40 ? T : never : never; | ||
| type AllowedIndexes<Tuple extends ReadonlyArray<any>, Keys extends number = never> = Tuple extends readonly [] ? Keys : Tuple extends readonly [infer _, ...infer Tail] ? AllowedIndexes<Tail, Keys | Tail['length']> : Keys; | ||
| type DeepKeys<T> = unknown extends T ? keyof T : object extends T ? string : T extends readonly any[] & IsTuple<T> ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>> : T extends any[] ? DeepKeys<T[number]> : T extends Date ? never : T extends object ? (keyof T & string) | DeepKeysPrefix<T, keyof T> : never; | ||
| type DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string) ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}` : never; | ||
| type DeepValue<T, TProp> = T extends Record<string | number, any> ? TProp extends `${infer TBranch}.${infer TDeepProp}` ? DeepValue<T[TBranch], TDeepProp> : T[TProp & string] : never; | ||
| type Narrowable = string | number | bigint | boolean; | ||
| type NarrowRaw<A> = (A extends [] ? [] : never) | (A extends Narrowable ? A : never) | { | ||
| [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>; | ||
| }; | ||
| type Narrow<A> = Try<A, [], NarrowRaw<A>>; | ||
| type Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch; | ||
| type Pretty<T> = { | ||
| [K in keyof T]: T[K]; | ||
| } & {}; | ||
| export { DeepKeys, DeepValue, Narrow, Pretty, RequiredByKey, Updater, UpdaterFn, functionalUpdate, getBy, setBy }; |
| // src/utils.ts | ||
| function functionalUpdate(updater, input) { | ||
| return typeof updater === "function" ? updater(input) : updater; | ||
| } | ||
| function getBy(obj, path) { | ||
| const pathArray = makePathArray(path); | ||
| const pathObj = pathArray; | ||
| return pathObj.reduce((current, pathPart) => { | ||
| if (typeof current !== "undefined") { | ||
| return current[pathPart]; | ||
| } | ||
| return void 0; | ||
| }, obj); | ||
| } | ||
| function setBy(obj, _path, updater) { | ||
| const path = makePathArray(_path); | ||
| function doSet(parent) { | ||
| if (!path.length) { | ||
| return functionalUpdate(updater, parent); | ||
| } | ||
| const key = path.shift(); | ||
| if (typeof key === "string") { | ||
| if (typeof parent === "object") { | ||
| return { | ||
| ...parent, | ||
| [key]: doSet(parent[key]) | ||
| }; | ||
| } | ||
| return { | ||
| [key]: doSet() | ||
| }; | ||
| } | ||
| if (typeof key === "number") { | ||
| if (Array.isArray(parent)) { | ||
| const prefix = parent.slice(0, key); | ||
| return [ | ||
| ...prefix.length ? prefix : new Array(key), | ||
| doSet(parent[key]), | ||
| ...parent.slice(key + 1) | ||
| ]; | ||
| } | ||
| return [...new Array(key), doSet()]; | ||
| } | ||
| throw new Error("Uh oh!"); | ||
| } | ||
| return doSet(obj); | ||
| } | ||
| var reFindNumbers0 = /^(\d*)$/gm; | ||
| var reFindNumbers1 = /\.(\d*)\./gm; | ||
| var reFindNumbers2 = /^(\d*)\./gm; | ||
| var reFindNumbers3 = /\.(\d*$)/gm; | ||
| var reFindMultiplePeriods = /\.{2,}/gm; | ||
| var intPrefix = "__int__"; | ||
| var intReplace = `${intPrefix}$1`; | ||
| function makePathArray(str) { | ||
| if (typeof str !== "string") { | ||
| throw new Error("Path must be a string."); | ||
| } | ||
| return str.replace("[", ".").replace("]", "").replace(reFindNumbers0, intReplace).replace(reFindNumbers1, `.${intReplace}.`).replace(reFindNumbers2, `${intReplace}.`).replace(reFindNumbers3, `.${intReplace}`).replace(reFindMultiplePeriods, ".").split(".").map((d) => { | ||
| if (d.indexOf(intPrefix) === 0) { | ||
| return parseInt(d.substring(intPrefix.length), 10); | ||
| } | ||
| return d; | ||
| }); | ||
| } | ||
| export { | ||
| functionalUpdate, | ||
| getBy, | ||
| setBy | ||
| }; | ||
| //# sourceMappingURL=utils.js.map |
| {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["export type UpdaterFn<TInput, TOutput = TInput> = (input: TInput) => TOutput\n\nexport type Updater<TInput, TOutput = TInput> =\n | TOutput\n | UpdaterFn<TInput, TOutput>\n\nexport function functionalUpdate<TInput, TOutput = TInput>(\n updater: Updater<TInput, TOutput>,\n input: TInput,\n): TOutput {\n return typeof updater === 'function'\n ? (updater as UpdaterFn<TInput, TOutput>)(input)\n : updater\n}\n\n/**\n * Get a value from an object using a path, including dot notation.\n */\nexport function getBy(obj: any, path: any) {\n const pathArray = makePathArray(path)\n const pathObj = pathArray\n return pathObj.reduce((current: any, pathPart: any) => {\n if (typeof current !== 'undefined') {\n return current[pathPart]\n }\n return undefined\n }, obj)\n}\n\n/**\n * Set a value on an object using a path, including dot notation.\n */\nexport function setBy(obj: any, _path: any, updater: Updater<any>) {\n const path = makePathArray(_path)\n\n function doSet(parent?: any): any {\n if (!path.length) {\n return functionalUpdate(updater, parent)\n }\n\n const key = path.shift()\n\n if (typeof key === 'string') {\n if (typeof parent === 'object') {\n return {\n ...parent,\n [key]: doSet(parent[key]),\n }\n }\n return {\n [key]: doSet(),\n }\n }\n\n if (typeof key === 'number') {\n if (Array.isArray(parent)) {\n const prefix = parent.slice(0, key)\n return [\n ...(prefix.length ? prefix : new Array(key)),\n doSet(parent[key]),\n ...parent.slice(key + 1),\n ]\n }\n return [...new Array(key), doSet()]\n }\n\n throw new Error('Uh oh!')\n }\n\n return doSet(obj)\n}\n\nconst reFindNumbers0 = /^(\\d*)$/gm\nconst reFindNumbers1 = /\\.(\\d*)\\./gm\nconst reFindNumbers2 = /^(\\d*)\\./gm\nconst reFindNumbers3 = /\\.(\\d*$)/gm\nconst reFindMultiplePeriods = /\\.{2,}/gm\n\nconst intPrefix = '__int__'\nconst intReplace = `${intPrefix}$1`\n\nfunction makePathArray(str: string) {\n if (typeof str !== 'string') {\n throw new Error('Path must be a string.')\n }\n\n return str\n .replace('[', '.')\n .replace(']', '')\n .replace(reFindNumbers0, intReplace)\n .replace(reFindNumbers1, `.${intReplace}.`)\n .replace(reFindNumbers2, `${intReplace}.`)\n .replace(reFindNumbers3, `.${intReplace}`)\n .replace(reFindMultiplePeriods, '.')\n .split('.')\n .map((d) => {\n if (d.indexOf(intPrefix) === 0) {\n return parseInt(d.substring(intPrefix.length), 10)\n }\n return d\n })\n}\n\nexport type RequiredByKey<T, K extends keyof T> = Omit<T, K> &\n Required<Pick<T, K>>\n\ntype ComputeRange<\n N extends number,\n Result extends Array<unknown> = [],\n> = Result['length'] extends N\n ? Result\n : ComputeRange<N, [...Result, Result['length']]>\ntype Index40 = ComputeRange<40>[number]\n\n// Is this type a tuple?\ntype IsTuple<T> = T extends readonly any[] & { length: infer Length }\n ? Length extends Index40\n ? T\n : never\n : never\n\n// If this type is a tuple, what indices are allowed?\ntype AllowedIndexes<\n Tuple extends ReadonlyArray<any>,\n Keys extends number = never,\n> = Tuple extends readonly []\n ? Keys\n : Tuple extends readonly [infer _, ...infer Tail]\n ? AllowedIndexes<Tail, Keys | Tail['length']>\n : Keys\n\nexport type DeepKeys<T> = unknown extends T\n ? keyof T\n : object extends T\n ? string\n : T extends readonly any[] & IsTuple<T>\n ? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>>\n : T extends any[]\n ? DeepKeys<T[number]>\n : T extends Date\n ? never\n : T extends object\n ? (keyof T & string) | DeepKeysPrefix<T, keyof T>\n : never\n\ntype DeepKeysPrefix<T, TPrefix> = TPrefix extends keyof T & (number | string)\n ? `${TPrefix}.${DeepKeys<T[TPrefix]> & string}`\n : never\n\nexport type DeepValue<T, TProp> = T extends Record<string | number, any>\n ? TProp extends `${infer TBranch}.${infer TDeepProp}`\n ? DeepValue<T[TBranch], TDeepProp>\n : T[TProp & string]\n : never\n\ntype Narrowable = string | number | bigint | boolean\n\ntype NarrowRaw<A> =\n | (A extends [] ? [] : never)\n | (A extends Narrowable ? A : never)\n | {\n [K in keyof A]: A[K] extends Function ? A[K] : NarrowRaw<A[K]>\n }\n\nexport type Narrow<A> = Try<A, [], NarrowRaw<A>>\n\ntype Try<A1, A2, Catch = never> = A1 extends A2 ? A1 : Catch\n\n// Hack to get TypeScript to show simplified types in error messages\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n"],"mappings":";AAMO,SAAS,iBACd,SACA,OACS;AACT,SAAO,OAAO,YAAY,aACrB,QAAuC,KAAK,IAC7C;AACN;AAKO,SAAS,MAAM,KAAU,MAAW;AACzC,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,UAAU;AAChB,SAAO,QAAQ,OAAO,CAAC,SAAc,aAAkB;AACrD,QAAI,OAAO,YAAY,aAAa;AAClC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO;AAAA,EACT,GAAG,GAAG;AACR;AAKO,SAAS,MAAM,KAAU,OAAY,SAAuB;AACjE,QAAM,OAAO,cAAc,KAAK;AAEhC,WAAS,MAAM,QAAmB;AAChC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,iBAAiB,SAAS,MAAM;AAAA,IACzC;AAEA,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,OAAO,WAAW,UAAU;AAC9B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,CAAC,GAAG,GAAG,MAAM,OAAO,GAAG,CAAC;AAAA,QAC1B;AAAA,MACF;AACA,aAAO;AAAA,QACL,CAAC,GAAG,GAAG,MAAM;AAAA,MACf;AAAA,IACF;AAEA,QAAI,OAAO,QAAQ,UAAU;AAC3B,UAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,cAAM,SAAS,OAAO,MAAM,GAAG,GAAG;AAClC,eAAO;AAAA,UACL,GAAI,OAAO,SAAS,SAAS,IAAI,MAAM,GAAG;AAAA,UAC1C,MAAM,OAAO,GAAG,CAAC;AAAA,UACjB,GAAG,OAAO,MAAM,MAAM,CAAC;AAAA,QACzB;AAAA,MACF;AACA,aAAO,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,MAAM,CAAC;AAAA,IACpC;AAEA,UAAM,IAAI,MAAM,QAAQ;AAAA,EAC1B;AAEA,SAAO,MAAM,GAAG;AAClB;AAEA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,wBAAwB;AAE9B,IAAM,YAAY;AAClB,IAAM,aAAa,GAAG,SAAS;AAE/B,SAAS,cAAc,KAAa;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,SAAO,IACJ,QAAQ,KAAK,GAAG,EAChB,QAAQ,KAAK,EAAE,EACf,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,gBAAgB,IAAI,UAAU,GAAG,EACzC,QAAQ,gBAAgB,GAAG,UAAU,GAAG,EACxC,QAAQ,gBAAgB,IAAI,UAAU,EAAE,EACxC,QAAQ,uBAAuB,GAAG,EAClC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM;AACV,QAAI,EAAE,QAAQ,SAAS,MAAM,GAAG;AAC9B,aAAO,SAAS,EAAE,UAAU,UAAU,MAAM,GAAG,EAAE;AAAA,IACnD;AACA,WAAO;AAAA,EACT,CAAC;AACL;","names":[]} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
49784
-84.69%10
-83.33%1563
-62.47%