@formisch/svelte 0.7.6 → 0.9.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.
@@ -179,31 +179,45 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
179
179
  * form reset functionality.
180
180
  *
181
181
  * @param internalFieldStore The field store to reset.
182
- * @param initialInput The new input value (can be any type including array or object).
182
+ * @param input The new input value (can be any type including array or object).
183
+ * @param keepStart Whether to keep `startInput` and `startItems` as the dirty
184
+ * baseline instead of resetting them to the new input. Used when a field store
185
+ * is reused for an in-place edit so its dirty state is detected correctly.
183
186
  */
184
- function resetItemState(internalFieldStore, initialInput) {
187
+ function resetItemState(internalFieldStore, input, keepStart = false) {
185
188
  batch(() => {
186
- internalFieldStore.elements = [];
189
+ const elements = [];
190
+ if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
191
+ internalFieldStore.elements = elements;
187
192
  internalFieldStore.errors.value = null;
188
193
  internalFieldStore.isTouched.value = false;
189
194
  internalFieldStore.isDirty.value = false;
190
195
  if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
191
- const objectInput = initialInput == null ? initialInput : true;
192
- internalFieldStore.startInput.value = objectInput;
196
+ const objectInput = input == null ? input : true;
197
+ if (!keepStart) internalFieldStore.startInput.value = objectInput;
193
198
  internalFieldStore.input.value = objectInput;
194
- if (internalFieldStore.kind === "array") if (initialInput) {
195
- const newItems = initialInput.map(createId);
196
- internalFieldStore.startItems.value = newItems;
199
+ if (internalFieldStore.kind === "array") if (input) {
200
+ const length = internalFieldStore.schema.type === "array" ? input.length : internalFieldStore.children.length;
201
+ const newItems = Array.from({ length }, createId);
202
+ if (!keepStart) internalFieldStore.startItems.value = newItems;
197
203
  internalFieldStore.items.value = newItems;
198
- for (let index = 0; index < initialInput.length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], initialInput[index]);
204
+ let path;
205
+ for (let index = 0; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], input[index], keepStart);
206
+ else {
207
+ path ??= JSON.parse(internalFieldStore.name);
208
+ internalFieldStore.children[index] = {};
209
+ path.push(index);
210
+ initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, input[index], path);
211
+ path.pop();
212
+ }
199
213
  } else {
200
- internalFieldStore.startItems.value = [];
214
+ if (!keepStart) internalFieldStore.startItems.value = [];
201
215
  internalFieldStore.items.value = [];
202
216
  }
203
- else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], initialInput?.[key]);
217
+ else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
204
218
  } else {
205
- internalFieldStore.startInput.value = initialInput;
206
- internalFieldStore.input.value = initialInput;
219
+ if (!keepStart) internalFieldStore.startInput.value = input;
220
+ internalFieldStore.input.value = input;
207
221
  }
208
222
  });
209
223
  }
@@ -273,6 +287,94 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
273
287
  });
274
288
  }
275
289
 
