formstand 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as PathDepth, D as DefaultPathDepth, F as Form, C as CreateFormOptions, a as FieldValidationResult, b as FormState, c as FieldPath, d as FieldValue, E as ErrorMap } from './createForm-jlzK2lCG.cjs';
2
- export { B as BoolMap, e as FieldSnapshot, f as FieldsValidationResult, I as InvalidSubmitHandler, R as ReadonlyStoreApi, g as ResetOptions, S as SettledFieldValidationResult, h as SettledFieldsValidationResult, i as SettledValidationResult, j as SubmitHandler, k as SubmitOptions, l as SubmitResult, V as ValidationMode, m as ValidationResult, n as ValidationTrigger, o as createForm, p as emptyValueForSchema, q as flattenIssues, r as isAsyncRequiredError, s as shouldValidateOn, v as validateAsync, t as validateSync } from './createForm-jlzK2lCG.cjs';
1
+ import { P as PathDepth, D as DefaultPathDepth, F as Form, C as CreateFormOptions, a as FieldValidationResult, S as SettledFieldValidationResult, b as FormState, c as FieldPath, d as FieldValue, E as ErrorMap } from './createForm-D0xLZH-Q.cjs';
2
+ export { B as BoolMap, e as FieldSnapshot, f as FieldsValidationResult, I as InvalidSubmitHandler, R as ReadonlyStoreApi, g as ResetOptions, h as SettledFieldsValidationResult, i as SettledValidationResult, j as SubmitHandler, k as SubmitOptions, l as SubmitResult, V as ValidationMode, m as ValidationResult, n as ValidationTrigger, o as createForm, p as emptyValueForSchema, q as flattenIssues, r as isAsyncRequiredError, s as shouldValidateOn, v as validateAsync, t as validateSync } from './createForm-D0xLZH-Q.cjs';
3
3
  import { z } from 'zod';
4
4
  import * as react from 'react';
5
5
  import { ReactNode, ChangeEvent } from 'react';
@@ -29,6 +29,8 @@ type PersistHandle = Readonly<{
29
29
  }>;
30
30
  declare const persistForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, options: PersistOptions<TSchema>) => PersistHandle;
31
31
 
32
+ declare const usePersistForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, options: PersistOptions<TSchema>) => PersistHandle;
33
+
32
34
  declare const useForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, D>;
33
35
 
