@reformer/core 1.0.0-beta.1 → 1.0.0-beta.2

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 (79) hide show
  1. package/dist/behaviors/compute-from.js +3 -3
  2. package/dist/behaviors/enable-when.js +5 -5
  3. package/dist/behaviors/reset-when.js +1 -1
  4. package/dist/behaviors/revalidate-when.js +1 -1
  5. package/dist/behaviors/sync-fields.js +5 -5
  6. package/dist/behaviors/watch-field.js +3 -3
  7. package/dist/behaviors.js +2 -2
  8. package/dist/core/types/field-path.d.ts +2 -2
  9. package/dist/core/types/index.d.ts +1 -1
  10. package/dist/core/types/validation-schema.d.ts +21 -50
  11. package/dist/core/validation/core/index.d.ts +0 -1
  12. package/dist/core/validation/core/validate-async.d.ts +9 -15
  13. package/dist/core/validation/core/validate.d.ts +15 -15
  14. package/dist/core/validation/index.d.ts +5 -3
  15. package/dist/core/validation/validation-applicator.d.ts +2 -56
  16. package/dist/core/validation/validation-registry.d.ts +18 -16
  17. package/dist/core/validation/validators/array-validators.d.ts +6 -31
  18. package/dist/core/validation/validators/email.d.ts +5 -29
  19. package/dist/core/validation/validators/future-date.d.ts +6 -28
  20. package/dist/core/validation/validators/index.d.ts +12 -4
  21. package/dist/core/validation/validators/integer.d.ts +13 -0
  22. package/dist/core/validation/validators/is-date.d.ts +5 -28
  23. package/dist/core/validation/validators/is-number.d.ts +14 -0
  24. package/dist/core/validation/validators/max-age.d.ts +6 -29
  25. package/dist/core/validation/validators/max-date.d.ts +6 -29
  26. package/dist/core/validation/validators/max-length.d.ts +5 -30
  27. package/dist/core/validation/validators/max.d.ts +7 -29
  28. package/dist/core/validation/validators/min-age.d.ts +6 -29
  29. package/dist/core/validation/validators/min-date.d.ts +6 -29
  30. package/dist/core/validation/validators/min-length.d.ts +5 -30
  31. package/dist/core/validation/validators/min.d.ts +7 -29
  32. package/dist/core/validation/validators/multiple-of.d.ts +13 -0
  33. package/dist/core/validation/validators/non-negative.d.ts +13 -0
  34. package/dist/core/validation/validators/non-zero.d.ts +13 -0
  35. package/dist/core/validation/validators/past-date.d.ts +5 -28
  36. package/dist/core/validation/validators/pattern.d.ts +5 -32
  37. package/dist/core/validation/validators/phone.d.ts +9 -19
  38. package/dist/core/validation/validators/required.d.ts +10 -31
  39. package/dist/core/validation/validators/url.d.ts +13 -15
  40. package/dist/{index-D25LsbRm.js → index-DLh_Ryb2.js} +1 -1
  41. package/dist/index.js +465 -397
  42. package/dist/{registry-helpers-Bv_BJ1s-.js → registry-helpers-CLPXad2B.js} +152 -292
  43. package/dist/validators/email.js +7 -9
  44. package/dist/validators/future-date.js +11 -12
  45. package/dist/validators/integer.d.ts +2 -0
  46. package/dist/validators/integer.js +10 -0
  47. package/dist/validators/is-date.js +6 -7
  48. package/dist/validators/is-number.d.ts +2 -0
  49. package/dist/validators/is-number.js +10 -0
  50. package/dist/validators/max-age.js +11 -12
  51. package/dist/validators/max-date.js +11 -12
  52. package/dist/validators/max-length.js +12 -8
  53. package/dist/validators/max.js +5 -6
  54. package/dist/validators/min-age.js +10 -11
  55. package/dist/validators/min-date.js +12 -13
  56. package/dist/validators/min-length.js +12 -8
  57. package/dist/validators/min.js +6 -7
  58. package/dist/validators/multiple-of.d.ts +2 -0
  59. package/dist/validators/multiple-of.js +10 -0
  60. package/dist/validators/non-negative.d.ts +2 -0
  61. package/dist/validators/non-negative.js +10 -0
  62. package/dist/validators/non-zero.d.ts +2 -0
  63. package/dist/validators/non-zero.js +10 -0
  64. package/dist/validators/past-date.js +11 -12
  65. package/dist/validators/pattern.js +6 -7
  66. package/dist/validators/phone.js +14 -32
  67. package/dist/validators/required.js +9 -10
  68. package/dist/validators/url.js +19 -16
  69. package/dist/validators-xJyP-jLZ.js +169 -0
  70. package/dist/validators.js +54 -50
  71. package/llms.txt +505 -790
  72. package/package.json +20 -4
  73. package/dist/core/validation/core/validate-tree.d.ts +0 -35
  74. package/dist/core/validation/validation-context.d.ts +0 -86
  75. package/dist/core/validation/validators/number.d.ts +0 -31
  76. package/dist/validate-C3XiA_zf.js +0 -10
  77. package/dist/validators/number.d.ts +0 -2
  78. package/dist/validators/number.js +0 -35
  79. package/dist/validators-BGsNOgT1.js +0 -207
package/llms.txt CHANGED
@@ -42,18 +42,18 @@
42
42
 
43
43
  ### Imports (CRITICALLY IMPORTANT)
44
44
 
45
- | What | Where |
46
- | ------------------------------------------------------------------------------- | --------------------------- |
47
- | `createForm`, `useFormControl`, `useFormControlValue`, `validateForm` | `@reformer/core` |
45
+ | What | Where |
46
+ | ------------------------------------------------------------------------------------------- | --------------------------- |
47
+ | `createForm`, `useFormControl`, `useFormControlValue`, `validateForm` | `@reformer/core` |
48
48
  | `ValidationSchemaFn`, `BehaviorSchemaFn`, `FieldPath`, `FormProxy`, `FieldNode` | `@reformer/core` |
49
- | `FormSchema`, `FieldConfig`, `ArrayNode` | `@reformer/core` |
50
- | `required`, `min`, `max`, `minLength`, `maxLength`, `email` | `@reformer/core/validators` |
51
- | `pattern`, `url`, `phone`, `number`, `date` | `@reformer/core/validators` |
52
- | `validate`, `validateAsync`, `validateTree`, `applyWhen` | `@reformer/core/validators` |
53
- | `notEmpty`, `validateItems` | `@reformer/core/validators` |
54
- | `computeFrom`, `enableWhen`, `disableWhen`, `watchField`, `copyFrom` | `@reformer/core/behaviors` |
55
- | `resetWhen`, `revalidateWhen`, `syncFields` | `@reformer/core/behaviors` |
56
- | `transformValue`, `transformers` | `@reformer/core/behaviors` |
49
+ | `FormSchema`, `FieldConfig`, `ArrayNode` | `@reformer/core` |
50
+ | `required`, `min`, `max`, `minLength`, `maxLength`, `email` | `@reformer/core/validators` |
51
+ | `pattern`, `url`, `phone`, `isNumber`, `integer`, `multipleOf`, `nonNegative`, `nonZero` | `@reformer/core/validators` |
52
+ | `validate`, `validateAsync`, `applyWhen`, `apply` | `@reformer/core/validators` |
53
+ | `notEmpty`, `validateItems` | `@reformer/core/validators` |
54
+ | `computeFrom`, `enableWhen`, `disableWhen`, `watchField`, `copyFrom` | `@reformer/core/behaviors` |
55
+ | `resetWhen`, `revalidateWhen`, `syncFields` | `@reformer/core/behaviors` |
56
+ | `transformValue`, `transformers` | `@reformer/core/behaviors` |
57
57
 
58
58
  ### Type Values
59
59
 
@@ -63,10 +63,10 @@
63
63
 
64
64
  ### React Hooks Comparison (CRITICALLY IMPORTANT)
65
65
 
66
- | Hook | Return Type | Subscribes To | Use Case |
67
- | ---------------------------- | ------------------------------------------- | ----------------- | ----------------------------- |
68
- | `useFormControl(field)` | `{ value, errors, disabled, touched, ... }` | All signals | Full field state, form inputs |
69
- | `useFormControlValue(field)` | `T` (value directly) | Only value signal | Conditional rendering |
66
+ | Hook | Return Type | Subscribes To | Use Case |
67
+ |------|-------------|---------------|----------|
68
+ | `useFormControl(field)` | `{ value, errors, disabled, touched, ... }` | All signals | Full field state, form inputs |
69
+ | `useFormControlValue(field)` | `T` (value directly) | Only value signal | Conditional rendering |
70
70
 
71
71
  **CRITICAL**: Do NOT destructure `useFormControlValue`! It returns `T` directly, NOT `{ value: T }`.
72
72
 
