@formisch/svelte 0.8.0 → 0.10.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/README.md CHANGED
@@ -9,6 +9,7 @@ Formisch is also available for [Preact][formisch-preact], [Qwik][formisch-qwik],
9
9
  - Small bundle size starting at 2.5 kB
10
10
  - Schema-based validation with Valibot
11
11
  - Type safety with autocompletion in editor
12
+ - Open source and fully tested with 100 % coverage
12
13
  - It's fast – DOM updates are fine-grained
13
14
  - Minimal, readable and well thought out API
14
15
  - Supports all native HTML form fields
@@ -55,7 +56,7 @@ Every form starts with the `createForm` function. It initializes your form's sto
55
56
  </Form>
56
57
  ```
57
58
 
58
- In addition, Formisch offers several functions (we call them "methods") that can be used to read and manipulate the form state. These include `focus`, `getErrors`, `getAllErrors`, `getInput`, `insert`, `move`, `remove`, `replace`, `reset`, `setErrors`, `setInput`, `submit`, `swap` and `validate`. These methods allow you to control the form programmatically.
59
+ In addition, Formisch offers several functions (we call them "methods") that can be used to read and manipulate the form state. These include `focus`, `getDeepErrorEntries`, `getDeepErrors`, `getDirtyInput`, `getDirtyPaths`, `getErrors`, `getInput`, `handleSubmit`, `insert`, `isDirty`, `isEdited`, `isTouched`, `isValid`, `move`, `pickDirty`, `remove`, `replace`, `reset`, `setErrors`, `setInput`, `submit`, `swap` and `validate`. These methods allow you to control the form programmatically.
59
60
 
60
61
  ## Comparison
61
62
 
@@ -1,6 +1,177 @@
1
1
  import * as v from "valibot";
2
2
  import { untrack } from "svelte";
3
3
 
4
+ //#region src/types/utils/utils.d.ts
5
+ /**
6
+ * Checks if a type is `any`.
7
+ */
8
+ type IsAny<T> = 0 extends 1 & T ? true : false;
9
+ /**
10
+ * Checks if a type is `never`.
11
+ */
12
+ type IsNever<T> = [T] extends [never] ? true : false;
13
+ /**
14
+ * Constructs a type that is maybe a promise.
15
+ */
16
+ type MaybePromise<T> = T | Promise<T>;
17
+ /**
18
+ * Makes all properties deeply optional.
19
+ */
20
+ type DeepPartial<TValue> = TValue extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> | undefined } : TValue | undefined;
21
+ /**
22
+ * Makes all value properties optional.
23
+ *
24
+ * Hint: For dynamic arrays, only plain objects and nested arrays have their
25
+ * values made optional. Primitives and class instances are kept as-is to avoid
26
+ * types like `(string | undefined)[]`.
27
+ */
28
+ type PartialValues<TValue> = TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? (TItem extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TItem]: PartialValues<TItem[TKey]> } : TItem)[] : { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue | undefined;
29
+ //#endregion
30
+ //#region src/types/path/path.d.ts
31
+ /**
32
+ * Path key type.
33
+ */
34
+ type PathKey = string | number;
35
+ /**
36
+ * Path type.
37
+ */
38
+ type Path = readonly PathKey[];
39
+ /**
40
+ * Required path type.
41
+ */
42
+ type RequiredPath = readonly [PathKey, ...Path];
43
+ /**
44
+ * Extracts the exact keys of a tuple, array or object. Tuples return their
45
+ * literal numeric indices, dynamic arrays return `number`, objects return
46
+ * their `keyof` keys, and any other input returns `never`.
47
+ */
48
+ type ExactKeysOf<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly unknown[] ? number extends TValue["length"] ? number : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? TIndex : never }[number] : TValue extends Record<PropertyKey, unknown> ? keyof TValue & PathKey : never;
49
+ /**
50
+ * Returns the flat object of all indexable properties of `TValue`. For object
51
+ * unions, properties from every member are merged so that any single property
52
+ * is accessible. For primitives and other non-indexable types, the result is
53
+ * `{}`.
54
+ *
55
+ * Hint: This is necessary to make properties accessible across union members.
56
+ * By default, properties that do not exist in all union options are not
57
+ * accessible and result in "any" when accessed.
58
+ */
59
+ type PropertiesOf<TValue> = { [TKey in ExactKeysOf<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
60
+ /**
61
+ * Lazily evaluates only the first valid path segment based on the given value.
62
+ */
63
+ type LazyPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValidPath : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOf<TValue>, ...infer TPathRest extends Path] ? LazyPath<Required<PropertiesOf<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOf<TValue>> extends false ? readonly [...TValidPath, ExactKeysOf<TValue>] : TValidPath;
64
+ /**
65
+ * Returns the path if valid, otherwise the first possible valid path based on
66
+ * the given value.
67
+ */
68
+ type ValidPath<TValue, TPath extends RequiredPath> = TPath extends LazyPath<Required<TValue>, TPath> ? TPath : LazyPath<Required<TValue>, TPath>;
69
+ /**
70
+ * Detects whether the consuming project is configured with
71
+ * `exactOptionalPropertyTypes: true`.
72
+ *
73
+ * Hint: If `false` the built-in `Required<T>` strips `| undefined` from
74
+ * optional properties, so `Required<{ key?: undefined }>['key']` collapses
75
+ * to `never` — under strict mode the same expression yields `undefined`.
76
+ */
77
+ type IsExactOptionalProps = Required<{
78
+ key?: undefined;
79
+ }>["key"] extends never ? false : true;
80
+ /**
81
+ * Like the built-in `Required<T>`, but preserves `| undefined` in two
82
+ * places where `Required<T>` strips it:
83
+ *
84
+ * 1. Optional property values under `exactOptionalPropertyTypes: false`
85
+ * — without this, input typings for `v.optional`/`v.nullish` schemas
86
+ * narrow incorrectly (issue #15).
87
+ * 2. Array/tuple element types — e.g. `(string | undefined)[]` stays
88
+ * `(string | undefined)[]` instead of becoming `string[]`. Arrays
89
+ * fall through unchanged because they only have a numeric index
90
+ * signature and don't structurally extend `Record<PropertyKey,
91
+ * unknown>` (which requires string keys).
92
+ */
93
+ type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExactOptionalProps extends true ? Required<TValue> : { [TKey in keyof Required<TValue>]: TValue[TKey] } : TValue;
94
+ /**
95
+ * Extracts the value type at the given path.
96
+ */
97
+ type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey, ...infer TRest extends Path] ? TKey extends ExactKeysOf<ExactRequired<TValue>> ? PathValue<PropertiesOf<ExactRequired<TValue>>[TKey], TRest> : unknown : TValue;
98
+ /**
99
+ * Checks whether a value is a dynamic array or contains one anywhere in its
100
+ * shape. A fixed-length tuple is not itself a dynamic array, but it counts when
101
+ * it contains one, so paths can still navigate through tuples to reach nested
102
+ * arrays.
103
+ *
104
+ * Hint: The inner conditionals (`TValue extends readonly unknown[]` and
105
+ * `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
106
+ * so the inner expression returns the union of each member's result (e.g.
107
+ * `true | false` when some members contain arrays and others don't).
108
+ * Downstream code uses `IsOrHasArray<T> extends true`, but
109
+ * `boolean extends true` is `false` — so we collapse the result via
110
+ * `true extends ...`, which is `true` whenever at least one union member
111
+ * contributed `true`.
112
+ */
113
+ type IsOrHasArray<TValue> = true extends (IsAny<TValue> extends true ? false : TValue extends readonly unknown[] ? number extends TValue["length"] ? true : IsOrHasArray<TValue[number]> : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<TValue[TKey]> }[keyof TValue] : false) ? true : false;
114
+ /**
115
+ * Extracts the exact keys of a tuple, array or object that contain arrays.
116
+ */
117
+ type ExactKeysOfArrayPath<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? IsOrHasArray<TItem> extends true ? number : never : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TIndex : never : never }[number] : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TKey : never }[keyof TValue] & PathKey : never;
118
+ /**
119
+ * Returns the flat object of indexable properties of `TValue` whose values
120
+ * are or contain arrays. Mirrors `PropertiesOf` but keyed by
121
+ * `ExactKeysOfArrayPath` so the lookup is provably valid for array-path
122
+ * navigation in `LazyArrayPath`.
123
+ */
124
+ type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
125
+ /**
126
+ * Lazily evaluates only the first valid array path segment based on the given value.
127
+ */
128
+ type LazyArrayPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValue extends readonly unknown[] ? number extends TValue["length"] ? TValidPath : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never : readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOfArrayPath<TValue>, ...infer TPathRest extends Path] ? LazyArrayPath<Required<PropertiesOfArrayPath<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never;
129
+ /**
130
+ * Returns the path if valid, otherwise the first possible valid array path
131
+ * based on the given value.
132
+ */
133
+ type ValidArrayPath<TValue, TPath extends RequiredPath> = TPath extends LazyArrayPath<Required<TValue>, TPath> ? TPath : LazyArrayPath<Required<TValue>, TPath>;
134
+ /**
135
+ * Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
136
+ * or falls through to `never` when the child is not an object.
137
+ */
138
+ type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
139
+ /**
140
+ * Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
141
+ * for a given input type. Object fields contribute their own path and the
142
+ * paths of their descendants; arrays and tuples are atomic and contribute
143
+ * only their own path, because dirty arrays are returned as complete units.
144
+ *
145
+ * Narrowing is exact for the first 5 levels of nesting; deeper paths fall
146
+ * back to `RequiredPath` to keep the result a complete superset of any
147
+ * path the runtime can address.
148
+ *
149
+ * Hint: Arrays and tuples are atomic because they don't structurally
150
+ * extend `Record<PropertyKey, unknown>` and so fall through to `never`
151
+ * via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
152
+ * a tuple-length counter capped at 5 to bound TypeScript instantiation
153
+ * cost.
154
+ */
155
+ type DirtyPath<TValue, TDepth extends 0[] = []> = TDepth["length"] extends 5 ? RequiredPath : TValue extends Record<PropertyKey, unknown> ? { [TKey in ExactKeysOf<TValue>]: readonly [TKey] | DeepDirtyPath<NonNullable<PropertiesOf<TValue>[TKey]>, TKey, TDepth> }[ExactKeysOf<TValue>] : never;
156
+ /**
157
+ * Recursive helper for `FieldPath` that prepends `TKey` to each deeper field
158
+ * path, or falls through to `never` when the child is a leaf value.
159
+ */
160
+ type DeepFieldPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends readonly unknown[] | Record<PropertyKey, unknown> ? readonly [TKey$1, ...FieldPath<TChild, [...TDepth, 0]>] : never;
161
+ /**
162
+ * Returns the union of all `RequiredPath`s that address a field within the
163
+ * given input type. Object and array fields contribute their own path and the
164
+ * paths of their descendants; unlike `DirtyPath`, arrays are recursed into so
165
+ * that fields at any depth, including array items, can be addressed. Leaf
166
+ * values contribute only their own path (emitted by their parent).
167
+ *
168
+ * Narrowing is exact for the first 5 levels of nesting; deeper paths fall
169
+ * back to `RequiredPath` to keep the result a complete superset of any path
170
+ * the runtime can address. `TDepth` is a tuple-length counter capped at 5 to
171
+ * bound TypeScript instantiation cost.
172
+ */
173
+ type FieldPath<TValue, TDepth extends 0[] = []> = TDepth["length"] extends 5 ? RequiredPath : TValue extends readonly unknown[] | Record<PropertyKey, unknown> ? { [TKey in ExactKeysOf<TValue>]: readonly [TKey] | DeepFieldPath<NonNullable<PropertiesOf<TValue>[TKey]>, TKey, TDepth> }[ExactKeysOf<TValue>] : never;
174
+ //#endregion
4
175
  //#region src/types/schema/schema.d.ts
