@reformer/core 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/dist/behaviors/compute-from.d.ts +2 -0
  2. package/dist/behaviors/compute-from.js +31 -0
  3. package/dist/behaviors/copy-from.d.ts +2 -0
  4. package/dist/behaviors/copy-from.js +29 -0
  5. package/dist/behaviors/enable-when.d.ts +2 -0
  6. package/dist/behaviors/enable-when.js +25 -0
  7. package/dist/behaviors/reset-when.d.ts +2 -0
  8. package/dist/behaviors/reset-when.js +24 -0
  9. package/dist/behaviors/revalidate-when.d.ts +2 -0
  10. package/dist/behaviors/revalidate-when.js +18 -0
  11. package/dist/behaviors/sync-fields.d.ts +2 -0
  12. package/dist/behaviors/sync-fields.js +41 -0
  13. package/dist/behaviors/transform-value.d.ts +2 -0
  14. package/dist/behaviors/transform-value.js +45 -0
  15. package/dist/behaviors/watch-field.d.ts +2 -0
  16. package/dist/behaviors/watch-field.js +21 -0
  17. package/dist/behaviors.js +26 -19
  18. package/dist/core/behavior/behavior-context.d.ts +26 -12
  19. package/dist/core/behavior/behavior-registry.d.ts +15 -27
  20. package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
  21. package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
  22. package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
  23. package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
  24. package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
  25. package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
  26. package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
  27. package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
  28. package/dist/core/behavior/compose-behavior.d.ts +2 -12
  29. package/dist/core/behavior/index.d.ts +0 -1
  30. package/dist/core/behavior/types.d.ts +2 -8
  31. package/dist/core/factories/node-factory.d.ts +6 -29
  32. package/dist/core/nodes/array-node.d.ts +39 -19
  33. package/dist/core/nodes/field-node.d.ts +51 -26
  34. package/dist/core/nodes/form-node.d.ts +18 -20
  35. package/dist/core/nodes/group-node.d.ts +25 -21
  36. package/dist/core/types/deep-schema.d.ts +2 -12
  37. package/dist/core/types/field-path.d.ts +1 -1
  38. package/dist/core/types/form-context.d.ts +26 -26
  39. package/dist/core/types/form-proxy.d.ts +2 -32
  40. package/dist/core/types/index.d.ts +51 -5
  41. package/dist/core/types/validation-schema.d.ts +3 -12
  42. package/dist/core/utils/abstract-registry.d.ts +74 -0
  43. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  44. package/dist/core/utils/create-form.d.ts +1 -18
  45. package/dist/core/utils/error-handler.d.ts +1 -18
  46. package/dist/core/utils/field-path-navigator.d.ts +1 -1
  47. package/dist/core/utils/field-path.d.ts +23 -11
  48. package/dist/core/utils/form-observer.d.ts +176 -0
  49. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  50. package/dist/core/utils/form-submitter.d.ts +121 -0
  51. package/dist/core/utils/index.d.ts +9 -2
  52. package/dist/core/utils/registry-helpers.d.ts +0 -7
  53. package/dist/core/utils/safe-effect.d.ts +73 -0
  54. package/dist/core/utils/status-machine.d.ts +153 -0
  55. package/dist/core/utils/type-guards.d.ts +5 -23
  56. package/dist/core/utils/unique-id.d.ts +53 -0
  57. package/dist/core/validation/core/apply-when.d.ts +3 -9
  58. package/dist/core/validation/core/apply.d.ts +2 -13
  59. package/dist/core/validation/core/validate-async.d.ts +2 -8
  60. package/dist/core/validation/core/validate-tree.d.ts +0 -6
  61. package/dist/core/validation/core/validate.d.ts +1 -7
  62. package/dist/core/validation/index.d.ts +8 -2
  63. package/dist/core/validation/validate-form.d.ts +1 -38
  64. package/dist/core/validation/validation-applicator.d.ts +2 -21
  65. package/dist/core/validation/validation-context.d.ts +58 -42
  66. package/dist/core/validation/validation-registry.d.ts +11 -25
  67. package/dist/core/validation/validators/array-validators.d.ts +2 -12
  68. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  69. package/dist/core/validation/validators/email.d.ts +2 -9
  70. package/dist/core/validation/validators/future-date.d.ts +35 -0
  71. package/dist/core/validation/validators/index.d.ts +7 -1
  72. package/dist/core/validation/validators/is-date.d.ts +36 -0
  73. package/dist/core/validation/validators/max-age.d.ts +36 -0
  74. package/dist/core/validation/validators/max-date.d.ts +36 -0
  75. package/dist/core/validation/validators/max-length.d.ts +3 -10
  76. package/dist/core/validation/validators/max.d.ts +3 -10
  77. package/dist/core/validation/validators/min-age.d.ts +36 -0
  78. package/dist/core/validation/validators/min-date.d.ts +36 -0
  79. package/dist/core/validation/validators/min-length.d.ts +3 -10
  80. package/dist/core/validation/validators/min.d.ts +3 -10
  81. package/dist/core/validation/validators/number.d.ts +2 -9
  82. package/dist/core/validation/validators/past-date.d.ts +35 -0
  83. package/dist/core/validation/validators/pattern.d.ts +2 -9
  84. package/dist/core/validation/validators/phone.d.ts +2 -9
  85. package/dist/core/validation/validators/required.d.ts +2 -9
  86. package/dist/core/validation/validators/url.d.ts +2 -9
  87. package/dist/date-utils-xUWFslTj.js +29 -0
  88. package/dist/field-path-DuKdGcIE.js +66 -0
  89. package/dist/hooks/types.d.ts +1 -1
  90. package/dist/hooks/useArrayLength.d.ts +31 -0
  91. package/dist/hooks/useFormControl.d.ts +4 -4
  92. package/dist/hooks/useFormControlValue.d.ts +2 -2
  93. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  94. package/dist/hooks/useSignalSubscription.d.ts +1 -1
  95. package/dist/index-D25LsbRm.js +73 -0
  96. package/dist/index.d.ts +2 -0
  97. package/dist/index.js +1171 -786
  98. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  99. package/dist/safe-effect-Dh8uw81c.js +20 -0
  100. package/dist/validate-C3XiA_zf.js +10 -0
  101. package/dist/validators/email.d.ts +2 -0
  102. package/dist/validators/email.js +13 -0
  103. package/dist/validators/future-date.d.ts +2 -0
  104. package/dist/validators/future-date.js +20 -0
  105. package/dist/validators/is-date.d.ts +2 -0
  106. package/dist/validators/is-date.js +12 -0
  107. package/dist/validators/max-age.d.ts +2 -0
  108. package/dist/validators/max-age.js +20 -0
  109. package/dist/validators/max-date.d.ts +2 -0
  110. package/dist/validators/max-date.js +20 -0
  111. package/dist/validators/max-length.d.ts +2 -0
  112. package/dist/validators/max-length.js +11 -0
  113. package/dist/validators/max.d.ts +2 -0
  114. package/dist/validators/max.js +11 -0
  115. package/dist/validators/min-age.d.ts +2 -0
  116. package/dist/validators/min-age.js +20 -0
  117. package/dist/validators/min-date.d.ts +2 -0
  118. package/dist/validators/min-date.js +20 -0
  119. package/dist/validators/min-length.d.ts +2 -0
  120. package/dist/validators/min-length.js +11 -0
  121. package/dist/validators/min.d.ts +2 -0
  122. package/dist/validators/min.js +11 -0
  123. package/dist/validators/number.d.ts +2 -0
  124. package/dist/validators/number.js +35 -0
  125. package/dist/validators/past-date.d.ts +2 -0
  126. package/dist/validators/past-date.js +20 -0
  127. package/dist/validators/pattern.d.ts +2 -0
  128. package/dist/validators/pattern.js +11 -0
  129. package/dist/validators/phone.d.ts +2 -0
  130. package/dist/validators/phone.js +35 -0
  131. package/dist/validators/required.d.ts +2 -0
  132. package/dist/validators/required.js +15 -0
  133. package/dist/validators/url.d.ts +2 -0
  134. package/dist/validators/url.js +19 -0
  135. package/dist/validators-BGsNOgT1.js +207 -0
  136. package/dist/validators.js +54 -29
  137. package/llms.txt +7878 -311
  138. package/package.json +83 -9
  139. package/dist/behaviors-DzYL8kY_.js +0 -499
  140. package/dist/core/behavior/create-field-path.d.ts +0 -7
  141. package/dist/core/context/form-context-impl.d.ts +0 -29
  142. package/dist/core/utils/debounce.d.ts +0 -160
  143. package/dist/core/utils/resources.d.ts +0 -41
  144. package/dist/core/validation/field-path.d.ts +0 -7
  145. package/dist/core/validation/validators/date.d.ts +0 -38
  146. package/dist/registry-helpers-BRxAr6nG.js +0 -490
  147. package/dist/validators-gXoHPdqM.js +0 -418
