@formisch/qwik 0.5.0 → 0.6.1

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.
@@ -3,16 +3,40 @@ import { $, Slot, component$, createComputed$, createSignal, implicit$FirstArg,
3
3
  import { jsx } from "@qwik.dev/core/jsx-runtime";
4
4
 
5
5
  //#region ../../packages/core/dist/index.qwik.js
6
+ /**
7
+ * The current framework being used.
8
+ */
6
9
  const framework = "qwik";
10
+ /**
11
+ * Creates a unique identifier string.
12
+ *
13
+ * @returns The unique identifier.
14
+ */
15
+ /* @__NO_SIDE_EFFECTS__ */
7
16
  function createId() {
8
17
  return Math.random().toString(36).slice(2);
9
18
  }
19
+ /**
20
+ * Batches multiple signal updates into a single update cycle. This is a
21
+ * no-op in Qwik as batching is not needed.
22
+ *
23
+ * @param fn The function to execute.
24
+ *
25
+ * @returns The return value of the function.
26
+ */
10
27
  function batch(fn) {
11
28
  return fn();
12
29
  }
13
30
  /**
14
- * TODO: Add comment
15
- * TODO: Should this stay in /primitives or move to /utils?
31
+ * Initializes a field store recursively based on the schema structure. Handles
32
+ * array, object, and value schemas, setting up all necessary signals and
33
+ * children. Supports wrapped schemas and schema options.
34
+ *
35
+ * @param internalFieldStore The partial field store to initialize.
36
+ * @param schema The Valibot schema defining the field structure.
37
+ * @param initialInput The initial input value.
38
+ * @param path The path to the field in the form.
39
+ * @param nullish Whether the schema is wrapped in a nullish schema.
16
40
  */
17
41
  function initializeFieldStore(internalFieldStore, schema, initialInput, path, nullish = false) {
18
42
  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`);
@@ -83,13 +107,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
83
107
  }
84
108
  }
85
109
  /**
86
- * Copies the deeply nested state (signal values) from one array item to another.
87
- * This includes the `isTouched`, `isDirty`, `startInput`, `input`, `startItems`, and `items` properties.
110
+ * Copies the deeply nested state (signal values) from one field store to
111
+ * another. This includes the `elements`, `errors`, `startInput`, `input`,
112
+ * `isTouched`, `isDirty`, and for arrays `startItems` and `items` properties.
88
113
  * Recursively walks through the field stores and copies all signal values.
89
114
  *
90
- * @param internalArrayStore - The field store of the array (not the array item)
91
- * @param fromIndex - The source index to copy from
92
- * @param toIndex - The destination index to copy to
115
+ * @param fromInternalFieldStore The source field store to copy from.
116
+ * @param toInternalFieldStore The destination field store to copy to.
93
117
  */
94
118
  function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
95
119
  batch(() => {
@@ -120,13 +144,14 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
120
144
  });
121
145
  }
122
146
  /**
123
- * Resets the state of an array item (signal values) deeply nested.
124
- * Sets `isTouched` and `isDirty` to `false` and `startInput`, `input`,
125
- * `startItems` and `items` to the new input.
126
- * Keeps the `initialInput` and `initialItems` state unchanged for form reset functionality.
147
+ * Resets the state of a field store (signal values) deeply nested. Sets
148
+ * `elements` to empty array, `errors` to `null`, `isTouched` and `isDirty` to
149
+ * `false`, and `startInput`, `input`, `startItems`, and `items` to the new
150
+ * input value. Keeps the `initialInput` and `initialItems` state unchanged for
151
+ * form reset functionality.
127
152
  *
128
- * @param internalFieldStore - The field store of the array item
129
- * @param initialInput - The new input value (can be any type including array or object)
153
+ * @param internalFieldStore The field store to reset.
154
+ * @param initialInput The new input value (can be any type including array or object).
130
155
  */
131
156
  function resetItemState(internalFieldStore, initialInput) {
132
157
  batch(() => {
@@ -155,12 +180,13 @@ function resetItemState(internalFieldStore, initialInput) {
155
180
  });
156
181
  }
157
182
  /**
158
- * Swaps the deeply nested state (signal values) between two field stores.
159
- * This includes the `isTouched`, `isDirty`, `startInput`, `input`, `startItems`, and `items` properties.
160
- * Recursively walks through the field stores and swaps all signal values.
183
+ * Swaps the deeply nested state (signal values) between two field stores. This
184
+ * includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
185
+ * `isDirty`, and for arrays `startItems` and `items` properties. Recursively
186
+ * walks through the field stores and swaps all signal values.
161
187
  *
162
- * @param firstInternalFieldStore - The first field store to swap
163
- * @param secondInternalFieldStore - The second field store to swap
188
+ * @param firstInternalFieldStore The first field store to swap.
189
+ * @param secondInternalFieldStore The second field store to swap.
164
190
  */
165
191
  function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
166
192
  batch(() => {
@@ -215,11 +241,21 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
215
241
  });
216
242
  });
217
243
  }
244
+ /**
245
+ * Returns the current input of the field store. For arrays and objects,
246
+ * recursively collects input from all children. Returns `null` or `undefined`
247
+ * for nullish array/object inputs, or the primitive value for value fields.
248
+ *
249
+ * @param internalFieldStore The field store to get input from.
250
+ *
251
+ * @returns The field input.
252
+ */
253
+ /* @__NO_SIDE_EFFECTS__ */
218
254
  function getFieldInput(internalFieldStore) {
219
255
  if (internalFieldStore.kind === "array") {
220
256
  if (internalFieldStore.input.value) {
221
257
  const value = [];
222
- for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = getFieldInput(internalFieldStore.children[index]);
258
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = /* @__PURE__ */ getFieldInput(internalFieldStore.children[index]);
223
259
  return value;
224
260
  }
225
261
  return internalFieldStore.input.value;
@@ -227,7 +263,7 @@ function getFieldInput(internalFieldStore) {
227
263
  if (internalFieldStore.kind === "object") {
228
264
  if (internalFieldStore.input.value) {
229
265
  const value = {};
230
- for (const key in internalFieldStore.children) value[key] = getFieldInput(internalFieldStore.children[key]);
266
+ for (const key in internalFieldStore.children) value[key] = /* @__PURE__ */ getFieldInput(internalFieldStore.children[key]);
231
267
  return value;
232
268
  }
233
269
  return internalFieldStore.input.value;
@@ -235,13 +271,15 @@ function getFieldInput(internalFieldStore) {
235
271
  return internalFieldStore.input.value;
236
272
  }
237
273
  /**
238
- * Returns the current input of the element.
274
+ * Returns the current input of the element. Handles special cases for select
275
+ * multiple, checkbox groups, radio groups, and file inputs.
239
276
  *
240
277
  * @param element The field element.
241
- * @param interalFieldStore The interal field store.
278
+ * @param internalFieldStore The internal field store.
242
279
  *
243
280
  * @returns The element input.
244
281
  */
282
+ /* @__NO_SIDE_EFFECTS__ */
245
283
  function getElementInput(element, internalFieldStore) {
246
284
  if (element.options && element.multiple) return [...element.options].filter((option) => option.selected && !option.disabled).map((option) => option.value);
247
285
  if (element.type === "checkbox") {
@@ -250,7 +288,7 @@ function getElementInput(element, internalFieldStore) {
250
288
  return element.checked;
251
289
  }
252
290
  if (element.type === "radio") {
253
- const prevValue = untrack(() => getFieldInput(internalFieldStore));
291
+ const prevValue = untrack(() => /* @__PURE__ */ getFieldInput(internalFieldStore));
254
292
  if (element.checked) return [...prevValue, element.value];
255
293
  return prevValue.filter((value) => value !== element.value);
256
294
  }
@@ -260,23 +298,51 @@ function getElementInput(element, internalFieldStore) {
260
298
  }
261
299
  return element.value;
262
300
  }
301
+ /**
302
+ * Returns whether the specified boolean property is true for the field store
303
+ * or any of its nested children. Recursively checks arrays and objects.
304
+ *
305
+ * @param internalFieldStore The field store to check.
306
+ * @param type The boolean property type to check.
307
+ *
308
+ * @returns Whether the property is true.
309
+ */
310
+ /* @__NO_SIDE_EFFECTS__ */
263
311
  function getFieldBool(internalFieldStore, type) {
264
312
  if (internalFieldStore[type].value) return true;
265
313
  if (internalFieldStore.kind === "array") {
266
- for (let index = 0; index < internalFieldStore.items.value.length; index++) if (getFieldBool(internalFieldStore.children[index], type)) return true;
314
+ for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
267
315
  return false;
268
316
  }
269
317
  if (internalFieldStore.kind == "object") {
270
- for (const key in internalFieldStore.children) if (getFieldBool(internalFieldStore.children[key], type)) return true;
318
+ for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
271
319
  return false;
272
320
  }
273
321
  return false;
274
322
  }
323
+ /**
324
+ * Returns the field store at the specified path by traversing the form store's
325
+ * children hierarchy.
326
+ *
327
+ * @param internalFormStore The form store to traverse.
328
+ * @param path The path to the field store.
329
+ *
330
+ * @returns The field store.
331
+ */
332
+ /* @__NO_SIDE_EFFECTS__ */
275
333
  function getFieldStore(internalFormStore, path) {
276
334
  let internalFieldStore = internalFormStore;
277
335
  for (const key of path) internalFieldStore = internalFieldStore.children[key];
278
336
  return internalFieldStore;
279
337
  }
338
+ /**
339
+ * Sets the specified boolean property for the field store and all nested
340
+ * children. Recursively updates arrays and objects.
341
+ *
342
+ * @param internalFieldStore The field store to update.
343
+ * @param type The boolean property type to set.
344
+ * @param bool The boolean value to set.
345
+ */
280
346
  function setFieldBool(internalFieldStore, type, bool) {
281
347
  batch(() => {
282
348
  if (internalFieldStore.kind === "array") {
@@ -286,6 +352,13 @@ function setFieldBool(internalFieldStore, type, bool) {
286
352
  else internalFieldStore[type].value = bool;
287
353
  });
288
354
  }
355
+ /**
356
+ * Sets the input for a nested field store and all its children, updating
357
+ * touched and dirty states accordingly. Handles dynamic array resizing.
358
+ *
359
+ * @param internalFieldStore The field store to update.
360
+ * @param input The new input value.
361
+ */
289
362
  function setNestedInput(internalFieldStore, input) {
290
363
  internalFieldStore.isTouched.value = true;
291
364
  if (internalFieldStore.kind === "array") {
@@ -304,7 +377,7 @@ function setNestedInput(internalFieldStore, input) {
304
377
  }
305
378
  internalFieldStore.items.value = [...items, ...arrayInput.slice(items.length).map(createId)];
306
379
  }
307
- for (let index = 0; index < items.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
380
+ for (let index = 0; index < arrayInput.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
308
381
  internalFieldStore.input.value = input == null ? input : true;
309
382
  internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== items.length;
310
383
  } else if (internalFieldStore.kind === "object") {
@@ -317,6 +390,14 @@ function setNestedInput(internalFieldStore, input) {
317
390
  internalFieldStore.isDirty.value = startInput !== input && (startInput !== void 0 || input !== "" && !Number.isNaN(input));
318
391
  }
319
392
  }
393
+ /**
394
+ * Sets the input for a field at the specified path in the form store,
395
+ * traversing the path and updating all parent fields along the way.
396
+ *
397
+ * @param internalFormStore The form store containing the field.
398
+ * @param path The path to the field.
399
+ * @param input The new input value.
400
+ */
320
401
  function setFieldInput(internalFormStore, path, input) {
321
402
  batch(() => {
322
403
  untrack(() => {
@@ -329,6 +410,14 @@ function setFieldInput(internalFormStore, path, input) {
329
410
  });
330
411
  });
331
412
  }
413
+ /**
414
+ * Sets the initial input for a field store and all its children recursively.
415
+ * For arrays, initializes missing children if needed. Updates `initialInput`
416
+ * and `initialItems` properties.
417
+ *
418
+ * @param internalFieldStore The field store to update.
419
+ * @param initialInput The initial input value.
420
+ */
332
421
  function setInitialFieldInput(internalFieldStore, initialInput) {
333
422
  batch(() => {
334
423
  if (internalFieldStore.kind === "array") {
@@ -351,14 +440,32 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
351
440
  } else internalFieldStore.initialInput.value = initialInput;
352
441
  });
353
442
  }
443
+ /**
444
+ * Walks through the field store and all nested children, calling the callback
445
+ * for each field store in depth-first order.
446
+ *
447
+ * @param internalFieldStore The field store to walk.
448
+ * @param callback The callback to invoke for each field store.
449
+ */
354
450
  function walkFieldStore(internalFieldStore, callback) {
355
451
  callback(internalFieldStore);
356
452
  if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
357
453
  else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
358
454
  }
455
+ /**
456
+ * Creates a new internal form store from the provided configuration.
457
+ * Initializes the field store hierarchy, sets validation modes, and
458
+ * creates form state signals.
459
+ *
460
+ * @param config The form configuration.
461
+ * @param parse The schema parse function.
462
+ *
463
+ * @returns The internal form store.
464
+ */
359
465
  function createFormStore(config, parse) {
360
466
  const store = {};
361
467
  initializeFieldStore(store, config.schema, config.initialInput, []);
468
+ store.validators = 0;
362
469
  store.validate = config.validate ?? "submit";
363
470
  store.revalidate = config.revalidate ?? "input";
364
471
  store.parse = parse;
@@ -367,10 +474,20 @@ function createFormStore(config, parse) {
367
474
  store.isValidating = createSignal(false);
368
475
  return store;
369
476
  }
477
+ /**
478
+ * Validates the form input using the configured Valibot schema. Parses the
479
+ * current form input, processes validation issues, assigns errors to fields,
480
+ * and optionally focuses the first field with an error.
481
+ *
482
+ * @param internalFormStore The form store to validate.
483
+ * @param config The validation configuration.
484
+ *
485
+ * @returns The Valibot validation result.
486
+ */
370
487
  async function validateFormInput(internalFormStore, config) {
371
488
  internalFormStore.validators++;
372
489
  internalFormStore.isValidating.value = true;
373
- const result = await internalFormStore.parse(untrack(() => getFieldInput(internalFormStore)));
490
+ const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
374
491
  let rootErrors;
375
492
  let nestedErrors;
376
493
  if (result.issues) {
@@ -409,16 +526,46 @@ async function validateFormInput(internalFormStore, config) {
409
526
  });
410
527
  return result;
411
528
  }
529
+ /**
530
+ * Validates the form input if required based on the validation mode and form
531
+ * state. Determines whether to use initial validation mode, revalidation mode,
532
+ * or skip validation entirely.
533
+ *
534
+ * @param internalFormStore The form store to validate.
535
+ * @param internalFieldStore The field store that triggered validation.
536
+ * @param validationMode The validation mode that triggered this check.
537
+ */
412
538
  function validateIfRequired(internalFormStore, internalFieldStore, validationMode) {
413
- if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
539
+ if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => /* @__PURE__ */ getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
414
540
  }
541
+ /**
542
+ * Internal symbol constant.
543
+ */
415
544
  const INTERNAL = "~internal";
416
545
 
417
546
  //#endregion
418
547
  //#region ../../packages/methods/dist/index.qwik.js
419
- function focus(config) {
420
- getFieldStore(config.form[INTERNAL], config.path).elements[0]?.focus();
548
+ /**
549
+ * Focuses the first input element of a field. This is useful for
550
+ * programmatically setting focus to a specific field, such as after
551
+ * validation errors or user interactions.
552
+ *
553
+ * @param form The form store containing the field.
554
+ * @param config The focus field configuration.
555
+ */
556
+ function focus(form, config) {
557
+ getFieldStore(form[INTERNAL], config.path).elements[0]?.focus();
421
558
  }
559
+ /**
560
+ * Retrieves all error messages from all fields in the form by walking through
561
+ * the entire field store tree. This is useful for displaying a summary of all
562
+ * validation errors across the form.
563
+ *
564
+ * @param form The form store to retrieve errors from.
565
+ *
566
+ * @returns A non-empty array of error messages, or null if no errors exist.
567
+ */
568
+ /* @__NO_SIDE_EFFECTS__ */
422
569
  function getAllErrors(form) {
423
570
  let allErrors = null;
424
571
  walkFieldStore(form[INTERNAL], (internalFieldStore) => {
@@ -428,12 +575,15 @@ function getAllErrors(form) {
428
575
  });
429
576
  return allErrors;
430
577
  }
578
+ /* @__NO_SIDE_EFFECTS__ */
431
579
  function getErrors(form, config) {
432
580
  return (config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]).errors.value;
433
581
  }
582
+ /* @__NO_SIDE_EFFECTS__ */
434
583
  function getInput(form, config) {
435
584
  return getFieldInput(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]);
436
585
  }
586
+ /* @__NO_SIDE_EFFECTS__ */
437
587
  function handleSubmit(form, handler) {
438
588
  return async (event) => {
439
589
  event.preventDefault();
@@ -450,6 +600,13 @@ function handleSubmit(form, handler) {
450
600
  }
451
601
  };
452
602
  }
603
+ /**
604
+ * Inserts a new item into a field array at the specified index. All items at
605
+ * or after the insertion point are shifted up by one index.
606
+ *
607
+ * @param form The form store containing the field array.
608
+ * @param config The insert configuration specifying the path, index, and initial value.
609
+ */
453
610
  function insert(form, config) {
454
611
  const internalFormStore = form[INTERNAL];
455
612
  let internalFieldStore = internalFormStore;
@@ -477,6 +634,13 @@ function insert(form, config) {
477
634
  validateIfRequired(internalFormStore, internalArrayStore, "input");
478
635
  });
479
636
  }
637
+ /**
638
+ * Moves an item from one index to another within a field array. All items
639
+ * between the source and destination indices are shifted accordingly.
640
+ *
641
+ * @param form The form store containing the field array.
642
+ * @param config The move configuration specifying the path and source/destination indices.
643
+ */
480
644
  function move(form, config) {
481
645
  const internalFormStore = form[INTERNAL];
482
646
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -487,15 +651,22 @@ function move(form, config) {
487
651
  internalArrayStore.items.value = newItems;
488
652
  const tempInternalFieldStore = {};
489
653
  initializeFieldStore(tempInternalFieldStore, internalArrayStore.schema.item, void 0, []);
490
- copyItemState(internalArrayStore.children[config.from < config.to ? config.from : config.to], tempInternalFieldStore);
654
+ copyItemState(internalArrayStore.children[config.from], tempInternalFieldStore);
491
655
  if (config.from < config.to) for (let index = config.from; index < config.to; index++) copyItemState(internalArrayStore.children[index + 1], internalArrayStore.children[index]);
492
656
  else for (let index = config.from; index > config.to; index--) copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
493
- copyItemState(tempInternalFieldStore, internalArrayStore.children[config.from < config.to ? config.to : config.from]);
657
+ copyItemState(tempInternalFieldStore, internalArrayStore.children[config.to]);
494
658
  internalArrayStore.isTouched.value = true;
495
659
  internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
496
660
  validateIfRequired(internalFormStore, internalArrayStore, "input");
497
661
  });
498
662
  }
663
+ /**
664
+ * Removes an item from a field array at the specified index. All items after
665
+ * the removed item are shifted down by one index.
666
+ *
667
+ * @param form The form store containing the field array.
668
+ * @param config The remove configuration specifying the path and index.
669
+ */
499
670
  function remove(form, config) {
500
671
  const internalFormStore = form[INTERNAL];
501
672
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -510,6 +681,12 @@ function remove(form, config) {
510
681
  validateIfRequired(internalFormStore, internalArrayStore, "input");
511
682
  });
512
683
  }
684
+ /**
685
+ * Replaces an item in a field array at the specified index with new initial input.
686
+ *
687
+ * @param form The form store containing the field array.
688
+ * @param config The replace configuration specifying the path, index, and initial input.
689
+ */
513
690
  function replace(form, config) {
514
691
  const internalFormStore = form[INTERNAL];
515
692
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -565,9 +742,21 @@ function setInput(form, config) {
565
742
  validateIfRequired(internalFormStore, config.path ? getFieldStore(internalFormStore, config.path) : internalFormStore, "input");
566
743
  });
567
744
  }
745
+ /**
746
+ * Programmatically requests form submission by calling the native
747
+ * `requestSubmit()` method on the underlying form element.
748
+ *
749
+ * @param form The form store to submit.
750
+ */
568
751
  function submit(form) {
569
752
  form[INTERNAL].element?.requestSubmit();
570
753
  }
754
+ /**
755
+ * Swaps two items in a field array by exchanging their positions.
756
+ *
757
+ * @param form The form store containing the field array.
758
+ * @param config The swap configuration specifying the path and indices to swap.
759
+ */
571
760
  function swap(form, config) {
572
761
  const internalFormStore = form[INTERNAL];
573
762
  const internalArrayStore = getFieldStore(internalFormStore, config.path);
@@ -584,25 +773,46 @@ function swap(form, config) {
584
773
  validateIfRequired(internalFormStore, internalArrayStore, "input");
585
774
  });
586
775
  }
776
+ /**
777
+ * Validates the entire form input against its schema. Returns a safe parse result
778
+ * indicating success or failure with detailed issues. Optionally focuses the first
779
+ * field with validation errors.
780
+ *
781
+ * @param form The form store to validate.
782
+ * @param config The validate form configuration specifying focus behavior.
783
+ *
784
+ * @returns A promise resolving to the validation result.
785
+ */
587
786
  function validate(form, config) {
588
787
  return validateFormInput(form[INTERNAL], config);
589
788
  }
590
789
 
591
790
  //#endregion
592
791
  //#region src/hooks/usePathSignal/usePathSignal.ts
792
+ /* @__NO_SIDE_EFFECTS__ */
593
793
  function isEqual(a, b) {
594
794
  if (a.length !== b.length) return false;
595
795
  for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
596
796
  return true;
597
797
  }
798
+ /**
799
+ * Creates a readonly signal for a field path. Compares new path values for
800
+ * equality to avoid unnecessary updates to the signal.
801
+ *
802
+ * @param path The field path to wrap in a signal.
803
+ *
804
+ * @returns A readonly signal containing the field path.
805
+ */
806
+ /* @__NO_SIDE_EFFECTS__ */
598
807
  function usePathSignal(path) {
599
808
  const signal = useSignal(path);
600
- if (!isEqual(signal.value, path)) signal.value = path;
809
+ if (!/* @__PURE__ */ isEqual(signal.value, path)) signal.value = path;
601
810
  return signal;
602
811
  }
603
812
 
604
813
  //#endregion
605
814
  //#region src/hooks/useField/useField.ts
815
+ /* @__NO_SIDE_EFFECTS__ */
606
816
  function useField(form, config) {
607
817
  const pathSignal = usePathSignal(config.path);
608
818
  const internalFormStore = form[INTERNAL];
@@ -648,6 +858,7 @@ function useField(form, config) {
648
858
 
649
859
  //#endregion
650
860
  //#region src/hooks/useFieldArray/useFieldArray.ts
861
+ /* @__NO_SIDE_EFFECTS__ */
651
862
  function useFieldArray(form, config) {
652
863
  const pathSignal = usePathSignal(config.path);
653
864
  const internalFieldStore = useComputed$(() => getFieldStore(form[INTERNAL], pathSignal.value));
@@ -663,6 +874,15 @@ function useFieldArray(form, config) {
663
874
 
664
875
  //#endregion
665
876
  //#region src/hooks/useResolvedQrl/useResolvedQrl.ts
877
+ /**
878
+ * Resolves a QRL to its value within a Qwik component. Caches the resolved value
879
+ * to avoid re-resolving on each render and throws a promise during resolution.
880
+ *
881
+ * @param qrl The QRL to resolve.
882
+ *
883
+ * @returns The resolved value from the QRL.
884
+ */
885
+ /* @__NO_SIDE_EFFECTS__ */
666
886
  function useResolvedQrl(qrl) {
667
887
  const store = useSignal();
668
888
  const resolved = qrl.resolved ?? store.value;
@@ -674,6 +894,7 @@ function useResolvedQrl(qrl) {
674
894
 
675
895
  //#endregion
676
896
  //#region src/hooks/useForm$/useForm$.ts
897
+ /* @__NO_SIDE_EFFECTS__ */
677
898
  function useFormQrl(configQrl) {
678
899
  const config = useResolvedQrl(configQrl);
679
900
  const form = useConstant(() => {
@@ -698,12 +919,22 @@ function useFormQrl(configQrl) {
698
919
  });
699
920
  return form;
700
921
  }
922
+ /**
923
+ * Creates a reactive form store from a form configuration. The form store
924
+ * manages form state and provides reactive properties.
925
+ *
926
+ * @returns The form store with reactive properties.
927
+ */
701
928
  const useForm$ = implicit$FirstArg(useFormQrl);
702
929
 
703
930
  //#endregion
704
931
  //#region src/components/Field/Field.tsx
705
932
  /**
706
- * Headless form field that provides reactive properties and state.
933
+ * Headless form field component that provides reactive properties and state.
934
+ * The field component takes a form store, path to field, and a render function
935
+ * that receives a field store to display field state and handle user interactions.
936
+ *
937
+ * @returns The UI of the field to be rendered.
707
938
  */
708
939
  const Field = component$(({ of, path, render$ }) => {
709
940
  const field = useField(of, { path });
@@ -714,7 +945,12 @@ const Field = component$(({ of, path, render$ }) => {
714
945
  //#endregion
715
946
  //#region src/components/FieldArray/FieldArray.tsx
716
947
  /**
717
- * Headless field array that provides reactive properties and state.
948
+ * Headless field array component that provides reactive properties and state.
949
+ * The field array component takes a form store, path to array field, and a render
950
+ * function that receives a field array store to manage array items and handle
951
+ * array operations.
952
+ *
953
+ * @returns The UI of the field array to be rendered.
718
954
  */
719
955
  const FieldArray = component$(({ of, path, render$ }) => {
720
956
  const field = useFieldArray(of, { path });
@@ -724,6 +960,12 @@ const FieldArray = component$(({ of, path, render$ }) => {
724
960
 
725
961
  //#endregion
726
962
  //#region src/components/Form/Form.tsx
963
+ /**
964
+ * Form component that manages form submission and applies internal state.
965
+ * Wraps form element and passes submission events to the provided handler.
966
+ *
967
+ * @returns The a native form element.
968
+ */
727
969
  const Form = component$(({ of, onSubmit$,...other }) => {
728
970
  return /* @__PURE__ */ jsx("form", {
729
971
  ...other,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formisch/qwik",
3
3
  "description": "The modular and type-safe form library for Qwik",
4
- "version": "0.5.0",
4
+ "version": "0.6.1",
5
5
  "license": "MIT",
6
6
  "author": "Fabian Hiller",
7
7
  "homepage": "https://formisch.dev",
@@ -49,8 +49,8 @@
49
49
  "valibot": "^1.1.0",
50
50
  "vite": "7.0.4",
51
51
  "vite-tsconfig-paths": "^5.1.4",
52
- "@formisch/core": "0.4.0",
53
- "@formisch/methods": "0.3.0"
52
+ "@formisch/core": "0.4.1",
53
+ "@formisch/methods": "0.4.1"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@qwik.dev/core": ">=2",