5
176
  /**
6
177
  * Schema type.
@@ -73,11 +244,22 @@ interface InternalBaseStore {
73
244
  */
74
245
  name: string;
75
246
  /**
247
+ * The path to the field.
248
+ */
249
+ path: Path;
250
+ /**
76
251
  * The schema of the field.
77
252
  */
78
253
  schema: Schema;
79
254
  /**
80
255
  * The initial elements of the field.
256
+ *
257
+ * Hint: This may look unused, but do not remove it. `copyItemState` and
258
+ * `swapItemState` move the `elements` reference between field stores when
259
+ * array items are inserted, moved, removed or swapped, and `reset` restores
260
+ * each field's original element via `elements = initialElements`. Without it,
261
+ * focus and file reset target the wrong element after a reorder followed by a
262
+ * reset.
81
263
  */
82
264
  initialElements: FieldElement[];
83
265
  /**
@@ -93,6 +275,15 @@ interface InternalBaseStore {
93
275
  */
94
276
  isTouched: Signal<boolean>;
95
277
  /**
278
+ * The edited state of the field.
279
+ *
280
+ * Hint: Unlike `isTouched`, which is also set when a field is focused, this
281
+ * is only set when the field's value is changed. Unlike `isDirty`, it stays
282
+ * `true` even if the value is changed back to its initial value. It is only
283
+ * reset when the field is reset.
284
+ */
285
+ isEdited: Signal<boolean>;
286
+ /**
96
287
  * The dirty state of the field.
97
288
  */