@@ -1,12 +1,5 @@
1
- /**
2
- * Условное включение/отключение полей
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/enableWhen
7
- */
8
- import type { FieldPathNode } from '../../types';
9
- import type { EnableWhenOptions } from '../types';
1
+ import { FieldPathNode } from '../../types';
2
+ import { EnableWhenOptions } from '../types';
10
3
  /**
11
4
  * Условное включение поля на основе значений других полей
12
5
  *
@@ -15,17 +8,68 @@ import type { EnableWhenOptions } from '../types';
15
8
  *
16
9
  * @param field - Поле для включения/выключения
17
10
  * @param condition - Функция условия (true = enable, false = disable)
18
- * @param options - Опции
11
+ * @param options - Опции (`resetOnDisable`, `debounce`)
19
12
  *
20
- * @example
13
+ * @example Базовый сценарий с `resetOnDisable: true`
21
14
  * ```typescript
22
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
23
- * // Включить поле только для ипотеки
15
+ * import { enableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
16
+ *
17
+ * interface LoanForm {
18
+ * loanType: 'mortgage' | 'consumer' | 'car';
19
+ * propertyValue: number;
20
+ * initialPayment: number;
21
+ * }
22
+ *
23
+ * export const loanBehavior: BehaviorSchemaFn<LoanForm> = (path) => {
24
+ * // Поля ипотеки активны только для loanType === 'mortgage'.
25
+ * // resetOnDisable: true гарантирует чистые initial values при переключении.
24
26
  * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage', {
25
- * resetOnDisable: true
27
+ * resetOnDisable: true,
28
+ * });
29
+ * enableWhen(path.initialPayment, (form) => form.loanType === 'mortgage', {
30
+ * resetOnDisable: true,
26
31
  * });
27
32
  * };
28
33
  * ```
34
+ *
35
+ * @example Множественные independent условия + cycle prevention
36
+ * ```typescript
37
+ * import { enableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
38
+ *
39
+ * interface ProfileForm {
40
+ * sameAsRegistration: boolean;
41
+ * employmentStatus: 'employed' | 'selfEmployed' | 'unemployed';
42
+ * residenceAddress: { city: string; street: string };
43
+ * companyName: string;
44
+ * companyInn: string;
45
+ * businessType: string;
46
+ * }
47
+ *
48
+ * export const profileBehavior: BehaviorSchemaFn<ProfileForm> = (path) => {
49
+ * // Адрес проживания: enabled, когда НЕ совпадает с регистрационным
50
+ * enableWhen(path.residenceAddress, (form) => form.sameAsRegistration === false, {
51
+ * resetOnDisable: true,
52
+ * });
53
+ *
54
+ * // Поля работодателя: только для employed
55
+ * enableWhen(path.companyName, (form) => form.employmentStatus === 'employed', {
56
+ * resetOnDisable: true,
57
+ * });
58
+ * enableWhen(path.companyInn, (form) => form.employmentStatus === 'employed', {
59
+ * resetOnDisable: true,
60
+ * });
61
+ *
62
+ * // ИП-поля: только для selfEmployed
63
+ * enableWhen(path.businessType, (form) => form.employmentStatus === 'selfEmployed', {
64
+ * resetOnDisable: true,
65
+ * });
66
+ *
67
+ * // ВАЖНО: condition не должен читать значение САМОГО поля — иначе цикл.
68
+ * // condition зависит ТОЛЬКО от независимых триггеров (loanType, employmentStatus, ...).
69
+ * };
70
+ * ```
71
+ *
72
+ * @see [docs/llms/22-cycle-detection.md](../../../../docs/llms/22-cycle-detection.md)
29
73
  */