34
36
  type FormProviderProps<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = Readonly<{
@@ -49,7 +51,7 @@ type FieldFormApi = Readonly<{
49
51
  setError(path: string, errors: string | readonly string[]): void;
50
52
  clearErrors(path?: string): void;
51
53
  validateField(path: string): FieldValidationResult;
52
- validateFieldAsync(path: string): Promise<FieldValidationResult>;
54
+ validateFieldAsync(path: string): Promise<SettledFieldValidationResult>;
53
55
  }>;
54
56
  type FieldPathArg<TValues> = string | ((state: FormState<TValues>) => string);
55
57
  type UseFieldOptions = Readonly<{
@@ -70,7 +72,7 @@ type UseFieldReturn<TValue> = Readonly<{
70
72
  setError: (errors: string | readonly string[]) => void;
71
73
  clearError: () => void;
72
74
  validate: () => FieldValidationResult;
73
- validateAsync: () => Promise<FieldValidationResult>;
75
+ validateAsync: () => Promise<SettledFieldValidationResult>;
74
76
  onBlur: () => void;
75
77
  }>;
76
78
  declare const inferredCallsOnly$2: unique symbol;
@@ -93,7 +95,7 @@ type VariantFieldValue<V, TField extends string> = V extends unknown ? TField ex
93
95
  type CommonKeys<V> = keyof V;
94
96
  type AllKeys<V> = V extends unknown ? keyof V : never;
95
97
  type VariantKeys<V> = Exclude<AllKeys<V>, CommonKeys<V>> & string;
96
- type UnionValueAt<TValues, P extends string> = P extends keyof TValues ? NonNullable<TValues[P]> : TValues extends readonly (infer Item)[] ? P extends `${number}` ? NonNullable<Item> : P extends `${infer Head}.${infer Tail}` ? Head extends `${number}` ? UnionValueAt<NonNullable<Item>, Tail> : never : never : P extends `${infer Head}.${infer Tail}` ? Head extends keyof TValues ? UnionValueAt<NonNullable<TValues[Head]>, Tail> : never : never;
98
+ type UnionValueAt<TValues, P extends string> = P extends keyof TValues ? NonNullable<TValues[P]> : TValues extends readonly (infer Item)[] ? P extends `${number}` ? NonNullable<Item> : P extends `${infer Head}.${infer Tail}` ? Head extends keyof TValues ? UnionValueAt<NonNullable<TValues[Head]>, Tail> : Head extends `${number}` ? UnionValueAt<NonNullable<Item>, Tail> : never : never : P extends `${infer Head}.${infer Tail}` ? Head extends keyof TValues ? UnionValueAt<NonNullable<TValues[Head]>, Tail> : never : never;
97
99
  declare const inferredCallsOnly$1: unique symbol;
98
100
  interface InferredTypeArg$1 {
99
101
  readonly [inferredCallsOnly$1]: typeof inferredCallsOnly$1;
@@ -115,16 +117,22 @@ type FieldArrayFormApi = Readonly<{
115
117
  arrayMove(path: string, from: number, to: number): void;
116
118
  arraySwap(path: string, a: number, b: number): void;
117
119
  validateField?(path: string): FieldValidationResult;
120
+ setError?(path: string, errors: string | readonly string[]): void;
121
+ clearErrors?(path?: string): void;
118
122
  }>;
119
123
  type FieldArrayEntry<TItem> = Readonly<{
120
124
  id: string;
121
125
  value: TItem;
122
126
  }>;
123
127
  type UseFieldArrayReturn<TItem> = Readonly<{
128
+ path: string;
124
129
  fields: readonly FieldArrayEntry<TItem>[];
125
130
  items: readonly TItem[];
126
131
  length: number;
127
132
  error: readonly string[] | undefined;
133
+ firstError: string | undefined;
134
+ setError: (errors: string | readonly string[]) => void;
135
+ clearError: () => void;
128
136
  push: (item: TItem) => void;
129
137
  remove: (index: number) => void;
130
138
  insert: (index: number, item: TItem) => void;
@@ -148,6 +156,22 @@ declare function useFieldArray<TItem = unknown>(form: FieldArrayFormApi & {
148
156
  }, path: FieldPathArg<unknown>): UseFieldArrayReturn<TItem>;
149
157
  declare function useFieldArray<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>, D>, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, path: P): UseFieldArrayReturn<ArrayItemOf<FieldValue<z.input<TSchema>, P>>>;
150
158
 
159
+ type UseFieldsReturn<TFields extends readonly unknown[]> = Readonly<{
160
+ fields: TFields;
161
+ error: readonly string[] | undefined;
162
+ firstError: string | undefined;
163
+ touched: boolean;
164
+ dirty: boolean;
165
+ isValidating: boolean;
166
+ onBlur: () => void;
167
+ }>;
168
+ declare function useFields(form: FieldFormApi & {
169
+ readonly schema?: undefined;
170
+ }, paths: readonly string[]): UseFieldsReturn<readonly UseFieldReturn<unknown>[]>;
171
+ declare function useFields<TSchema extends z.ZodType, const P extends readonly FieldPath<z.input<TSchema>, D>[], D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, paths: P): UseFieldsReturn<{
172
+ readonly [K in keyof P]: UseFieldReturn<FieldValue<z.input<TSchema>, P[K] & string>>;
173
+ }>;
174
+
151
175
  type BoundUseField<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = {
152
176
  (pathSelector: (state: FormState<z.input<TSchema>>) => string, options?: UseFieldOptions): UseFieldReturn<unknown>;
153
177
  <P extends FieldPath<z.input<TSchema>, D>>(path: P, options?: UseFieldOptions): UseFieldReturn<FieldValue<z.input<TSchema>, P>>;
@@ -175,10 +199,19 @@ type FormHooks<TSchema extends z.ZodType, N extends string, D extends PathDepth
175
199
  [K in `use${Capitalize<N>}IsDirty`]: BoundUseFlag<TSchema, D>;
176
200
  } & {
177
201
  [K in `use${Capitalize<N>}IsValid`]: BoundUseFlag<TSchema, D>;
202
+ } & {
203
+ [K in `use${Capitalize<N>}IsValidating`]: BoundUseFlag<TSchema, D>;
178
204
  } & {
179
205
  [K in `use${Capitalize<N>}IsSubmitting`]: () => boolean;
180
206
  } & {
181
207
  [K in `use${Capitalize<N>}SubmitCount`]: () => number;
208
+ } & {
209
+ [K in `use${Capitalize<N>}Values`]: () => z.input<TSchema>;
210
+ } & {
211
+ [K in `use${Capitalize<N>}Fields`]: BoundUseFields<TSchema, D>;
212
+ }>;
213
+ type BoundUseFields<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = <const P extends readonly FieldPath<z.input<TSchema>, D>[]>(paths: P) => UseFieldsReturn<{
214
+ readonly [K in keyof P]: UseFieldReturn<FieldValue<z.input<TSchema>, P[K] & string>>;
182
215
  }>;
183
216
  declare const createFormHooks: <TSchema extends z.ZodType, N extends string = "", D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, name?: N) => FormHooks<TSchema, N, D>;
184
217
 
@@ -210,25 +243,13 @@ declare function useIsValid(form: FormStateApi & {
210
243
  readonly schema?: undefined;
211
244
  }, path?: string): boolean;
212
245
  declare function useIsValid<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>, D>, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, path?: P): boolean;
246
+ declare function useIsValidating(form: FormStateApi & {
247
+ readonly schema?: undefined;
248
+ }, path?: string): boolean;
249
+ declare function useIsValidating<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>, D>, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, path?: P): boolean;
213
250
  declare const useIsSubmitting: (form: FormStateApi) => boolean;
214
251
  declare const useSubmitCount: (form: FormStateApi) => number;
215
252
 
216
- type UseFieldsReturn<TFields extends readonly unknown[]> = Readonly<{
217
- fields: TFields;
218
- error: readonly string[] | undefined;
219
- firstError: string | undefined;
220
- touched: boolean;
221
- dirty: boolean;
222
- isValidating: boolean;
223
- onBlur: () => void;
224
- }>;
225
- declare function useFields(form: FieldFormApi & {
226
- readonly schema?: undefined;
227
- }, paths: readonly string[]): UseFieldsReturn<readonly UseFieldReturn<unknown>[]>;
228
- declare function useFields<TSchema extends z.ZodType, const P extends readonly FieldPath<z.input<TSchema>, D>[], D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, paths: P): UseFieldsReturn<{
229
- readonly [K in keyof P]: UseFieldReturn<FieldValue<z.input<TSchema>, P[K] & string>>;
230
- }>;
231
-
232
253
  type PathsOf<F extends FieldFormApi> = F extends Form<infer TSchema extends z.ZodType, infer D extends PathDepth> ? FieldPath<z.input<TSchema>, D> : string;