98
289
  isDirty: Signal<boolean>;
@@ -106,6 +297,14 @@ interface InternalArrayStore extends InternalBaseStore {
106
297
  */
107
298
  kind: "array";
108
299
  /**
300
+ * Whether the array schema is wrapped in a nullish schema.
301
+ *
302
+ * Hint: This indicates whether a missing input should be represented as the
303
+ * nullish value (`null`/`undefined`) or as a present but empty array
304
+ * (`true`). It keeps resetting consistent with the initial state.
305
+ */
306
+ isNullish: boolean;
307
+ /**
109
308
  * The children of the array field.
110
309
  */
111
310
  children: InternalFieldStore[];
@@ -158,6 +357,14 @@ interface InternalObjectStore extends InternalBaseStore {
158
357
  */
159
358
  kind: "object";
160
359
  /**
360
+ * Whether the object schema is wrapped in a nullish schema.
361
+ *
362
+ * Hint: This indicates whether a missing input should be represented as the
363
+ * nullish value (`null`/`undefined`) or as a present but empty object
364
+ * (`true`). It keeps resetting consistent with the initial state.
365
+ */
366
+ isNullish: boolean;
367
+ /**
161
368
  * The children of the object field.
162
369
  */
163
370
  children: Record<string, InternalFieldStore>;
@@ -221,32 +428,6 @@ type InternalFieldStore = InternalArrayStore | InternalObjectStore | InternalVal
221
428
  */
222
429
  declare const INTERNAL: "~internal";
223
430
  //#endregion
224
- //#region src/types/utils/utils.d.ts
225
- /**
226
- * Checks if a type is `any`.
227
- */
228
- type IsAny<T> = 0 extends 1 & T ? true : false;
229
- /**
230
- * Checks if a type is `never`.
231
- */
232
- type IsNever<T> = [T] extends [never] ? true : false;
233
- /**
234
- * Constructs a type that is maybe a promise.
235
- */
236
- type MaybePromise<T> = T | Promise<T>;
237
- /**
238
- * Makes all properties deeply optional.
239
- */
240
- type DeepPartial<TValue> = TValue extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> | undefined } : TValue | undefined;
241
- /**
242
- * Makes all value properties optional.
243
- *
244
- * Hint: For dynamic arrays, only plain objects and nested arrays have their
245
- * values made optional. Primitives and class instances are kept as-is to avoid
246
- * types like `(string | undefined)[]`.
247
- */
248
- type PartialValues<TValue> = TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? (TItem extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TItem]: PartialValues<TItem[TKey]> } : TItem)[] : { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue | undefined;
249
- //#endregion
250
431
  //#region src/types/form/form.d.ts
251
432
  /**
252
433
  * Validation mode type.
@@ -330,136 +511,13 @@ type SubmitHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>
330
511
  */
331
512
  type SubmitEventHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>, event: SubmitEvent) => MaybePromise<unknown>;
