@reformer/core 1.1.0 → 2.0.0-beta.11

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 (221) 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.d.ts +6 -2
  18. package/dist/behaviors.js +27 -228
  19. package/dist/core/behavior/behavior-context.d.ts +32 -14
  20. package/dist/core/behavior/behavior-registry.d.ts +15 -27
  21. package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
  22. package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
  23. package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
  24. package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
  25. package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
  26. package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
  27. package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
  28. package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
  29. package/dist/core/behavior/compose-behavior.d.ts +2 -12
  30. package/dist/core/behavior/index.d.ts +0 -1
  31. package/dist/core/behavior/types.d.ts +2 -8
  32. package/dist/core/factories/node-factory.d.ts +6 -29
  33. package/dist/core/nodes/array-node.d.ts +42 -22
  34. package/dist/core/nodes/field-node.d.ts +51 -26
  35. package/dist/core/nodes/form-node.d.ts +18 -20
  36. package/dist/core/nodes/group-node.d.ts +37 -212
  37. package/dist/core/types/deep-schema.d.ts +2 -12
  38. package/dist/core/types/field-path.d.ts +1 -1
  39. package/dist/core/types/form-context.d.ts +36 -30
  40. package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
  41. package/dist/core/types/index.d.ts +52 -6
  42. package/dist/core/types/validation-schema.d.ts +3 -12
  43. package/dist/core/utils/abstract-registry.d.ts +74 -0
  44. package/dist/core/utils/aggregate-signals.d.ts +71 -0
  45. package/dist/core/utils/create-form.d.ts +3 -20
  46. package/dist/core/utils/error-handler.d.ts +1 -18
  47. package/dist/core/utils/field-path-navigator.d.ts +1 -1
  48. package/dist/core/{validation → utils}/field-path.d.ts +23 -6
  49. package/dist/core/utils/form-observer.d.ts +176 -0
  50. package/dist/core/utils/form-proxy-builder.d.ts +25 -0
  51. package/dist/core/utils/form-submitter.d.ts +121 -0
  52. package/dist/core/utils/index.d.ts +10 -2
  53. package/dist/core/utils/registry-helpers.d.ts +0 -7
  54. package/dist/core/utils/safe-effect.d.ts +73 -0
  55. package/dist/core/utils/status-machine.d.ts +153 -0
  56. package/dist/core/utils/type-guards.d.ts +5 -23
  57. package/dist/core/utils/unique-id.d.ts +53 -0
  58. package/dist/core/validation/core/apply-when.d.ts +3 -9
  59. package/dist/core/validation/core/apply.d.ts +2 -13
  60. package/dist/core/validation/core/validate-async.d.ts +2 -8
  61. package/dist/core/validation/core/validate-tree.d.ts +10 -10
  62. package/dist/core/validation/core/validate.d.ts +1 -7
  63. package/dist/core/validation/index.d.ts +8 -2
  64. package/dist/core/validation/validate-form.d.ts +1 -38
  65. package/dist/core/validation/validation-applicator.d.ts +2 -21
  66. package/dist/core/validation/validation-context.d.ts +67 -28
  67. package/dist/core/validation/validation-registry.d.ts +11 -25
  68. package/dist/core/validation/validators/array-validators.d.ts +2 -12
  69. package/dist/core/validation/validators/date-utils.d.ts +26 -0
  70. package/dist/core/validation/validators/email.d.ts +2 -9
  71. package/dist/core/validation/validators/future-date.d.ts +35 -0
  72. package/dist/core/validation/validators/index.d.ts +7 -1
  73. package/dist/core/validation/validators/is-date.d.ts +36 -0
  74. package/dist/core/validation/validators/max-age.d.ts +36 -0
  75. package/dist/core/validation/validators/max-date.d.ts +36 -0
  76. package/dist/core/validation/validators/max-length.d.ts +3 -10
  77. package/dist/core/validation/validators/max.d.ts +3 -10
  78. package/dist/core/validation/validators/min-age.d.ts +36 -0
  79. package/dist/core/validation/validators/min-date.d.ts +36 -0
  80. package/dist/core/validation/validators/min-length.d.ts +3 -10
  81. package/dist/core/validation/validators/min.d.ts +3 -10
  82. package/dist/core/validation/validators/number.d.ts +2 -9
  83. package/dist/core/validation/validators/past-date.d.ts +35 -0
  84. package/dist/core/validation/validators/pattern.d.ts +2 -9
  85. package/dist/core/validation/validators/phone.d.ts +2 -9
  86. package/dist/core/validation/validators/required.d.ts +2 -9
  87. package/dist/core/validation/validators/url.d.ts +2 -9
  88. package/dist/date-utils-xUWFslTj.js +29 -0
  89. package/dist/field-path-DuKdGcIE.js +66 -0
  90. package/dist/hooks/types.d.ts +328 -0
  91. package/dist/hooks/useArrayLength.d.ts +31 -0
  92. package/dist/hooks/useFormControl.d.ts +15 -39
  93. package/dist/hooks/useFormControlValue.d.ts +167 -0
  94. package/dist/hooks/useHiddenCondition.d.ts +25 -0
  95. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  96. package/dist/index-D25LsbRm.js +73 -0
  97. package/dist/index.d.ts +8 -1
  98. package/dist/index.js +3271 -8
  99. package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
  100. package/dist/safe-effect-Dh8uw81c.js +20 -0
  101. package/dist/validate-C3XiA_zf.js +10 -0
  102. package/dist/validators/email.d.ts +2 -0
  103. package/dist/validators/email.js +13 -0
  104. package/dist/validators/future-date.d.ts +2 -0
  105. package/dist/validators/future-date.js +20 -0
  106. package/dist/validators/is-date.d.ts +2 -0
  107. package/dist/validators/is-date.js +12 -0
  108. package/dist/validators/max-age.d.ts +2 -0
  109. package/dist/validators/max-age.js +20 -0
  110. package/dist/validators/max-date.d.ts +2 -0
  111. package/dist/validators/max-date.js +20 -0
  112. package/dist/validators/max-length.d.ts +2 -0
  113. package/dist/validators/max-length.js +11 -0
  114. package/dist/validators/max.d.ts +2 -0
  115. package/dist/validators/max.js +11 -0
  116. package/dist/validators/min-age.d.ts +2 -0
  117. package/dist/validators/min-age.js +20 -0
  118. package/dist/validators/min-date.d.ts +2 -0
  119. package/dist/validators/min-date.js +20 -0
  120. package/dist/validators/min-length.d.ts +2 -0
  121. package/dist/validators/min-length.js +11 -0
  122. package/dist/validators/min.d.ts +2 -0
  123. package/dist/validators/min.js +11 -0
  124. package/dist/validators/number.d.ts +2 -0
  125. package/dist/validators/number.js +35 -0
  126. package/dist/validators/past-date.d.ts +2 -0
  127. package/dist/validators/past-date.js +20 -0
  128. package/dist/validators/pattern.d.ts +2 -0
  129. package/dist/validators/pattern.js +11 -0
  130. package/dist/validators/phone.d.ts +2 -0
  131. package/dist/validators/phone.js +35 -0
  132. package/dist/validators/required.d.ts +2 -0
  133. package/dist/validators/required.js +15 -0
  134. package/dist/validators/url.d.ts +2 -0
  135. package/dist/validators/url.js +19 -0
  136. package/dist/validators-BGsNOgT1.js +207 -0
  137. package/dist/validators.d.ts +6 -2
  138. package/dist/validators.js +54 -296
  139. package/llms.txt +8887 -59
  140. package/package.json +87 -8
  141. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  142. package/dist/core/behavior/behavior-applicator.js +0 -92
  143. package/dist/core/behavior/behavior-context.js +0 -38
  144. package/dist/core/behavior/behavior-registry.js +0 -198
  145. package/dist/core/behavior/behaviors/compute-from.js +0 -84
  146. package/dist/core/behavior/behaviors/copy-from.js +0 -64
  147. package/dist/core/behavior/behaviors/enable-when.js +0 -81
  148. package/dist/core/behavior/behaviors/index.js +0 -11
  149. package/dist/core/behavior/behaviors/reset-when.js +0 -63
  150. package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
  151. package/dist/core/behavior/behaviors/sync-fields.js +0 -66
  152. package/dist/core/behavior/behaviors/transform-value.js +0 -110
  153. package/dist/core/behavior/behaviors/watch-field.js +0 -56
  154. package/dist/core/behavior/compose-behavior.js +0 -166
  155. package/dist/core/behavior/create-field-path.d.ts +0 -20
  156. package/dist/core/behavior/create-field-path.js +0 -69
  157. package/dist/core/behavior/index.js +0 -17
  158. package/dist/core/behavior/types.js +0 -7
  159. package/dist/core/context/form-context-impl.d.ts +0 -29
  160. package/dist/core/context/form-context-impl.js +0 -37
  161. package/dist/core/factories/index.js +0 -6
  162. package/dist/core/factories/node-factory.js +0 -281
  163. package/dist/core/nodes/array-node.js +0 -534
  164. package/dist/core/nodes/field-node.js +0 -510
  165. package/dist/core/nodes/form-node.js +0 -343
  166. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  167. package/dist/core/nodes/group-node/field-registry.js +0 -215
  168. package/dist/core/nodes/group-node/index.d.ts +0 -11
  169. package/dist/core/nodes/group-node/index.js +0 -11
  170. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  171. package/dist/core/nodes/group-node/proxy-builder.js +0 -161
  172. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  173. package/dist/core/nodes/group-node/state-manager.js +0 -265
  174. package/dist/core/nodes/group-node.js +0 -770
  175. package/dist/core/types/deep-schema.js +0 -11
  176. package/dist/core/types/field-path.js +0 -4
  177. package/dist/core/types/form-context.js +0 -25
  178. package/dist/core/types/group-node-proxy.js +0 -31
  179. package/dist/core/types/index.js +0 -4
  180. package/dist/core/types/validation-schema.js +0 -10
  181. package/dist/core/utils/create-form.js +0 -24
  182. package/dist/core/utils/debounce.d.ts +0 -160
  183. package/dist/core/utils/debounce.js +0 -197
  184. package/dist/core/utils/error-handler.js +0 -226
  185. package/dist/core/utils/field-path-navigator.js +0 -374
  186. package/dist/core/utils/index.js +0 -14
  187. package/dist/core/utils/registry-helpers.js +0 -79
  188. package/dist/core/utils/registry-stack.js +0 -86
  189. package/dist/core/utils/resources.d.ts +0 -41
  190. package/dist/core/utils/resources.js +0 -69
  191. package/dist/core/utils/subscription-manager.js +0 -214
  192. package/dist/core/utils/type-guards.js +0 -169
  193. package/dist/core/validation/core/apply-when.js +0 -41
  194. package/dist/core/validation/core/apply.js +0 -38
  195. package/dist/core/validation/core/index.js +0 -8
  196. package/dist/core/validation/core/validate-async.js +0 -45
  197. package/dist/core/validation/core/validate-tree.js +0 -37
  198. package/dist/core/validation/core/validate.js +0 -38
  199. package/dist/core/validation/field-path.js +0 -147
  200. package/dist/core/validation/index.js +0 -33
  201. package/dist/core/validation/validate-form.js +0 -152
  202. package/dist/core/validation/validation-applicator.js +0 -217
  203. package/dist/core/validation/validation-context.js +0 -75
  204. package/dist/core/validation/validation-registry.js +0 -298
  205. package/dist/core/validation/validators/array-validators.js +0 -86
  206. package/dist/core/validation/validators/date.d.ts +0 -38
  207. package/dist/core/validation/validators/date.js +0 -117
  208. package/dist/core/validation/validators/email.js +0 -60
  209. package/dist/core/validation/validators/index.js +0 -14
  210. package/dist/core/validation/validators/max-length.js +0 -60
  211. package/dist/core/validation/validators/max.js +0 -60
  212. package/dist/core/validation/validators/min-length.js +0 -60
  213. package/dist/core/validation/validators/min.js +0 -60
  214. package/dist/core/validation/validators/number.js +0 -90
  215. package/dist/core/validation/validators/pattern.js +0 -62
  216. package/dist/core/validation/validators/phone.js +0 -58
  217. package/dist/core/validation/validators/required.js +0 -69
  218. package/dist/core/validation/validators/url.js +0 -55
  219. package/dist/create-field-path-CdPF3lIK.js +0 -704
  220. package/dist/hooks/useFormControl.js +0 -298
  221. package/dist/node-factory-D7DOnSSN.js +0 -3200
