@formisch/preact 0.5.0 → 0.6.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +692 -24
  2. package/dist/index.js +260 -33
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -4,13 +4,29 @@ import { useLayoutEffect, useMemo } from "preact/hooks";
4
4
  import { jsx } from "preact/jsx-runtime";
5
5
 
6
6
  //#region ../../packages/core/dist/index.preact.js
7
+ /**
8
+ * The current framework being used.
9
+ */
7
10
  const framework = "preact";
11
+ /**
12
+ * Creates a unique identifier string.
13
+ *
14
+ * @returns The unique identifier.
15
+ */
16
+ /* @__NO_SIDE_EFFECTS__ */
8
17
  function createId() {
9
18
  return Math.random().toString(36).slice(2);
10
19
  }
11
20
  /**
12
- * TODO: Add comment
13
- * TODO: Should this stay in /primitives or move to /utils?
21
+ * Initializes a field store recursively based on the schema structure. Handles
22
+ * array, object, and value schemas, setting up all necessary signals and
23
+ * children. Supports wrapped schemas and schema options.
24
+ *
25
+ * @param internalFieldStore The partial field store to initialize.
26
+ * @param schema The Valibot schema defining the field structure.
27
+ * @param initialInput The initial input value.
28
+ * @param path The path to the field in the form.
29
+ * @param nullish Whether the schema is wrapped in a nullish schema.
14
30
  */
15
31
  function initializeFieldStore(internalFieldStore, schema, initialInput, path, nullish = false) {
16
32
  if (framework === "qwik" && schema.type === "lazy" || schema.type === "object_with_rest" || schema.type === "record" || schema.type === "tuple_with_rest" || schema.type === "promise") throw new Error(`"${schema.type}" schema is not supported`);
@@ -81,13 +97,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
81
97
  }
82
98
  }
83
99
  /**
84
- * Copies the deeply nested state (signal values) from one array item to another.
85
- * This includes the `isTouched`, `isDirty`, `startInput`, `input`, `startItems`, and `items` properties.
100
+ * Copies the deeply nested state (signal values) from one field store to
101
+ * another. This includes the `elements`, `errors`, `startInput`, `input`,
102
+ * `isTouched`, `isDirty`, and for arrays `startItems` and `items` properties.
86
103
  * Recursively walks through the field stores and copies all signal values.
87
104
  *
88
- * @param internalArrayStore - The field store of the array (not the array item)
89
- * @param fromIndex - The source index to copy from
90
- * @param toIndex - The destination index to copy to
105
+ * @param fromInternalFieldStore The source field store to copy from.
106
+ * @param toInternalFieldStore The destination field store to copy to.
91
107
  */
92
108
  function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
93
109
  batch(() => {
@@ -118,13 +134,14 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
118
134
  });
119
135
  }
120
136
  /**
121
- * Resets the state of an array item (signal values) deeply nested.
122
- * Sets `isTouched` and `isDirty` to `false` and `startInput`, `input`,
123
- * `startItems` and `items` to the new input.
124
- * Keeps the `initialInput` and `initialItems` state unchanged for form reset functionality.
137
+ * Resets the state of a field store (signal values) deeply nested. Sets
138
+ * `elements` to empty array, `errors` to `null`, `isTouched` and `isDirty` to
139
+ * `false`, and `startInput`, `input`, `startItems`, and `items` to the new
140
+ * input value. Keeps the `initialInput` and `initialItems` state unchanged for
141
+ * form reset functionality.
125
142
  *
126
- * @param internalFieldStore - The field store of the array item
127
- * @param initialInput - The new input value (can be any type including array or object)
143
+ * @param internalFieldStore The field store to reset.
144
+ * @param initialInput The new input value (can be any type including array or object).
128
145
  */
129
146
  function resetItemState(internalFieldStore, initialInput) {
130
147
  batch(() => {
@@ -153,12 +170,13 @@ function resetItemState(internalFieldStore, initialInput) {
153
170
  });
154
171
  }
155
172
  /**
156
- * Swaps the deeply nested state (signal values) between two field stores.
157
- * This includes the `isTouched`, `isDirty`, `startInput`, `input`, `startItems`, and `items` properties.
158
- * Recursively walks through the field stores and swaps all signal values.
173
+ * Swaps the deeply nested state (signal values) between two field stores. This
174
+ * includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
175
+ * `isDirty`, and for arrays `startItems` and `items` properties. Recursively
176
+ * walks through the field stores and swaps all signal values.
159
177
  *
160
- * @param firstInternalFieldStore - The first field store to swap
161
- * @param secondInternalFieldStore - The second field store to swap
178
+ * @param firstInternalFieldStore The first field store to swap.
179
+ * @param secondInternalFieldStore The second field store to swap.
162
180
  */
163
181
  function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
164
182
  batch(() => {
@@ -213,11 +231,21 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
213
231
  });
214
232
  });