30
74
  export declare function enableWhen<TForm>(field: FieldPathNode<TForm, any>, condition: (form: TForm) => boolean, options?: EnableWhenOptions): void;
31
75
  /**
@@ -36,13 +80,38 @@ export declare function enableWhen<TForm>(field: FieldPathNode<TForm, any>, cond
36
80
  *
37
81
  * @param field - Поле для выключения
38
82
  * @param condition - Функция условия (true = disable, false = enable)
39
- * @param options - Опции
83
+ * @param options - Опции (`resetOnDisable`, `debounce`)
40
84
  *
41
- * @example
85
+ * @example Базовый сценарий — readonly после подтверждения
42
86
  * ```typescript
43
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
44
- * // Выключить поле для потребительского кредита
45
- * disableWhen(path.propertyValue, (form) => form.loanType === 'consumer');
87
+ * import { disableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
88
+ *
89
+ * interface ConfirmForm {
90
+ * isConfirmed: boolean;
91
+ * editableField: string;
92
+ * }
93
+ *
94
+ * export const confirmBehavior: BehaviorSchemaFn<ConfirmForm> = (path) => {
95
+ * // Поле блокируется после установки чекбокса подтверждения
96
+ * disableWhen(path.editableField, (form) => form.isConfirmed === true);
97
+ * // resetOnDisable НЕ ставим — сохраняем введённый текст
98
+ * };
99
+ * ```
100
+ *
101
+ * @example С `resetOnDisable` для очистки заблокированного поля
102
+ * ```typescript
103
+ * import { disableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
104
+ *
105
+ * interface PromoForm {
106
+ * loanType: 'mortgage' | 'consumer';
107
+ * promoCode: string;
108
+ * }
109
+ *
110
+ * export const promoBehavior: BehaviorSchemaFn<PromoForm> = (path) => {
111
+ * // Промокод недоступен для потребительских кредитов и сбрасывается
112
+ * disableWhen(path.promoCode, (form) => form.loanType === 'consumer', {
113
+ * resetOnDisable: true,
114
+ * });
46
115
  * };
47
116
  * ```
48
117
  */