233
254
  type FieldRef<T> = ((instance: T | null) => void) | Readonly<{
234
255
  current: T | null;
@@ -358,4 +379,4 @@ declare const dateInputProps: <T extends Date | null | undefined>(field: UseFiel
358
379
  declare const checkboxProps: <T extends boolean | null | undefined>(field: UseFieldReturn<T>) => CheckboxProps;
359
380
  declare const selectProps: <T extends string | null | undefined>(field: UseFieldReturn<T>) => SelectProps;
360
381
 
361
- export { type BoundUseField, type BoundUseFieldArray, type BoundUseFlag, type BoundUseSelector, CheckboxField, type CheckboxFieldProps, type CheckboxProps, CreateFormOptions, DateField, type DateFieldProps, type DateInputProps, DefaultPathDepth, ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, FieldPath, type FieldPathArg, type FieldRef, FieldValidationResult, FieldValue, Form, type FormContextApi, type FormHooks, type FormProviderProps, FormState, type FormStateApi, NumberField, type NumberFieldProps, type NumberInputBinding, type NumberInputProps, type ParsedDateText, type ParsedNumberText, PathDepth, type PathSegment, type PathsOf, type PersistHandle, type PersistOptions, type PersistStorage, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type UseFieldsReturn, checkboxProps, createFormContext, createFormHooks, dateInputProps, dateToInputText, focusField, focusFirstError, getAtPath, hasFieldError, numberInputProps, numberToInputText, parseDateText, parseNumberText, parsePath, persistForm, selectProps, setAtPath, textInputProps, useField, useFieldArray, useFields, useForm, useFormError, useFormSelector, useFormSelectorShallow, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useNumberInput, useSubmitCount, useVariantField };
382
+ export { type BoundUseField, type BoundUseFieldArray, type BoundUseFlag, type BoundUseSelector, CheckboxField, type CheckboxFieldProps, type CheckboxProps, CreateFormOptions, DateField, type DateFieldProps, type DateInputProps, DefaultPathDepth, ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, FieldPath, type FieldPathArg, type FieldRef, FieldValidationResult, FieldValue, Form, type FormContextApi, type FormHooks, type FormProviderProps, FormState, type FormStateApi, NumberField, type NumberFieldProps, type NumberInputBinding, type NumberInputProps, type ParsedDateText, type ParsedNumberText, PathDepth, type PathSegment, type PathsOf, type PersistHandle, type PersistOptions, type PersistStorage, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, SettledFieldValidationResult, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type UseFieldsReturn, checkboxProps, createFormContext, createFormHooks, dateInputProps, dateToInputText, focusField, focusFirstError, getAtPath, hasFieldError, numberInputProps, numberToInputText, parseDateText, parseNumberText, parsePath, persistForm, selectProps, setAtPath, textInputProps, useField, useFieldArray, useFields, useForm, useFormError, useFormSelector, useFormSelectorShallow, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useIsValidating, useNumberInput, usePersistForm, useSubmitCount, useVariantField };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as PathDepth, D as DefaultPathDepth, F as Form, C as CreateFormOptions, a as FieldValidationResult, b as FormState, c as FieldPath, d as FieldValue, E as ErrorMap } from './createForm-jlzK2lCG.js';
2
- export { B as BoolMap, e as FieldSnapshot, f as FieldsValidationResult, I as InvalidSubmitHandler, R as ReadonlyStoreApi, g as ResetOptions, S as SettledFieldValidationResult, h as SettledFieldsValidationResult, i as SettledValidationResult, j as SubmitHandler, k as SubmitOptions, l as SubmitResult, V as ValidationMode, m as ValidationResult, n as ValidationTrigger, o as createForm, p as emptyValueForSchema, q as flattenIssues, r as isAsyncRequiredError, s as shouldValidateOn, v as validateAsync, t as validateSync } from './createForm-jlzK2lCG.js';
1
+ import { P as PathDepth, D as DefaultPathDepth, F as Form, C as CreateFormOptions, a as FieldValidationResult, S as SettledFieldValidationResult, b as FormState, c as FieldPath, d as FieldValue, E as ErrorMap } from './createForm-D0xLZH-Q.js';
2
+ export { B as BoolMap, e as FieldSnapshot, f as FieldsValidationResult, I as InvalidSubmitHandler, R as ReadonlyStoreApi, g as ResetOptions, h as SettledFieldsValidationResult, i as SettledValidationResult, j as SubmitHandler, k as SubmitOptions, l as SubmitResult, V as ValidationMode, m as ValidationResult, n as ValidationTrigger, o as createForm, p as emptyValueForSchema, q as flattenIssues, r as isAsyncRequiredError, s as shouldValidateOn, v as validateAsync, t as validateSync } from './createForm-D0xLZH-Q.js';
3
3
  import { z } from 'zod';
4
4
  import * as react from 'react';
5
5
  import { ReactNode, ChangeEvent } from 'react';
@@ -29,6 +29,8 @@ type PersistHandle = Readonly<{
29
29
  }>;
30
30
  declare const persistForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, options: PersistOptions<TSchema>) => PersistHandle;
31
31
 
32
+ declare const usePersistForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, options: PersistOptions<TSchema>) => PersistHandle;
33
+
32
34
  declare const useForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, D>;