215
233
  }
234
+ /**
235
+ * Returns the current input of the field store. For arrays and objects,
236
+ * recursively collects input from all children. Returns `null` or `undefined`
237
+ * for nullish array/object inputs, or the primitive value for value fields.
238
+ *
239
+ * @param internalFieldStore The field store to get input from.
240
+ *
241
+ * @returns The field input.
242
+ */
243
+ /* @__NO_SIDE_EFFECTS__ */
216
244
  function getFieldInput(internalFieldStore) {
217
245
  if (internalFieldStore.kind === "array") {
218
246
  if (internalFieldStore.input.value) {
219
247
  const value = [];
220
- for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = getFieldInput(internalFieldStore.children[index]);
248
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = /* @__PURE__ */ getFieldInput(internalFieldStore.children[index]);
221
249
  return value;
222
250
  }
223
251
  return internalFieldStore.input.value;
@@ -225,7 +253,7 @@ function getFieldInput(internalFieldStore) {
225
253
  if (internalFieldStore.kind === "object") {
226
254
  if (internalFieldStore.input.value) {
227
255
  const value = {};
228
- for (const key in internalFieldStore.children) value[key] = getFieldInput(internalFieldStore.children[key]);
256
+ for (const key in internalFieldStore.children) value[key] = /* @__PURE__ */ getFieldInput(internalFieldStore.children[key]);
229
257
  return value;
230
258
  }
231
259
  return internalFieldStore.input.value;
@@ -233,13 +261,15 @@ function getFieldInput(internalFieldStore) {
233
261
  return internalFieldStore.input.value;
234
262
  }
235
263
  /**
236
- * Returns the current input of the element.
264
+ * Returns the current input of the element. Handles special cases for select
265
+ * multiple, checkbox groups, radio groups, and file inputs.
237
266
  *
238
267
  * @param element The field element.
239
- * @param interalFieldStore The interal field store.
268
+ * @param internalFieldStore The internal field store.
240
269
  *
241
270
  * @returns The element input.
242
271
  */
272
+ /* @__NO_SIDE_EFFECTS__ */
243
273
  function getElementInput(element, internalFieldStore) {
244
274
  if (element.options && element.multiple) return [...element.options].filter((option) => option.selected && !option.disabled).map((option) => option.value);
245
275
  if (element.type === "checkbox") {
@@ -248,7 +278,7 @@ function getElementInput(element, internalFieldStore) {
248
278
  return element.checked;
249
279
  }
250
280
  if (element.type === "radio") {
251
- const prevValue = untrack(() => getFieldInput(internalFieldStore));
281
+ const prevValue = untrack(() => /* @__PURE__ */ getFieldInput(internalFieldStore));
252
282
  if (element.checked) return [...prevValue, element.value];
253
283
  return prevValue.filter((value) => value !== element.value);
254
284
  }
@@ -258,23 +288,51 @@ function getElementInput(element, internalFieldStore) {
258
288
  }
259
289
  return element.value;
260
290
  }
291
+ /**
292
+ * Returns whether the specified boolean property is true for the field store
293
+ * or any of its nested children. Recursively checks arrays and objects.
294
+ *
295
+ * @param internalFieldStore The field store to check.
296
+ * @param type The boolean property type to check.
297
+ *
298
+ * @returns Whether the property is true.
299
+ */
300
+ /* @__NO_SIDE_EFFECTS__ */
261
301
  function getFieldBool(internalFieldStore, type) {
262
302
  if (internalFieldStore[type].value) return true;
263
303
  if (internalFieldStore.kind === "array") {
264
- for (let index = 0; index < internalFieldStore.items.value.length; index++) if (getFieldBool(internalFieldStore.children[index], type)) return true;
304
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
265
305
  return false;
266
306
  }
267
307
  if (internalFieldStore.kind == "object") {
268
- for (const key in internalFieldStore.children) if (getFieldBool(internalFieldStore.children[key], type)) return true;
308
+ for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
269
309
  return false;
270
310
  }
271
311
  return false;
272
312
  }
313
+ /**
314
+ * Returns the field store at the specified path by traversing the form store's
315
+ * children hierarchy.
316
+ *
317
+ * @param internalFormStore The form store to traverse.
318
+ * @param path The path to the field store.
319
+ *
320
+ * @returns The field store.
321
+ */
322
+ /* @__NO_SIDE_EFFECTS__ */
273
323
  function getFieldStore(internalFormStore, path) {
274
324
  let internalFieldStore = internalFormStore;
275
325
  for (const key of path) internalFieldStore = internalFieldStore.children[key];
276
326
  return internalFieldStore;
277
327
  }
328
+ /**
329
+ * Sets the specified boolean property for the field store and all nested
330
+ * children. Recursively updates arrays and objects.
331
+ *
332
+ * @param internalFieldStore The field store to update.
333
+ * @param type The boolean property type to set.
334
+ * @param bool The boolean value to set.
335
+ */
278
336
  function setFieldBool(internalFieldStore, type, bool) {
279
337
  batch(() => {
280
338
  if (internalFieldStore.kind === "array") {
@@ -284,6 +342,13 @@ function setFieldBool(internalFieldStore, type, bool) {
284
342
  else internalFieldStore[type].value = bool;
285
343
  });
286
344
  }
345
+ /**
346
+ * Sets the input for a nested field store and all its children, updating
347
+ * touched and dirty states accordingly. Handles dynamic array resizing.
348
+ *
349
+ * @param internalFieldStore The field store to update.
350
+ * @param input The new input value.
351
+ */
287
352
  function setNestedInput(internalFieldStore, input) {
288
353
  internalFieldStore.isTouched.value = true;
289
354
  if (internalFieldStore.kind === "array") {
@@ -302,7 +367,7 @@ function setNestedInput(internalFieldStore, input) {
302
367
  }
303
368
  internalFieldStore.items.value = [...items, ...arrayInput.slice(items.length).map(createId)];
304
369
  }
305
- for (let index = 0; index < items.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
370
+ for (let index = 0; index < arrayInput.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
306
371
  internalFieldStore.input.value = input == null ? input : true;
307
372
  internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== items.length;
308
373
  } else if (internalFieldStore.kind === "object") {
@@ -315,6 +380,14 @@ function setNestedInput(internalFieldStore, input) {
315
380
  internalFieldStore.isDirty.value = startInput !== input && (startInput !== void 0 || input !== "" && !Number.isNaN(input));
316
381
  }
317
382
  }
383
+ /**
384
+ * Sets the input for a field at the specified path in the form store,
385
+ * traversing the path and updating all parent fields along the way.
386
+ *
387
+ * @param internalFormStore The form store containing the field.
388
+ * @param path The path to the field.
389
+ * @param input The new input value.
390
+ */
318
391
  function setFieldInput(internalFormStore, path, input) {
319
392
  batch(() => {
320
393
  untrack(() => {
@@ -327,6 +400,14 @@ function setFieldInput(internalFormStore, path, input) {
327
400
  });
328
401
  });
329
402
  }
403
+ /**
404
+ * Sets the initial input for a field store and all its children recursively.
405
+ * For arrays, initializes missing children if needed. Updates `initialInput`
406
+ * and `initialItems` properties.
407
+ *
408
+ * @param internalFieldStore The field store to update.
409
+ * @param initialInput The initial input value.
410
+ */
330
411
  function setInitialFieldInput(internalFieldStore, initialInput) {
331
412
  batch(() => {
332
413
  if (internalFieldStore.kind === "array") {
@@ -349,14 +430,32 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
349
430
  } else internalFieldStore.initialInput.value = initialInput;
350
431
  });
351
432
  }
433
+ /**
434
+ * Walks through the field store and all nested children, calling the callback
435
+ * for each field store in depth-first order.
436
+ *
437
+ * @param internalFieldStore The field store to walk.
438
+ * @param callback The callback to invoke for each field store.
439
+ */
352
440
  function walkFieldStore(internalFieldStore, callback) {
353
441
  callback(internalFieldStore);
354
442
  if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
355
443
  else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
356
444
  }
445
+ /**
446
+ * Creates a new internal form store from the provided configuration.
447
+ * Initializes the field store hierarchy, sets validation modes, and
448
+ * creates form state signals.
449
+ *
450
+ * @param config The form configuration.
451
+ * @param parse The schema parse function.
452
+ *
453
+ * @returns The internal form store.
454
+ */
357
455
  function createFormStore(config, parse) {
358
456
  const store = {};
359
457
  initializeFieldStore(store, config.schema, config.initialInput, []);
458
+ store.validators = 0;
360
459
  store.validate = config.validate ?? "submit";
361
460
  store.revalidate = config.revalidate ?? "input";
362
461
  store.parse = parse;
@@ -365,10 +464,20 @@ function createFormStore(config, parse) {
365
464
  store.isValidating = createSignal(false);
366
465
  return store;
367
466
  }
467
+ /**
468
+ * Validates the form input using the configured Valibot schema. Parses the
469
+ * current form input, processes validation issues, assigns errors to fields,
470
+ * and optionally focuses the first field with an error.
471
+ *
472
+ * @param internalFormStore The form store to validate.
473
+ * @param config The validation configuration.
474
+ *
475
+ * @returns The Valibot validation result.
476
+ */
368
477
  async function validateFormInput(internalFormStore, config) {
369
478
  internalFormStore.validators++;
370
479
  internalFormStore.isValidating.value = true;
371
- const result = await internalFormStore.parse(untrack(() => getFieldInput(internalFormStore)));
480
+ const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
372
481
  let rootErrors;
373
482
  let nestedErrors;
374
483
  if (result.issues) {
@@ -407,16 +516,46 @@ async function validateFormInput(internalFormStore, config) {
407
516
  });
408
517
  return result;
409
518
  }
519
+ /**
520
+ * Validates the form input if required based on the validation mode and form
521
+ * state. Determines whether to use initial validation mode, revalidation mode,
522
+ * or skip validation entirely.
523
+ *
524
+ * @param internalFormStore The form store to validate.
525
+ * @param internalFieldStore The field store that triggered validation.
526
+ * @param validationMode The validation mode that triggered this check.
527
+ */
410
528
  function validateIfRequired(internalFormStore, internalFieldStore, validationMode) {
411
- if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
529
+ if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => /* @__PURE__ */ getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
412
530
  }
531
+ /**
532
+ * Internal symbol constant.
533
+ */
413
534
  const INTERNAL = "~internal";
414
535
 
415
536
  //#endregion
416
537
  //#region ../../packages/methods/dist/index.preact.js
417
- function focus(config) {
418
- getFieldStore(config.form[INTERNAL], config.path).elements[0]?.focus();
538
+ /**
539
+ * Focuses the first input element of a field. This is useful for
540
+ * programmatically setting focus to a specific field, such as after
541
+ * validation errors or user interactions.
542
+ *
543
+ * @param form The form store containing the field.
544
+ * @param config The focus field configuration.
545
+ */
546
+ function focus(form, config) {
547
+ getFieldStore(form[INTERNAL], config.path).elements[0]?.focus();
419
548
  }
549
+ /**
550
+ * Retrieves all error messages from all fields in the form by walking through
551
+ * the entire field store tree. This is useful for displaying a summary of all
552
+ * validation errors across the form.
553
+ *
554
+ * @param form The form store to retrieve errors from.
555
+ *
556
+ * @returns A non-empty array of error messages, or null if no errors exist.
557
+ */
558
+ /* @__NO_SIDE_EFFECTS__ */
420
559
  function getAllErrors(form) {
421
560
  let allErrors = null;
422
561
  walkFieldStore(form[INTERNAL], (internalFieldStore) => {
@@ -426,12 +565,15 @@ function getAllErrors(form) {
426
565
  });
427
566
  return allErrors;
428
567
  }
568
+ /* @__NO_SIDE_EFFECTS__ */
429
569
  function getErrors(form, config) {
430
570
  return (config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]).errors.value;
431
571
  }
572
+ /* @__NO_SIDE_EFFECTS__ */
432
573
  function getInput(form, config) {
433
574
  return getFieldInput(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]);
434
575
  }
576
+ /* @__NO_SIDE_EFFECTS__ */
435
577
  function handleSubmit(form, handler) {
436
578
  return async (event) => {
437
579
  event.preventDefault();
@@ -448,6 +590,13 @@ function handleSubmit(form, handler) {
448
590
  }
449
591
  };
450
592
  }
593
+ /**
594
+ * Inserts a new item into a field array at the specified index. All items at
595
+ * or after the insertion point are shifted up by one index.
596
+ *
597
+ * @param form The form store containing the field array.
598
+ * @param config The insert configuration specifying the path, index, and initial value.
599
+ */
451
600
  function insert(form, config) {
452
601
  const internalFormStore = form[INTERNAL];
453
602
  let internalFieldStore = internalFormStore;
@@ -475,6 +624,13 @@ function insert(form, config) {
475
624
  validateIfRequired(internalFormStore, internalArrayStore, "input");
476
625
  });
477
626
  }
627
+ /**
628
+ * Moves an item from one index to another within a field array. All items
629
+ * between the source and destination indices are shifted accordingly.
630
+ *
631
+ * @param form The form store containing the field array.
632
+ * @param config The move configuration specifying the path and source/destination indices.
633
+ */
478
634
  function move(form, config) {
479
635
  const internalFormStore = form[INTERNAL];
480
636
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -494,6 +650,13 @@ function move(form, config) {
494
650
  validateIfRequired(internalFormStore, internalArrayStore, "input");
495
651
  });
496
652
  }
653
+ /**
654
+ * Removes an item from a field array at the specified index. All items after
655
+ * the removed item are shifted down by one index.
656
+ *
657
+ * @param form The form store containing the field array.
658
+ * @param config The remove configuration specifying the path and index.
659
+ */
497
660
  function remove(form, config) {
498
661
  const internalFormStore = form[INTERNAL];
499
662
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -508,6 +671,12 @@ function remove(form, config) {
508
671
  validateIfRequired(internalFormStore, internalArrayStore, "input");
509
672
  });
510
673
  }
674
+ /**
675
+ * Replaces an item in a field array at the specified index with new initial input.
676
+ *
677
+ * @param form The form store containing the field array.
678
+ * @param config The replace configuration specifying the path, index, and initial input.
679
+ */
511
680
  function replace(form, config) {
512
681
  const internalFormStore = form[INTERNAL];
513
682
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -563,9 +732,21 @@ function setInput(form, config) {
563
732
  validateIfRequired(internalFormStore, config.path ? getFieldStore(internalFormStore, config.path) : internalFormStore, "input");
564
733
  });
565
734
  }
735
+ /**
736
+ * Programmatically requests form submission by calling the native
737
+ * `requestSubmit()` method on the underlying form element.
738
+ *
739
+ * @param form The form store to submit.
740
+ */
566
741
  function submit(form) {
567
742
  form[INTERNAL].element?.requestSubmit();
568
743
  }
744
+ /**
745
+ * Swaps two items in a field array by exchanging their positions.
746
+ *
747
+ * @param form The form store containing the field array.
748
+ * @param config The swap configuration specifying the path and indices to swap.
749
+ */
569
750
  function swap(form, config) {
570
751
  const internalFormStore = form[INTERNAL];
571
752
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -582,25 +763,53 @@ function swap(form, config) {
582
763
  validateIfRequired(internalFormStore, internalArrayStore, "input");
583
764
  });
584
765
  }
766
+ /**
767
+ * Validates the entire form input against its schema. Returns a safe parse result
768
+ * indicating success or failure with detailed issues. Optionally focuses the first
769
+ * field with validation errors.
770
+ *
771
+ * @param form The form store to validate.
772
+ * @param config The validate form configuration specifying focus behavior.
773
+ *
774
+ * @returns A promise resolving to the validation result.
775
+ */
585
776
  function validate(form, config) {
586
777
  return validateFormInput(form[INTERNAL], config);
587
778
  }
588
779
 
589
780
  //#endregion
590
781
  //#region src/hooks/usePathSignal/usePathSignal.ts
782
+ /**
783
+ * Compares two paths for equality.
784
+ *
785
+ * @param a The first path.
786
+ * @param b The second path.
787
+ *
788
+ * @returns Whether the paths are equal.
789
+ */
790
+ /* @__NO_SIDE_EFFECTS__ */
591
791
  function isEqual(a, b) {
592
792
  if (a.length !== b.length) return false;
593
793
  for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
594
794
  return true;
595
795
  }
796
+ /**
797
+ * Creates a readonly signal for a path that updates when the path changes.
798
+ *
799
+ * @param path The path value.
800
+ *
801
+ * @returns A readonly signal containing the path.
802
+ */
803
+ /* @__NO_SIDE_EFFECTS__ */
596
804
  function usePathSignal(path) {
597
805
  const signal = useSignal(path);
598
- if (!isEqual(signal.value, path)) signal.value = path;
806
+ if (!/* @__PURE__ */ isEqual(signal.value, path)) signal.value = path;
599
807
  return signal;
600
808
  }
601
809
 
602
810
  //#endregion
603
811
  //#region src/hooks/useField/useField.ts
812
+ /* @__NO_SIDE_EFFECTS__ */
604
813
  function useField(form, config) {
605
814
  const pathSignal = usePathSignal(config.path);
606
815
  const internalFormStore = form[INTERNAL];
@@ -645,6 +854,7 @@ function useField(form, config) {
645
854
 
646
855
  //#endregion
647
856
  //#region src/hooks/useFieldArray/useFieldArray.ts
857
+ /* @__NO_SIDE_EFFECTS__ */
648
858
  function useFieldArray(form, config) {
649
859
  const pathSignal = usePathSignal(config.path);
650
860
  const internalFieldStore = useComputed(() => getFieldStore(form[INTERNAL], pathSignal.value));
@@ -660,6 +870,7 @@ function useFieldArray(form, config) {
660
870
 
661
871
  //#endregion
662
872
  //#region src/hooks/useForm/useForm.ts
873
+ /* @__NO_SIDE_EFFECTS__ */
663
874
  function useForm(config) {
664
875
  const form = useMemo(() => {
665
876
  const internalFormStore = createFormStore(config, (input) => v.safeParseAsync(config.schema, input));
@@ -683,8 +894,15 @@ function useForm(config) {
683
894
  //#endregion
684
895
  //#region src/components/Field/Field.tsx
685
896
  /**
686
- * Headless form field that provides reactive properties and state.
897
+ * Headless form field component that provides reactive properties and state.
898
+ * The field component takes a form store, path to field, and a render function
899
+ * that receives a field store to display field state and handle user interactions.
900
+ *
901
+ * @param props The field component props.
902
+ *
903
+ * @returns The UI of the field to be rendered.
687
904
  */
905
+ /* @__NO_SIDE_EFFECTS__ */
688
906
  function Field({ of, path, children }) {
689
907
  const field = useField(of, { path });
690
908
  return children(field);
@@ -693,8 +911,16 @@ function Field({ of, path, children }) {
693
911
  //#endregion
694
912
  //#region src/components/FieldArray/FieldArray.tsx
695
913
  /**
696
- * Headless field array that provides reactive properties and state.
914
+ * Headless field array component that provides reactive properties and state.
915
+ * The field array component takes a form store, path to array field, and a render
916
+ * function that receives a field array store to manage array items and handle
917
+ * array operations.
918
+ *
919
+ * @param props The field array component props.
920
+ *
921
+ * @returns The UI of the field array to be rendered.
697
922
  */
923
+ /* @__NO_SIDE_EFFECTS__ */
698
924
  function FieldArray({ of, path, children }) {
699
925
  const field = useFieldArray(of, { path });
700
926
  return children(field);
@@ -702,6 +928,7 @@ function FieldArray({ of, path, children }) {
702
928
 
703
929
  //#endregion
704
930
  //#region src/components/Form/Form.tsx
931
+ /* @__NO_SIDE_EFFECTS__ */
705
932
  function Form({ of, onSubmit,...other }) {
706
933
  return /* @__PURE__ */ jsx("form", {
707
934
  ...other,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formisch/preact",
3
3
  "description": "The modular and type-safe form library for Preact",
4
- "version": "0.5.0",
4
+ "version": "0.6.0",
5
5
  "license": "MIT",
6
6
  "author": "Fabian Hiller",
7
7
  "homepage": "https://formisch.dev",
@@ -43,8 +43,8 @@
43
43
  "typescript": "^5.8.3",
44
44
  "typescript-eslint": "^8.37.0",
45
45
  "vite": "^6.0.4",
46
- "@formisch/core": "0.4.0",
47
- "@formisch/methods": "0.3.0"
46
+ "@formisch/methods": "0.4.0",
47
+ "@formisch/core": "0.4.1"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@preact/signals": "^2.0.0",