332
513
  //#endregion
333
- //#region src/types/path/path.d.ts
334
- /**
335
- * Path key type.
336
- */
337
- type PathKey = string | number;
338
- /**
339
- * Path type.
340
- */
341
- type Path = readonly PathKey[];
342
- /**
343
- * Required path type.
344
- */
345
- type RequiredPath = readonly [PathKey, ...Path];
346
- /**
347
- * Extracts the exact keys of a tuple, array or object. Tuples return their
348
- * literal numeric indices, dynamic arrays return `number`, objects return
349
- * their `keyof` keys, and any other input returns `never`.
350
- */
351
- type ExactKeysOf<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly unknown[] ? number extends TValue["length"] ? number : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? TIndex : never }[number] : TValue extends Record<PropertyKey, unknown> ? keyof TValue & PathKey : never;
352
- /**
353
- * Returns the flat object of all indexable properties of `TValue`. For object
354
- * unions, properties from every member are merged so that any single property
355
- * is accessible. For primitives and other non-indexable types, the result is
356
- * `{}`.
357
- *
358
- * Hint: This is necessary to make properties accessible across union members.
359
- * By default, properties that do not exist in all union options are not
360
- * accessible and result in "any" when accessed.
361
- */
362
- type PropertiesOf<TValue> = { [TKey in ExactKeysOf<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
363
- /**
364
- * Lazily evaluates only the first valid path segment based on the given value.
365
- */
366
- type LazyPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValidPath : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOf<TValue>, ...infer TPathRest extends Path] ? LazyPath<Required<PropertiesOf<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOf<TValue>> extends false ? readonly [...TValidPath, ExactKeysOf<TValue>] : TValidPath;
367
- /**
368
- * Returns the path if valid, otherwise the first possible valid path based on
369
- * the given value.
370
- */
371
- type ValidPath<TValue, TPath extends RequiredPath> = TPath extends LazyPath<Required<TValue>, TPath> ? TPath : LazyPath<Required<TValue>, TPath>;
372
- /**
373
- * Detects whether the consuming project is configured with
374
- * `exactOptionalPropertyTypes: true`.
375
- *
376
- * Hint: If `false` the built-in `Required<T>` strips `| undefined` from
377
- * optional properties, so `Required<{ key?: undefined }>['key']` collapses
378
- * to `never` — under strict mode the same expression yields `undefined`.
379
- */
380
- type IsExactOptionalProps = Required<{
381
- key?: undefined;
382
- }>["key"] extends never ? false : true;
383
- /**
384
- * Like the built-in `Required<T>`, but preserves `| undefined` in two
385
- * places where `Required<T>` strips it:
386
- *
387
- * 1. Optional property values under `exactOptionalPropertyTypes: false`
388
- * — without this, input typings for `v.optional`/`v.nullish` schemas
389
- * narrow incorrectly (issue #15).
390
- * 2. Array/tuple element types — e.g. `(string | undefined)[]` stays
391
- * `(string | undefined)[]` instead of becoming `string[]`. Arrays
392
- * fall through unchanged because they only have a numeric index
393
- * signature and don't structurally extend `Record<PropertyKey,
394
- * unknown>` (which requires string keys).
395
- */
396
- type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExactOptionalProps extends true ? Required<TValue> : { [TKey in keyof Required<TValue>]: TValue[TKey] } : TValue;
397
- /**
398
- * Extracts the value type at the given path.
399
- */
400
- type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey, ...infer TRest extends Path] ? TKey extends ExactKeysOf<ExactRequired<TValue>> ? PathValue<PropertiesOf<ExactRequired<TValue>>[TKey], TRest> : unknown : TValue;
401
- /**
402
- * Checks whether a value is an array or contains one anywhere in its shape.
403
- *
404
- * Hint: The inner conditionals (`TValue extends readonly unknown[]` and
405
- * `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
406
- * so the inner expression returns the union of each member's result (e.g.
407
- * `true | false` when some members contain arrays and others don't).
408
- * Downstream code uses `IsOrHasArray<T> extends true`, but
409
- * `boolean extends true` is `false` — so we collapse the result via
410
- * `true extends ...`, which is `true` whenever at least one union member
411
- * contributed `true`.
412
- */
413
- type IsOrHasArray<TValue> = true extends (IsAny<TValue> extends true ? false : TValue extends readonly unknown[] ? true : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<TValue[TKey]> }[keyof TValue] : false) ? true : false;
414
- /**
415
- * Extracts the exact keys of a tuple, array or object that contain arrays.
416
- */
417
- type ExactKeysOfArrayPath<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? IsOrHasArray<TItem> extends true ? number : never : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TIndex : never : never }[number] : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TKey : never }[keyof TValue] & PathKey : never;
418
- /**
419
- * Returns the flat object of indexable properties of `TValue` whose values
420
- * are or contain arrays. Mirrors `PropertiesOf` but keyed by
421
- * `ExactKeysOfArrayPath` so the lookup is provably valid for array-path
422
- * navigation in `LazyArrayPath`.
423
- */
424
- type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
425
- /**
426
- * Lazily evaluates only the first valid array path segment based on the given value.
427
- */
428
- type LazyArrayPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValue extends readonly unknown[] ? TValidPath : readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOfArrayPath<TValue>, ...infer TPathRest extends Path] ? LazyArrayPath<Required<PropertiesOfArrayPath<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never;
429
- /**
430
- * Returns the path if valid, otherwise the first possible valid array path
431
- * based on the given value.
432
- */
433
- type ValidArrayPath<TValue, TPath extends RequiredPath> = TPath extends LazyArrayPath<Required<TValue>, TPath> ? TPath : LazyArrayPath<Required<TValue>, TPath>;
434
- /**
435
- * Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
436
- * or falls through to `never` when the child is not an object.
437
- */
438
- type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
439
- /**
440
- * Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
441
- * for a given input type. Object fields contribute their own path and the
442
- * paths of their descendants; arrays and tuples are atomic and contribute
443
- * only their own path, because dirty arrays are returned as complete units.
444
- *
445
- * Narrowing is exact for the first 5 levels of nesting; deeper paths fall
446
- * back to `RequiredPath` to keep the result a complete superset of any
447
- * path the runtime can address.
448
- *
449
- * Hint: Arrays and tuples are atomic because they don't structurally
450
- * extend `Record<PropertyKey, unknown>` and so fall through to `never`
451
- * via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
452
- * a tuple-length counter capped at 5 to bound TypeScript instantiation
453
- * cost.
454
- */
455
- type DirtyPath<TValue, TDepth extends 0[] = []> = TDepth["length"] extends 5 ? RequiredPath : TValue extends Record<PropertyKey, unknown> ? { [TKey in ExactKeysOf<TValue>]: readonly [TKey] | DeepDirtyPath<NonNullable<PropertiesOf<TValue>[TKey]>, TKey, TDepth> }[ExactKeysOf<TValue>] : never;
456
- //#endregion
457
514
  //#region src/array/copyItemState/copyItemState.d.ts