@@ -1,226 +0,0 @@
1
- /**
2
- * FormErrorHandler - централизованная обработка ошибок в формах
3
- *
4
- * Устраняет несогласованность обработки ошибок между:
5
- * - field-node.ts (логирует и конвертирует в ValidationError)
6
- * - behavior-applicator.ts (логирует и пробрасывает)
7
- * - validation-applicator.ts (логирует и проглатывает)
8
- *
9
- * @example
10
- * ```typescript
11
- * try {
12
- * await validator(value);
13
- * } catch (error) {
14
- * return FormErrorHandler.handle(error, 'AsyncValidator', ErrorStrategy.CONVERT);
15
- * }
16
- * ```
17
- */
18
- /**
19
- * Стратегия обработки ошибок
20
- *
21
- * Определяет, что делать с ошибкой после логирования
22
- */
23
- export var ErrorStrategy;
24
- (function (ErrorStrategy) {
25
- /**
26
- * Пробросить ошибку дальше (throw)
27
- * Используется когда ошибка критична и должна остановить выполнение
28
- */
29
- ErrorStrategy["THROW"] = "throw";
30
- /**
31
- * Залогировать и проглотить ошибку (продолжить выполнение)
32
- * Используется когда ошибка не критична
33
- */
34
- ErrorStrategy["LOG"] = "log";
35
- /**
36
- * Конвертировать ошибку в ValidationError
37
- * Используется в async validators для отображения ошибки валидации пользователю
38
- */
39
- ErrorStrategy["CONVERT"] = "convert";
40
- })(ErrorStrategy || (ErrorStrategy = {}));
41
- /**
42
- * Централизованный обработчик ошибок для форм
43
- *
44
- * Обеспечивает:
45
- * - Единообразное логирование ошибок в DEV режиме
46
- * - Гибкие стратегии обработки (throw/log/convert)
47
- * - Типобезопасное извлечение сообщений из Error/string/unknown
48
- *
49
- * @example
50
- * ```typescript
51
- * // В async validator (конвертировать в ValidationError)
52
- * try {
53
- * await validateEmail(value);
54
- * } catch (error) {
55
- * return FormErrorHandler.handle(error, 'EmailValidator', ErrorStrategy.CONVERT);
56
- * }
57
- *
58
- * // В behavior applicator (пробросить критичную ошибку)
59
- * try {
60
- * applyBehavior(schema);
61
- * } catch (error) {
62
- * FormErrorHandler.handle(error, 'BehaviorApplicator', ErrorStrategy.THROW);
63
- * }
64
- *
65
- * // В validator (залогировать и продолжить)
66
- * try {
67
- * validator(value);
68
- * } catch (error) {
69
- * FormErrorHandler.handle(error, 'Validator', ErrorStrategy.LOG);
70
- * }
71
- * ```
72
- */
73
- export class FormErrorHandler {
74
- /**
75
- * Обработать ошибку согласно заданной стратегии
76
- *
77
- * @param error Ошибка для обработки (Error | string | unknown)
78
- * @param context Контекст ошибки для логирования (например, 'AsyncValidator', 'BehaviorRegistry')
79
- * @param strategy Стратегия обработки (THROW | LOG | CONVERT)
80
- * @returns ValidationError если strategy = CONVERT, undefined если strategy = LOG, никогда не возвращается если strategy = THROW
81
- *
82
- * @example
83
- * ```typescript
84
- * // THROW - пробросить ошибку
85
- * try {
86
- * riskyOperation();
87
- * } catch (error) {
88
- * FormErrorHandler.handle(error, 'RiskyOperation', ErrorStrategy.THROW);
89
- * // Этот код никогда не выполнится
90
- * }
91
- *
92
- * // LOG - залогировать и продолжить
93
- * try {
94
- * nonCriticalOperation();
95
- * } catch (error) {
96
- * FormErrorHandler.handle(error, 'NonCritical', ErrorStrategy.LOG);
97
- * // Продолжаем выполнение
98
- * }
99
- *
100
- * // CONVERT - конвертировать в ValidationError
101
- * try {
102
- * await validator(value);
103
- * } catch (error) {
104
- * const validationError = FormErrorHandler.handle(
105
- * error,
106
- * 'AsyncValidator',
107
- * ErrorStrategy.CONVERT
108
- * );
109
- * return validationError;
110
- * }
111
- * ```
112
- */
113
- static handle(error, context, strategy = ErrorStrategy.THROW) {
114
- // Извлекаем сообщение из ошибки
115
- const message = this.extractMessage(error);
116
- // Логируем в DEV режиме
117
- if (import.meta.env.DEV) {
118
- console.error(`[${context}]`, error);
119
- }
120
- // Применяем стратегию
121
- switch (strategy) {
122
- case ErrorStrategy.THROW:
123
- throw error;
124
- case ErrorStrategy.LOG:
125
- // Просто логируем, не возвращаем ничего
126
- return;
127
- case ErrorStrategy.CONVERT:
128
- // Конвертируем в ValidationError
129
- return {
130
- code: 'validator_error',
131
- message,
132
- params: { field: context },
133
- };
134
- }
135
- }
136
- /**
137
- * Извлечь сообщение из ошибки
138
- *
139
- * Обрабатывает различные типы ошибок:
140
- * - Error объекты → error.message
141
- * - Строки → возвращает как есть
142
- * - Объекты с message → извлекает message
143
- * - Другое → String(error)
144
- *
145
- * @param error Ошибка для извлечения сообщения
146
- * @returns Сообщение ошибки
147
- * @private
148
- *
149
- * @example
150
- * ```typescript
151
- * FormErrorHandler.extractMessage(new Error('Test'));
152
- * // 'Test'
153
- *
154
- * FormErrorHandler.extractMessage('String error');
155
- * // 'String error'
156
- *
157
- * FormErrorHandler.extractMessage({ message: 'Object error' });
158
- * // 'Object error'
159
- *
160
- * FormErrorHandler.extractMessage(null);
161
- * // 'null'
162
- * ```
163
- */
164
- static extractMessage(error) {
165
- if (error instanceof Error) {
166
- return error.message;
167
- }
168
- if (typeof error === 'string') {
169
- return error;
170
- }
171
- if (typeof error === 'object' && error !== null && 'message' in error) {
172
- return String(error.message);
173
- }
174
- return String(error);
175
- }
176
- /**
177
- * Создать ValidationError с заданными параметрами
178
- *
179
- * Утилитная функция для создания ValidationError объектов
180
- *
181
- * @param code Код ошибки
182
- * @param message Сообщение ошибки
183
- * @param field Поле (опционально)
184
- * @returns ValidationError объект
185
- *
186
- * @example
187
- * ```typescript
188
- * const error = FormErrorHandler.createValidationError(
189
- * 'required',
190
- * 'This field is required',
191
- * 'email'
192
- * );
193
- * // { code: 'required', message: 'This field is required', field: 'email' }
194
- * ```
195
- */
196
- static createValidationError(code, message, field) {
197
- return {
198
- code,
199
- message,
200
- params: field ? { field } : undefined,
201
- };
202
- }
203
- /**
204
- * Проверить, является ли объект ValidationError
205
- *
206
- * Type guard для ValidationError
207
- *
208
- * @param value Значение для проверки
209
- * @returns true если value является ValidationError
210
- *
211
- * @example
212
- * ```typescript
213
- * if (FormErrorHandler.isValidationError(result)) {
214
- * console.log(result.code); // OK, типобезопасно
215
- * }
216
- * ```
217
- */
218
- static isValidationError(value) {
219
- return (typeof value === 'object' &&
220
- value !== null &&
221
- 'code' in value &&
222
- 'message' in value &&
223
- typeof value.code === 'string' &&
224
- typeof value.message === 'string');
225
- }
226
- }
@@ -1,374 +0,0 @@
1
- /**
2
- * Навигация по путям к полям формы
3
- *
4
- * Централизует логику парсинга и навигации по путям к полям формы.
5
- * Используется в ValidationContext, BehaviorContext, GroupNode для единообразной
6
- * обработки путей вида "address.city" или "items[0].name".
7
- *
8
- * Устраняет дублирование логики парсинга путей в 4 местах кодовой базы.
9
- *
10
- * @internal
11
- *
12
- * @example
13
- * ```typescript
14
- * const navigator = new FieldPathNavigator();
15
- *
16
- * // Парсинг пути
17
- * const segments = navigator.parsePath('items[0].title');
18
- * // [{ key: 'items', index: 0 }, { key: 'title' }]
19
- *
20
- * // Получение значения из объекта
21
- * const obj = { items: [{ title: 'Item 1' }] };
22
- * const value = navigator.getValueByPath(obj, 'items[0].title');
23
- * // 'Item 1'
24
- *
25
- * // Получение узла формы
26
- * const titleNode = navigator.getNodeByPath(form, 'items[0].title');
27
- * titleNode?.setValue('New Title');
28
- * ```
29
- */
30
- export class FieldPathNavigator {
31
- /**
32
- * Парсит путь в массив сегментов
33
- *
34
- * Поддерживаемые форматы:
35
- * - Простые пути: "name", "email"
36
- * - Вложенные пути: "address.city", "user.profile.avatar"
37
- * - Массивы: "items[0]", "items[0].name", "tags[1][0]"
38
- * - Комбинации: "orders[0].items[1].price"
39
- *
40
- * @param path Путь к полю (строка с точками и квадратными скобками)
41
- * @returns Массив сегментов пути
42
- *
43
- * @example
44
- * ```typescript
45
- * navigator.parsePath('email');
46
- * // [{ key: 'email' }]
47
- *
48
- * navigator.parsePath('address.city');
49
- * // [{ key: 'address' }, { key: 'city' }]
50
- *
51
- * navigator.parsePath('items[0].name');
52
- * // [{ key: 'items', index: 0 }, { key: 'name' }]
53
- * ```
54
- */
55
- parsePath(path) {
56
- const segments = [];
57
- let currentPart = '';
58
- let inBrackets = false;
59
- for (let i = 0; i < path.length; i++) {
60
- const char = path[i];
61
- if (char === '[') {
62
- inBrackets = true;
63
- currentPart += char;
64
- }
65
- else if (char === ']') {
66
- inBrackets = false;
67
- currentPart += char;
68
- }
69
- else if (char === '.' && !inBrackets) {
70
- // Разделитель найден, обрабатываем накопленную часть
71
- if (currentPart) {
72
- this.addSegment(segments, currentPart);
73
- currentPart = '';
74
- }
75
- }
76
- else {
77
- currentPart += char;
78
- }
79
- }
80
- // Добавляем последнюю часть
81
- if (currentPart) {
82
- this.addSegment(segments, currentPart);
83
- }
84
- return segments;
85
- }
86
- /**
87
- * Добавляет сегмент в массив, обрабатывая массивы
88
- * @private
89
- */
90
- addSegment(segments, part) {
91
- // Проверка на массив: items[0]
92
- const arrayMatch = part.match(/^(.+)\[(\d+)\]$/);
93
- if (arrayMatch) {
94
- segments.push({
95
- key: arrayMatch[1],
96
- index: parseInt(arrayMatch[2], 10),
97
- });
98
- }
99
- else {
100
- segments.push({ key: part });
101
- }
102
- }
103
- /**
104
- * Получает значение по пути из объекта
105
- *
106
- * Проходит по всем сегментам пути и возвращает конечное значение.
107
- * Если путь не найден, возвращает undefined.
108
- *
109
- * @param obj Объект для навигации
110
- * @param path Путь к значению
111
- * @returns Значение или undefined, если путь не найден
112
- *
113
- * @example
114
- * ```typescript
115
- * const obj = {
116
- * email: 'test@mail.com',
117
- * address: { city: 'Moscow' },
118
- * items: [{ title: 'Item 1' }]
119
- * };
120
- *
121
- * navigator.getValueByPath(obj, 'email');
122
- * // 'test@mail.com'
123
- *
124
- * navigator.getValueByPath(obj, 'address.city');
125
- * // 'Moscow'
126
- *
127
- * navigator.getValueByPath(obj, 'items[0].title');
128
- * // 'Item 1'
129
- *
130
- * navigator.getValueByPath(obj, 'invalid.path');
131
- * // undefined
132
- * ```
133
- */
134
- getValueByPath(obj, path) {
135
- const segments = this.parsePath(path);
136
- let current = obj;
137
- for (const segment of segments) {
138
- if (current == null)
139
- return undefined;
140
- current = current[segment.key];
141
- if (segment.index !== undefined) {
142
- if (!Array.isArray(current))
143
- return undefined;
144
- current = current[segment.index];
145
- }
146
- }
147
- return current;
148
- }
149
- /**
150
- * Устанавливает значение по пути в объекте (мутирует объект)
151
- *
152
- * Создает промежуточные объекты, если они не существуют.
153
- * Выбрасывает ошибку, если ожидается массив, но его нет.
154
- *
155
- * @param obj Объект для модификации
156
- * @param path Путь к значению
157
- * @param value Новое значение
158
- *
159
- * @throws {Error} Если ожидается массив по пути, но его нет
160
- *
161
- * @example
162
- * ```typescript
163
- * const obj = { address: { city: '' } };
164
- * navigator.setValueByPath(obj, 'address.city', 'Moscow');
165
- * // obj.address.city === 'Moscow'
166
- *
167
- * const obj2: UnknownRecord = {};
168
- * navigator.setValueByPath(obj2, 'address.city', 'Moscow');
169
- * // Создаст { address: { city: 'Moscow' } }
170
- *
171
- * const obj3 = { items: [{ title: 'Old' }] };
172
- * navigator.setValueByPath(obj3, 'items[0].title', 'New');
173
- * // obj3.items[0].title === 'New'
174
- * ```
175
- */
176
- setValueByPath(obj, path, value) {
177
- const segments = this.parsePath(path);
178
- if (segments.length === 0) {
179
- throw new Error('Cannot set value: empty path');
180
- }
181
- let current = obj;
182
- // Проходим до предпоследнего сегмента
183
- for (let i = 0; i < segments.length - 1; i++) {
184
- const segment = segments[i];
185
- let next = current[segment.key];
186
- if (segment.index !== undefined) {
187
- // Доступ к массиву: items[0]
188
- if (!Array.isArray(next)) {
189
- throw new Error(`Expected array at path segment: ${segment.key}, but got ${typeof next}`);
190
- }
191
- current = next[segment.index];
192
- }
193
- else {
194
- // Доступ к объекту: address
195
- if (next == null) {
196
- // Создаем объект, если его нет
197
- current[segment.key] = {};
198
- next = current[segment.key];
199
- }
200
- current = next;
201
- }
202
- }
203
- // Устанавливаем значение в последнем сегменте
204
- const lastSegment = segments[segments.length - 1];
205
- if (lastSegment.index !== undefined) {
206
- const arr = current[lastSegment.key];
207
- if (!Array.isArray(arr)) {
208
- throw new Error(`Expected array at path segment: ${lastSegment.key}, but got ${typeof arr}`);
209
- }
210
- arr[lastSegment.index] = value;
211
- }
212
- else {
213
- current[lastSegment.key] = value;
214
- }
215
- }
216
- /**
217
- * Получить значение из FormNode по пути
218
- *
219
- * Автоматически извлекает значение из FormNode (через .value.value).
220
- * Используется в ValidationContext и BehaviorContext для единообразного
221
- * доступа к значениям полей формы.
222
- *
223
- * @param form Корневой узел формы (обычно GroupNode)
224
- * @param path Путь к полю
225
- * @returns Значение поля или undefined, если путь не найден
226
- *
227
- * @example
228
- * ```typescript
229
- * const form = new GroupNode({
230
- * email: { value: 'test@mail.com', component: Input },
231
- * address: {
232
- * city: { value: 'Moscow', component: Input }
233
- * },
234
- * items: [{ title: { value: 'Item 1', component: Input } }]
235
- * });
236
- *
237
- * navigator.getFormNodeValue(form, 'email');
238
- * // 'test@mail.com'
239
- *
240
- * navigator.getFormNodeValue(form, 'address.city');
241
- * // 'Moscow'
242
- *
243
- * navigator.getFormNodeValue(form, 'items[0].title');
244
- * // 'Item 1'
245
- *
246
- * navigator.getFormNodeValue(form, 'invalid.path');
247
- * // undefined
248
- * ```
249
- */
250
- getFormNodeValue(form, path) {
251
- const node = this.getNodeByPath(form, path);
252
- if (node == null) {
253
- return undefined;
254
- }
255
- // FormNode возвращает .value.value
256
- if (this.isFormNode(node)) {
257
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
258
- return node.value.value;
259
- }
260
- // Для обычных объектов возвращаем как есть
261
- return node;
262
- }
263
- /**
264
- * Type guard для проверки, является ли объект FormNode
265
- *
266
- * Проверяет наличие характерных свойств FormNode:
267
- * - value (Signal)
268
- * - value.value (значение Signal)
269
- *
270
- * @param obj Объект для проверки
271
- * @returns true, если объект является FormNode
272
- * @private
273
- */
274
- isFormNode(obj) {
275
- return (obj != null &&
276
- typeof obj === 'object' &&
277
- 'value' in obj &&
278
- typeof obj.value === 'object' &&
279
- obj.value != null &&
280
- 'value' in obj.value);
281
- }
282
- /**
283
- * Получает узел формы по пути
284
- *
285
- * Навигирует по структуре FormNode (GroupNode/FieldNode/ArrayNode)
286
- * и возвращает узел по указанному пути.
287
- *
288
- * Поддерживает:
289
- * - Доступ к полям GroupNode через fields Map
290
- * - Доступ к элементам ArrayNode через индекс
291
- * - Proxy-доступ к полям (для обратной совместимости)
292
- *
293
- * @param form Корневой узел формы (обычно GroupNode)
294
- * @param path Путь к узлу
295
- * @returns Узел формы или null, если путь не найден
296
- *
297
- * @example
298
- * ```typescript
299
- * const form = new GroupNode({
300
- * email: { value: '', component: Input },
301
- * address: {
302
- * city: { value: '', component: Input }
303
- * },
304
- * items: [{ title: { value: '', component: Input } }]
305
- * });
306
- *
307
- * const emailNode = navigator.getNodeByPath(form, 'email');
308
- * // FieldNode
309
- *
310
- * const cityNode = navigator.getNodeByPath(form, 'address.city');
311
- * // FieldNode
312
- *
313
- * const itemNode = navigator.getNodeByPath(form, 'items[0]');
314
- * // GroupNode
315
- *
316
- * const titleNode = navigator.getNodeByPath(form, 'items[0].title');
317
- * // FieldNode
318
- *
319
- * const invalidNode = navigator.getNodeByPath(form, 'invalid.path');
320
- * // null
321
- * ```
322
- */
323
- getNodeByPath(form, path) {
324
- const segments = this.parsePath(path);
325
- let current = form;
326
- for (const segment of segments) {
327
- if (current == null)
328
- return null;
329
- const currentRecord = current;
330
- // Для GroupNode: доступ через fields Map
331
- if (currentRecord.fields && currentRecord.fields instanceof Map) {
332
- current = currentRecord.fields.get(segment.key);
333
- // Если это не ArrayNode или нет индекса, продолжаем
334
- if (segment.index === undefined) {
335
- if (current == null)
336
- return null;
337
- continue;
338
- }
339
- }
340
- // Для ArrayNode: доступ через items и индекс
341
- else if (segment.index !== undefined && currentRecord.items) {
342
- const items = currentRecord.items.value || currentRecord.items;
343
- if (!Array.isArray(items))
344
- return null;
345
- current = items[segment.index];
346
- if (current == null)
347
- return null;
348
- continue;
349
- }
350
- // Proxy-доступ (для обратной совместимости)
351
- else if (segment.index === undefined) {
352
- current = currentRecord[segment.key];
353
- if (current == null)
354
- return null;
355
- continue;
356
- }
357
- // Если нашли ArrayNode и есть индекс, получаем элемент массива
358
- if (current && segment.index !== undefined && current.items) {
359
- const items = current.items.value ||
360
- current.items;
361
- if (!Array.isArray(items))
362
- return null;
363
- current = items[segment.index];
364
- }
365
- // Если запрашивается индекс, но узел не является ArrayNode, возвращаем null
366
- else if (current && segment.index !== undefined && !current.items) {
367
- return null;
368
- }
369
- if (current == null)
370
- return null;
371
- }
372
- return current;
373
- }
374
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Утилиты для работы с формами
3
- *
4
- * Централизованные вспомогательные классы и функции.
5
- */
6
- export { FieldPathNavigator } from './field-path-navigator';
7
- export { SubscriptionManager } from './subscription-manager';
8
- export { getCurrentValidationRegistry, getCurrentBehaviorRegistry } from './registry-helpers';
9
- export { RegistryStack } from './registry-stack';
10
- export { isFormNode, isFieldNode, isGroupNode, isArrayNode, getNodeType } from './type-guards';
11
- export { Debouncer } from './debounce';
12
- export { FormErrorHandler, ErrorStrategy } from './error-handler';
13
- export { createForm } from './create-form';
14
- export * from './resources';
@@ -1,79 +0,0 @@
1
- /**
2
- * Вспомогательные функции для работы с реестрами
3
- *
4
- * Централизует логику получения текущего активного реестра валидации/поведения
5
- * из context stack, избегая дублирования кода в schema-validators.ts,
6
- * array-validators.ts и schema-behaviors.ts
7
- */
8
- import { ValidationRegistry } from '../validation/validation-registry';
9
- import { BehaviorRegistry } from '../behavior/behavior-registry';
10
- /**
11
- * Получить текущий активный ValidationRegistry из context stack
12
- *
13
- * Используется внутри validation schema функций (validate, validateAsync, required и т.д.)
14
- * для регистрации валидаторов в активном контексте GroupNode
15
- *
16
- * @returns Текущий ValidationRegistry или заглушка в production
17
- * @throws Error если нет активного контекста (только в DEV режиме)
18
- *
19
- * @internal
20
- *
21
- * @example
22
- * ```typescript
23
- * function required(fieldPath, options) {
24
- * const registry = getCurrentValidationRegistry();
25
- * registry.registerSync(path, validator, options);
26
- * }
27
- * ```
28
- */
29
- export function getCurrentValidationRegistry() {
30
- const registry = ValidationRegistry.getCurrent();
31
- if (!registry) {
32
- if (import.meta.env.DEV) {
33
- throw new Error('No active ValidationRegistry context. Make sure to call applyValidationSchema() within a GroupNode context.');
34
- }
35
- // В production возвращаем заглушку чтобы не ломать приложение
36
- return {
37
- registerSync: () => { },
38
- registerAsync: () => { },
39
- registerTree: () => { },
40
- enterCondition: () => { },
41
- exitCondition: () => { },
42
- registerArrayItemValidation: () => { },
43
- };
44
- }
45
- return registry;
46
- }
47
- /**
48
- * Получить текущий активный BehaviorRegistry из context stack
49
- *
50
- * Используется внутри behavior schema функций (copyFrom, enableWhen, computeFrom и т.д.)
51
- * для регистрации поведений в активном контексте GroupNode
52
- *
53
- * @returns Текущий BehaviorRegistry или заглушка в production
54
- * @throws Error если нет активного контекста (только в DEV режиме)
55
- *
56
- * @internal
57
- *
58
- * @example
59
- * ```typescript
60
- * function copyFrom(target, source, options) {
61
- * const registry = getCurrentBehaviorRegistry();
62
- * const handler = createCopyBehavior(target, source, options);
63
- * registry.register(handler, { debounce: options?.debounce });
64
- * }
65
- * ```
66
- */
67
- export function getCurrentBehaviorRegistry() {
68
- const registry = BehaviorRegistry.getCurrent();
69
- if (!registry) {
70
- if (import.meta.env.DEV) {
71
- throw new Error('No active BehaviorRegistry context. Make sure to call applyBehaviorSchema() within a GroupNode context.');
72
- }
73
- // В production возвращаем заглушку чтобы не ломать приложение
74
- return {
75
- register: () => { },
76
- };
77
- }
78
- return registry;
79
- }