Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@tanstack/form-core

Package Overview
Dependencies
Maintainers
2
Versions
205
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tanstack/form-core - npm Package Compare versions

Comparing version
0.2.0
to
0.3.0
+1
-1
package.json
{
"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",

@@ -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'
}
}
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 {

@@ -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":"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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(
() => {
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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAsB;AAGtB,mBAA+C;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,cACiC,oBAAM,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,OAAG,+BAAiB,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,YAAQ,oBAAM,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,mBAAO,wBAAM,oBAAM,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":[]}
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":"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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 };
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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,mBAAsB;AAyDtB,IAAI,MAAM;AAkBH,IAAM,WAAN,MAAM,UAA2B;AAAA,EAiBtC,YAAY,MAAyC;AAFrD,mBAAuD,CAAC;AA0CxD,iBAAQ,MAAM;AACZ,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,WAAK,QAAQ,UAAU,IAAa;AAEpC,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;AAGjE,UAAI,KAAK,MAAM,UAAU,QAAW;AAClC,cAAM,cACJ,KAAK,KAAK,QAAQ,gBAAgB,KAAK,IAAuB;AAEhE,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,+BAAsB,MAAM;AAC1B,YAAM,SAAS,KAAK,QAAQ,EAAE,wBAAwB,KAAK;AAC3D,WAAK,QAAQ,EAAE,uBAAuB;AACtC,aAAO;AAAA,IACT;AAEA,+BAAsB,MAAM;AAE1B,WAAK,oBAAoB;AAEzB,WAAK,QAAQ,CAAC,UAAU;AAAA,QACtB,GAAG;AAAA,QACH,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ;AAEA,yBAAgB,OAAO,QAAQ,KAAK,MAAM,OAAO,UAA2B;AAC1E,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,KAAK,oBAAoB;AAEtD,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,iBAAK,QAAQ,EAAE,oBAAoB,KAAK;AAAA,UAC1C;AAAA,QACF,SAAS,OAAO;AACd,cAAI,YAAY,GAAG;AACjB,iBAAK,QAAQ,EAAE,mBAAmB,KAAK;AACvC,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;AAAA,EAmIA;AAiIF;AAEA,SAAS,eAAe,UAA4B;AAClD,MAAI,UAAU;AACZ,QAAI,OAAO,aAAa,UAAU;AAChC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","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":[]}