290
+ //#endregion
291
+ //#region src/field/focusFieldElement/focusFieldElement.ts
292
+ /**
293
+ * Focuses the first focusable element of a field store. The elements are tried
294
+ * in order and the first one that actually receives focus wins, so detached,
295
+ * disabled or hidden elements are skipped. The browser decides focusability,
296
+ * which is read back via the element's root `activeElement` so elements in a
297
+ * shadow root or another document are handled correctly.
298
+ *
299
+ * Hint: A `display: none` or `hidden` element is correctly skipped in real
300
+ * browsers, but jsdom has no layout and focuses it anyway, so that case cannot
301
+ * be covered by unit tests.
302
+ *
303
+ * @param internalFieldStore The field store to focus.
304
+ *
305
+ * @returns Whether an element was focused.
306
+ */
307
+ function focusFieldElement(internalFieldStore) {
308
+ for (const element of internalFieldStore.elements) {
309
+ element.focus();
310
+ if (element.getRootNode().activeElement === element) return true;
311
+ }
312
+ return false;
313
+ }
314
+
315
+ //#endregion
316
+ //#region src/field/getFieldBool/getFieldBool.ts
317
+ /**
318
+ * Returns whether the specified boolean property is true for the field store
319
+ * or any of its nested children. Recursively checks arrays and objects.
320
+ *
321
+ * @param internalFieldStore The field store to check.
322
+ * @param type The boolean property type to check.
323
+ *
324
+ * @returns Whether the property is true.
325
+ */
326
+ /* @__NO_SIDE_EFFECTS__ */
327
+ function getFieldBool(internalFieldStore, type) {
328
+ if (internalFieldStore[type].value) return true;
329
+ if (internalFieldStore.kind === "array") {
330
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
331
+ return false;
332
+ }
333
+ if (internalFieldStore.kind == "object") {
334
+ for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
335
+ return false;
336
+ }
337
+ return false;
338
+ }
339
+
340
+ //#endregion
341
+ //#region src/field/getDirtyFieldInput/getDirtyFieldInput.ts
342
+ /**
343
+ * Returns only the dirty input of the field store. Arrays are treated as
344
+ * atomic and returned in full if any item is dirty, while object keys without
345
+ * a dirty descendant are omitted. Returns `undefined` if no descendant is
346
+ * dirty.
347
+ *
348
+ * @param internalFieldStore The field store to get dirty input from.
349
+ * @param dirtyOnly Whether to only include dirty fields. Defaults to `true`.
350
+ *
351
+ * @returns The dirty input, or `undefined` if no descendant is dirty.
352
+ */
353
+ /* @__NO_SIDE_EFFECTS__ */
354
+ function getDirtyFieldInput(internalFieldStore, dirtyOnly = true) {
355
+ if (dirtyOnly && !/* @__PURE__ */ getFieldBool(internalFieldStore, "isDirty")) return;
356
+ if (internalFieldStore.kind === "array") {
357
+ if (internalFieldStore.input.value) {
358
+ const value = [];
359
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = /* @__PURE__ */ getDirtyFieldInput(internalFieldStore.children[index], false);
360
+ return value;
361
+ }
362
+ return internalFieldStore.input.value;
363
+ }
364
+ if (internalFieldStore.kind === "object") {
365
+ if (internalFieldStore.input.value) {
366
+ const value = {};
367
+ for (const key in internalFieldStore.children) {
368
+ const child = internalFieldStore.children[key];
369
+ if (!dirtyOnly || /* @__PURE__ */ getFieldBool(child, "isDirty")) value[key] = /* @__PURE__ */ getDirtyFieldInput(child, dirtyOnly);
370
+ }
371
+ return value;
372
+ }
373
+ return internalFieldStore.input.value;
374
+ }
375
+ return internalFieldStore.input.value;
376
+ }
377
+
276
378
  //#endregion
277
379
  //#region src/field/getFieldInput/getFieldInput.ts
278
380
  /**
@@ -335,31 +437,6 @@ function getElementInput(element, internalFieldStore) {
335
437
  return element.value;
336
438
  }
337
439
 
338
- //#endregion
339
- //#region src/field/getFieldBool/getFieldBool.ts
340
- /**
341
- * Returns whether the specified boolean property is true for the field store
342
- * or any of its nested children. Recursively checks arrays and objects.
343
- *
344
- * @param internalFieldStore The field store to check.
345
- * @param type The boolean property type to check.
346
- *
347
- * @returns Whether the property is true.
348
- */
349
- /* @__NO_SIDE_EFFECTS__ */
350
- function getFieldBool(internalFieldStore, type) {
351
- if (internalFieldStore[type].value) return true;
352
- if (internalFieldStore.kind === "array") {
353
- for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
354
- return false;
355
- }
356
- if (internalFieldStore.kind == "object") {
357
- for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
358
- return false;
359
- }
360
- return false;
361
- }
362
-
363
440
  //#endregion
364
441
  //#region src/field/getFieldStore/getFieldStore.ts
365
442
  /**
@@ -390,11 +467,9 @@ function getFieldStore(internalFormStore, path) {
390
467
  */