@@ -188,26 +188,39 @@ const schema: FormSchema<{ properties: PropertyItem[] }> = {
188
188
 
189
189
  ### Validators
190
190
 
191
- ```typescript
192
- // Basic validators
193
- required(path, options?: { message?: string })
194
- min(path, value: number, options?: { message?: string })
195
- max(path, value: number, options?: { message?: string })
196
- minLength(path, length: number, options?: { message?: string })
197
- maxLength(path, length: number, options?: { message?: string })
198
- email(path, options?: { message?: string })
199
-
200
- // Additional validators
201
- pattern(path, regex: RegExp, options?: { message?: string })
202
- url(path, options?: { message?: string })
203
- phone(path, options?: { message?: string; format?: PhoneFormat })
204
- number(path, options?: { message?: string })
205
- date(path, options?: { message?: string; minAge?: number; maxAge?: number; noFuture?: boolean; noPast?: boolean })
206
-
207
- // Custom validators
208
- validate(path, validator: (value, ctx) => ValidationError | null)
209
- validateAsync(path, validator: async (value, ctx) => ValidationError | null)
210
- validateTree(validator: (ctx) => ValidationError | null, options?: { targetField?: string })
191
+ Operators register validators in the schema. Validator factories return pure
192
+ `Validator<TForm, TField>` functions of shape `(value, control, root) => ValidationError | null`.
193
+
194
+ ```typescript
195
+ // Operators (register validators in schema)
196
+ validate(path, validator: Validator<TForm, TField>, options?: { message?: string })
197
+ validateAsync(path, validator: AsyncValidator<TForm, TField>, options?: { message?: string; debounce?: number })
198
+
199
+ // Validator factories (return Validator). Pass to validate().
200
+ required(options?: { message?: string })
201
+ min(value: number, options?: { message?: string })
202
+ max(value: number, options?: { message?: string })
203
+ minLength(length: number, options?: { message?: string })
204
+ maxLength(length: number, options?: { message?: string })
205
+ email(options?: { message?: string })
206
+ pattern(regex: RegExp, options?: { message?: string })
207
+ url(options?: { message?: string; requireProtocol?: boolean })
208
+ phone(options?: { message?: string; format?: PhoneFormat })
209
+ // Number validator factories (atomic compose via several validate() calls)
210
+ isNumber(options?: { message?: string })
211
+ integer(options?: { message?: string })
212
+ multipleOf(divisor: number, options?: { message?: string })
213
+ nonNegative(options?: { message?: string }) // value >= 0
214
+ nonZero(options?: { message?: string }) // value !== 0
215
+ date(options?: { message?: string; minAge?: number; maxAge?: number; noFuture?: boolean; noPast?: boolean })
216
+ notEmpty(options?: { message?: string })
217
+
218
+ // Usage: pass a factory result to validate()
219
+ validate(path.email, required());
220
+ validate(path.age, min(18));
221
+ validate(path.amount, integer());
222
+ validate(path.amount, min(0));
223
+ validate(path.amount, max(1000));
211
224
 
212
225
  // Conditional validation (3 arguments!)
213
226
  applyWhen(fieldPath, condition: (fieldValue) => boolean, validatorsFn: (path) => void)
@@ -220,8 +233,8 @@ applyWhen(
220
233
  path.loanType, // 1st: field to watch
221
234
  (type) => type === 'mortgage', // 2nd: condition on field value
222
235
  (p) => { // 3rd: validators to apply
223
- required(p.propertyValue);
224
- min(p.propertyValue, 100000);
236
+ validate(p.propertyValue, required());
237
+ validate(p.propertyValue, min(100000));
225
238
  }
226
239
  );
227
240
 
@@ -230,8 +243,8 @@ applyWhen(
230
243
  path.address.country,
231
244
  (country) => country === 'US',
232
245
  (p) => {
233
- required(p.address.state);
234
- pattern(p.address.zip, /^\d{5}(-\d{4})?$/);
246
+ validate(p.address.state, required());
247
+ validate(p.address.zip, pattern(/^\d{5}(-\d{4})?$/));
235
248
  }
236
249
  );
237
250
 
@@ -240,19 +253,18 @@ applyWhen(
240
253
  path.hasInsurance,
241
254
  (has) => has === true,
242
255
  (p) => {
243
- required(p.insuranceCompany);
244
- required(p.policyNumber);
256
+ validate(p.insuranceCompany, required());
257
+ validate(p.policyNumber, required());
245
258
  }
246
259
  );
247
260
 
248
261
  // WRONG - only 2 arguments (React Hook Form pattern)
249
262
  applyWhen(
250
263
  (form) => form.loanType === 'mortgage', // WRONG!
251
- () => { required(path.propertyValue); }
264
+ () => { validate(path.propertyValue, required()); }
252
265
  );
253
266
 
254
- // Array validators
255
- notEmpty(path, options?: { message?: string })
267
+ // Array item schema operator
256
268
  validateItems(arrayPath, itemValidatorsFn: (itemPath) => void)
257
269
  ```
258
270
 
@@ -426,26 +438,29 @@ const { value } = useFormControl(form.field as FieldNode<ExpectedType>);
426
438
 
427
439
  ### Validation Priority (IMPORTANT)
428
440
 
429
- **Always prefer built-in validators over custom ones:**
441
+ **Operators register validators; built-in factories return validators. Pass factories to `validate()`.**
442
+
443
+ Operators: `validate`, `validateAsync`, `applyWhen`, `apply`, `validateItems`.
444
+ Factories (return `Validator<TForm, TField>`): `required`, `email`, `min`, `max`, `minLength`, `maxLength`,
445
+ `pattern`, `url`, `phone`, `number`, `date`, `notEmpty`.
430
446
 
431
447
  ```typescript
432
- // 1. BEST: Use built-in validators when available
433
- required(path.email);
434
- email(path.email);
435
- min(path.age, 18);
436
- minLength(path.password, 8);
437
- pattern(path.phone, /^\+7\d{10}$/);
448
+ // 1. BEST: Pass built-in factories to validate()
449
+ validate(path.email, required());
450
+ validate(path.email, email());
451
+ validate(path.age, min(18));
452
+ validate(path.password, minLength(8));
453
+ validate(path.phone, pattern(/^\+7\d{10}$/));
438
454
 
439
- // 2. GOOD: Use validate() only when no built-in validator exists
440
- validate(path.customField, (value, ctx) => {
441
- // Custom logic that can't be expressed with built-in validators
455
+ // 2. GOOD: Custom validator when no factory fits. Signature: (value, control, root).
456
+ validate(path.customField, (value, control, root) => {
442
457
  if (customCondition(value)) {
443
458
  return { code: 'custom', message: 'Custom error' };
444
459
  }
445
460
  return null;
446
461
  });
447
462
 
448
- // 3. WRONG: Don't recreate built-in validators
463
+ // 3. WRONG: Don't recreate built-in validators inline
449
464
  validate(path.email, (value) => {
450
465
  if (!value) return { code: 'required', message: 'Required' }; // Use required() instead!
451
466
  if (!value.includes('@')) return { code: 'email', message: 'Invalid' }; // Use email() instead!
@@ -532,6 +547,68 @@ const behavior: BehaviorSchemaFn<LoanForm> = (path) => {
532
547
 
533
548
  Reference patterns: `complex-multy-step-form/schemas/credit-application-behavior.ts`.
534
549
 
550
+ ### Extracting Nested Rules
551
+
552
+ When the body of `applyWhen` / `validate` grows beyond a few lines,
553
+ extract it to a **named top-level function** typed with one of the public types from
554
+ `@reformer/core`:
555
+
556
+ - `ValidationSchemaFn<TForm>` — sub-schema passed to `applyWhen` or `apply`.
557
+ - `Validator<TForm, TField>` / `AsyncValidator<TForm, TField>` — field validator passed to `validate` / `validateAsync`. Cross-field правила пишутся в той же сигнатуре — `(value, control, root) => …` — и навешиваются на конкретное поле-носитель ошибки.
558
+
559
+ This keeps the schema body flat (reads like a table of contents) and surfaces the
560
+ **intent** of each rule via a meaningful name.
561
+
562
+ ```typescript
563
+ import type { ValidationSchemaFn, Validator } from '@reformer/core';
564
+
565
+ // 1. Cross-field rule — extracted Validator (reads other fields via `root`)
566
+ const initialPaymentVsPropertyValue: Validator<CreditApplicationForm, unknown> = (
567
+ _value,
568
+ _control,
569
+ root
570
+ ) => {
571
+ const form = root.getValue();
572
+ if (form.initialPayment && form.propertyValue && form.initialPayment > form.propertyValue) {
573
+ return { code: 'initialPaymentTooHigh', message: 'Взнос не может превышать стоимость' };
574
+ }
575
+ return null;
576
+ };
577
+
578
+ // 2. Custom field validator — extracted Validator
579
+ const validateAdultAge: Validator<CreditApplicationForm, string | undefined> = (value) => {
580
+ if (!value) return null;
581
+ const age = new Date().getFullYear() - new Date(value).getFullYear();
582
+ if (age < 18) return { code: 'tooYoung', message: 'Минимум 18 лет' };
583
+ return null;
584
+ };
585
+
586
+ // 3. Nested schema for applyWhen — extracted ValidationSchemaFn
587
+ const mortgageFieldsRules: ValidationSchemaFn<CreditApplicationForm> = (path) => {
588
+ validate(path.propertyValue, required());
589
+ validate(path.propertyValue, min(1000000));
590
+ validate(path.initialPayment, required());
591
+ validate(path.initialPayment, initialPaymentVsPropertyValue);
592
+ };
593
+
594
+ // 4. Main schema — flat, reads as a table of contents
595
+ export const basicInfoValidation: ValidationSchemaFn<CreditApplicationForm> = (path) => {
596
+ validate(path.loanType, required());
597
+ validate(path.personalData.birthDate, validateAdultAge);
598
+ applyWhen(path.loanType, (type) => type === 'mortgage', mortgageFieldsRules);
599
+ };
600
+ ```
601
+
602
+ **Naming convention** (camelCase, semantic — not echoing the operator name):
603
+
604
+ - `applyWhen` sub-schema → describes the conditional branch: `mortgageFieldsRules`, `employedFieldsRules`.
605
+ - Cross-field `Validator` → describes the invariant: `initialPaymentVsPropertyValue`, `paymentToIncomeUnderHalf`.
606
+ - Field-level `Validator` → describes the check: `validateAdultAge`, `validatePasswordsMatch`.
607
+
608
+ **When to extract.** Inline lambdas are fine for short one-liners
609
+ (`applyWhen(path.x, (v) => v === 'mortgage', mortgageFieldsRules)` — the condition stays inline).
610
+ Extract whenever the body spans more than ~3 lines or contains nested rules.
611
+
535
612
  ## 6. 4. COMMON MISTAKES
536
613
 
537
614
  ### TSC overload-resolution error: `'form' does not exist in FormSchema<T>`
@@ -1274,19 +1351,32 @@ enableWhen(path.vehicle.vin, (form) => form.insuranceType === 'casco', { resetOn
1274
1351
 
1275
1352
  See `22-cycle-detection.md` for complete pattern.
1276
1353
 
1277
- ### validateTree Typing
1354
+ ### Cross-field валидация — через `validate` + `root`
1278
1355
 
1279
1356
  ```typescript
1280
- // WRONG - implicit any
1281
- validateTree((ctx) => { ... });
1357
+ // Validator signature: (value, control, root) => ValidationError | null
1358
+ // Cross-field правило вешается на ПОЛЕ-НОСИТЕЛЬ ошибки. Соседние поля читаются через `root`.
1282
1359
 
1283
- // CORRECT - explicit typing
1284
- validateTree((ctx: { form: MyForm }) => {
1285
- if (ctx.form.field1 > ctx.form.field2) {
1360
+ // CORRECT ошибка ложится на path.field1
1361
+ validate(path.field1, (_value, _control, root) => {
1362
+ const form = root.getValue(); // typed as TForm
1363
+ if (form.field1 > form.field2) {
1286
1364
  return { code: 'error', message: 'Invalid' };
1287
1365
  }
1288
1366
  return null;
1289
1367
  });
1368
+
1369
+ // CORRECT — те же поля через reactive-доступ
1370
+ validate(path.address.city, (value, _control, root) => {
1371
+ if (!value && root.address.region.value.value) {
1372
+ return { code: 'cityRequired', message: 'City required when region is set' };
1373
+ }
1374
+ return null;
1375
+ });
1376
+
1377
+ // WRONG - old API removed
1378
+ validateGroup(scopePath, (scope, root) => { ... }); // validateGroup убран
1379
+ validateTree((ctx) => { ... }); // validateTree no longer exists
1290
1380
  ```
1291
1381
 
1292
1382
  ## 16. 14. PROJECT STRUCTURE (COLOCATION)
@@ -1787,18 +1877,18 @@ The "one watcher per trigger" cycle-prevention rule means **never register two w
1787
1877
 
1788
1878
  ```typescript
1789
1879
  // WRONG - bracket notation does NOT work!
1790
- const first = form.items[0]; // undefined or error
1791
- const second = form.items[1]; // undefined or error
1880
+ const first = form.items[0]; // undefined or error
1881
+ const second = form.items[1]; // undefined or error
1792
1882
 
1793
1883
  // CORRECT - use .at() method
1794
- const first = form.items.at(0); // FormProxy<ItemType> | undefined
1795
- const second = form.items.at(1); // FormProxy<ItemType> | undefined
1884
+ const first = form.items.at(0); // FormProxy<ItemType> | undefined
1885
+ const second = form.items.at(1); // FormProxy<ItemType> | undefined
1796
1886
 
1797
1887
  // CORRECT - iterate with map (most common pattern)
1798
1888
  form.items.map((item, index) => {
1799
1889
  // item is fully typed GroupNode
1800
1890
  item.name.setValue('New Name');
1801
- item.price.value.value; // read value
1891
+ item.price.value.value; // read value
1802
1892
  });
1803
1893
  ```
1804
1894
 
@@ -1841,7 +1931,9 @@ function ItemsList({ form }: { form: FormProxy<MyForm> }) {
1841
1931
 
1842
1932
  {length === 0 && <p>No items yet</p>}
1843
1933
 
1844
- <button onClick={() => form.items.push({ name: '', price: 0 })}>Add Item</button>
1934
+ <button onClick={() => form.items.push({ name: '', price: 0 })}>
1935
+ Add Item
1936
+ </button>
1845
1937
  </div>
1846
1938
  );
1847
1939
  }
@@ -1849,35 +1941,35 @@ function ItemsList({ form }: { form: FormProxy<MyForm> }) {
1849
1941
 
1850
1942
  ### Array Cross-Validation
1851
1943
 
1944
+ Cross-field правила по массиву пишутся как обычный `Validator`, вешаются на сам массив
1945
+ (или на любое поле-носитель ошибки), а данные читаются через `root`.
1946
+
1852
1947
  ```typescript
1853
1948
  // Validate uniqueness across array items
1854
- validateTree(
1855
- (ctx: { form: MyForm }) => {
1856
- const items = ctx.form.items;
1857
- const names = items.map((item) => item.name.value.value);
1858
- const uniqueNames = new Set(names);
1859
-
1860
- if (names.length !== uniqueNames.size) {
1861
- return { code: 'duplicate', message: 'Item names must be unique' };
1862
- }
1863
- return null;
1864
- },
1865
- { targetField: 'items' }
1866
- );
1949
+ validate(path.items, (_value, _control, root) => {
1950
+ const items = root.items;
1951
+ const names = items.map((item) => item.name.value.value);
1952
+ const uniqueNames = new Set(names);
1953
+
1954
+ if (names.length !== uniqueNames.size) {
1955
+ return { code: 'duplicate', message: 'Item names must be unique' };
1956
+ }
1957
+ return null;
1958
+ });
1867
1959
 
1868
1960
  // Validate sum of percentages
1869
- validateTree(
1870
- (ctx: { form: MyForm }) => {
1871
- const items = ctx.form.items;
1872
- const totalPercent = items.reduce((sum, item) => sum + (item.percentage.value.value || 0), 0);
1961
+ validate(path.items, (_value, _control, root) => {
1962
+ const items = root.items;
1963
+ const totalPercent = items.reduce(
1964
+ (sum, item) => sum + (item.percentage.value.value || 0),
1965
+ 0
1966
+ );
1873
1967
 
1874
- if (Math.abs(totalPercent - 100) > 0.01) {
1875
- return { code: 'invalid_total', message: 'Percentages must sum to 100%' };
1876
- }
1877
- return null;
1878
- },
1879
- { targetField: 'items' }
1880
- );
1968
+ if (Math.abs(totalPercent - 100) > 0.01) {
1969
+ return { code: 'invalid_total', message: 'Percentages must sum to 100%' };
1970
+ }
1971
+ return null;
1972
+ });
1881
1973
  ```
1882
1974
 
1883
1975
  ## 22. Cycle Detection Prevention Checklist
@@ -4265,6 +4357,25 @@ export interface ArrayNodeLike {
4265
4357
 
4266
4358
  _Source: src/core/types/index.ts_
4267
4359
 
4360
+ ### AsyncValidator
4361
+
4362
+ **Kind:** `type`
4363
+
4364
+ Чистый асинхронный валидатор поля.
4365
+
4366
+ Регистрируется через `validateAsync(path, validator, { debounce })`.
4367
+
4368
+ **Signature:**
4369
+ ```typescript
4370
+ export type AsyncValidator<TForm, TField> = (
4371
+ value: TField,
4372
+ control: FormProxy<TField>,
4373
+ root: FormProxy<TForm>
4374
+ ) => Promise<ValidationError | null>;
4375
+ ```
4376
+
4377
+ _Source: src/core/types/validation-schema.ts_
4378
+
4268
4379
  ### AsyncValidatorFn
4269
4380
 
4270
4381
  **Kind:** `type`
@@ -4611,7 +4722,7 @@ _Source: src/core/behavior/types.ts_
4611
4722
 
4612
4723
  **Kind:** `type`
4613
4724
 
4614
- Функция условия для applyWhen
4725
+ Функция условия для applyWhen.
4615
4726
 
4616
4727
  **Signature:**
4617
4728
  ```typescript
@@ -4651,61 +4762,6 @@ export interface ConfigWithValue {
4651
4762
 
4652
4763
  _Source: src/core/types/index.ts_
4653
4764
 
4654
- ### ContextualAsyncValidatorFn
4655
-
4656
- **Kind:** `type`
4657
-
4658
- Асинхронная функция валидации поля с контекстом
4659
-
4660
- **Signature:**
4661
- ```typescript
4662
- export type ContextualAsyncValidatorFn<TForm, TField> = (
4663
- value: TField,
4664
- ctx: FormContext<TForm>
4665
- ) => Promise<ValidationError | null>;
4666
- ```
4667
-
4668
- **Examples:**
4669
-
4670
- ```typescript
4671
- validateAsync(path.email, async (value, ctx) => {
4672
- const exists = await checkEmailExists(value);
4673
- if (exists) return { code: 'exists', message: 'Email already taken' };
4674
- return null;
4675
- });
4676
- ```
4677
-
4678
- _Source: src/core/types/validation-schema.ts_
4679
-
4680
- ### ContextualValidatorFn
4681
-
4682
- **Kind:** `type`
4683
-
4684
- Функция валидации поля с контекстом
4685
-
4686
- Новый паттерн: (value, ctx: FormContext) => ValidationError | null
4687
-
4688
- **Signature:**
4689
- ```typescript
4690
- export type ContextualValidatorFn<TForm, TField> = (
4691
- value: TField,
4692
- ctx: FormContext<TForm>
4693
- ) => ValidationError | null;
4694
- ```
4695
-
4696
- **Examples:**
4697
-
4698
- ```typescript
4699
- validate(path.email, (value, ctx) => {
4700
- if (!value) return { code: 'required', message: 'Email required' };
4701
- const confirm = ctx.form.confirmEmail.value.value;
4702
- if (value !== confirm) return { code: 'mismatch', message: 'Must match' };
4703
- return null;
4704
- });
4705
- ```
4706
-
4707
- _Source: src/core/types/validation-schema.ts_
4708
-
4709
4765
  ### copyFrom
4710
4766
 
4711
4767
  **Kind:** `function`
@@ -4978,43 +5034,22 @@ _Source: src/core/behavior/behaviors/enable-when.ts_
4978
5034
 
4979
5035
  **Kind:** `function`
4980
5036
 
4981
- Валидатор формата email
5037
+ Фабрика валидатора формата email.
4982
5038
 
4983
- Проверяет, что значение соответствует формату email адреса.
4984
5039
  Пустые значения пропускаются (используйте `required` для обязательности).
4985
5040
 
4986
5041
  **Signature:**
4987
5042
  ```typescript
4988
- export function email<TForm, TField extends string | null | undefined = string>(
4989
- fieldPath: FieldPathNode<TForm, TField> | undefined,
5043
+ export function email<TForm = unknown, TField extends string | undefined = string>(
4990
5044
  options?: ValidateOptions
4991
- ): void
5045
+ ): Validator<TForm, TField>
4992
5046
  ```
4993
5047
 
4994
- **Parameters:**
4995
- - `fieldPath` — - Путь к полю для валидации
4996
- - `options` — - Опции валидации (message, params)
4997
-
4998
5048
  **Examples:**
4999
5049
 
5000
5050
  ```typescript
5001
- // Базовое использование
5002
- validationSchema: (path) => [
5003
- required(path.email),
5004
- email(path.email),
5005
- ]
5006
-
5007
- // С кастомным сообщением
5008
- email(path.email, { message: 'Введите корректный email адрес' })
5009
- ```
5010
-
5011
- ```typescript
5012
- // Ошибка валидации
5013
- {
5014
- code: 'email',
5015
- message: 'Неверный формат email',
5016
- params: {}
5017
- }
5051
+ validate(path.email, email());
5052
+ validate(path.email, email({ message: 'Введите корректный email' }));
5018
5053
  ```
5019
5054
 
5020
5055
  _Source: src/core/validation/validators/email.ts_
@@ -5529,15 +5564,20 @@ const validation = (path: FieldPath<MyForm>) => {
5529
5564
 
5530
5565
  **Signature:**
5531
5566
  ```typescript
5532
- export type FieldPath<T> = {
5533
- [K in keyof T]: NonNullable<T[K]> extends Array<unknown>
5534
- ? FieldPathNode<T, T[K], K> // Массивы - не рекурсим (обрабатываются отдельно)
5535
- : NonNullable<T[K]> extends Date | File | Blob | AnyFunction
5536
- ? FieldPathNode<T, T[K], K> // Специальные объекты - не рекурсим
5537
- : NonNullable<T[K]> extends object
5538
- ? FieldPathNode<T, T[K], K> & FieldPath<NonNullable<T[K]>> // Обычные объекты - рекурсия!
5539
- : FieldPathNode<T, T[K], K>; // Примитивы
5540
- };
5567
+ export type FieldPath<T> =
5568
+ NonNullable<T> extends object
5569
+ ? NonNullable<T> extends Array<unknown> | Date | File | Blob | AnyFunction
5570
+ ? FieldPathNode<unknown, T> // Опаковые объекты не разворачиваем в mapped type
5571
+ : {
5572
+ [K in keyof T]: NonNullable<T[K]> extends Array<unknown>
5573
+ ? FieldPathNode<T, T[K], K> // Массивы - не рекурсим (обрабатываются отдельно)
5574
+ : NonNullable<T[K]> extends Date | File | Blob | AnyFunction
5575
+ ? FieldPathNode<T, T[K], K> // Специальные объекты - не рекурсим
5576
+ : NonNullable<T[K]> extends object
5577
+ ? FieldPathNode<T, T[K], K> & FieldPath<NonNullable<T[K]>> // Обычные объекты - рекурсия!
5578
+ : FieldPathNode<T, T[K], K>; // Примитивы
5579
+ }
5580
+ : FieldPathNode<unknown, T>;
5541
5581
  ```
5542
5582
 
5543
5583
  _Source: src/core/types/field-path.ts_
@@ -6276,41 +6316,23 @@ _Source: src/core/types/index.ts_
6276
6316
 
6277
6317
  **Kind:** `function`
6278
6318
 
6279
- Проверяет, что дата находится в будущем (не в прошлом)
6319
+ Фабрика валидатора, проверяющего что дата не в прошлом.
6280
6320
 
6281
- Пустые значения и невалидные даты пропускаются (используйте `required` и `isDate`).
6321
+ Пустые и невалидные даты пропускаются.
6282
6322
 
6283
6323
  **Signature:**
6284
6324
  ```typescript
6285
- export function futureDate<TForm, TField extends string | Date | undefined = string | Date>(
6286
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6287
- options?: ValidateOptions
6288
- ): void
6325
+ export function futureDate<
6326
+ TForm = unknown,
6327
+ TField extends string | Date | undefined = string | Date,
6328
+ >(options?: ValidateOptions): Validator<TForm, TField>
6289
6329
  ```
6290
6330
 
6291
- **Parameters:**
6292
- - `fieldPath` — - Путь к полю для валидации
6293
- - `options` — - Опции валидации (message, params)
6294
-
6295
6331
  **Examples:**
6296
6332
 
6297
6333
  ```typescript
6298
- // Базовое использование - дата события должна быть в будущем
6299
- validationSchema: (path) => [
6300
- futureDate(path.eventDate),
6301
- ]
6302
-
6303
- // С кастомным сообщением
6304
- futureDate(path.appointmentDate, { message: 'Дата записи должна быть в будущем' })
6305
- ```
6306
-
6307
- ```typescript
6308
- // Ошибка валидации
6309
- {
6310
- code: 'date_past',
6311
- message: 'Дата не может быть в прошлом',
6312
- params: {}
6313
- }
6334
+ validate(path.eventDate, futureDate());
6335
+ validate(path.appointmentDate, futureDate({ message: 'Дата записи должна быть в будущем' }));
6314
6336
  ```
6315
6337
 
6316
6338
  _Source: src/core/validation/validators/future-date.ts_
@@ -6485,6 +6507,30 @@ export interface GroupNodeConfig<T> {
6485
6507
 
6486
6508
  _Source: src/core/types/index.ts_
6487
6509
 
6510
+ ### integer
6511
+
6512
+ **Kind:** `function`
6513
+
6514
+ Фабрика валидатора, проверяющего что число — целое.
6515
+
6516
+ Пустые значения и не-числа пропускаются (используйте `required` и `isNumber`).
6517
+
6518
+ **Signature:**
6519
+ ```typescript
6520
+ export function integer<TForm = unknown, TField extends number | undefined = number>(
6521
+ options?: ValidateOptions
6522
+ ): Validator<TForm, TField>
6523
+ ```
6524
+
6525
+ **Examples:**
6526
+
6527
+ ```typescript
6528
+ validate(path.age, integer());
6529
+ validate(path.count, integer({ message: 'Должно быть целым числом' }));
6530
+ ```
6531
+
6532
+ _Source: src/core/validation/validators/integer.ts_
6533
+
6488
6534
  ### isArrayNode
6489
6535
 
6490
6536
  **Kind:** `function`
@@ -6520,42 +6566,22 @@ _Source: src/core/utils/type-guards.ts_
6520
6566
 
6521
6567
  **Kind:** `function`
6522
6568
 
6523
- Проверяет, что значение является валидной датой
6569
+ Фабрика валидатора, проверяющего что значение валидная дата.
6524
6570
 
6525
6571
  Пустые значения пропускаются (используйте `required` для обязательности).
6526
6572
 
6527
6573
  **Signature:**
6528
6574
  ```typescript
6529
- export function isDate<TForm, TField extends string | Date | undefined = string | Date>(
6530
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6575
+ export function isDate<TForm = unknown, TField extends string | Date | undefined = string | Date>(
6531
6576
  options?: ValidateOptions
6532
- ): void
6577
+ ): Validator<TForm, TField>
6533
6578
  ```
6534
6579
 
6535
- **Parameters:**
6536
- - `fieldPath` — - Путь к полю для валидации
6537
- - `options` — - Опции валидации (message, params)
6538
-
6539
6580
  **Examples:**
6540
6581
 
6541
6582
  ```typescript
6542
- // Базовое использование
6543
- validationSchema: (path) => [
6544
- isDate(path.birthDate),
6545
- isDate(path.eventDate),
6546
- ]
6547
-
6548
- // С кастомным сообщением
6549
- isDate(path.birthDate, { message: 'Введите корректную дату' })
6550
- ```
6551
-
6552
- ```typescript
6553
- // Ошибка валидации
6554
- {
6555
- code: 'date_invalid',
6556
- message: 'Неверный формат даты',
6557
- params: {}
6558
- }
6583
+ validate(path.birthDate, isDate());
6584
+ validate(path.eventDate, isDate({ message: 'Введите корректную дату' }));
6559
6585
  ```
6560
6586
 
6561
6587
  _Source: src/core/validation/validators/is-date.ts_
@@ -6650,49 +6676,55 @@ if (isGroupNode(node)) {
6650
6676
 
6651
6677
  _Source: src/core/utils/type-guards.ts_
6652
6678
 
6653
- ### max
6679
+ ### isNumber
6654
6680
 
6655
6681
  **Kind:** `function`
6656
6682
 
6657
- Валидатор максимального числового значения
6683
+ Фабрика валидатора, проверяющего что значение — конечное число (не NaN, не строка).
6658
6684
 
6659
- Проверяет, что числовое значение не превышает указанный максимум.
6660
- Пустые значения пропускаются (используйте `required` для обязательности).
6685
+ Пустые значения (`null`/`undefined`) пропускаются (используйте `required` для обязательности).
6686
+ В отличие от других number-валидаторов, **не** пропускает не-числа — это его задача.
6661
6687
 
6662
6688
  **Signature:**
6663
6689
  ```typescript
6664
- export function max<TForm, TField extends number | null | undefined = number>(
6665
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6666
- maxValue: number,
6690
+ export function isNumber<TForm = unknown, TField extends number | undefined = number>(
6667
6691
  options?: ValidateOptions
6668
- ): void
6692
+ ): Validator<TForm, TField>
6669
6693
  ```
6670
6694
 
6671
- **Parameters:**
6672
- - `fieldPath` — - Путь к полю для валидации
6673
- - `maxValue` — - Максимально допустимое значение
6674
- - `options` — - Опции валидации (message, params)
6675
-
6676
6695
  **Examples:**
6677
6696
 
6678
6697
  ```typescript
6679
- // Базовое использование
6680
- validationSchema: (path) => [
6681
- max(path.quantity, 100),
6682
- max(path.discount, 50),
6683
- ]
6698
+ validate(path.amount, isNumber());
6699
+ validate(path.amount, isNumber({ message: 'Введите число' }));
6700
+ ```
6701
+
6702
+ _Source: src/core/validation/validators/is-number.ts_
6684
6703
 
6685
- // С кастомным сообщением
6686
- max(path.quantity, 100, { message: 'Максимум 100 единиц' })
6704
+ ### max
6705
+
6706
+ **Kind:** `function`
6707
+
6708
+ Фабрика валидатора максимального числового значения.
6709
+
6710
+ Пустые значения пропускаются (используйте `required` для обязательности).
6711
+
6712
+ **Signature:**
6713
+ ```typescript
6714
+ export function max<TForm = unknown, TField extends number | undefined = number>(
6715
+ maxValue: number,
6716
+ options?: ValidateOptions
6717
+ ): Validator<TForm, TField>
6687
6718
  ```
6688
6719
 
6720
+ **Examples:**
6721
+
6689
6722
  ```typescript
6690
- // Ошибка валидации
6691
- {
6692
- code: 'max',
6693
- message: 'Максимальное значение: 100',
6694
- params: { max: 100, actual: 150 }
6695
- }
6723
+ import { validate } from '@reformer/core';
6724
+ import { max } from '@reformer/core/validators';
6725
+
6726
+ validate(path.quantity, max(100));
6727
+ validate(path.discount, max(50, { message: 'Не более 50%' }));
6696
6728
  ```
6697
6729
 
6698
6730
  _Source: src/core/validation/validators/max.ts_
@@ -6701,43 +6733,23 @@ _Source: src/core/validation/validators/max.ts_
6701
6733
 
6702
6734
  **Kind:** `function`
6703
6735
 
6704
- Проверяет, что возраст (вычисленный из даты рождения) не больше указанного
6736
+ Фабрика валидатора максимального возраста (по дате рождения).
6705
6737
 
6706
- Пустые значения и невалидные даты пропускаются (используйте `required` и `isDate`).
6738
+ Пустые и невалидные даты пропускаются.
6707
6739
 
6708
6740
  **Signature:**
6709
6741
  ```typescript
6710
- export function maxAge<TForm, TField extends string | Date | null | undefined = string | Date>(
6711
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6712
- maxAgeValue: number,
6713
- options?: ValidateOptions
6714
- ): void
6742
+ export function maxAge<
6743
+ TForm = unknown,
6744
+ TField extends string | Date | null | undefined = string | Date,
6745
+ >(maxAgeValue: number, options?: ValidateOptions): Validator<TForm, TField>
6715
6746
  ```
6716
6747
 
6717
- **Parameters:**
6718
- - `fieldPath` — - Путь к полю для валидации (дата рождения)
6719
- - `maxAgeValue` — - Максимально допустимый возраст в годах
6720
- - `options` — - Опции валидации (message, params)
6721
-
6722
6748
  **Examples:**
6723
6749
 
6724
6750
  ```typescript
6725
- // Базовое использование - максимум 65 лет
6726
- validationSchema: (path) => [
6727
- maxAge(path.birthDate, 65),
6728
- ]
6729
-
6730
- // С кастомным сообщением
6731
- maxAge(path.birthDate, 100, { message: 'Проверьте правильность даты рождения' })
6732
- ```
6733
-
6734
- ```typescript
6735
- // Ошибка валидации
6736
- {
6737
- code: 'date_max_age',
6738
- message: 'Максимальный возраст: 65 лет',
6739
- params: { maxAge: 65, currentAge: 70 }
6740
- }
6751
+ validate(path.birthDate, maxAge(65));
6752
+ validate(path.birthDate, maxAge(100, { message: 'Проверьте дату рождения' }));
6741
6753
  ```
6742
6754
 
6743
6755
  _Source: src/core/validation/validators/max-age.ts_
@@ -6746,43 +6758,23 @@ _Source: src/core/validation/validators/max-age.ts_
6746
6758
 
6747
6759
  **Kind:** `function`
6748
6760
 
6749
- Проверяет, что дата не позже указанной максимальной
6761
+ Фабрика валидатора максимальной даты (включительно).
6750
6762
 
6751
- Пустые значения и невалидные даты пропускаются (используйте `required` и `isDate`).
6763
+ Пустые и невалидные даты пропускаются.
6752
6764
 
6753
6765
  **Signature:**
6754
6766
  ```typescript
6755
- export function maxDate<TForm, TField extends string | Date | null | undefined = string | Date>(
6756
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6757
- maxDateValue: Date,
6758
- options?: ValidateOptions
6759
- ): void
6767
+ export function maxDate<
6768
+ TForm = unknown,
6769
+ TField extends string | Date | null | undefined = string | Date,
6770
+ >(maxDateValue: Date, options?: ValidateOptions): Validator<TForm, TField>
6760
6771
  ```
6761
6772
 
6762
- **Parameters:**
6763
- - `fieldPath` — - Путь к полю для валидации
6764
- - `maxDateValue` — - Максимально допустимая дата (включительно)
6765
- - `options` — - Опции валидации (message, params)
6766
-
6767
6773
  **Examples:**
6768
6774
 
6769
6775
  ```typescript
6770
- // Базовое использование
6771
- validationSchema: (path) => [
6772
- maxDate(path.birthDate, new Date()), // Не позже сегодня
6773
- ]
6774
-
6775
- // С кастомным сообщением
6776
- maxDate(path.endDate, new Date('2025-12-31'), { message: 'Дата не может быть позже конца года' })
6777
- ```
6778
-
6779
- ```typescript
6780
- // Ошибка валидации
6781
- {
6782
- code: 'date_max',
6783
- message: 'Дата должна быть не позднее 31.12.2025',
6784
- params: { maxDate: Date }
6785
- }
6776
+ validate(path.birthDate, maxDate(new Date())); // не позже сегодня
6777
+ validate(path.endDate, maxDate(new Date('2025-12-31')));
6786
6778
  ```
6787
6779
 
6788
6780
  _Source: src/core/validation/validators/max-date.ts_
@@ -6791,45 +6783,23 @@ _Source: src/core/validation/validators/max-date.ts_
6791
6783
 
6792
6784
  **Kind:** `function`
6793
6785
 
6794
- Валидатор максимальной длины строки
6786
+ Фабрика валидатора максимальной длины строки или массива.
6795
6787
 
6796
- Проверяет, что длина строки не превышает указанный максимум.
6797
6788
  Пустые значения пропускаются (используйте `required` для обязательности).
6798
6789
 
6799
6790
  **Signature:**
6800
6791
  ```typescript
6801
- export function maxLength<TForm, TField extends string | null | undefined = string>(
6802
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6792
+ export function maxLength<TForm = unknown, TField = unknown>(
6803
6793
  maxLen: number,
6804
6794
  options?: ValidateOptions
6805
- ): void
6795
+ ): Validator<TForm, TField>
6806
6796
  ```
6807
6797
 
6808
- **Parameters:**
6809
- - `fieldPath` — - Путь к полю для валидации
6810
- - `maxLen` — - Максимальная допустимая длина строки
6811
- - `options` — - Опции валидации (message, params)
6812
-
6813
6798
  **Examples:**
6814
6799
 
6815
6800
  ```typescript
6816
- // Базовое использование
6817
- validationSchema: (path) => [
6818
- maxLength(path.name, 50),
6819
- maxLength(path.bio, 500),
6820
- ]
6821
-
6822
- // С кастомным сообщением
6823
- maxLength(path.bio, 500, { message: 'Максимум 500 символов' })
6824
- ```
6825
-
6826
- ```typescript
6827
- // Ошибка валидации
6828
- {
6829
- code: 'maxLength',
6830
- message: 'Максимальная длина: 500 символов',
6831
- params: { maxLength: 500, actualLength: 512 }
6832
- }
6801
+ validate(path.name, maxLength(50));
6802
+ validate(path.bio, maxLength(500, { message: 'Max 500 символов' }));
6833
6803
  ```
6834
6804
 
6835
6805
  _Source: src/core/validation/validators/max-length.ts_
@@ -6838,45 +6808,26 @@ _Source: src/core/validation/validators/max-length.ts_
6838
6808
 
6839
6809
  **Kind:** `function`
6840
6810
 
6841
- Валидатор минимального числового значения
6811
+ Фабрика валидатора минимального числового значения.
6842
6812
 
6843
- Проверяет, что числовое значение не меньше указанного минимума.
6844
6813
  Пустые значения пропускаются (используйте `required` для обязательности).
6845
6814
 
6846
6815
  **Signature:**
6847
6816
  ```typescript
6848
- export function min<TForm, TField extends number | null | undefined = number>(
6849
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6817
+ export function min<TForm = unknown, TField extends number | undefined = number>(
6850
6818
  minValue: number,
6851
6819
  options?: ValidateOptions
6852
- ): void
6820
+ ): Validator<TForm, TField>
6853
6821
  ```
6854
6822
 
6855
- **Parameters:**
6856
- - `fieldPath` — - Путь к полю для валидации
6857
- - `minValue` — - Минимально допустимое значение
6858
- - `options` — - Опции валидации (message, params)
6859
-
6860
6823
  **Examples:**
6861
6824
 
6862
6825
  ```typescript
6863
- // Базовое использование
6864
- validationSchema: (path) => [
6865
- min(path.age, 18),
6866
- min(path.quantity, 1),
6867
- ]
6826
+ import { validate } from '@reformer/core';
6827
+ import { min } from '@reformer/core/validators';
6868
6828
 
6869
- // С кастомным сообщением
6870
- min(path.age, 18, { message: 'Вам должно быть не менее 18 лет' })
6871
- ```
6872
-
6873
- ```typescript
6874
- // Ошибка валидации
6875
- {
6876
- code: 'min',
6877
- message: 'Минимальное значение: 18',
6878
- params: { min: 18, actual: 16 }
6879
- }
6829
+ validate(path.age, min(18));
6830
+ validate(path.quantity, min(1, { message: 'Минимум 1' }));
6880
6831
  ```
6881
6832
 
6882
6833
  _Source: src/core/validation/validators/min.ts_
@@ -6885,43 +6836,23 @@ _Source: src/core/validation/validators/min.ts_
6885
6836
 
6886
6837
  **Kind:** `function`
6887
6838
 
6888
- Проверяет, что возраст (вычисленный из даты рождения) не меньше указанного
6839
+ Фабрика валидатора минимального возраста (по дате рождения).
6889
6840
 
6890
- Пустые значения и невалидные даты пропускаются (используйте `required` и `isDate`).
6841
+ Пустые и невалидные даты пропускаются.
6891
6842
 
6892
6843
  **Signature:**
6893
6844
  ```typescript
6894
- export function minAge<TForm, TField extends string | Date | null | undefined = string | Date>(
6895
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6896
- minAgeValue: number,
6897
- options?: ValidateOptions
6898
- ): void
6845
+ export function minAge<
6846
+ TForm = unknown,
6847
+ TField extends string | Date | null | undefined = string | Date,
6848
+ >(minAgeValue: number, options?: ValidateOptions): Validator<TForm, TField>
6899
6849
  ```
6900
6850
 
6901
- **Parameters:**
6902
- - `fieldPath` — - Путь к полю для валидации (дата рождения)
6903
- - `minAgeValue` — - Минимально допустимый возраст в годах
6904
- - `options` — - Опции валидации (message, params)
6905
-
6906
6851
  **Examples:**
6907
6852
 
6908
6853
  ```typescript
6909
- // Базовое использование - минимум 18 лет
6910
- validationSchema: (path) => [
6911
- minAge(path.birthDate, 18),
6912
- ]
6913
-
6914
- // С кастомным сообщением
6915
- minAge(path.birthDate, 21, { message: 'Вам должно быть не менее 21 года' })
6916
- ```
6917
-
6918
- ```typescript
6919
- // Ошибка валидации
6920
- {
6921
- code: 'date_min_age',
6922
- message: 'Минимальный возраст: 18 лет',
6923
- params: { minAge: 18, currentAge: 16 }
6924
- }
6854
+ validate(path.birthDate, minAge(18));
6855
+ validate(path.birthDate, minAge(21, { message: 'Вам должно быть не менее 21 года' }));
6925
6856
  ```
6926
6857
 
6927
6858
  _Source: src/core/validation/validators/min-age.ts_
@@ -6930,43 +6861,23 @@ _Source: src/core/validation/validators/min-age.ts_
6930
6861
 
6931
6862
  **Kind:** `function`
6932
6863
 
6933
- Проверяет, что дата не раньше указанной минимальной
6864
+ Фабрика валидатора минимальной даты (включительно).
6934
6865
 
6935
- Пустые значения и невалидные даты пропускаются (используйте `required` и `isDate`).
6866
+ Пустые и невалидные даты пропускаются (используйте `required` и `isDate`).
6936
6867
 
6937
6868
  **Signature:**
6938
6869
  ```typescript
6939
- export function minDate<TForm, TField extends string | Date | null | undefined = string | Date>(
6940
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6941
- minDateValue: Date,
6942
- options?: ValidateOptions
6943
- ): void
6870
+ export function minDate<
6871
+ TForm = unknown,
6872
+ TField extends string | Date | null | undefined = string | Date,
6873
+ >(minDateValue: Date, options?: ValidateOptions): Validator<TForm, TField>
6944
6874
  ```
6945
6875
 
6946
- **Parameters:**
6947
- - `fieldPath` — - Путь к полю для валидации
6948
- - `minDateValue` — - Минимально допустимая дата (включительно)
6949
- - `options` — - Опции валидации (message, params)
6950
-
6951
6876
  **Examples:**
6952
6877
 
6953
6878
  ```typescript
6954
- // Базовое использование
6955
- validationSchema: (path) => [
6956
- minDate(path.eventDate, new Date('2024-01-01')),
6957
- ]
6958
-
6959
- // С кастомным сообщением
6960
- minDate(path.startDate, new Date(), { message: 'Дата должна быть не раньше сегодня' })
6961
- ```
6962
-
6963
- ```typescript
6964
- // Ошибка валидации
6965
- {
6966
- code: 'date_min',
6967
- message: 'Дата должна быть не ранее 01.01.2024',
6968
- params: { minDate: Date }
6969
- }
6879
+ validate(path.eventDate, minDate(new Date('2024-01-01')));
6880
+ validate(path.startDate, minDate(new Date(), { message: 'Дата должна быть не раньше сегодня' }));
6970
6881
  ```
6971
6882
 
6972
6883
  _Source: src/core/validation/validators/min-date.ts_
@@ -6975,48 +6886,51 @@ _Source: src/core/validation/validators/min-date.ts_
6975
6886
 
6976
6887
  **Kind:** `function`
6977
6888
 
6978
- Валидатор минимальной длины строки
6889
+ Фабрика валидатора минимальной длины строки или массива.
6979
6890
 
6980
- Проверяет, что длина строки не меньше указанного минимума.
6981
6891
  Пустые значения пропускаются (используйте `required` для обязательности).
6982
6892
 
6983
6893
  **Signature:**
6984
6894
  ```typescript
6985
- export function minLength<TForm, TField extends string | null | undefined = string>(
6986
- fieldPath: FieldPathNode<TForm, TField> | undefined,
6895
+ export function minLength<TForm = unknown, TField = unknown>(
6987
6896
  minLen: number,
6988
6897
  options?: ValidateOptions
6989
- ): void
6898
+ ): Validator<TForm, TField>
6990
6899
  ```
6991
6900
 
6992
- **Parameters:**
6993
- - `fieldPath` — - Путь к полю для валидации
6994
- - `minLen` — - Минимальная допустимая длина строки
6995
- - `options` — - Опции валидации (message, params)
6996
-
6997
6901
  **Examples:**
6998
6902
 
6999
6903
  ```typescript
7000
- // Базовое использование
7001
- validationSchema: (path) => [
7002
- minLength(path.name, 2),
7003
- minLength(path.password, 8),
7004
- ]
6904
+ validate(path.name, minLength(2));
6905
+ validate(path.password, minLength(8, { message: 'Min 8 символов' }));
6906
+ ```
6907
+
6908
+ _Source: src/core/validation/validators/min-length.ts_
6909
+
6910
+ ### multipleOf
7005
6911
 
7006
- // С кастомным сообщением
7007
- minLength(path.password, 8, { message: 'Пароль должен быть не менее 8 символов' })
6912
+ **Kind:** `function`
6913
+
6914
+ Фабрика валидатора, проверяющего что число кратно заданному.
6915
+
6916
+ Пустые значения и не-числа пропускаются (используйте `required` и `isNumber`).
6917
+
6918
+ **Signature:**
6919
+ ```typescript
6920
+ export function multipleOf<TForm = unknown, TField extends number | undefined = number>(
6921
+ divisor: number,
6922
+ options?: ValidateOptions
6923
+ ): Validator<TForm, TField>
7008
6924
  ```
7009
6925
 
6926
+ **Examples:**
6927
+
7010
6928
  ```typescript
7011
- // Ошибка валидации
7012
- {
7013
- code: 'minLength',
7014
- message: 'Минимальная длина: 8 символов',
7015
- params: { minLength: 8, actualLength: 3 }
7016
- }
6929
+ validate(path.price, multipleOf(0.01));
6930
+ validate(path.rating, multipleOf(0.5, { message: 'Только шаг 0.5' }));
7017
6931
  ```
7018
6932
 
7019
- _Source: src/core/validation/validators/min-length.ts_
6933
+ _Source: src/core/validation/validators/multiple-of.ts_
7020
6934
 
7021
6935
  ### NodeFactory
7022
6936
 
@@ -7087,79 +7001,74 @@ const node = NodeFactory.create({ value: '' }); // → FieldNode<string>
7087
7001
 
7088
7002
  _Source: src/core/factories/node-factory.ts_
7089
7003
 
7090
- ### notEmpty
7004
+ ### nonNegative
7091
7005
 
7092
7006
  **Kind:** `function`
7093
7007
 
7094
- Проверить что массив содержит хотя бы один элемент
7008
+ Фабрика валидатора, проверяющего что число неотрицательное (`≥ 0`).
7095
7009
 
7096
- Это удобный алиас для `minLength(field, 1)`, оптимизированный для массивов.
7010
+ Пустые значения и не-числа пропускаются (используйте `required` и `isNumber`).
7097
7011
 
7098
7012
  **Signature:**
7099
7013
  ```typescript
7100
- export function notEmpty<TForm, TItem>(
7101
- fieldPath: FieldPathNode<TForm, TItem[] | undefined> | undefined,
7014
+ export function nonNegative<TForm = unknown, TField extends number | undefined = number>(
7102
7015
  options?: ValidateOptions
7103
- ): void
7016
+ ): Validator<TForm, TField>
7104
7017
  ```
7105
7018
 
7106
- **Parameters:**
7107
- - `fieldPath` — - Поле-массив для валидации
7108
- - `options` — - Опции валидации (message, params и т.д.)
7109
-
7110
7019
  **Examples:**
7111
7020
 
7112
7021
  ```typescript
7113
- // Простая проверка
7114
- notEmpty(path.properties, { message: 'Добавьте хотя бы один объект имущества' });
7022
+ validate(path.quantity, nonNegative());
7023
+ validate(path.balance, nonNegative({ message: 'Баланс не может быть отрицательным' }));
7024
+ ```
7115
7025
 
7116
- // С дополнительными параметрами
7117
- notEmpty(path.coBorrowers, {
7118
- message: 'Требуется хотя бы один созаемщик',
7119
- params: { minItems: 1 }
7120
- });
7026
+ _Source: src/core/validation/validators/non-negative.ts_
7027
+
7028
+ ### nonZero
7029
+
7030
+ **Kind:** `function`
7031
+
7032
+ Фабрика валидатора, проверяющего что число не равно нулю.
7033
+
7034
+ Пустые значения и не-числа пропускаются (используйте `required` и `isNumber`).
7035
+
7036
+ **Signature:**
7037
+ ```typescript
7038
+ export function nonZero<TForm = unknown, TField extends number | undefined = number>(
7039
+ options?: ValidateOptions
7040
+ ): Validator<TForm, TField>
7121
7041
  ```
7122
7042
 
7123
- _Source: src/core/validation/validators/array-validators.ts_
7043
+ **Examples:**
7044
+
7045
+ ```typescript
7046
+ validate(path.quantity, nonZero());
7047
+ validate(path.divisor, nonZero({ message: 'Не может быть нулём' }));
7048
+ ```
7124
7049
 
7125
- ### number
7050
+ _Source: src/core/validation/validators/non-zero.ts_
7051
+
7052
+ ### notEmpty
7126
7053
 
7127
7054
  **Kind:** `function`
7128
7055
 
7129
- Адаптер для number валидатора
7130
- Проверяет, что значение является числом и соответствует заданным ограничениям
7056
+ Фабрика валидатора, проверяющего что массив (или строка) не пустой.
7131
7057
 
7132
7058
  **Signature:**
7133
7059
  ```typescript
7134
- export function number<TForm, TField extends number | undefined = number>(
7135
- fieldPath: FieldPathNode<TForm, TField> | undefined,
7136
- options?: ValidateOptions & {
7137
- /** Минимальное значение (включительно) */
7138
- min?: number;
7139
- /** Максимальное значение (включительно) */
7140
- max?: number;
7141
- /** Требовать целое число */
7142
- integer?: boolean;
7143
- /** Значение должно быть кратно этому числу */
7144
- multipleOf?: number;
7145
- /** Разрешить отрицательные числа (по умолчанию true) */
7146
- allowNegative?: boolean;
7147
- /** Разрешить ноль (по умолчанию true) */
7148
- allowZero?: boolean;
7149
- }
7150
- ): void
7060
+ export function notEmpty<TForm = unknown, TField = unknown>(
7061
+ options?: ValidateOptions
7062
+ ): Validator<TForm, TField>
7151
7063
  ```
7152
7064
 
7153
7065
  **Examples:**
7154
7066
 
7155
7067
  ```typescript
7156
- number(path.age);
7157
- number(path.price, { min: 0, max: 1000000 });
7158
- number(path.percentage, { min: 0, max: 100, integer: true });
7159
- number(path.rating, { min: 0, max: 5, multipleOf: 0.5 });
7068
+ validate(path.properties, notEmpty({ message: 'Добавьте хотя бы один элемент' }));
7160
7069
  ```
7161
7070
 
7162
- _Source: src/core/validation/validators/number.ts_
7071
+ _Source: src/core/validation/validators/array-validators.ts_
7163
7072
 
7164
7073
  ### ObservableForm
7165
7074
 
@@ -7204,41 +7113,21 @@ _Source: src/core/utils/form-observer.ts_
7204
7113
 
7205
7114
  **Kind:** `function`
7206
7115
 
7207
- Проверяет, что дата находится в прошлом (не в будущем)
7116
+ Фабрика валидатора, проверяющего что дата не в будущем.
7208
7117
 
7209
- Пустые значения и невалидные даты пропускаются (используйте `required` и `isDate`).
7118
+ Пустые и невалидные даты пропускаются.
7210
7119
 
7211
7120
  **Signature:**
7212
7121
  ```typescript
7213
- export function pastDate<TForm, TField extends string | Date | undefined = string | Date>(
7214
- fieldPath: FieldPathNode<TForm, TField> | undefined,
7122
+ export function pastDate<TForm = unknown, TField extends string | Date | undefined = string | Date>(
7215
7123
  options?: ValidateOptions
7216
- ): void
7124
+ ): Validator<TForm, TField>
7217
7125
  ```
7218
7126
 
7219
- **Parameters:**
7220
- - `fieldPath` — - Путь к полю для валидации
7221
- - `options` — - Опции валидации (message, params)
7222
-
7223
7127
  **Examples:**
7224
7128
 
7225
7129
  ```typescript
7226
- // Базовое использование - дата рождения не может быть в будущем
7227
- validationSchema: (path) => [
7228
- pastDate(path.birthDate),
7229
- ]
7230
-
7231
- // С кастомным сообщением
7232
- pastDate(path.birthDate, { message: 'Дата рождения не может быть в будущем' })
7233
- ```
7234
-
7235
- ```typescript
7236
- // Ошибка валидации
7237
- {
7238
- code: 'date_future',
7239
- message: 'Дата не может быть в будущем',
7240
- params: {}
7241
- }
7130
+ validate(path.birthDate, pastDate({ message: 'Дата рождения не может быть в будущем' }));
7242
7131
  ```
7243
7132
 
7244
7133
  _Source: src/core/validation/validators/past-date.ts_
@@ -7285,47 +7174,23 @@ _Source: src/core/utils/field-path-navigator.ts_
7285
7174
 
7286
7175
  **Kind:** `function`
7287
7176
 
7288
- Валидатор паттерна (регулярного выражения)
7177
+ Фабрика валидатора регулярного выражения.
7289
7178
 
7290
- Проверяет, что значение соответствует указанному регулярному выражению.
7291
7179
  Пустые значения пропускаются (используйте `required` для обязательности).
7292
7180
 
7293
7181
  **Signature:**
7294
7182
  ```typescript
7295
- export function pattern<TForm, TField extends string | null | undefined = string>(
7296
- fieldPath: FieldPathNode<TForm, TField> | undefined,
7183
+ export function pattern<TForm = unknown, TField extends string | undefined = string>(
7297
7184
  regex: RegExp,
7298
7185
  options?: ValidateOptions
7299
- ): void
7186
+ ): Validator<TForm, TField>
7300
7187
  ```
7301
7188
 
7302
- **Parameters:**
7303
- - `fieldPath` — - Путь к полю для валидации
7304
- - `regex` — - Регулярное выражение для проверки
7305
- - `options` — - Опции валидации (message, params)
7306
-
7307
7189
  **Examples:**
7308
7190
 
7309
7191
  ```typescript
7310
- // Только буквы
7311
- pattern(path.name, /^[а-яА-Яa-zA-Z]+$/)
7312
-
7313
- // Только цифры
7314
- pattern(path.code, /^\d+$/)
7315
-
7316
- // С кастомным сообщением
7317
- pattern(path.username, /^[a-z0-9_]+$/i, {
7318
- message: 'Только латинские буквы, цифры и подчёркивание'
7319
- })
7320
- ```
7321
-
7322
- ```typescript
7323
- // Ошибка валидации
7324
- {
7325
- code: 'pattern',
7326
- message: 'Значение не соответствует требуемому формату',
7327
- params: { pattern: '^[a-z]+$' }
7328
- }
7192
+ validate(path.name, pattern(/^[а-яА-Яa-zA-Z]+$/));
7193
+ validate(path.code, pattern(/^\d+$/, { message: 'Только цифры' }));
7329
7194
  ```
7330
7195
 
7331
7196
  _Source: src/core/validation/validators/pattern.ts_
@@ -7334,26 +7199,22 @@ _Source: src/core/validation/validators/pattern.ts_
7334
7199
 
7335
7200
  **Kind:** `function`
7336
7201
 
7337
- Адаптер для phone валидатора
7338
- Поддерживает опциональные поля (string | undefined)
7202
+ Фабрика валидатора номера телефона.
7203
+
7204
+ Пустые значения пропускаются (используйте `required` для обязательности).
7339
7205
 
7340
7206
  **Signature:**
7341
7207
  ```typescript
7342
- export function phone<TForm, TField extends string | null | undefined = string>(
7343
- fieldPath: FieldPathNode<TForm, TField> | undefined,
7344
- options?: ValidateOptions & {
7345
- /** Формат телефона */
7346
- format?: PhoneFormat;
7347
- }
7348
- ): void
7208
+ export function phone<TForm = unknown, TField extends string | undefined = string>(
7209
+ options?: PhoneValidatorOptions
7210
+ ): Validator<TForm, TField>
7349
7211
  ```
7350
7212
 
7351
7213
  **Examples:**
7352
7214
 
7353
7215
  ```typescript
7354
- phone(path.phoneNumber);
7355
- phone(path.phoneNumber, { format: 'ru' });
7356
- phone(path.phoneNumber, { format: 'international', message: 'Неверный формат телефона' });
7216
+ validate(path.phone, phone());
7217
+ validate(path.phone, phone({ format: 'ru' }));
7357
7218
  ```
7358
7219
 
7359
7220
  _Source: src/core/validation/validators/phone.ts_
@@ -7362,8 +7223,6 @@ _Source: src/core/validation/validators/phone.ts_
7362
7223
 
7363
7224
  **Kind:** `type`
7364
7225
 
7365
- Формат телефона для валидации
7366
-
7367
7226
  **Signature:**
7368
7227
  ```typescript
7369
7228
  export type PhoneFormat = 'international' | 'ru' | 'us' | 'any';
@@ -7411,47 +7270,29 @@ _Source: src/core/utils/registry-stack.ts_
7411
7270
 
7412
7271
  **Kind:** `function`
7413
7272
 
7414
- Валидатор обязательного поля
7273
+ Фабрика валидатора обязательного поля.
7274
+
7275
+ Возвращает чистую функцию-валидатор `(value, control, root)`. Передаётся в `validate()`.
7415
7276
 
7416
- Проверяет, что поле имеет непустое значение.
7417
7277
  Пустыми считаются: `null`, `undefined`, `''` (пустая строка).
7418
7278
  Для boolean полей требуется значение `true`.
7419
7279
 
7420
7280
  **Signature:**
7421
7281
  ```typescript
7422
- export function required<TForm, TField>(
7423
- fieldPath: FieldPathNode<TForm, TField> | undefined,
7282
+ export function required<TForm = unknown, TField = unknown>(
7424
7283
  options?: ValidateOptions
7425
- ): void
7284
+ ): Validator<TForm, TField>
7426
7285
  ```
7427
7286
 
7428
- **Parameters:**
7429
- - `fieldPath` — - Путь к полю для валидации
7430
- - `options` — - Опции валидации (message, params)
7431
-
7432
7287
  **Examples:**
7433
7288
 
7434
7289
  ```typescript
7435
- // Базовое использование
7436
- validationSchema: (path, { validate }) => [
7437
- required(path.name),
7438
- required(path.email),
7439
- ]
7440
-
7441
- // С кастомным сообщением
7442
- required(path.phone, { message: 'Укажите номер телефона' })
7443
-
7444
- // Для checkbox (требует true)
7445
- required(path.agreeToTerms, { message: 'Необходимо принять условия' })
7446
- ```
7290
+ import { validate } from '@reformer/core';
7291
+ import { required } from '@reformer/core/validators';
7447
7292
 
7448
- ```typescript
7449
- // Ошибка валидации
7450
- {
7451
- code: 'required',
7452
- message: 'Поле обязательно для заполнения',
7453
- params: {}
7454
- }
7293
+ validate(path.email, required());
7294
+ validate(path.phone, required({ message: 'Укажите номер телефона' }));
7295
+ validate(path.agreeToTerms, required({ message: 'Необходимо принять условия' }));
7455
7296
  ```
7456
7297
 
7457
7298
  _Source: src/core/validation/validators/required.ts_
@@ -8166,60 +8007,6 @@ export interface TransformValueOptions {
8166
8007
 
8167
8008
  _Source: src/core/behavior/behaviors/transform-value.ts_
8168
8009
 
8169
- ### TreeValidationContextImpl
8170
-
8171
- **Kind:** `class`
8172
-
8173
- Контекст cross-field валидации. Передаётся в `validateTree`/`validateForm`
8174
- callback'и — напрямую инстанцировать не нужно.
8175
-
8176
- **Signature:**
8177
- ```typescript
8178
- export class TreeValidationContextImpl<TForm> extends BaseValidationContext<TForm> { /* … */ }
8179
- ```
8180
-
8181
- **Examples:**
8182
-
8183
- ```typescript
8184
- import { validateForm } from '@reformer/core';
8185
-
8186
- validateForm(form, (ctx) => {
8187
- // ctx — экземпляр TreeValidationContextImpl
8188
- if (ctx.form.startDate.value > ctx.form.endDate.value) {
8189
- return [{ code: 'date-range', message: 'Дата начала позже даты окончания' }];
8190
- }
8191
- return [];
8192
- });
8193
- ```
8194
-
8195
- _Source: src/core/validation/validation-context.ts_
8196
-
8197
- ### TreeValidatorFn
8198
-
8199
- **Kind:** `type`
8200
-
8201
- Функция cross-field валидации
8202
-
8203
- **Signature:**
8204
- ```typescript
8205
- export type TreeValidatorFn<TForm> = (ctx: FormContext<TForm>) => ValidationError | null;
8206
- ```
8207
-
8208
- **Examples:**
8209
-
8210
- ```typescript
8211
- validateTree((ctx) => {
8212
- const password = ctx.form.password.value.value;
8213
- const confirm = ctx.form.confirmPassword.value.value;
8214
- if (password !== confirm) {
8215
- return { code: 'mismatch', message: 'Passwords must match' };
8216
- }
8217
- return null;
8218
- });
8219
- ```
8220
-
8221
- _Source: src/core/types/validation-schema.ts_
8222
-
8223
8010
  ### uniqueId
8224
8011
 
8225
8012
  **Kind:** `function`
@@ -8293,28 +8080,23 @@ _Source: src/core/types/index.ts_
8293
8080
 
8294
8081
  **Kind:** `function`
8295
8082
 
8296
- Адаптер для URL валидатора
8297
- Поддерживает опциональные поля (string | undefined)
8083
+ Фабрика валидатора URL.
8084
+
8085
+ Пустые значения пропускаются (используйте `required` для обязательности).
8298
8086
 
8299
8087
  **Signature:**
8300
8088
  ```typescript
8301
- export function url<TForm, TField extends string | null | undefined = string>(
8302
- fieldPath: FieldPathNode<TForm, TField> | undefined,
8303
- options?: ValidateOptions & {
8304
- /** Требовать наличие протокола (http:// или https://) */
8305
- requireProtocol?: boolean;
8306
- /** Разрешенные протоколы */
8307
- allowedProtocols?: string[];
8308
- }
8309
- ): void
8089
+ export function url<TForm = unknown, TField extends string | undefined = string>(
8090
+ options?: UrlValidatorOptions
8091
+ ): Validator<TForm, TField>
8310
8092
  ```
8311
8093
 
8312
8094
  **Examples:**
8313
8095
 
8314
8096
  ```typescript
8315
- url(path.website);
8316
- url(path.website, { message: 'Введите корректный URL' });
8317
- url(path.website, { requireProtocol: true });
8097
+ validate(path.website, url());
8098
+ validate(path.website, url({ requireProtocol: true }));
8099
+ validate(path.website, url({ allowedProtocols: ['https'] }));
8318
8100
  ```
8319
8101
 
8320
8102
  _Source: src/core/validation/validators/url.ts_
@@ -8809,14 +8591,16 @@ _Source: src/hooks/useHiddenCondition.ts_
8809
8591
 
8810
8592
  **Kind:** `function`
8811
8593
 
8812
- Зарегистрировать кастомный синхронный валидатор для поля
8813
- Поддерживает опциональные поля
8594
+ Зарегистрировать синхронный валидатор для поля.
8595
+
8596
+ Принимает чистую функцию-валидатор `(value, control, root) => error | null`,
8597
+ либо фабрику из `@reformer/core/validators` (например, `required()`).
8814
8598
 
8815
8599
  **Signature:**
8816
8600
  ```typescript
8817
8601
  export function validate<TForm, TField>(
8818
8602
  fieldPath: FieldPathNode<TForm, TField> | undefined,
8819
- validatorFn: ContextualValidatorFn<TForm, TField>,
8603
+ validator: Validator<TForm, TField>,
8820
8604
  options?: ValidateOptions
8821
8605
  ): void
8822
8606
  ```
@@ -8824,20 +8608,14 @@ export function validate<TForm, TField>(
8824
8608
  **Examples:**
8825
8609
 
8826
8610
  ```typescript
8827
- validate(path.birthDate, (ctx: ValidationContext<TForm, TField>) => {
8828
- const birthDate = new Date(ctx.value());
8829
- const age = calculateAge(birthDate);
8830
-
8831
- if (age < 18) {
8832
- return {
8833
- code: 'tooYoung',
8834
- message: 'Заемщику должно быть не менее 18 лет',
8835
- };
8836
- }
8837
-
8611
+ // Кастомный валидатор
8612
+ validate(path.birthDate, (value, _control, root) => {
8613
+ const age = calculateAge(new Date(value));
8614
+ if (age < 18) return { code: 'tooYoung', message: 'Только 18+' };
8838
8615
  return null;
8839
8616
  });
8840
- ```
8617
+
8618
+ // Фабрика из
8841
8619
 
8842
8620
  _Source: src/core/validation/core/validate.ts_
8843
8621
 
@@ -8845,13 +8623,15 @@ _Source: src/core/validation/core/validate.ts_
8845
8623
 
8846
8624
  **Kind:** `function`
8847
8625
 
8848
- Зарегистрировать асинхронный валидатор для поля
8626
+ Зарегистрировать асинхронный валидатор для поля.
8627
+
8628
+ Принимает чистую async-функцию `(value, control, root) => Promise<error | null>`.
8849
8629
 
8850
8630
  **Signature:**
8851
8631
  ```typescript
8852
8632
  export function validateAsync<TForm, TField>(
8853
8633
  fieldPath: FieldPathNode<TForm, TField>,
8854
- validatorFn: ContextualAsyncValidatorFn<TForm, TField>,
8634
+ validator: AsyncValidator<TForm, TField>,
8855
8635
  options?: ValidateAsyncOptions
8856
8636
  ): void
8857
8637
  ```
@@ -8861,23 +8641,16 @@ export function validateAsync<TForm, TField>(
8861
8641
  ```typescript
8862
8642
  validateAsync(
8863
8643
  path.inn,
8864
- async (ctx: ValidationContext<TForm, TField>) => {
8865
- const inn = ctx.value();
8866
- if (!inn) return null;
8867
-
8644
+ async (value, _control, _root) => {
8645
+ if (!value) return null;
8868
8646
  const response = await fetch('/api/validate-inn', {
8869
8647
  method: 'POST',
8870
- body: JSON.stringify({ inn }),
8648
+ body: JSON.stringify({ inn: value }),
8871
8649
  });
8872
-
8873
8650
  const data = await response.json();
8874
8651
  if (!data.valid) {
8875
- return {
8876
- code: 'invalidInn',
8877
- message: 'ИНН не найден в базе данных ФНС',
8878
- };
8652
+ return { code: 'invalidInn', message: 'ИНН не найден в базе ФНС' };
8879
8653
  }
8880
-
8881
8654
  return null;
8882
8655
  },
8883
8656
  { debounce: 1000 }
@@ -8890,7 +8663,7 @@ _Source: src/core/validation/core/validate-async.ts_
8890
8663
 
8891
8664
  **Kind:** `interface`
8892
8665
 
8893
- Опции для функции validateAsync
8666
+ Опции для функции validateAsync.
8894
8667
 
8895
8668
  **Signature:**
8896
8669
  ```typescript
@@ -8968,7 +8741,7 @@ _Source: src/core/validation/validate-form.ts_
8968
8741
 
8969
8742
  **Kind:** `function`
8970
8743
 
8971
- Применить validation schema к каждому элементу массива
8744
+ Применить validation schema к каждому элементу массива.
8972
8745
 
8973
8746
  Регистрирует схему валидации, которая будет автоматически применяться
8974
8747
  к каждому элементу ArrayNode (как существующим, так и новым).
@@ -8981,21 +8754,13 @@ export function validateItems<TForm, TItem>(
8981
8754
  ): void
8982
8755
  ```
8983
8756
 
8984
- **Parameters:**
8985
- - `fieldPath` — - Поле-массив для валидации элементов
8986
- - `itemSchemaFn` — - Validation schema для одного элемента
8987
-
8988
8757
  **Examples:**
8989
8758
 
8990
8759
  ```typescript
8991
8760
  import { propertyValidation } from './property-validation';
8992
8761
 
8993
- // В additionalValidation
8994
8762
  applyWhen(path.hasProperty, (value) => value === true, (path) => {
8995
- // Проверка что массив не пустой
8996
- notEmpty(path.properties, { message: 'Добавьте хотя бы один объект имущества' });
8997
-
8998
- // Валидация каждого элемента
8763
+ validate(path.properties, notEmpty({ message: 'Добавьте хотя бы один объект' }));
8999
8764
  validateItems(path.properties, propertyValidation);
9000
8765
  });
9001
8766
  ```
@@ -9006,7 +8771,7 @@ _Source: src/core/validation/validators/array-validators.ts_
9006
8771
 
9007
8772
  **Kind:** `interface`
9008
8773
 
9009
- Опции для функции validate
8774
+ Опции для функции validate.
9010
8775
 
9011
8776
  **Signature:**
9012
8777
  ```typescript
@@ -9020,88 +8785,6 @@ export interface ValidateOptions {
9020
8785
 
9021
8786
  _Source: src/core/types/validation-schema.ts_
9022
8787
 
9023
- ### validateTree
9024
-
9025
- **Kind:** `function`
9026
-
9027
- Зарегистрировать cross-field валидатор
9028
-
9029
- Используется для валидации, которая зависит от нескольких полей
9030
-
9031
- **Signature:**
9032
- ```typescript
9033
- export function validateTree<TForm>(
9034
- validatorFn: TreeValidatorFn<TForm>,
9035
- options?: ValidateTreeOptions
9036
- ): void
9037
- ```
9038
-
9039
- **Examples:**
9040
-
9041
- ```typescript
9042
- // Явная типизация ctx для избежания implicit any
9043
- validateTree(
9044
- (ctx: { form: MyForm }) => {
9045
- if (ctx.form.initialPayment && ctx.form.propertyValue) {
9046
- if (ctx.form.initialPayment > ctx.form.propertyValue) {
9047
- return {
9048
- code: 'initialPaymentTooHigh',
9049
- message: 'Первоначальный взнос не может превышать стоимость',
9050
- };
9051
- }
9052
- }
9053
- return null;
9054
- },
9055
- { targetField: 'initialPayment' }
9056
- );
9057
- ```
9058
-
9059
- _Source: src/core/validation/core/validate-tree.ts_
9060
-
9061
- ### ValidateTreeOptions
9062
-
9063
- **Kind:** `interface`
9064
-
9065
- Опции для функции validateTree
9066
-
9067
- **Signature:**
9068
- ```typescript
9069
- export interface ValidateTreeOptions {
9070
- /** Поле, к которому привязать ошибку */
9071
- targetField?: string;
9072
- }
9073
- ```
9074
-
9075
- _Source: src/core/types/validation-schema.ts_
9076
-
9077
- ### ValidationContextImpl
9078
-
9079
- **Kind:** `class`
9080
-
9081
- Контекст валидации одного поля. Создаётся фреймворком и передаётся в валидаторы
9082
- (`required`, `validate`, …) — напрямую инстанцировать не нужно.
9083
-
9084
- **Signature:**
9085
- ```typescript
9086
- export class ValidationContextImpl<TForm, TField> extends BaseValidationContext<TForm> { /* … */ }
9087
- ```
9088
-
9089
- **Examples:**
9090
-
9091
- ```typescript
9092
- import { validate } from '@reformer/core/validators';
9093
-
9094
- validate(path.password, (value, ctx) => {
9095
- // ctx — экземпляр ValidationContextImpl, доступ к ctx.form для cross-field логики
9096
- if (value !== ctx.form.confirmPassword.value) {
9097
- return { code: 'mismatch', message: 'Пароли не совпадают' };
9098
- }
9099
- return null;
9100
- });
9101
- ```
9102
-
9103
- _Source: src/core/validation/validation-context.ts_
9104
-
9105
8788
  ### ValidationError
9106
8789
 
9107
8790
  **Kind:** `interface`
@@ -9130,14 +8813,18 @@ _Source: src/core/types/index.ts_
9130
8813
  Каждый экземпляр GroupNode создает собственный реестр (композиция).
9131
8814
  Устраняет race conditions и изолирует формы друг от друга.
9132
8815
 
9133
- Наследует AbstractRegistry для унификации:
9134
- - Управления global stack
9135
- - Template methods begin/end registration
8816
+ Context stack используется для tracking текущего активного реестра:
8817
+ - beginRegistration() помещает this в global stack
8818
+ - endRegistration() извлекает из global stack
8819
+ - getCurrent() возвращает текущий активный реестр
9136
8820
 
9137
8821
  **Signature:**
9138
8822
  ```typescript
9139
- export class ValidationRegistry extends AbstractRegistry<ValidatorRegistration> {
9140
- /** Внутренний стек контекстов для управления condition blocks */ /* … */ }
8823
+ export class ValidationRegistry {
8824
+ /**
8825
+ * Global stack активных реестров
8826
+ * Используется для изоляции форм друг от друга
8827
+ */ /* … */ }
9141
8828
  ```
9142
8829
 
9143
8830
  **Examples:**
@@ -9160,9 +8847,9 @@ _Source: src/core/validation/validation-registry.ts_
9160
8847
 
9161
8848
  **Kind:** `type`
9162
8849
 
9163
- Функция validation schema
8850
+ Функция validation schema.
9164
8851
 
9165
- Принимает FieldPath и определяет все правила валидации для формы
8852
+ Принимает FieldPath и определяет все правила валидации для формы.
9166
8853
 
9167
8854
  **Signature:**
9168
8855
  ```typescript
@@ -9171,6 +8858,36 @@ export type ValidationSchemaFn<T> = (path: FieldPath<T>) => void;
9171
8858
 
9172
8859
  _Source: src/core/types/validation-schema.ts_
9173
8860
 
8861
+ ### Validator
8862
+
8863
+ **Kind:** `type`
8864
+
8865
+ Чистый синхронный валидатор поля.
8866
+
8867
+ Принимает значение поля, прокси текущего поля (control) и прокси корня формы (root).
8868
+ Возвращает ValidationError либо null. Не знает про реестр валидации.
8869
+
8870
+ **Signature:**
8871
+ ```typescript
8872
+ export type Validator<TForm, TField> = (
8873
+ value: TField,
8874
+ control: FormProxy<TField>,
8875
+ root: FormProxy<TForm>
8876
+ ) => ValidationError | null;
8877
+ ```
8878
+
8879
+ **Examples:**
8880
+
8881
+ ```typescript
8882
+ const isAdult: Validator<MyForm, number> = (value, control, root) => {
8883
+ if (value < 18) return { code: 'tooYoung', message: '18+' };
8884
+ return null;
8885
+ };
8886
+ validate(path.age, isAdult);
8887
+ ```
8888
+
8889
+ _Source: src/core/types/validation-schema.ts_
8890
+
9174
8891
  ### ValidatorFn
9175
8892
 
9176
8893
  **Kind:** `type`
@@ -9194,12 +8911,10 @@ _Source: src/core/types/index.ts_
9194
8911
  ```typescript
9195
8912
  export interface ValidatorRegistration {
9196
8913
  fieldPath: string;
9197
- type: 'sync' | 'async' | 'tree';
9198
- validator:
9199
- | ContextualValidatorFn<unknown, unknown>
9200
- | ContextualAsyncValidatorFn<unknown, unknown>
9201
- | TreeValidatorFn<unknown>;
9202
- options?: ValidateOptions | ValidateAsyncOptions | ValidateTreeOptions;
8914
+ type: 'sync' | 'async' | 'array-items';
8915
+ validator: Validator<unknown, unknown> | AsyncValidator<unknown, unknown>;
8916
+ options?: ValidateOptions | ValidateAsyncOptions;
8917
+ /** Для условной валидации (applyWhen). */
9203
8918
  condition?: {
9204
8919
  fieldPath: string;
9205
8920
  conditionFn: ConditionFn<unknown>;