33
35
 
34
36
  type FormProviderProps<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = Readonly<{
@@ -49,7 +51,7 @@ type FieldFormApi = Readonly<{
49
51
  setError(path: string, errors: string | readonly string[]): void;
50
52
  clearErrors(path?: string): void;
51
53
  validateField(path: string): FieldValidationResult;
52
- validateFieldAsync(path: string): Promise<FieldValidationResult>;
54
+ validateFieldAsync(path: string): Promise<SettledFieldValidationResult>;
53
55
  }>;
54
56
  type FieldPathArg<TValues> = string | ((state: FormState<TValues>) => string);
55
57
  type UseFieldOptions = Readonly<{
@@ -70,7 +72,7 @@ type UseFieldReturn<TValue> = Readonly<{
70
72
  setError: (errors: string | readonly string[]) => void;
71
73
  clearError: () => void;
72
74
  validate: () => FieldValidationResult;
73
- validateAsync: () => Promise<FieldValidationResult>;
75
+ validateAsync: () => Promise<SettledFieldValidationResult>;
74
76
  onBlur: () => void;
75
77
  }>;
76
78
  declare const inferredCallsOnly$2: unique symbol;
@@ -93,7 +95,7 @@ type VariantFieldValue<V, TField extends string> = V extends unknown ? TField ex
93
95
  type CommonKeys<V> = keyof V;
94
96
  type AllKeys<V> = V extends unknown ? keyof V : never;
95
97
  type VariantKeys<V> = Exclude<AllKeys<V>, CommonKeys<V>> & string;
96
- type UnionValueAt<TValues, P extends string> = P extends keyof TValues ? NonNullable<TValues[P]> : TValues extends readonly (infer Item)[] ? P extends `${number}` ? NonNullable<Item> : P extends `${infer Head}.${infer Tail}` ? Head extends `${number}` ? UnionValueAt<NonNullable<Item>, Tail> : never : never : P extends `${infer Head}.${infer Tail}` ? Head extends keyof TValues ? UnionValueAt<NonNullable<TValues[Head]>, Tail> : never : never;
98
+ type UnionValueAt<TValues, P extends string> = P extends keyof TValues ? NonNullable<TValues[P]> : TValues extends readonly (infer Item)[] ? P extends `${number}` ? NonNullable<Item> : P extends `${infer Head}.${infer Tail}` ? Head extends keyof TValues ? UnionValueAt<NonNullable<TValues[Head]>, Tail> : Head extends `${number}` ? UnionValueAt<NonNullable<Item>, Tail> : never : never : P extends `${infer Head}.${infer Tail}` ? Head extends keyof TValues ? UnionValueAt<NonNullable<TValues[Head]>, Tail> : never : never;
97
99
  declare const inferredCallsOnly$1: unique symbol;
98
100
  interface InferredTypeArg$1 {
99
101
  readonly [inferredCallsOnly$1]: typeof inferredCallsOnly$1;
@@ -115,16 +117,22 @@ type FieldArrayFormApi = Readonly<{
115
117
  arrayMove(path: string, from: number, to: number): void;
116
118
  arraySwap(path: string, a: number, b: number): void;
117
119
  validateField?(path: string): FieldValidationResult;
120
+ setError?(path: string, errors: string | readonly string[]): void;
121
+ clearErrors?(path?: string): void;
118
122
  }>;
119
123
  type FieldArrayEntry<TItem> = Readonly<{
120
124
  id: string;
121
125
  value: TItem;
122
126
  }>;
123
127
  type UseFieldArrayReturn<TItem> = Readonly<{
128
+ path: string;
124
129
  fields: readonly FieldArrayEntry<TItem>[];
125
130
  items: readonly TItem[];
126
131
  length: number;
127
132
  error: readonly string[] | undefined;
133
+ firstError: string | undefined;
134
+ setError: (errors: string | readonly string[]) => void;
135
+ clearError: () => void;
128
136
  push: (item: TItem) => void;
129
137
  remove: (index: number) => void;
130
138
  insert: (index: number, item: TItem) => void;
@@ -148,6 +156,22 @@ declare function useFieldArray<TItem = unknown>(form: FieldArrayFormApi & {
148
156
  }, path: FieldPathArg<unknown>): UseFieldArrayReturn<TItem>;
149
157
  declare function useFieldArray<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>, D>, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, path: P): UseFieldArrayReturn<ArrayItemOf<FieldValue<z.input<TSchema>, P>>>;
150
158
 
159
+ type UseFieldsReturn<TFields extends readonly unknown[]> = Readonly<{
160
+ fields: TFields;
161
+ error: readonly string[] | undefined;
162
+ firstError: string | undefined;
163
+ touched: boolean;
164
+ dirty: boolean;
165
+ isValidating: boolean;
166
+ onBlur: () => void;
167
+ }>;
168
+ declare function useFields(form: FieldFormApi & {
169
+ readonly schema?: undefined;
170
+ }, paths: readonly string[]): UseFieldsReturn<readonly UseFieldReturn<unknown>[]>;
171
+ declare function useFields<TSchema extends z.ZodType, const P extends readonly FieldPath<z.input<TSchema>, D>[], D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, paths: P): UseFieldsReturn<{
172
+ readonly [K in keyof P]: UseFieldReturn<FieldValue<z.input<TSchema>, P[K] & string>>;
173
+ }>;
174
+
151
175
  type BoundUseField<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = {
152
176
  (pathSelector: (state: FormState<z.input<TSchema>>) => string, options?: UseFieldOptions): UseFieldReturn<unknown>;
153
177
  <P extends FieldPath<z.input<TSchema>, D>>(path: P, options?: UseFieldOptions): UseFieldReturn<FieldValue<z.input<TSchema>, P>>;
@@ -175,10 +199,19 @@ type FormHooks<TSchema extends z.ZodType, N extends string, D extends PathDepth
175
199
  [K in `use${Capitalize<N>}IsDirty`]: BoundUseFlag<TSchema, D>;
176
200
  } & {
177
201
  [K in `use${Capitalize<N>}IsValid`]: BoundUseFlag<TSchema, D>;
202
+ } & {
203
+ [K in `use${Capitalize<N>}IsValidating`]: BoundUseFlag<TSchema, D>;
178
204
  } & {
179
205
  [K in `use${Capitalize<N>}IsSubmitting`]: () => boolean;
180
206
  } & {
181
207
  [K in `use${Capitalize<N>}SubmitCount`]: () => number;
208
+ } & {
209
+ [K in `use${Capitalize<N>}Values`]: () => z.input<TSchema>;
210
+ } & {
211
+ [K in `use${Capitalize<N>}Fields`]: BoundUseFields<TSchema, D>;
212
+ }>;
213
+ type BoundUseFields<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = <const P extends readonly FieldPath<z.input<TSchema>, D>[]>(paths: P) => UseFieldsReturn<{
214
+ readonly [K in keyof P]: UseFieldReturn<FieldValue<z.input<TSchema>, P[K] & string>>;
182
215
  }>;
183
216
  declare const createFormHooks: <TSchema extends z.ZodType, N extends string = "", D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, name?: N) => FormHooks<TSchema, N, D>;
184
217
 
@@ -210,25 +243,13 @@ declare function useIsValid(form: FormStateApi & {
210
243
  readonly schema?: undefined;
211
244
  }, path?: string): boolean;
212
245
  declare function useIsValid<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>, D>, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, path?: P): boolean;