458
515
  /**
459
516
  * Copies the deeply nested state (signal values) from one field store to
460
517
  * another. This includes the `elements`, `errors`, `startInput`, `input`,
461
- * `isTouched`, `isDirty`, and for arrays `startItems` and `items` properties.
462
- * Recursively walks through the field stores and copies all signal values.
518
+ * `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
519
+ * properties. Recursively walks through the field stores and copies all signal
520
+ * values.
463
521
  *
464
522
  * @param fromInternalFieldStore The source field store to copy from.
465
523
  * @param toInternalFieldStore The destination field store to copy to.
@@ -469,28 +527,49 @@ declare function copyItemState(fromInternalFieldStore: InternalFieldStore, toInt
469
527
  //#region src/array/resetItemState/resetItemState.d.ts
470
528
  /**
471
529
  * Resets the state of a field store (signal values) deeply nested. Sets
472
- * `elements` to empty array, `errors` to `null`, `isTouched` and `isDirty` to
473
- * `false`, and `startInput`, `input`, `startItems`, and `items` to the new
474
- * input value. Keeps the `initialInput` and `initialItems` state unchanged for
475
- * form reset functionality.
530
+ * `elements` to empty array, `errors` to `null`, `isTouched`, `isEdited` and
531
+ * `isDirty` to `false`, and `startInput`, `input`, `startItems`, and `items` to
532
+ * the new input value. Keeps the `initialInput` and `initialItems` state
533
+ * unchanged for form reset functionality.
476
534
  *
477
535
  * @param internalFieldStore The field store to reset.
478
- * @param initialInput The new input value (can be any type including array or object).
536
+ * @param input The new input value (can be any type including array or object).
537
+ * @param keepStart Whether to keep `startInput` and `startItems` as the dirty
538
+ * baseline instead of resetting them to the new input. Used when a field store
539
+ * is reused for an in-place edit so its dirty state is detected correctly.
479
540
  */