@@ -1,11 +1,4 @@
1
- /**
2
- * Условный сброс полей
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/resetWhen
7
- */
8
- import type { FieldPathNode, FormFields, FormValue } from '../../types';
1
+ import { FieldPathNode, FormFields, FormValue } from '../../types';
9
2
  /**
10
3
  * Опции для resetWhen
11
4
  *
@@ -26,25 +19,45 @@ export interface ResetWhenOptions {
26
19
  *
27
20
  * @param field - Поле для сброса
28
21
  * @param condition - Функция условия (true = reset)
29
- * @param options - Опции
22
+ * @param options - Опции (`resetValue`, `onlyIfDirty`, `debounce`)
30
23
  *
31
- * @example
24
+ * @example Сброс зависимого поля при смене типа платежа
32
25
  * ```typescript
33
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
34
- * // Сбросить поле при изменении типа кредита
35
- * resetWhen(path.propertyValue, (form) => form.loanType !== 'mortgage');
26
+ * import { resetWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
27
+ *
28
+ * interface CheckoutForm {
29
+ * paymentType: 'card' | 'cash';
30
+ * cardNumber: string;
31
+ * }
36
32
  *
37
- * // Сбросить с кастомным значением
38
- * resetWhen(path.initialPayment, (form) => !form.propertyValue, {
39
- * resetValue: 0
33
+ * export const checkoutBehavior: BehaviorSchemaFn<CheckoutForm> = (path) => {
34
+ * // Когда выбрано НЕ card — обнуляем номер карты в пустую строку
35
+ * resetWhen(path.cardNumber, (form) => form.paymentType !== 'card', {
36
+ * resetValue: '',
40
37
  * });
38
+ * };
39
+ * ```
40
+ *
41
+ * @example `onlyIfDirty` — не трогаем нетронутые initial значения
42
+ * ```typescript
43
+ * import { resetWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
44
+ *
45
+ * interface CarForm {
46
+ * loanType: 'mortgage' | 'car' | 'consumer';
47
+ * carPrice: number;
48
+ * }
41
49
  *
42
- * // Сбросить только если поле было изменено пользователем
50
+ * export const carBehavior: BehaviorSchemaFn<CarForm> = (path) => {
51
+ * // Если пользователь не вводил carPrice — оставляем default из схемы.
52
+ * // Сбрасываем только если поле dirty (была пользовательская правка).
43
53
  * resetWhen(path.carPrice, (form) => form.loanType !== 'car', {
44
- * onlyIfDirty: true
54
+ * resetValue: 0,
55
+ * onlyIfDirty: true,
45
56
  * });
46
57
  * };
47
58
  * ```
59
+ *
60
+ * @see [docs/llms/25-reset-when.md](../../../../docs/llms/25-reset-when.md)
48
61
  */