246
+ declare function useIsValidating(form: FormStateApi & {
247
+ readonly schema?: undefined;
248
+ }, path?: string): boolean;
249
+ declare function useIsValidating<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>, D>, D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, path?: P): boolean;
213
250
  declare const useIsSubmitting: (form: FormStateApi) => boolean;
214
251
  declare const useSubmitCount: (form: FormStateApi) => number;
215
252
 
216
- type UseFieldsReturn<TFields extends readonly unknown[]> = Readonly<{
217
- fields: TFields;
218
- error: readonly string[] | undefined;
219
- firstError: string | undefined;
220
- touched: boolean;
221
- dirty: boolean;
222
- isValidating: boolean;
223
- onBlur: () => void;
224
- }>;
225
- declare function useFields(form: FieldFormApi & {
226
- readonly schema?: undefined;
227
- }, paths: readonly string[]): UseFieldsReturn<readonly UseFieldReturn<unknown>[]>;
228
- declare function useFields<TSchema extends z.ZodType, const P extends readonly FieldPath<z.input<TSchema>, D>[], D extends PathDepth = DefaultPathDepth>(form: Form<TSchema, D>, paths: P): UseFieldsReturn<{
229
- readonly [K in keyof P]: UseFieldReturn<FieldValue<z.input<TSchema>, P[K] & string>>;
230
- }>;
231
-
232
253
  type PathsOf<F extends FieldFormApi> = F extends Form<infer TSchema extends z.ZodType, infer D extends PathDepth> ? FieldPath<z.input<TSchema>, D> : string;