480
- declare function resetItemState(internalFieldStore: InternalFieldStore, initialInput: unknown): void;
541
+ declare function resetItemState(internalFieldStore: InternalFieldStore, input: unknown, keepStart?: boolean): void;
481
542
  //#endregion
482
543
  //#region src/array/swapItemState/swapItemState.d.ts
483
544
  /**
484
545
  * Swaps the deeply nested state (signal values) between two field stores. This
485
546
  * includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
486
- * `isDirty`, and for arrays `startItems` and `items` properties. Recursively
487
- * walks through the field stores and swaps all signal values.
547
+ * `isEdited`, `isDirty`, and for arrays `startItems` and `items` properties.
548
+ * Recursively walks through the field stores and swaps all signal values.
488
549
  *
489
550
  * @param firstInternalFieldStore The first field store to swap.
490
551
  * @param secondInternalFieldStore The second field store to swap.
491
552
  */
492
553
  declare function swapItemState(firstInternalFieldStore: InternalFieldStore, secondInternalFieldStore: InternalFieldStore): void;
493
554
  //#endregion
555
+ //#region src/field/focusFieldElement/focusFieldElement.d.ts
556
+ /**
557
+ * Focuses the first focusable element of a field store. The elements are tried
558
+ * in order and the first one that actually receives focus wins, so detached,
559
+ * disabled or hidden elements are skipped. The browser decides focusability,
560
+ * which is read back via the element's root `activeElement` so elements in a
561
+ * shadow root or another document are handled correctly.
562
+ *
563
+ * Hint: A `display: none` or `hidden` element is correctly skipped in real
564
+ * browsers, but jsdom has no layout and focuses it anyway, so that case cannot
565
+ * be covered by unit tests.
566
+ *
567
+ * @param internalFieldStore The field store to focus.
568
+ *
569
+ * @returns Whether an element was focused.
570
+ */
571
+ declare function focusFieldElement(internalFieldStore: InternalFieldStore): boolean;
572
+ //#endregion
494
573
  //#region src/field/getDirtyFieldInput/getDirtyFieldInput.d.ts