49
62
  export declare function resetWhen<TForm extends FormFields>(field: FieldPathNode<TForm, FormValue>, condition: (form: TForm) => boolean, options?: ResetWhenOptions & {
50
63
  debounce?: number;
@@ -1,12 +1,5 @@
1
- /**
2
- * Перевалидация полей при изменениях
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/revalidateWhen
7
- */
8
- import type { FieldPathNode, FormValue } from '../../types';
9
- import type { RevalidateWhenOptions } from '../types';
1
+ import { FieldPathNode, FormValue } from '../../types';
2
+ import { RevalidateWhenOptions } from '../types';
10
3
  /**
11
4
  * Перевалидирует поле при изменении других полей
12
5
  *
@@ -14,17 +7,47 @@ import type { RevalidateWhenOptions } from '../types';
14
7
  * @category Behavior Rules
15
8
  *
16
9
  * @param target - Поле для перевалидации
17
- * @param triggers - Поля-триггеры
18
- * @param options - Опции
10
+ * @param triggers - Поля-триггеры (НЕ должно содержать `target`)
11
+ * @param options - Опции (`debounce`)
12
+ *
13
+ * @example Парная перевалидация — confirmPassword при смене password
14
+ * ```typescript
15
+ * import { revalidateWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
16
+ * import { equalTo } from '@reformer/core/validators';
17
+ * import type { FieldPath } from '@reformer/core';
18
+ *
19
+ * interface RegistrationForm { password: string; confirmPassword: string }
19
20
  *
20
- * @example
21
+ * export const validation = (path: FieldPath<RegistrationForm>) => {
22
+ * equalTo(path.confirmPassword, path.password, { message: 'Пароли не совпадают' });
23
+ * };
24
+ *
25
+ * export const behavior: BehaviorSchemaFn<RegistrationForm> = (path) => {
26
+ * // Если пользователь сначала ввёл confirm, потом меняет password —
27
+ * // без revalidateWhen ошибка confirmPassword останется устаревшей.
28
+ * revalidateWhen(path.confirmPassword, [path.password]);
29
+ * };
30
+ * ```
31
+ *
32
+ * @example Несколько триггеров + `debounce` для async-валидаторов
21
33
  * ```typescript
22
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
23
- * // Перевалидировать initialPayment при изменении propertyValue
24
- * revalidateWhen(path.initialPayment, [path.propertyValue], {
25
- * debounce: 300
26
- * });
34
+ * import { revalidateWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
35
+ *
36
+ * interface MortgageForm {
37
+ * propertyValue: number;
38
+ * loanAmount: number;
39
+ * initialPayment: number; // правило: initialPayment >= propertyValue * 0.2 - loanAmount
40
+ * }
41
+ *
42
+ * export const mortgageBehavior: BehaviorSchemaFn<MortgageForm> = (path) => {
43
+ * revalidateWhen(
44
+ * path.initialPayment,
45
+ * [path.propertyValue, path.loanAmount],
46
+ * { debounce: 300 }, // не дёргаем сервер на каждый keystroke
47
+ * );
27
48
  * };
28
49
  * ```
50
+ *
51
+ * @see [docs/llms/27-revalidate-when.md](../../../../docs/llms/27-revalidate-when.md)
29
52
  */
30
53
  export declare function revalidateWhen<TForm>(target: FieldPathNode<TForm, FormValue>, triggers: FieldPathNode<TForm, FormValue>[], options?: RevalidateWhenOptions): void;
@@ -1,12 +1,5 @@
1
- /**
2
- * Двусторонняя синхронизация полей
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/syncFields
7
- */
8
- import type { FieldPathNode, FormFields, FormValue } from '../../types';
9
- import type { SyncFieldsOptions } from '../types';
1
+ import { FieldPathNode, FormFields, FormValue } from '../../types';
2
+ import { SyncFieldsOptions } from '../types';
10
3
  /**
11
4
  * Двусторонняя синхронизация двух полей
12
5
  *
@@ -15,14 +8,41 @@ import type { SyncFieldsOptions } from '../types';
15
8
  *
16
9
  * @param field1 - Первое поле
17
10
  * @param field2 - Второе поле
18
- * @param options - Опции
11
+ * @param options - Опции (`transform` асимметричен — применяется только field1 → field2; `debounce`)
12
+ *
13
+ * @example Базовый mirror двух текстовых полей
14
+ * ```typescript
15
+ * import { syncFields, type BehaviorSchemaFn } from '@reformer/core/behaviors';
19
16
  *
20
- * @example
17
+ * interface MirrorForm {
18
+ * syncField1: string;
19
+ * syncField2: string;
20
+ * }
21
+ *
22
+ * export const mirrorBehavior: BehaviorSchemaFn<MirrorForm> = (path) => {
23
+ * syncFields(path.syncField1, path.syncField2);
24
+ * };
25
+ * ```
26
+ *
27
+ * @example С `transform` (асимметричный) и `debounce` для защиты от частых перезаписей
21
28
  * ```typescript
22
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
23
- * // Синхронизировать два поля
24
- * syncFields(path.email, path.emailCopy);
29
+ * import { syncFields, type BehaviorSchemaFn } from '@reformer/core/behaviors';
30
+ *
31
+ * interface CodeForm {
32
+ * internalCode: string; // канонический формат
33
+ * displayCode: string; // показываем пользователю
34
+ * }
35
+ *
36
+ * export const codeBehavior: BehaviorSchemaFn<CodeForm> = (path) => {
37
+ * // internalCode → displayCode: применяется toUpperCase
38
+ * // displayCode → internalCode: пишется как есть
39
+ * syncFields(path.internalCode, path.displayCode, {
40
+ * transform: (value) => (typeof value === 'string' ? value.toUpperCase() : value),
41
+ * debounce: 150, // сглаживает дёргание каретки
42
+ * });
25
43
  * };
26
44
  * ```
45
+ *
46
+ * @see [docs/llms/24-sync-fields.md](../../../../docs/llms/24-sync-fields.md)
27
47
  */
28
48
  export declare function syncFields<TForm extends FormFields, T extends FormValue>(field1: FieldPathNode<TForm, T>, field2: FieldPathNode<TForm, T>, options?: SyncFieldsOptions<T>): void;
@@ -1,11 +1,4 @@
1
- /**
2
- * Трансформация значений полей
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/transformValue
7
- */
8
- import type { FieldPathNode, FormFields, FormValue } from '../../types';
1
+ import { FieldPathNode, FormFields, FormValue } from '../../types';
9
2
  /**
10
3
  * Опции для transformValue
11
4
  *
@@ -26,34 +19,52 @@ export interface TransformValueOptions {
26
19
  * @category Behavior Rules
27
20
  *
28
21
  * @param field - Поле для трансформации
29
- * @param transformer - Функция трансформации
30
- * @param options - Опции
22
+ * @param transformer - Функция трансформации (ОБЯЗАТЕЛЬНО идемпотентная: f(f(x)) === f(x))
23
+ * @param options - Опции (`onUserChangeOnly`, `emitEvent`, `debounce`)
24
+ *
25
+ * @example Базовая нормализация — uppercase + trim email
26
+ * ```typescript
27
+ * import { transformValue, type BehaviorSchemaFn } from '@reformer/core/behaviors';
28
+ *
29
+ * interface RegistrationForm {
30
+ * promoCode: string;
31
+ * email: string;
32
+ * }
33
+ *
34
+ * export const registrationBehavior: BehaviorSchemaFn<RegistrationForm> = (path) => {
35
+ * // Идемпотентно: toUpperCase(toUpperCase(x)) === toUpperCase(x) ✓
36
+ * transformValue(path.promoCode, (value) => (value ?? '').toUpperCase());
37
+ * transformValue(path.email, (value) => (value ?? '').trim().toLowerCase());
38
+ * };
39
+ * ```
31
40
  *
32
- * @example
41
+ * @example `onUserChangeOnly` + idempotent guard для не-тривиальных форматов
33
42
  * ```typescript
34
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
35
- * // Автоматически переводить текст в верхний регистр
36
- * transformValue(path.code, (value) => value?.toUpperCase());
37
- *
38
- * // Форматировать номер телефона
39
- * transformValue(path.phone, (value) => {
40
- * if (!value) return value;
41
- * const digits = value.replace(/\D/g, '');
42
- * if (digits.length === 11) {
43
- * return `+7 (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7, 9)}-${digits.slice(9)}`;
44
- * }
45
- * return value;
46
- * });
47
- *
48
- * // Удалять пробелы из email
49
- * transformValue(path.email, (value) => value?.trim().toLowerCase());
50
- *
51
- * // Округлять числа
52
- * transformValue(path.amount, (value) => {
53
- * return typeof value === 'number' ? Math.round(value) : value;
54
- * });
43
+ * import { transformValue, type BehaviorSchemaFn } from '@reformer/core/behaviors';
44
+ *
45
+ * interface ProfileForm {
46
+ * inn: string; // ИНН — только цифры
47
+ * prefixedCode: string; // должен иметь префикс "ID-"
48
+ * }
49
+ *
50
+ * export const profileBehavior: BehaviorSchemaFn<ProfileForm> = (path) => {
51
+ * // Цифры из ИНН: трансформер идемпотентный естественно
52
+ * transformValue(path.inn, (v) => (v ?? '').replace(/\D/g, ''));
53
+ *
54
+ * // Префикс — ВАЖНО guard «уже преобразовано», иначе бесконечный цикл
55
+ * // f("ID-123") должно === "ID-123", а не "ID-ID-123"
56
+ * transformValue(
57
+ * path.prefixedCode,
58
+ * (v) => (v?.startsWith('ID-') ? v : `ID-${v ?? ''}`),
59
+ * {
60
+ * onUserChangeOnly: true, // не трогаем значение из patchValue/preload
61
+ * debounce: 200,
62
+ * },
63
+ * );
55
64
  * };
56
65
  * ```
66
+ *
67
+ * @see [docs/llms/26-transform-value.md](../../../../docs/llms/26-transform-value.md)
57
68
  */
58
69
  export declare function transformValue<TForm extends FormFields, TValue extends FormValue = FormValue>(field: FieldPathNode<TForm, TValue>, transformer: (value: TValue) => TValue, options?: TransformValueOptions & {
59
70
  debounce?: number;
@@ -64,18 +75,42 @@ export declare function transformValue<TForm extends FormFields, TValue extends
64
75
  * @group Behaviors
65
76
  * @category Behavior Rules
66
77
  *
67
- * @example
78
+ * @param transformer - Идемпотентная функция преобразования значения
79
+ * @param defaultOptions - Опции, применяемые ко всем вызовам созданного трансформера
80
+ *
81
+ * @example Доменно-специфичные трансформеры (банковский счёт, СНИЛС)
68
82
  * ```typescript
69
- * // Создаем переиспользуемые трансформеры
70
- * const toUpperCase = createTransformer<string>((value) => value?.toUpperCase());
71
- * const toLowerCase = createTransformer<string>((value) => value?.toLowerCase());
72
- * const trim = createTransformer<string>((value) => value?.trim());
73
- *
74
- * // Используем в форме
75
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
76
- * toUpperCase(path.code);
77
- * toLowerCase(path.email);
78
- * trim(path.username);
83
+ * import { createTransformer, type BehaviorSchemaFn } from '@reformer/core/behaviors';
84
+ *
85
+ * // Сохраняем только цифры и форматируем СНИЛС: 000-000-000 00
86
+ * const formatSnils = createTransformer<string>((v) => {
87
+ * const d = (v ?? '').replace(/\D/g, '').slice(0, 11);
88
+ * if (d.length < 9) return d;
89
+ * return `${d.slice(0, 3)}-${d.slice(3, 6)}-${d.slice(6, 9)}${d.length > 9 ? ' ' + d.slice(9) : ''}`;
90
+ * });
91
+ *
92
+ * interface ProfileForm { snils: string }
93
+ *
94
+ * export const profileBehavior: BehaviorSchemaFn<ProfileForm> = (path) => {
95
+ * formatSnils(path.snils, { debounce: 100 });
96
+ * };
97
+ * ```
98
+ *
99
+ * @example С `defaultOptions` — единые настройки на серию полей
100
+ * ```typescript
101
+ * import { createTransformer, type BehaviorSchemaFn } from '@reformer/core/behaviors';
102
+ *
103
+ * // Все коды должны быть uppercase, но только после правки пользователем
104
+ * const upperOnUserEdit = createTransformer<string>(
105
+ * (v) => (v ?? '').toUpperCase(),
106
+ * { onUserChangeOnly: true, debounce: 100 },
107
+ * );
108
+ *
109
+ * interface PromoForm { promoCode: string; partnerCode: string }
110
+ *
111
+ * export const promoBehavior: BehaviorSchemaFn<PromoForm> = (path) => {
112
+ * upperOnUserEdit(path.promoCode);
113
+ * upperOnUserEdit(path.partnerCode);
79
114
  * };
80
115
  * ```
81
116
  */
@@ -83,10 +118,47 @@ export declare function createTransformer<TValue extends FormValue = FormValue>(
83
118
  debounce?: number;
84
119
  }) => void;
85
120
  /**
86
- * Готовые трансформеры для частых случаев
121
+ * Готовые трансформеры для частых случаев. Все идемпотентны и безопасны для повторного применения.
87
122
  *
88
123
  * @group Behaviors
89
124
  * @category Behavior Rules
125
+ *
126
+ * @example Готовые трансформеры в схеме формы
127
+ * ```typescript
128
+ * import { transformers, type BehaviorSchemaFn } from '@reformer/core/behaviors';
129
+ *
130
+ * interface RegistrationForm {
131
+ * username: string;
132
+ * email: string;
133
+ * promoCode: string;
134
+ * inn: string;
135
+ * amount: number;
136
+ * }
137
+ *
138
+ * export const behavior: BehaviorSchemaFn<RegistrationForm> = (path) => {
139
+ * transformers.trim(path.username);
140
+ * transformers.toLowerCase(path.email);
141
+ * transformers.toUpperCase(path.promoCode);
142
+ * transformers.digitsOnly(path.inn);
143
+ * transformers.roundTo2(path.amount);
144
+ * };
145
+ * ```
146
+ *
147
+ * @example Композиция готовых трансформеров через createTransformer
148
+ * ```typescript
149
+ * import { createTransformer, type BehaviorSchemaFn } from '@reformer/core/behaviors';
150
+ *
151
+ * // trim + lowercase в одном трансформере (применяется как одна операция)
152
+ * const normalizeEmail = createTransformer<string>(
153
+ * (v) => (v ?? '').trim().toLowerCase(),
154
+ * );
155
+ *
156
+ * interface ContactForm { email: string }
157
+ *
158
+ * export const contactBehavior: BehaviorSchemaFn<ContactForm> = (path) => {
159
+ * normalizeEmail(path.email);
160
+ * };
161
+ * ```
90
162
  */
91
163
  export declare const transformers: {
92
164
  /** Перевести в верхний регистр */
@@ -1,12 +1,5 @@
1
- /**
2
- * Отслеживание изменений поля
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/watchField
7
- */
8
- import type { FieldPathNode } from '../../types';
9
- import type { BehaviorContext, WatchFieldOptions } from '../types';
1
+ import { FieldPathNode } from '../../types';
2
+ import { BehaviorContext, WatchFieldOptions } from '../types';
10
3
  /**
11
4
  * Выполняет callback при изменении поля
12
5
  *
@@ -15,21 +8,73 @@ import type { BehaviorContext, WatchFieldOptions } from '../types';
15
8
  *
16
9
  * @param field - Поле для отслеживания
17
10
  * @param callback - Функция обратного вызова
18
- * @param options - Опции
11
+ * @param options - Опции (`debounce`, `immediate`)
12
+ *
13
+ * @example Async loader with try/catch + guard + debounce
14
+ * ```typescript
15
+ * import { watchField, type BehaviorSchemaFn } from '@reformer/core/behaviors';
16
+ *
17
+ * interface AddressForm { region: string; city: string }
19
18
  *
20
- * @example
19
+ * export const addressBehavior: BehaviorSchemaFn<AddressForm> = (path) => {
20
+ * watchField(
21
+ * path.region,
22
+ * async (region, ctx) => {
23
+ * if (!region) {
24
+ * ctx.form.city.updateComponentProps({ options: [] });
25
+ * return; // guard: пустое значение не триггерит fetch
26
+ * }
27
+ * try {
28
+ * const { data: cities } = await fetchCities(region);
29
+ * ctx.form.city.updateComponentProps({ options: cities });
30
+ * } catch (error) {
31
+ * console.error('[addressBehavior] failed to load cities:', error);
32
+ * ctx.form.city.updateComponentProps({ options: [] });
33
+ * }
34
+ * },
35
+ * { immediate: false, debounce: 300 }, // обязательные опции для async
36
+ * );
37
+ * };
38
+ * ```
39
+ *
40
+ * @example Sync handler с консолидацией нескольких зависимостей в один watcher
21
41
  * ```typescript
22
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
23
- * // Динамическая загрузка городов при изменении страны
24
- * watchField(path.registrationAddress.country, async (country, ctx) => {
25
- * if (country) {
26
- * const cities = await fetchCities(country);
27
- * ctx.updateComponentProps(path.registrationAddress.city, {
28
- * options: cities
29
- * });
30
- * }
31
- * });
42
+ * import { watchField, type BehaviorSchemaFn } from '@reformer/core/behaviors';
43
+ *
44
+ * interface InsuranceForm {
45
+ * insuranceType: 'casco' | 'osago' | 'property' | '';
46
+ * vehicleVin: string;
47
+ * propertyType: string;
48
+ * }
49
+ *
50
+ * export const insuranceBehavior: BehaviorSchemaFn<InsuranceForm> = (path) => {
51
+ * // ОДИН watchField на trigger-поле — несколько watcher'ов на одно поле = "Cycle detected"
52
+ * watchField(
53
+ * path.insuranceType,
54
+ * (type, ctx) => {
55
+ * const isVehicle = type === 'casco' || type === 'osago';
56
+ * const isProperty = type === 'property';
57
+ *
58
+ * // Guard — ставим только если состояние реально меняется
59
+ * if (isVehicle) {
60
+ * if (ctx.form.vehicleVin.disabled.value) ctx.form.vehicleVin.enable();
61
+ * } else {
62
+ * if (!ctx.form.vehicleVin.disabled.value) ctx.form.vehicleVin.disable();
63
+ * if (ctx.form.vehicleVin.getValue() !== '') ctx.form.vehicleVin.setValue('');
64
+ * }
65
+ *
66
+ * if (isProperty) {
67
+ * if (ctx.form.propertyType.disabled.value) ctx.form.propertyType.enable();
68
+ * } else {
69
+ * if (!ctx.form.propertyType.disabled.value) ctx.form.propertyType.disable();
70
+ * if (ctx.form.propertyType.getValue() !== '') ctx.form.propertyType.setValue('');
71
+ * }
72
+ * },
73
+ * { immediate: false }, // CRITICAL: предотвращает запуск во время инициализации
74
+ * );
32
75
  * };
33
76
  * ```
77
+ *
78
+ * @see [docs/llms/22-cycle-detection.md](../../../../docs/llms/22-cycle-detection.md)
34
79
  */
35
80
  export declare function watchField<TForm, TField>(field: FieldPathNode<TForm, TField>, callback: (value: TField, ctx: BehaviorContext<TForm>) => void | Promise<void>, options?: WatchFieldOptions): void;
@@ -1,15 +1,5 @@
1
- /**
2
- * Композиция behavior схем
3
- *
4
- * Предоставляет функции для переиспользования behavior схем:
5
- * - toBehaviorFieldPath: преобразование FieldPath во вложенный путь
6
- * - apply: применение схемы к полям
7
- * - applyWhen: условное применение схемы
8
- *
9
- * Аналог toFieldPath и applyWhen из validation API.
10
- */
11
- import type { FieldPath, FieldPathNode, FormFields, FormValue } from '../types';
12
- import type { BehaviorSchemaFn } from './types';
1
+ import { FieldPath, FieldPathNode, FormFields, FormValue } from '../types';
2
+ import { BehaviorSchemaFn } from './types';
13
3
  /**
14
4
  * Преобразовать FieldPath во вложенный путь для композиции behavior схем
15
5
  *
@@ -9,4 +9,3 @@ export * from './behaviors';
9
9
  export { apply, applyWhen, toBehaviorFieldPath } from './compose-behavior';
10
10
  export { BehaviorRegistry } from './behavior-registry';
11
11
  export { BehaviorContextImpl } from './behavior-context';
12
- export { createFieldPath } from './create-field-path';