@formisch/react 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.
- package/README.md +1 -0
- package/dist/index.d.ts +14 -4
- package/dist/index.js +116 -74
- package/package.json +3 -3
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 – re-renders only if necessary
|
|
13
14
|
- Minimal, readable and well thought out API
|
|
14
15
|
- Supports all native HTML form fields
|
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,13 @@ interface InternalBaseStore {
|
|
|
72
72
|
schema: Schema;
|
|
73
73
|
/**
|
|
74
74
|
* The initial elements of the field.
|
|
75
|
+
*
|
|
76
|
+
* Hint: This may look unused, but do not remove it. `copyItemState` and
|
|
77
|
+
* `swapItemState` move the `elements` reference between field stores when
|
|
78
|
+
* array items are inserted, moved, removed or swapped, and `reset` restores
|
|
79
|
+
* each field's original element via `elements = initialElements`. Without it,
|
|
80
|
+
* focus and file reset target the wrong element after a reorder followed by a
|
|
81
|
+
* reset.
|
|
75
82
|
*/
|
|
76
83
|
initialElements: FieldElement[];
|
|
77
84
|
/**
|
|
@@ -395,7 +402,10 @@ type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExa
|
|
|
395
402
|
*/
|
|
396
403
|
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;
|
|
397
404
|
/**
|
|
398
|
-
* Checks whether a value is
|
|
405
|
+
* Checks whether a value is a dynamic array or contains one anywhere in its
|
|
406
|
+
* shape. A fixed-length tuple is not itself a dynamic array, but it counts when
|
|
407
|
+
* it contains one, so paths can still navigate through tuples to reach nested
|
|
408
|
+
* arrays.
|
|
399
409
|
*
|
|
400
410
|
* Hint: The inner conditionals (`TValue extends readonly unknown[]` and
|
|
401
411
|
* `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
|
|
@@ -406,7 +416,7 @@ type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey,
|
|
|
406
416
|
* `true extends ...`, which is `true` whenever at least one union member
|
|
407
417
|
* contributed `true`.
|
|
408
418
|
*/
|
|
409
|
-
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;
|
|
419
|
+
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;
|
|
410
420
|
/**
|
|
411
421
|
* Extracts the exact keys of a tuple, array or object that contain arrays.
|
|
412
422
|
*/
|
|
@@ -421,7 +431,7 @@ type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: T
|
|
|
421
431
|
/**
|
|
422
432
|
* Lazily evaluates only the first valid array path segment based on the given value.
|
|
423
433
|
*/
|
|
424
|
-
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;
|
|
434
|
+
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;
|
|
425
435
|
/**
|
|
426
436
|
* Returns the path if valid, otherwise the first possible valid array path
|
|
427
437
|
* based on the given value.
|
|
@@ -473,7 +483,7 @@ interface FocusFieldConfig<TSchema extends FormSchema, TFieldPath extends Requir
|
|
|
473
483
|
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
474
484
|
}
|
|
475
485
|
/**
|
|
476
|
-
* Focuses the first input element of a field. This is useful for
|
|
486
|
+
* Focuses the first focusable input element of a field. This is useful for
|
|
477
487
|
* programmatically setting focus to a specific field, such as after
|
|
478
488
|
* validation errors or user interactions.
|
|
479
489
|
*
|
package/dist/index.js
CHANGED
|
@@ -228,31 +228,45 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
228
228
|
* form reset functionality.
|
|
229
229
|
*
|
|
230
230
|
* @param internalFieldStore The field store to reset.
|
|
231
|
-
* @param
|
|
231
|
+
* @param input The new input value (can be any type including array or object).
|
|
232
|
+
* @param keepStart Whether to keep `startInput` and `startItems` as the dirty
|
|
233
|
+
* baseline instead of resetting them to the new input. Used when a field store
|
|
234
|
+
* is reused for an in-place edit so its dirty state is detected correctly.
|
|
232
235
|
*/
|
|
233
|
-
function resetItemState(internalFieldStore,
|
|
236
|
+
function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
234
237
|
batch(() => {
|
|
235
|
-
|
|
238
|
+
const elements = [];
|
|
239
|
+
if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
|
|
240
|
+
internalFieldStore.elements = elements;
|
|
236
241
|
internalFieldStore.errors.value = null;
|
|
237
242
|
internalFieldStore.isTouched.value = false;
|
|
238
243
|
internalFieldStore.isDirty.value = false;
|
|
239
244
|
if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
|
|
240
|
-
const objectInput =
|
|
241
|
-
internalFieldStore.startInput.value = objectInput;
|
|
245
|
+
const objectInput = input == null ? input : true;
|
|
246
|
+
if (!keepStart) internalFieldStore.startInput.value = objectInput;
|
|
242
247
|
internalFieldStore.input.value = objectInput;
|
|
243
|
-
if (internalFieldStore.kind === "array") if (
|
|
244
|
-
const
|
|
245
|
-
|
|
248
|
+
if (internalFieldStore.kind === "array") if (input) {
|
|
249
|
+
const length = internalFieldStore.schema.type === "array" ? input.length : internalFieldStore.children.length;
|
|
250
|
+
const newItems = Array.from({ length }, createId);
|
|
251
|
+
if (!keepStart) internalFieldStore.startItems.value = newItems;
|
|
246
252
|
internalFieldStore.items.value = newItems;
|
|
247
|
-
|
|
253
|
+
let path;
|
|
254
|
+
for (let index = 0; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], input[index], keepStart);
|
|
255
|
+
else {
|
|
256
|
+
path ??= JSON.parse(internalFieldStore.name);
|
|
257
|
+
internalFieldStore.children[index] = {};
|
|
258
|
+
path.push(index);
|
|
259
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, input[index], path);
|
|
260
|
+
path.pop();
|
|
261
|
+
}
|
|
248
262
|
} else {
|
|
249
|
-
internalFieldStore.startItems.value = [];
|
|
263
|
+
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
250
264
|
internalFieldStore.items.value = [];
|
|
251
265
|
}
|
|
252
|
-
else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key],
|
|
266
|
+
else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
253
267
|
} else {
|
|
254
|
-
internalFieldStore.startInput.value =
|
|
255
|
-
internalFieldStore.input.value =
|
|
268
|
+
if (!keepStart) internalFieldStore.startInput.value = input;
|
|
269
|
+
internalFieldStore.input.value = input;
|
|
256
270
|
}
|
|
257
271
|
});
|
|
258
272
|
}
|
|
@@ -319,6 +333,28 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
319
333
|
});
|
|
320
334
|
}
|
|
321
335
|
/**
|
|
336
|
+
* Focuses the first focusable element of a field store. The elements are tried
|
|
337
|
+
* in order and the first one that actually receives focus wins, so detached,
|
|
338
|
+
* disabled or hidden elements are skipped. The browser decides focusability,
|
|
339
|
+
* which is read back via the element's root `activeElement` so elements in a
|
|
340
|
+
* shadow root or another document are handled correctly.
|
|
341
|
+
*
|
|
342
|
+
* Hint: A `display: none` or `hidden` element is correctly skipped in real
|
|
343
|
+
* browsers, but jsdom has no layout and focuses it anyway, so that case cannot
|
|
344
|
+
* be covered by unit tests.
|
|
345
|
+
*
|
|
346
|
+
* @param internalFieldStore The field store to focus.
|
|
347
|
+
*
|
|
348
|
+
* @returns Whether an element was focused.
|
|
349
|
+
*/
|
|
350
|
+
function focusFieldElement(internalFieldStore) {
|
|
351
|
+
for (const element of internalFieldStore.elements) {
|
|
352
|
+
element.focus();
|
|
353
|
+
if (element.getRootNode().activeElement === element) return true;
|
|
354
|
+
}
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
322
358
|
* Returns whether the specified boolean property is true for the field store
|
|
323
359
|
* or any of its nested children. Recursively checks arrays and objects.
|
|
324
360
|
*
|
|
@@ -456,11 +492,9 @@ function getFieldStore(internalFormStore, path) {
|
|
|
456
492
|
*/
|
|
457
493
|
function setFieldBool(internalFieldStore, type, bool) {
|
|
458
494
|
batch(() => {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
} else if (internalFieldStore.kind == "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
|
|
463
|
-
else internalFieldStore[type].value = bool;
|
|
495
|
+
internalFieldStore[type].value = bool;
|
|
496
|
+
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) setFieldBool(internalFieldStore.children[index], type, bool);
|
|
497
|
+
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) setFieldBool(internalFieldStore.children[key], type, bool);
|
|
464
498
|
});
|
|
465
499
|
}
|
|
466
500
|
/**
|
|
@@ -475,20 +509,20 @@ function setNestedInput(internalFieldStore, input) {
|
|
|
475
509
|
if (internalFieldStore.kind === "array") {
|
|
476
510
|
const arrayInput = input ?? [];
|
|
477
511
|
const items = internalFieldStore.items.value;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
512
|
+
const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
|
|
513
|
+
if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
|
|
514
|
+
else if (length > items.length) {
|
|
515
|
+
const path = JSON.parse(internalFieldStore.name);
|
|
516
|
+
for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
|
|
517
|
+
else {
|
|
518
|
+
internalFieldStore.children[index] = {};
|
|
519
|
+
path.push(index);
|
|
520
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
|
|
521
|
+
path.pop();
|
|
488
522
|
}
|
|
489
|
-
internalFieldStore.items.value = [...items, ...
|
|
523
|
+
internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
|
|
490
524
|
}
|
|
491
|
-
for (let index = 0; index <
|
|
525
|
+
for (let index = 0; index < length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
|
|
492
526
|
internalFieldStore.input.value = input == null ? input : true;
|
|
493
527
|
internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
|
|
494
528
|
} else if (internalFieldStore.kind === "object") {
|
|
@@ -532,21 +566,22 @@ function setFieldInput(internalFormStore, path, input) {
|
|
|
532
566
|
function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
533
567
|
batch(() => {
|
|
534
568
|
if (internalFieldStore.kind === "array") {
|
|
535
|
-
internalFieldStore.
|
|
569
|
+
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
536
570
|
const initialArrayInput = initialInput ?? [];
|
|
537
|
-
|
|
571
|
+
const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
|
|
572
|
+
if (length > internalFieldStore.children.length) {
|
|
538
573
|
const path = JSON.parse(internalFieldStore.name);
|
|
539
|
-
for (let index = internalFieldStore.children.length; index <
|
|
574
|
+
for (let index = internalFieldStore.children.length; index < length; index++) {
|
|
540
575
|
internalFieldStore.children[index] = {};
|
|
541
576
|
path.push(index);
|
|
542
577
|
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
|
|
543
578
|
path.pop();
|
|
544
579
|
}
|
|
545
580
|
}
|
|
546
|
-
internalFieldStore.initialItems.value =
|
|
581
|
+
internalFieldStore.initialItems.value = Array.from({ length }, createId);
|
|
547
582
|
for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
|
|
548
583
|
} else if (internalFieldStore.kind === "object") {
|
|
549
|
-
internalFieldStore.
|
|
584
|
+
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
550
585
|
for (const key in internalFieldStore.children) setInitialFieldInput(internalFieldStore.children[key], initialInput?.[key]);
|
|
551
586
|
} else internalFieldStore.initialInput.value = initialInput;
|
|
552
587
|
});
|
|
@@ -598,44 +633,49 @@ function createFormStore(config, parse) {
|
|
|
598
633
|
async function validateFormInput(internalFormStore, config) {
|
|
599
634
|
internalFormStore.validators++;
|
|
600
635
|
internalFormStore.isValidating.value = true;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const path
|
|
608
|
-
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const name = JSON.stringify(path);
|
|
616
|
-
const fieldErrors = nestedErrors[name];
|
|
617
|
-
if (fieldErrors) fieldErrors.push(issue.message);
|
|
618
|
-
else nestedErrors[name] = [issue.message];
|
|
619
|
-
} else if (rootErrors) rootErrors.push(issue.message);
|
|
620
|
-
else rootErrors = [issue.message];
|
|
621
|
-
}
|
|
622
|
-
let shouldFocus = config?.shouldFocus ?? false;
|
|
623
|
-
batch(() => {
|
|
624
|
-
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
625
|
-
if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
|
|
626
|
-
else {
|
|
627
|
-
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
628
|
-
internalFieldStore.errors.value = fieldErrors;
|
|
629
|
-
if (shouldFocus && fieldErrors) {
|
|
630
|
-
internalFieldStore.elements[0]?.focus();
|
|
631
|
-
shouldFocus = false;
|
|
636
|
+
try {
|
|
637
|
+
const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
|
|
638
|
+
let rootErrors;
|
|
639
|
+
let nestedErrors;
|
|
640
|
+
if (result.issues) {
|
|
641
|
+
nestedErrors = {};
|
|
642
|
+
for (const issue of result.issues) if (issue.path) {
|
|
643
|
+
const path = [];
|
|
644
|
+
for (const pathItem of issue.path) {
|
|
645
|
+
const key = pathItem.key;
|
|
646
|
+
const keyType = typeof key;
|
|
647
|
+
const itemType = pathItem.type;
|
|
648
|
+
if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
|
|
649
|
+
path.push(key);
|
|
632
650
|
}
|
|
633
|
-
|
|
651
|
+
const name = JSON.stringify(path);
|
|
652
|
+
const fieldErrors = nestedErrors[name];
|
|
653
|
+
if (fieldErrors) fieldErrors.push(issue.message);
|
|
654
|
+
else nestedErrors[name] = [issue.message];
|
|
655
|
+
} else if (rootErrors) rootErrors.push(issue.message);
|
|
656
|
+
else rootErrors = [issue.message];
|
|
657
|
+
}
|
|
658
|
+
let shouldFocus = config?.shouldFocus ?? false;
|
|
659
|
+
batch(() => {
|
|
660
|
+
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
661
|
+
if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
|
|
662
|
+
else {
|
|
663
|
+
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
664
|
+
internalFieldStore.errors.value = fieldErrors;
|
|
665
|
+
if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
internalFormStore.validators--;
|
|
669
|
+
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
634
670
|
});
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
671
|
+
return result;
|
|
672
|
+
} catch (error) {
|
|
673
|
+
batch(() => {
|
|
674
|
+
internalFormStore.validators--;
|
|
675
|
+
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
676
|
+
});
|
|
677
|
+
throw error;
|
|
678
|
+
}
|
|
639
679
|
}
|
|
640
680
|
/**
|
|
641
681
|
* Validates the form input if required based on the validation mode and form
|
|
@@ -657,7 +697,7 @@ const INTERNAL = "~internal";
|
|
|
657
697
|
//#endregion
|
|
658
698
|
//#region ../../packages/methods/dist/index.react.js
|
|
659
699
|
/**
|
|
660
|
-
* Focuses the first input element of a field. This is useful for
|
|
700
|
+
* Focuses the first focusable input element of a field. This is useful for
|
|
661
701
|
* programmatically setting focus to a specific field, such as after
|
|
662
702
|
* validation errors or user interactions.
|
|
663
703
|
*
|
|
@@ -665,7 +705,7 @@ const INTERNAL = "~internal";
|
|
|
665
705
|
* @param config The focus field configuration.
|
|
666
706
|
*/
|
|
667
707
|
function focus(form, config) {
|
|
668
|
-
getFieldStore(form[INTERNAL], config.path)
|
|
708
|
+
focusFieldElement(getFieldStore(form[INTERNAL], config.path));
|
|
669
709
|
}
|
|
670
710
|
/**
|
|
671
711
|
* Retrieves all error messages from all fields in the form by walking through
|
|
@@ -1001,7 +1041,9 @@ function useField(form, config) {
|
|
|
1001
1041
|
const internalFieldStore = getFieldStore(internalFormStore, config.path);
|
|
1002
1042
|
useEffect(() => {
|
|
1003
1043
|
return () => {
|
|
1004
|
-
|
|
1044
|
+
const elements = internalFieldStore.elements.filter((element) => element.isConnected);
|
|
1045
|
+
if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
|
|
1046
|
+
internalFieldStore.elements = elements;
|
|
1005
1047
|
};
|
|
1006
1048
|
}, [internalFieldStore]);
|
|
1007
1049
|
return useMemo(() => ({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formisch/react",
|
|
3
3
|
"description": "The lightweight, schema-first, and fully type-safe form library for React",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Fabian Hiller",
|
|
7
7
|
"homepage": "https://formisch.dev",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@types/react": "^19.2.5",
|
|
52
52
|
"@types/react-dom": "^19.2.3",
|
|
53
53
|
"@vitejs/plugin-react": "^5.1.1",
|
|
54
|
-
"@vitest/coverage-v8": "^
|
|
54
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
55
55
|
"eslint": "^9.39.1",
|
|
56
56
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
57
57
|
"eslint-plugin-react-refresh": "^0.4.24",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"tsdown": "^0.16.8",
|
|
63
63
|
"typescript": "~5.9.3",
|
|
64
64
|
"vite": "^7.2.4",
|
|
65
|
-
"vitest": "^
|
|
65
|
+
"vitest": "^4.1.7"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|