495
574
  /**
496
575
  * Returns only the dirty input of the field store. Arrays are treated as
@@ -527,7 +606,7 @@ declare function getElementInput(element: FieldElement, internalFieldStore: Inte
527
606
  *
528
607
  * @returns Whether the property is true.
529
608
  */
530
- declare function getFieldBool(internalFieldStore: InternalFieldStore, type: "errors" | "isTouched" | "isDirty"): boolean;
609
+ declare function getFieldBool(internalFieldStore: InternalFieldStore, type: "errors" | "isTouched" | "isEdited" | "isDirty"): boolean;
531
610
  //#endregion
532
611
  //#region src/field/getFieldInput/getFieldInput.d.ts
533
612
  /**
@@ -566,7 +645,7 @@ type FieldSchema = v.ArraySchema<v.BaseSchema<unknown, unknown, v.BaseIssue<unkn
566
645
  * @param path The path to the field in the form.
567
646
  * @param nullish Whether the schema is wrapped in a nullish schema.
568
647
  */
569
- declare function initializeFieldStore(internalFieldStore: Partial<InternalFieldStore>, schema: FieldSchema, initialInput: unknown, path: PathKey[], nullish?: boolean): void;
648
+ declare function initializeFieldStore(internalFieldStore: Partial<InternalFieldStore>, schema: FieldSchema, initialInput: unknown, path: Path, nullish?: boolean): void;
570
649
  //#endregion
571
650
  //#region src/field/setFieldBool/setFieldBool.d.ts
572
651
  /**
@@ -604,12 +683,19 @@ declare function setInitialFieldInput(internalFieldStore: InternalFieldStore, in
604
683
  //#region src/field/walkFieldStore/walkFieldStore.d.ts
605
684
  /**
606
685
  * Walks through the field store and all nested children, calling the callback
607
- * for each field store in depth-first order.
686
+ * for each field store in depth-first order. The callback may return `true` to
687
+ * stop the walk early, in which case `walkFieldStore` returns `true` as well.
688
+ *
689
+ * The walk reads array `items` reactively, so a reactive caller subscribes to
690
+ * structural changes naturally. Imperative callers that must not subscribe
691
+ * (e.g. when invoked inside an effect) should wrap the call in `untrack`.
608
692
  *
609
693
  * @param internalFieldStore The field store to walk.
610
- * @param callback The callback to invoke for each field store.
694
+ * @param callback The callback to invoke for each field store. Return `true` to stop the walk early.
695
+ *
696
+ * @returns Whether the walk was stopped early by the callback.
611
697
  */