233
254
  type FieldRef<T> = ((instance: T | null) => void) | Readonly<{
234
255
  current: T | null;
@@ -358,4 +379,4 @@ declare const dateInputProps: <T extends Date | null | undefined>(field: UseFiel
358
379
  declare const checkboxProps: <T extends boolean | null | undefined>(field: UseFieldReturn<T>) => CheckboxProps;
359
380
  declare const selectProps: <T extends string | null | undefined>(field: UseFieldReturn<T>) => SelectProps;
360
381
 
361
- export { type BoundUseField, type BoundUseFieldArray, type BoundUseFlag, type BoundUseSelector, CheckboxField, type CheckboxFieldProps, type CheckboxProps, CreateFormOptions, DateField, type DateFieldProps, type DateInputProps, DefaultPathDepth, ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, FieldPath, type FieldPathArg, type FieldRef, FieldValidationResult, FieldValue, Form, type FormContextApi, type FormHooks, type FormProviderProps, FormState, type FormStateApi, NumberField, type NumberFieldProps, type NumberInputBinding, type NumberInputProps, type ParsedDateText, type ParsedNumberText, PathDepth, type PathSegment, type PathsOf, type PersistHandle, type PersistOptions, type PersistStorage, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type UseFieldsReturn, checkboxProps, createFormContext, createFormHooks, dateInputProps, dateToInputText, focusField, focusFirstError, getAtPath, hasFieldError, numberInputProps, numberToInputText, parseDateText, parseNumberText, parsePath, persistForm, selectProps, setAtPath, textInputProps, useField, useFieldArray, useFields, useForm, useFormError, useFormSelector, useFormSelectorShallow, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useNumberInput, useSubmitCount, useVariantField };
382
+ export { type BoundUseField, type BoundUseFieldArray, type BoundUseFlag, type BoundUseSelector, CheckboxField, type CheckboxFieldProps, type CheckboxProps, CreateFormOptions, DateField, type DateFieldProps, type DateInputProps, DefaultPathDepth, ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, FieldPath, type FieldPathArg, type FieldRef, FieldValidationResult, FieldValue, Form, type FormContextApi, type FormHooks, type FormProviderProps, FormState, type FormStateApi, NumberField, type NumberFieldProps, type NumberInputBinding, type NumberInputProps, type ParsedDateText, type ParsedNumberText, PathDepth, type PathSegment, type PathsOf, type PersistHandle, type PersistOptions, type PersistStorage, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, SettledFieldValidationResult, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type UseFieldsReturn, checkboxProps, createFormContext, createFormHooks, dateInputProps, dateToInputText, focusField, focusFirstError, getAtPath, hasFieldError, numberInputProps, numberToInputText, parseDateText, parseNumberText, parsePath, persistForm, selectProps, setAtPath, textInputProps, useField, useFieldArray, useFields, useForm, useFormError, useFormSelector, useFormSelectorShallow, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useIsValidating, useNumberInput, usePersistForm, useSubmitCount, useVariantField };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ export { getAtPath, parsePath, setAtPath, useFormSelector, useFormSelectorShallo
3
3
  import { createStore } from 'zustand/vanilla';
4
4
  import { devtools } from 'zustand/middleware';
5
5
  import { z } from 'zod';
6
- import { useRef, useEffect, createContext, useMemo, useCallback, useState, useContext, useId } from 'react';
6
+ import { useRef, useEffect, useMemo, createContext, useCallback, useState, useContext, useId } from 'react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { useStore } from 'zustand/react';
9
9
  import { useShallow } from 'zustand/react/shallow';
@@ -163,7 +163,7 @@ var schemaHasPath = (schema, path) => {
163
163
  if (head === void 0) return true;
164
164
  if (s instanceof z.ZodObject) {
165
165
  const shape = s.shape;
166
- const child = shape[String(head)];
166
+ const child = Object.hasOwn(shape, String(head)) ? shape[String(head)] : void 0;
167
167
  if (child !== void 0) return walk(child, rest, budget - 1);
168
168
  const catchall = s.def.catchall;
169
169
  return catchall !== void 0 && !(catchall instanceof z.ZodNever);
@@ -183,7 +183,7 @@ var fieldSchemaAtPath = (schema, path) => parsePath(path).reduce((current, segme
183
183
  if (current === null || hasChecks(current)) return null;
184
184
  if (current instanceof z.ZodObject && typeof segment === "string") {
185
185
  const shape = current.shape;
186
- return shape[segment] ?? null;
186
+ return Object.hasOwn(shape, segment) ? shape[segment] ?? null : null;
187
187
  }
188
188
  if (current instanceof z.ZodArray && typeof segment === "number") {
189
189
  return current.element;
@@ -588,7 +588,7 @@ var createForm = (schema, options) => {
588
588
  };
589
589
  const applyArrayOp = (path, nextArray, mapper, indexInBounds) => {
590
590
  const current = getAtPath(store.getState().values, path);
591
- if (current !== void 0 && !Array.isArray(current)) {
591
+ if (current !== void 0 && current !== null && !Array.isArray(current)) {
592
592
  console.warn(
593
593
  `[formstand] array op on "${path}" but the value at that path is not an array (got ${typeof current}). Operation skipped.`
594
594
  );
@@ -697,6 +697,7 @@ var createForm = (schema, options) => {
697
697
  try {
698
698
  await onValid(result.data);
699
699
  } catch (error) {
700
+ submitOptions?.onError?.(error);
700
701
  return { kind: "error", error };
701
702
  }
702
703
  return { kind: "valid", data: result.data };
@@ -804,6 +805,17 @@ var createForm = (schema, options) => {
804
805
  ...state,
805
806
  ...errorChannels(state, { serverErrors: errors })
806
807
  })),
808
+ addErrors: (errors) => store.setState((state) => ({
809
+ ...state,
810
+ ...errorChannels(state, {
811
+ serverErrors: Object.entries(errors).reduce(
812
+ // Computed keys throughout, so a server field named
813
+ // "__proto__" lands as an own property like everywhere else.
814
+ (acc, [key, list]) => list === void 0 || list.length === 0 ? omitKey(acc, key) : { ...acc, [key]: list },
815
+ state.serverErrors
816
+ )
817
+ })
818
+ })),
807
819
  clearErrors: (path) => store.setState((state) => {
808
820
  const scrub = (map) => path === void 0 ? emptyErrors : omitScope(map, [path]);
809
821
  return {
@@ -943,7 +955,14 @@ var createForm = (schema, options) => {
943
955
  ),
944
956
  handleSubmit: (onValid, onInvalid, opts) => async (event) => {
945
957
  event?.preventDefault();
946
- return submit(onValid, onInvalid, opts);
958
+ const result = await submit(onValid, onInvalid, opts);
959
+ if (result.kind === "error" && opts?.onError === void 0 && process.env.NODE_ENV !== "production") {
960
+ console.warn(
961
+ "[formstand] submit handler threw and nothing observed it \u2014 the form shows no feedback. Pass { onError } to handleSubmit (or setError inside your handler):",
962
+ result.error
963
+ );
964
+ }
965
+ return result;
947
966
  },
948
967
  diff: () => {
949
968
  const state = store.getState();
@@ -1051,6 +1070,9 @@ var persistForm = (form, options) => {
1051
1070
  const parsed = JSON.parse(raw);
1052
1071
  const versioned = options.version !== void 0 && typeof parsed === "object" && parsed !== null && "__v" in parsed;
1053
1072
  if (options.version !== void 0 && !versioned) return false;
1073
+ if (options.version === void 0 && typeof parsed === "object" && parsed !== null && Object.hasOwn(parsed, "__v") && Object.hasOwn(parsed, "values")) {
1074
+ return false;
1075
+ }
1054
1076
  const wrapper = parsed;
1055
1077
  if (versioned && wrapper.__v !== options.version) return false;
1056
1078
  if (versioned && wrapper.values === void 0) return false;
@@ -1114,6 +1136,33 @@ var persistForm = (form, options) => {
1114
1136
  }
1115
1137
  });
1116
1138
  };
1139
+ var usePersistForm = (form, options) => {
1140
+ const inner = useRef(null);
1141
+ const latestOptions = useRef(options);
1142
+ useEffect(() => {
1143
+ latestOptions.current = options;
1144
+ });
1145
+ const key = options.key;
1146
+ useEffect(() => {
1147
+ const handle = persistForm(form, latestOptions.current);
1148
+ inner.current = handle;
1149
+ return () => {
1150
+ inner.current = null;
1151
+ handle.dispose();
1152
+ };
1153
+ }, [form, key]);
1154
+ return useMemo(
1155
+ () => ({
1156
+ restore: () => inner.current?.restore() ?? false,
1157
+ clear: () => inner.current?.clear(),
1158
+ // dispose stops THIS component's watching early; the effect cleanup
1159
+ // still runs its own dispose harmlessly (persistForm's dispose is
1160
+ // idempotent by construction — it only cancels and unsubscribes).
1161
+ dispose: () => inner.current?.dispose()
1162
+ }),
1163
+ []
1164
+ );
1165
+ };
1117
1166
  var useForm = (schema, options) => {
1118
1167
  const formRef = useRef(null);
1119
1168
  if (formRef.current === null) {
@@ -1190,9 +1239,13 @@ function useField(form, pathArg, options) {
1190
1239
  if (debounceTimerRef.current !== null) {
1191
1240
  clearTimeout(debounceTimerRef.current);
1192
1241
  }
1242
+ const scheduled = getAtPath(form.store.getState().values, path);
1193
1243
  debounceTimerRef.current = setTimeout(() => {
1194
- void form.validateFieldAsync(path);
1195
1244
  debounceTimerRef.current = null;
1245
+ const state = form.store.getState();
1246
+ if (state.values === state.initialValues) return;
1247
+ if (!Object.is(getAtPath(state.values, path), scheduled)) return;
1248
+ void form.validateFieldAsync(path);
1196
1249
  }, debounceMs);
1197
1250
  return;
1198
1251
  }
@@ -1453,25 +1506,64 @@ function useFieldArray(form, pathArg) {
1453
1506
  })),
1454
1507
  [items, ids]
1455
1508
  );
1509
+ const setError = useCallback(
1510
+ // Same string-shorthand normalization as useField.setError, so custom
1511
+ // implementations typed for readonly string[] never receive a bare
1512
+ // string.
1513
+ (errors) => form.setError?.(path, typeof errors === "string" ? [errors] : errors),
1514
+ [form, path]
1515
+ );
1516
+ const clearError = useCallback(() => form.clearErrors?.(path), [form, path]);
1456
1517
  return useMemo(
1457
1518
  () => ({
1519
+ path,
1458
1520
  fields,
1459
1521
  items,
1460
1522
  length: items.length,
1461
1523
  error,
1524
+ firstError: error?.[0],
1525
+ setError,
1526
+ clearError,
1462
1527
  push,
1463
1528
  remove,
1464
1529
  insert,
1465
1530
  move,
1466
1531
  swap
1467
1532
  }),
1468
- [fields, items, error, push, remove, insert, move, swap]
1533
+ [path, fields, items, error, setError, clearError, push, remove, insert, move, swap]
1469
1534
  );
1470
1535
  }
1471
1536
  var useFormError = (form) => useStore(
1472
1537
  form.store,
1473
1538
  useShallow((state) => state.errors[""])
1474
1539
  );
1540
+ function useFormValues(form) {
1541
+ return useStore(form.store, (state) => state.values);
1542
+ }
1543
+
1544
+ // src/react/useFields.ts
1545
+ function useFields(form, paths) {
1546
+ const fields = paths.map(
1547
+ (path) => (
1548
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- a fixed hook count by the stable-arity contract above; each element must be a REAL UseFieldReturn so it spreads into the prop builders
1549
+ useField(form, path)
1550
+ )
1551
+ );
1552
+ const merged = [...new Set(fields.flatMap((field) => field.error ?? []))];
1553
+ return {
1554
+ fields,
1555
+ error: merged.length > 0 ? merged : void 0,
1556
+ firstError: merged[0],
1557
+ touched: fields.some((field) => field.touched),
1558
+ dirty: fields.some((field) => field.dirty),
1559
+ isValidating: fields.some((field) => field.isValidating),
1560
+ onBlur: () => {
1561
+ fields.forEach((field) => {
1562
+ field.onBlur();
1563
+ });
1564
+ }
1565
+ };
1566
+ }
1475
1567
  function useIsDirty(form, path) {
1476
1568
  return useStore(
1477
1569
  form.store,
@@ -1492,6 +1584,19 @@ function useIsValid(form, path) {
1492
1584
  return true;
1493
1585
  });
1494
1586
  }
1587
+ function useIsValidating(form, path) {
1588
+ return useStore(form.store, (state) => {
1589
+ if (path === void 0 || path === "") {
1590
+ if (state.isValidatingForm) return true;
1591
+ }
1592
+ for (const k in state.isValidating) {
1593
+ if (state.isValidating[k] === true && (path === void 0 || path === "" || isPathOrChild(k, path))) {
1594
+ return true;
1595
+ }
1596
+ }
1597
+ return false;
1598
+ });
1599
+ }
1495
1600
  var useIsSubmitting = (form) => useStore(form.store, (state) => state.isSubmitting);
1496
1601
  var useSubmitCount = (form) => useStore(form.store, (state) => state.submitCount);
1497
1602
 
@@ -1511,7 +1616,13 @@ var createFormHooks = (form, name) => {
1511
1616
  const useBoundError = () => useFormError(structural);
1512
1617
  const useBoundIsDirty = (path) => useIsDirty(structural, path);
1513
1618
  const useBoundIsValid = (path) => useIsValid(structural, path);
1619
+ const useBoundIsValidating = (path) => useIsValidating(structural, path);
1514
1620
  const useBoundIsSubmitting = () => useIsSubmitting(structural);
1621
+ const useBoundValues = () => useFormValues(structural);
1622
+ const useBoundFields = (paths) => useFields(
1623
+ structural,
1624
+ paths
1625
+ );
1515
1626
  const useBoundSubmitCount = () => useSubmitCount(structural);
1516
1627
  const prefix = capitalize(name ?? "");
1517
1628
  return {
@@ -1523,37 +1634,13 @@ var createFormHooks = (form, name) => {
1523
1634
  [`use${prefix}Error`]: useBoundError,
1524
1635
  [`use${prefix}IsDirty`]: useBoundIsDirty,
1525
1636
  [`use${prefix}IsValid`]: useBoundIsValid,
1637
+ [`use${prefix}IsValidating`]: useBoundIsValidating,
1526
1638
  [`use${prefix}IsSubmitting`]: useBoundIsSubmitting,
1527
- [`use${prefix}SubmitCount`]: useBoundSubmitCount
1639
+ [`use${prefix}SubmitCount`]: useBoundSubmitCount,
1640
+ [`use${prefix}Values`]: useBoundValues,
1641
+ [`use${prefix}Fields`]: useBoundFields
1528
1642
  };
1529
1643
  };
1530
- function useFormValues(form) {
1531
- return useStore(form.store, (state) => state.values);
1532
- }
1533
-
1534
- // src/react/useFields.ts
1535
- function useFields(form, paths) {
1536
- const fields = paths.map(
1537
- (path) => (
1538
- // eslint-disable-next-line react-hooks/rules-of-hooks -- a fixed hook count by the stable-arity contract above; each element must be a REAL UseFieldReturn so it spreads into the prop builders
1539
- useField(form, path)
1540
- )
1541
- );
1542
- const merged = [...new Set(fields.flatMap((field) => field.error ?? []))];
1543
- return {
1544
- fields,
1545
- error: merged.length > 0 ? merged : void 0,
1546
- firstError: merged[0],
1547
- touched: fields.some((field) => field.touched),
1548
- dirty: fields.some((field) => field.dirty),
1549
- isValidating: fields.some((field) => field.isValidating),
1550
- onBlur: () => {
1551
- fields.forEach((field) => {
1552
- field.onBlur();
1553
- });
1554
- }
1555
- };
1556
- }
1557
1644
 
1558
1645
  // src/react/inputProps.ts
1559
1646
  var hasFieldError = (error) => error !== void 0 && error.length > 0;
@@ -1583,6 +1670,7 @@ var numberInputProps = (field) => ({
1583
1670
  "aria-invalid": ariaInvalid(field),
1584
1671
  onChange: (e) => {
1585
1672
  const parsed = parseNumberText(e.target.value);
1673
+ if (parsed.kind === "invalid") return;
1586
1674
  field.setValue(
1587
1675
  parsed.kind === "number" ? parsed.value : field.emptyValue
1588
1676
  );
@@ -1943,6 +2031,6 @@ var focusField = (path, root) => {
1943
2031
  return path === "" ? (root !== void 0 || document.querySelectorAll("form").length <= 1) && focusFirstOf(namedControls(scope).filter(isFocusCandidate)) : focusFirstOf(pathCandidates(scope, [path]).filter(isFocusCandidate));
1944
2032
  };
1945
2033
 
1946
- export { CheckboxField, DateField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, createFormHooks, dateInputProps, dateToInputText, emptyValueForSchema, flattenIssues, focusField, focusFirstError, hasFieldError, isAsyncRequiredError, numberInputProps, numberToInputText, parseDateText, parseNumberText, persistForm, selectProps, shouldValidateOn, textInputProps, useField, useFieldArray, useFields, useForm, useFormError, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useNumberInput, useSubmitCount, useVariantField, validateAsync, validateSync };
2034
+ export { CheckboxField, DateField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, createFormHooks, dateInputProps, dateToInputText, emptyValueForSchema, flattenIssues, focusField, focusFirstError, hasFieldError, isAsyncRequiredError, numberInputProps, numberToInputText, parseDateText, parseNumberText, persistForm, selectProps, shouldValidateOn, textInputProps, useField, useFieldArray, useFields, useForm, useFormError, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useIsValidating, useNumberInput, usePersistForm, useSubmitCount, useVariantField, validateAsync, validateSync };
1947
2035
  //# sourceMappingURL=index.js.map
1948
2036
  //# sourceMappingURL=index.js.map