391
468
  function setFieldBool(internalFieldStore, type, bool) {
392
469
  batch(() => {
393
- if (internalFieldStore.kind === "array") {
394
- internalFieldStore[type].value = bool;
395
- for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
396
- } else if (internalFieldStore.kind == "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
397
- else internalFieldStore[type].value = bool;
470
+ internalFieldStore[type].value = bool;
471
+ if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
472
+ else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
398
473
  });
399
474
  }
400
475
 
@@ -412,20 +487,20 @@ function setNestedInput(internalFieldStore, input) {
412
487
  if (internalFieldStore.kind === "array") {
413
488
  const arrayInput = input ?? [];
414
489
  const items = internalFieldStore.items.value;
415
- if (arrayInput.length < items.length) internalFieldStore.items.value = items.slice(0, arrayInput.length);
416
- else if (arrayInput.length > items.length) {
417
- if (arrayInput.length > internalFieldStore.children.length) {
418
- const path = JSON.parse(internalFieldStore.name);
419
- for (let index = internalFieldStore.children.length; index < arrayInput.length; index++) {
420
- internalFieldStore.children[index] = {};
421
- path.push(index);
422
- initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
423
- path.pop();
424
- }
490
+ const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
491
+ if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
492
+ else if (length > items.length) {
493
+ const path = JSON.parse(internalFieldStore.name);
494
+ for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
495
+ else {
496
+ internalFieldStore.children[index] = {};
497
+ path.push(index);
498
+ initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
499
+ path.pop();
425
500
  }
426
- internalFieldStore.items.value = [...items, ...arrayInput.slice(items.length).map(createId)];
501
+ internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
427
502
  }
428
- for (let index = 0; index < arrayInput.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
503
+ for (let index = 0; index < length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
429
504
  internalFieldStore.input.value = input == null ? input : true;
430
505
  internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
431
506
  } else if (internalFieldStore.kind === "object") {
@@ -472,21 +547,22 @@ function setFieldInput(internalFormStore, path, input) {
472
547
  function setInitialFieldInput(internalFieldStore, initialInput) {
473
548
  batch(() => {
474
549
  if (internalFieldStore.kind === "array") {
475
- internalFieldStore.input.value = initialInput == null ? initialInput : true;
550
+ internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
476
551
  const initialArrayInput = initialInput ?? [];
477
- if (initialArrayInput.length > internalFieldStore.children.length) {
552
+ const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
553
+ if (length > internalFieldStore.children.length) {
478
554
  const path = JSON.parse(internalFieldStore.name);
479
- for (let index = internalFieldStore.children.length; index < initialArrayInput.length; index++) {
555
+ for (let index = internalFieldStore.children.length; index < length; index++) {
480
556
  internalFieldStore.children[index] = {};
481
557
  path.push(index);
482
558
  initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
483
559
  path.pop();
484
560
  }
485
561
  }
486
- internalFieldStore.initialItems.value = initialArrayInput.map(createId);
562
+ internalFieldStore.initialItems.value = Array.from({ length }, createId);
487
563
  for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
488
564
  } else if (internalFieldStore.kind === "object") {
489
- internalFieldStore.input.value = initialInput == null ? initialInput : true;
565
+ internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
490
566
  for (const key in internalFieldStore.children) setInitialFieldInput(internalFieldStore.children[key], initialInput?.[key]);
491
567
  } else internalFieldStore.initialInput.value = initialInput;
492
568
  });
@@ -532,6 +608,203 @@ function createFormStore(config, parse) {
532
608
  return store;
533
609
  }
534
610
 
611
+ //#endregion
612
+ //#region src/form/decodeFormData/decodeFormData.ts
613
+ const NUMBER_REGEX = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/u;
614
+ const ISO_DATE_TIME_REGEX = /^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])T(?:0\d|1\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?$/u;
615
+ const MAX_ARRAY_LENGTH = 5e3;
616
+ /**
617
+ * Unwraps wrapper and lazy schemas until a concrete schema is reached.
618
+ *
619
+ * @param schema The schema to unwrap.
620
+ *
621
+ * @returns The unwrapped schema.
622
+ */
623
+ function unwrapSchema(schema) {
624
+ switch (schema.type) {
625
+ case "exact_optional":
626
+ case "nullable":
627
+ case "nullish":
628
+ case "optional":
629
+ case "undefinedable":
630
+ case "non_nullable":
631
+ case "non_nullish":
632
+ case "non_optional": return unwrapSchema(schema.wrapped);
633
+ case "lazy": return unwrapSchema(schema.getter(void 0));
634
+ default: return schema;
635
+ }
636
+ }
637
+ /**
638
+ * Returns the child schema for the given key by traversing objects, arrays,
639
+ * tuples and schema options. Returns `undefined` if no child schema is found.
640
+ *
641
+ * @param schema The parent schema.
642
+ * @param key The path key.
643
+ *
644
+ * @returns The child schema or `undefined`.
645
+ */
646
+ function getChildSchema(schema, key) {
647
+ if (schema) {
648
+ const unwrapped = unwrapSchema(schema);
649
+ if (unwrapped.type === "object" || unwrapped.type === "loose_object" || unwrapped.type === "strict_object") return unwrapped.entries[key];
650
+ if (unwrapped.type === "array") return unwrapped.item;
651
+ if (unwrapped.type === "tuple" || unwrapped.type === "loose_tuple" || unwrapped.type === "strict_tuple") return unwrapped.items[key];
652
+ if (unwrapped.type === "union" || unwrapped.type === "intersect" || unwrapped.type === "variant") for (const option of unwrapped.options) {
653
+ const childSchema = getChildSchema(option, key);
654
+ if (childSchema !== void 0) return childSchema;
655
+ }
656
+ }
657
+ }
658
+ /**
659
+ * Decodes a stringified date based on its format. Empty strings become `null`.
660
+ *
661
+ * @param value The stringified value.
662
+ *
663
+ * @returns The decoded date.
664
+ */
665
+ function decodeDate(value) {
666
+ if (!value || value === "null") return null;
667
+ if (value === "undefined") return;
668
+ if (ISO_DATE_TIME_REGEX.test(value)) return /* @__PURE__ */ new Date(`${value}Z`);
669
+ return new Date(value);
670
+ }
671
+ /**
672
+ * Decodes a stringified boolean. Empty strings become `null`.
673
+ *
674
+ * @param value The stringified value.
675
+ *
676
+ * @returns The decoded boolean.
677
+ */
678
+ function decodeBoolean(value) {
679
+ if (!value || value === "null") return null;
680
+ if (value === "undefined") return;
681
+ return !(value === "false" || value === "off" || value === "0");
682
+ }
683
+ /**
684
+ * Decodes a stringified number. Empty strings become `null` and non-numeric
685
+ * values become `NaN`.
686
+ *
687
+ * @param value The stringified value.
688
+ *
689
+ * @returns The decoded number.
690
+ */
691
+ function decodeNumber(value) {
692
+ if (!value || value === "null") return null;
693
+ if (value === "undefined") return;
694
+ if (NUMBER_REGEX.test(value)) return Number(value);
695
+ return NaN;
696
+ }
697
+ /**
698
+ * Decodes a stringified bigint. Empty strings become `null` and invalid values
699
+ * are returned unchanged.
700
+ *
701
+ * @param value The stringified value.
702
+ *
703
+ * @returns The decoded bigint.
704
+ */
705
+ function decodeBigint(value) {
706
+ if (!value || value === "null") return null;
707
+ if (value === "undefined") return;
708
+ try {
709
+ return BigInt(value);
710
+ } catch {
711
+ return value;
712
+ }
713
+ }
714
+ /**
715
+ * Decodes a single form data value based on the concrete schema type. Files
716
+ * and unknown types are returned unchanged.
717
+ *
718
+ * @param value The form data value.
719
+ * @param schema The schema of the value.
720
+ *
721
+ * @returns The decoded value.
722
+ */
723
+ function decodeValue(value, schema) {
724
+ if (typeof value !== "string" || !schema) return value;
725
+ switch (unwrapSchema(schema).type) {
726
+ case "number": return decodeNumber(value);
727
+ case "boolean": return decodeBoolean(value);
728
+ case "date": return decodeDate(value);
729
+ case "bigint": return decodeBigint(value);
730
+ default: return value;
731
+ }
732
+ }
733
+ /**
734
+ * Fills in default values that are lost during the form data transfer. Booleans
735
+ * of unchecked checkboxes become `false` and absent arrays become empty. Only
736
+ * containers that are present in the decoded data are completed.
737
+ *
738
+ * @param schema The schema of the value.
739
+ * @param parent The parent object or array holding the value.
740
+ * @param key The key of the value within its parent.
741
+ */
742
+ function fillDefaults(schema, parent, key) {
743
+ const unwrappedSchema = unwrapSchema(schema);
744
+ if (unwrappedSchema.type === "boolean") {
745
+ if (parent[key] === void 0) parent[key] = false;
746
+ } else if (unwrappedSchema.type === "array") if (Array.isArray(parent[key])) for (let index = 0; index < parent[key].length; index++) fillDefaults(unwrappedSchema.item, parent[key], index);
747
+ else parent[key] = [];
748
+ else if (unwrappedSchema.type === "tuple" || unwrappedSchema.type === "loose_tuple" || unwrappedSchema.type === "strict_tuple") {
749
+ if (Array.isArray(parent[key])) for (let index = 0; index < unwrappedSchema.items.length; index++) fillDefaults(unwrappedSchema.items[index], parent[key], index);
750
+ } else if (unwrappedSchema.type === "object" || unwrappedSchema.type === "loose_object" || unwrappedSchema.type === "strict_object") {
751
+ if (parent[key] && typeof parent[key] === "object") for (const entryKey in unwrappedSchema.entries) fillDefaults(unwrappedSchema.entries[entryKey], parent[key], entryKey);
752
+ } else if (unwrappedSchema.type === "union" || unwrappedSchema.type === "intersect" || unwrappedSchema.type === "variant") for (const option of unwrappedSchema.options) fillDefaults(option, parent, key);
753
+ }
754
+ /**
755
+ * Decodes the entries of a form data object into nested form values using the
756
+ * Valibot schema as the source of truth. Information that is lost during the
757
+ * transfer via HTTP, like numbers, booleans, dates and unchecked checkboxes,
758
+ * is restored based on the schema.
759
+ *
760
+ * The keys of the form data are expected to be the stringified field paths that
761
+ * Formisch assigns to its field elements (for example `["todos",0,"label"]`).
762
+ *
763
+ * @param schema The form schema.
764
+ * @param formData The form data object.
765
+ *
766
+ * @returns The decoded form values.
767
+ */
768
+ /* @__NO_SIDE_EFFECTS__ */
769
+ function decodeFormData(schema, formData) {
770
+ const values = {};
771
+ formData.forEach((value, key) => {
772
+ let path = null;
773
+ try {
774
+ path = JSON.parse(key);
775
+ } catch {}
776
+ if (Array.isArray(path) && path.length > 0 && (typeof value === "string" || value.size > 0 || value.name !== "")) {
777
+ let parentValue = values;
778
+ let parentSchema = schema;
779
+ for (let index = 0; index < path.length; index++) {
780
+ const segment = path[index];
781
+ if (typeof segment !== "string" && typeof segment !== "number" || segment === "" || segment === "__proto__" || segment === "prototype" || segment === "constructor") break;
782
+ if (Array.isArray(parentValue)) {
783
+ if (typeof segment === "string") break;
784
+ if (segment >= MAX_ARRAY_LENGTH) throw new Error(`Array exceeds the maximum length of ${MAX_ARRAY_LENGTH}`);
785
+ }
786
+ const childSchema = getChildSchema(parentSchema, segment);
787
+ if (index === path.length - 1) {
788
+ const unwrappedSchema = childSchema && unwrapSchema(childSchema);
789
+ if (unwrappedSchema && unwrappedSchema.type === "array") {
790
+ parentValue[segment] ??= [];
791
+ parentValue[segment].push(decodeValue(value, unwrappedSchema.item));
792
+ } else parentValue[segment] = decodeValue(value, childSchema);
793
+ } else {
794
+ if (parentValue[segment] == null) {
795
+ const schemaType = childSchema && unwrapSchema(childSchema).type;
796
+ parentValue[segment] = schemaType === "array" || schemaType === "tuple" || schemaType === "loose_tuple" || schemaType === "strict_tuple" ? [] : {};
797
+ } else if (typeof parentValue[segment] !== "object") break;
798
+ parentValue = parentValue[segment];
799
+ parentSchema = childSchema;
800
+ }
801
+ }
802
+ }
803
+ });
804
+ fillDefaults(schema, { values }, "values");
805
+ return values;
806
+ }
807
+
535
808
  //#endregion
536
809
  //#region src/form/validateFormInput/validateFormInput.ts
537
810
  /**
@@ -547,44 +820,49 @@ function createFormStore(config, parse) {
547
820
  async function validateFormInput(internalFormStore, config) {
548
821
  internalFormStore.validators++;
549
822
  internalFormStore.isValidating.value = true;
550
- const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
551
- let rootErrors;
552
- let nestedErrors;
553
- if (result.issues) {
554
- nestedErrors = {};
555
- for (const issue of result.issues) if (issue.path) {
556
- const path = [];
557
- for (const pathItem of issue.path) {
558
- const key = pathItem.key;
559
- const keyType = typeof key;
560
- const itemType = pathItem.type;
561
- if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
562
- path.push(key);
563
- }
564
- const name = JSON.stringify(path);
565
- const fieldErrors = nestedErrors[name];
566
- if (fieldErrors) fieldErrors.push(issue.message);
567
- else nestedErrors[name] = [issue.message];
568
- } else if (rootErrors) rootErrors.push(issue.message);
569
- else rootErrors = [issue.message];
570
- }
571
- let shouldFocus = config?.shouldFocus ?? false;
572
- batch(() => {
573
- walkFieldStore(internalFormStore, (internalFieldStore) => {
574
- if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
575
- else {
576
- const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
577
- internalFieldStore.errors.value = fieldErrors;
578
- if (shouldFocus && fieldErrors) {
579
- internalFieldStore.elements[0]?.focus();
580
- shouldFocus = false;
823
+ try {
824
+ const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
825
+ let rootErrors;
826
+ let nestedErrors;
827
+ if (result.issues) {
828
+ nestedErrors = {};
829
+ for (const issue of result.issues) if (issue.path) {
830
+ const path = [];
831
+ for (const pathItem of issue.path) {
832
+ const key = pathItem.key;
833
+ const keyType = typeof key;
834
+ const itemType = pathItem.type;
835
+ if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
836
+ path.push(key);
581
837
  }
582
- }
838
+ const name = JSON.stringify(path);
839
+ const fieldErrors = nestedErrors[name];
840
+ if (fieldErrors) fieldErrors.push(issue.message);
841
+ else nestedErrors[name] = [issue.message];
842
+ } else if (rootErrors) rootErrors.push(issue.message);
843
+ else rootErrors = [issue.message];
844
+ }
845
+ let shouldFocus = config?.shouldFocus ?? false;
846
+ batch(() => {
847
+ walkFieldStore(internalFormStore, (internalFieldStore) => {
848
+ if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
849
+ else {
850
+ const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
851
+ internalFieldStore.errors.value = fieldErrors;
852
+ if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
853
+ }
854
+ });
855
+ internalFormStore.validators--;
856
+ internalFormStore.isValidating.value = internalFormStore.validators > 0;
583
857
  });
584
- internalFormStore.validators--;
585
- internalFormStore.isValidating.value = internalFormStore.validators > 0;
586
- });
587
- return result;
858
+ return result;
859
+ } catch (error) {
860
+ batch(() => {
861
+ internalFormStore.validators--;
862
+ internalFormStore.isValidating.value = internalFormStore.validators > 0;
863
+ });
864
+ throw error;
865
+ }
588
866
  }
589
867
 
590
868
  //#endregion
@@ -610,4 +888,4 @@ function validateIfRequired(internalFormStore, internalFieldStore, validationMod
610
888
  const INTERNAL = "~internal";
611
889
 
612
890
  //#endregion
613
- export { INTERNAL, batch, copyItemState, createFormStore, createId, createSignal, framework, getElementInput, getFieldBool, getFieldInput, getFieldStore, initializeFieldStore, resetItemState, setFieldBool, setFieldInput, setInitialFieldInput, swapItemState, untrack, validateFormInput, validateIfRequired, walkFieldStore };
891
+ export { INTERNAL, batch, copyItemState, createFormStore, createId, createSignal, decodeFormData, focusFieldElement, framework, getDirtyFieldInput, getElementInput, getFieldBool, getFieldInput, getFieldStore, initializeFieldStore, resetItemState, setFieldBool, setFieldInput, setInitialFieldInput, swapItemState, untrack, validateFormInput, validateIfRequired, walkFieldStore };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { DeepPartial, FieldElement, FormConfig, PartialValues, PathValue, RequiredPath, Schema, SubmitEventHandler, SubmitHandler, ValidArrayPath, ValidationMode, ValidPath, } from './core/index.svelte';
1
+ export type { DeepPartial, FieldElement, FormConfig, PartialValues, PathValue, RequiredPath, FormSchema, Schema, SubmitEventHandler, SubmitHandler, ValidArrayPath, ValidationMode, ValidPath, } from './core/index.svelte';
2
2
  export * from './methods/index.svelte';
3
3
  export * from './components/index';
4
4
  export * from './runes/index';