612
- declare function walkFieldStore(internalFieldStore: InternalFieldStore, callback: (internalFieldStore: InternalFieldStore) => void): void;
698
+ declare function walkFieldStore(internalFieldStore: InternalFieldStore, callback: (internalFieldStore: InternalFieldStore) => boolean | void): boolean;
613
699
  //#endregion
614
700
  //#region src/form/createFormStore/createFormStore.d.ts
615
701
  /**
@@ -624,6 +710,23 @@ declare function walkFieldStore(internalFieldStore: InternalFieldStore, callback
624
710
  */
625
711
  declare function createFormStore(config: FormConfig, parse: (input: unknown) => Promise<v.SafeParseResult<FormSchema>>): InternalFormStore;
626
712
  //#endregion
713
+ //#region src/form/decodeFormData/decodeFormData.d.ts
714
+ /**
715
+ * Decodes the entries of a form data object into nested form values using the
716
+ * Valibot schema as the source of truth. Information that is lost during the
717
+ * transfer via HTTP, like numbers, booleans, dates and unchecked checkboxes,
718
+ * is restored based on the schema.
719
+ *
720
+ * The keys of the form data are expected to be the stringified field paths that
721
+ * Formisch assigns to its field elements (for example `["todos",0,"label"]`).
722
+ *
723
+ * @param schema The form schema.
724
+ * @param formData The form data object.
725
+ *
726
+ * @returns The decoded form values.
727
+ */
728
+ declare function decodeFormData<TSchema extends FormSchema>(schema: TSchema, formData: FormData): unknown;
729
+ //#endregion
627
730
  //#region src/form/validateFormInput/validateFormInput.d.ts
628
731
  /**
629
732
  * Validate form input config interface.
@@ -693,4 +796,4 @@ declare function createSignal<T>(initialValue: T): Signal<T>;
693
796
  */
694
797
  declare function batch<T>(fn: () => T): T;
695
798
  //#endregion
696
- export { BaseFormStore, Batch, DeepPartial, DirtyPath, FieldElement, FieldSchema, FormConfig, FormSchema, INTERNAL, InternalArrayStore, InternalBaseStore, InternalFieldStore, InternalFormStore, InternalObjectStore, InternalValueStore, IsAny, IsNever, MaybePromise, PartialValues, Path, PathKey, PathValue, RequiredPath, Schema, Signal, SubmitEventHandler, SubmitHandler, Untrack, ValidArrayPath, ValidPath, ValidateFormInputConfig, ValidationMode, batch, copyItemState, createFormStore, createId, createSignal, framework, getDirtyFieldInput, getElementInput, getFieldBool, getFieldInput, getFieldStore, initializeFieldStore, resetItemState, setFieldBool, setFieldInput, setInitialFieldInput, swapItemState, untrack, validateFormInput, validateIfRequired, walkFieldStore };
799
+ export { BaseFormStore, Batch, DeepPartial, DirtyPath, FieldElement, FieldPath, FieldSchema, FormConfig, FormSchema, INTERNAL, InternalArrayStore, InternalBaseStore, InternalFieldStore, InternalFormStore, InternalObjectStore, InternalValueStore, IsAny, IsNever, MaybePromise, PartialValues, Path, PathKey, PathValue, RequiredPath, Schema, Signal, SubmitEventHandler, SubmitHandler, Untrack, ValidArrayPath, ValidPath, ValidateFormInputConfig, ValidationMode, batch, copyItemState, createFormStore, createId, createSignal, decodeFormData, focusFieldElement, framework, getDirtyFieldInput, getElementInput, getFieldBool, getFieldInput, getFieldStore, initializeFieldStore, resetItemState, setFieldBool, setFieldInput, setInitialFieldInput, swapItemState, untrack, validateFormInput, validateIfRequired, walkFieldStore };