@reformer/core 10.2.0 → 11.0.0-beta.10

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 (285) hide show
  1. package/README.md +90 -68
  2. package/dist/behaviors-value-D05z3my6.js +109 -0
  3. package/dist/behaviors.d.ts +1 -1
  4. package/dist/behaviors.js +376 -211
  5. package/dist/create-model-DPSG3I0O.js +30 -0
  6. package/dist/date-utils-90-qZbse.js +43 -0
  7. package/dist/en-HP8XEych.js +44 -0
  8. package/dist/form/aggregate-signals.d.ts +21 -63
  9. package/dist/form/behaviors/collections.d.ts +84 -0
  10. package/dist/form/behaviors/context.d.ts +56 -0
  11. package/dist/form/behaviors/index.d.ts +30 -0
  12. package/dist/form/behaviors/internals.d.ts +41 -0
  13. package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +28 -5
  14. package/dist/form/behaviors/operators.d.ts +59 -0
  15. package/dist/form/behaviors/schema-operators.d.ts +83 -0
  16. package/dist/form/behaviors/types.d.ts +37 -0
  17. package/dist/form/create-form.d.ts +59 -70
  18. package/dist/form/flow/flow.d.ts +87 -0
  19. package/dist/form/form-bundle.d.ts +153 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +23 -27
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +159 -0
  37. package/dist/form/schema-subtree.d.ts +28 -0
  38. package/dist/form/signal-node-registry.d.ts +27 -22
  39. package/dist/form/submission.d.ts +93 -0
  40. package/dist/form/type-guards.d.ts +14 -73
  41. package/dist/form/types/contracts.d.ts +21 -91
  42. package/dist/form/types/field-config.d.ts +27 -0
  43. package/dist/form/types/index.d.ts +5 -91
  44. package/dist/form/types/nodes.d.ts +159 -0
  45. package/dist/form/types/schema-node.d.ts +148 -50
  46. package/dist/form/types/validation-schema.d.ts +24 -41
  47. package/dist/form/validation/context.d.ts +67 -0
  48. package/dist/form/validation/index.d.ts +24 -28
  49. package/dist/form/validation/operators.d.ts +101 -0
  50. package/dist/form/validation/run.d.ts +120 -0
  51. package/dist/form/validation/strategy.d.ts +89 -0
  52. package/dist/form/validation/types.d.ts +83 -0
  53. package/dist/form/validators/date-utils.d.ts +42 -0
  54. package/dist/form/validators/email.d.ts +23 -0
  55. package/dist/form/{validation/validators → validators}/file-type.d.ts +10 -10
  56. package/dist/form/{validation/validators → validators}/file-utils.d.ts +3 -2
  57. package/dist/form/validators/future-date.d.ts +23 -0
  58. package/dist/form/{validation/validators → validators}/index.d.ts +7 -4
  59. package/dist/form/validators/integer.d.ts +22 -0
  60. package/dist/form/validators/is-date.d.ts +24 -0
  61. package/dist/form/validators/is-number.d.ts +25 -0
  62. package/dist/form/validators/max-age.d.ts +28 -0
  63. package/dist/form/validators/max-date.d.ts +25 -0
  64. package/dist/form/{validation/validators → validators}/max-file-size.d.ts +10 -10
  65. package/dist/form/validators/max-files.d.ts +26 -0
  66. package/dist/form/validators/max-length.d.ts +33 -0
  67. package/dist/form/validators/max-total-file-size.d.ts +26 -0
  68. package/dist/form/validators/max.d.ts +26 -0
  69. package/dist/form/validators/min-age.d.ts +27 -0
  70. package/dist/form/validators/min-date.d.ts +25 -0
  71. package/dist/form/{validation/validators → validators}/min-file-size.d.ts +10 -10
  72. package/dist/form/validators/min-files.d.ts +27 -0
  73. package/dist/form/validators/min-length.d.ts +33 -0
  74. package/dist/form/validators/min.d.ts +26 -0
  75. package/dist/form/validators/multiple-of.d.ts +25 -0
  76. package/dist/form/validators/non-negative.d.ts +22 -0
  77. package/dist/form/validators/non-zero.d.ts +22 -0
  78. package/dist/form/validators/past-date.d.ts +23 -0
  79. package/dist/form/validators/pattern.d.ts +26 -0
  80. package/dist/form/{validation/validators → validators}/phone.d.ts +11 -11
  81. package/dist/form/validators/required.d.ts +29 -0
  82. package/dist/form/validators/url.d.ts +45 -0
  83. package/dist/form/validators/validation-issue.d.ts +8 -0
  84. package/dist/i18n/descriptor.d.ts +54 -0
  85. package/dist/i18n/en.json.d.ts +49 -0
  86. package/dist/i18n/loader.d.ts +71 -0
  87. package/dist/i18n/locale-store.d.ts +46 -0
  88. package/dist/i18n/locale.d.ts +90 -0
  89. package/dist/i18n/message-format.d.ts +136 -0
  90. package/dist/i18n/resolve-localized.d.ts +17 -0
  91. package/dist/i18n/ru.json.d.ts +49 -0
  92. package/dist/i18n/translator.d.ts +61 -0
  93. package/dist/i18n/validation-message.d.ts +53 -0
  94. package/dist/i18n.d.ts +26 -0
  95. package/dist/i18n.js +86 -0
  96. package/dist/index.d.ts +18 -6
  97. package/dist/index.js +932 -2779
  98. package/dist/loader-BWkigpVe.js +52 -0
  99. package/dist/locale/en.d.ts +2 -0
  100. package/dist/locale/en.js +5 -0
  101. package/dist/locale/en.json +45 -0
  102. package/dist/locale/index.d.ts +18 -0
  103. package/dist/locale/ru.d.ts +2 -0
  104. package/dist/locale/ru.js +44 -0
  105. package/dist/locale/ru.json +45 -0
  106. package/dist/locale-CG54AU2Q.js +219 -0
  107. package/dist/locale.d.ts +2 -0
  108. package/dist/locale.js +9 -0
  109. package/dist/{state → model}/behaviors-value.d.ts +24 -6
  110. package/dist/{state/form-model.d.ts → model/create-model.d.ts} +18 -4
  111. package/dist/{state → model}/derived-registry.d.ts +1 -1
  112. package/dist/model/index.d.ts +42 -0
  113. package/dist/model/model-nodes.d.ts +190 -0
  114. package/dist/model/model-signals-proxy.d.ts +63 -0
  115. package/dist/model/model-value-proxy.d.ts +84 -0
  116. package/dist/{state → model}/safe-effect.d.ts +50 -2
  117. package/dist/model/types.d.ts +239 -0
  118. package/dist/model.d.ts +2 -0
  119. package/dist/model.js +27 -0
  120. package/dist/node-kind-C-r6ZMdo.js +15 -0
  121. package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
  122. package/dist/platforms/react/field-binding/field-handle.d.ts +53 -0
  123. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  124. package/dist/{form → platforms/react}/hooks/types.d.ts +60 -38
  125. package/dist/platforms/react/hooks/use-form-bundle.d.ts +41 -0
  126. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  127. package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +13 -7
  128. package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +6 -6
  129. package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +7 -9
  130. package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +18 -18
  131. package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +9 -1
  132. package/dist/platforms/react/i18n/context.d.ts +18 -0
  133. package/dist/platforms/react/i18n/hooks.d.ts +65 -0
  134. package/dist/platforms/react/i18n/provider.d.ts +59 -0
  135. package/dist/platforms/react/index.d.ts +30 -0
  136. package/dist/react.d.ts +22 -0
  137. package/dist/react.js +505 -0
  138. package/dist/runtime-token.d.ts +22 -0
  139. package/dist/safe-effect-BazLOv1g.js +669 -0
  140. package/dist/schema-controller-CpiO2JoK.js +143 -0
  141. package/dist/signals.d.ts +15 -9
  142. package/dist/signals.js +11 -6
  143. package/dist/strategy-DzvnsWLh.js +375 -0
  144. package/dist/type-guards-D-N_5Ddh.js +19 -0
  145. package/dist/validation-issue-B9mjI8jz.js +7 -0
  146. package/dist/validation-message-Ced9MGxs.js +194 -0
  147. package/dist/validation.d.ts +1 -1
  148. package/dist/validation.js +11 -165
  149. package/dist/validators/email.d.ts +1 -1
  150. package/dist/validators/email.js +5 -8
  151. package/dist/validators/future-date.d.ts +1 -1
  152. package/dist/validators/future-date.js +10 -13
  153. package/dist/validators/integer.d.ts +1 -1
  154. package/dist/validators/integer.js +4 -7
  155. package/dist/validators/is-date.d.ts +1 -1
  156. package/dist/validators/is-date.js +5 -8
  157. package/dist/validators/is-number.d.ts +1 -1
  158. package/dist/validators/is-number.js +4 -7
  159. package/dist/validators/max-age.d.ts +1 -1
  160. package/dist/validators/max-age.js +8 -12
  161. package/dist/validators/max-date.d.ts +1 -1
  162. package/dist/validators/max-date.js +10 -13
  163. package/dist/validators/max-length.d.ts +1 -1
  164. package/dist/validators/max-length.js +7 -10
  165. package/dist/validators/max.d.ts +1 -1
  166. package/dist/validators/max.js +4 -7
  167. package/dist/validators/min-age.d.ts +1 -1
  168. package/dist/validators/min-age.js +10 -11
  169. package/dist/validators/min-date.d.ts +1 -1
  170. package/dist/validators/min-date.js +8 -11
  171. package/dist/validators/min-length.d.ts +1 -1
  172. package/dist/validators/min-length.js +4 -7
  173. package/dist/validators/min.d.ts +1 -1
  174. package/dist/validators/min.js +4 -7
  175. package/dist/validators/multiple-of.d.ts +1 -1
  176. package/dist/validators/multiple-of.js +11 -10
  177. package/dist/validators/non-negative.d.ts +1 -1
  178. package/dist/validators/non-negative.js +2 -5
  179. package/dist/validators/non-zero.d.ts +1 -1
  180. package/dist/validators/non-zero.js +4 -7
  181. package/dist/validators/past-date.d.ts +1 -1
  182. package/dist/validators/past-date.js +10 -13
  183. package/dist/validators/pattern.d.ts +1 -1
  184. package/dist/validators/pattern.js +5 -8
  185. package/dist/validators/phone.d.ts +1 -1
  186. package/dist/validators/phone.js +10 -11
  187. package/dist/validators/required.d.ts +1 -1
  188. package/dist/validators/required.js +4 -7
  189. package/dist/validators/url.d.ts +1 -1
  190. package/dist/validators/url.js +48 -17
  191. package/dist/validators.d.ts +1 -5
  192. package/dist/validators.js +98 -154
  193. package/docs/llms/01-api-reference.md +89 -0
  194. package/docs/llms/02-quick-start.md +181 -0
  195. package/docs/llms/03-api-signatures.md +494 -0
  196. package/docs/llms/04-common-patterns.md +204 -0
  197. package/docs/llms/05-common-mistakes.md +192 -0
  198. package/docs/llms/06-troubleshooting.md +16 -0
  199. package/docs/llms/07-complete-import.md +130 -0
  200. package/docs/llms/08-form-types.md +54 -0
  201. package/docs/llms/09-formschema.md +191 -0
  202. package/docs/llms/10-arrays.md +241 -0
  203. package/docs/llms/11-async-watchfield.md +53 -0
  204. package/docs/llms/12-array-cleanup.md +31 -0
  205. package/docs/llms/13-multi-step.md +162 -0
  206. package/docs/llms/14-extended-mistakes.md +101 -0
  207. package/docs/llms/15-project-structure.md +279 -0
  208. package/docs/llms/16-ui-components.md +229 -0
  209. package/docs/llms/17-nonexistent-api.md +278 -0
  210. package/docs/llms/18-conditional-fields.md +222 -0
  211. package/docs/llms/19-reading-values.md +100 -0
  212. package/docs/llms/20-compute-vs-watch.md +153 -0
  213. package/docs/llms/21-array-operations.md +163 -0
  214. package/docs/llms/22-cycle-detection.md +86 -0
  215. package/docs/llms/23-copy-from.md +141 -0
  216. package/docs/llms/24-sync-fields.md +115 -0
  217. package/docs/llms/25-reset-when.md +122 -0
  218. package/docs/llms/26-transform-value.md +136 -0
  219. package/docs/llms/27-revalidate-when.md +133 -0
  220. package/docs/llms/28-submit-and-reset.md +409 -0
  221. package/docs/llms/29-async-preload.md +203 -0
  222. package/docs/llms/30-type-safety-recipes.md +213 -0
  223. package/docs/llms/31-async-validator-debounce.md +178 -0
  224. package/docs/llms/32-async-options-loading.md +108 -0
  225. package/docs/llms/33-validation-strategy.md +305 -0
  226. package/docs/llms/34-i18n.md +298 -0
  227. package/docs/llms/35-unified-contract.md +309 -0
  228. package/docs/llms/36-issue-messages.md +284 -0
  229. package/docs/llms/37-compiled-schema.md +332 -0
  230. package/docs/llms/38-form-flow.md +396 -0
  231. package/llms-index.json +1 -0
  232. package/llms.txt +10014 -3826
  233. package/package.json +77 -15
  234. package/dist/behaviors-node-DGEFMTQu.js +0 -18
  235. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  236. package/dist/date-utils-dTT_x_Be.js +0 -31
  237. package/dist/derived-registry-CluZCQ7U.js +0 -16
  238. package/dist/form/behaviors.d.ts +0 -133
  239. package/dist/form/error-handler.d.ts +0 -166
  240. package/dist/form/factories/index.d.ts +0 -6
  241. package/dist/form/factories/node-factory.d.ts +0 -188
  242. package/dist/form/form-submitter.d.ts +0 -125
  243. package/dist/form/nodes/array-node.d.ts +0 -360
  244. package/dist/form/nodes/field-node.d.ts +0 -294
  245. package/dist/form/nodes/form-node.d.ts +0 -340
  246. package/dist/form/nodes/group-node.d.ts +0 -288
  247. package/dist/form/nodes/model-array-node.d.ts +0 -121
  248. package/dist/form/status-machine.d.ts +0 -153
  249. package/dist/form/types/deep-schema.d.ts +0 -116
  250. package/dist/form/types/form-proxy.d.ts +0 -141
  251. package/dist/form/unique-id.d.ts +0 -53
  252. package/dist/form/validation/validators/date-utils.d.ts +0 -30
  253. package/dist/form/validation/validators/email.d.ts +0 -23
  254. package/dist/form/validation/validators/future-date.d.ts +0 -23
  255. package/dist/form/validation/validators/integer.d.ts +0 -22
  256. package/dist/form/validation/validators/is-date.d.ts +0 -23
  257. package/dist/form/validation/validators/is-number.d.ts +0 -24
  258. package/dist/form/validation/validators/max-age.d.ts +0 -25
  259. package/dist/form/validation/validators/max-date.d.ts +0 -25
  260. package/dist/form/validation/validators/max-files.d.ts +0 -26
  261. package/dist/form/validation/validators/max-length.d.ts +0 -27
  262. package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
  263. package/dist/form/validation/validators/max.d.ts +0 -25
  264. package/dist/form/validation/validators/min-age.d.ts +0 -25
  265. package/dist/form/validation/validators/min-date.d.ts +0 -25
  266. package/dist/form/validation/validators/min-files.d.ts +0 -26
  267. package/dist/form/validation/validators/min-length.d.ts +0 -27
  268. package/dist/form/validation/validators/min.d.ts +0 -25
  269. package/dist/form/validation/validators/multiple-of.d.ts +0 -24
  270. package/dist/form/validation/validators/non-negative.d.ts +0 -22
  271. package/dist/form/validation/validators/non-zero.d.ts +0 -22
  272. package/dist/form/validation/validators/past-date.d.ts +0 -23
  273. package/dist/form/validation/validators/pattern.d.ts +0 -33
  274. package/dist/form/validation/validators/required.d.ts +0 -32
  275. package/dist/form/validation/validators/url.d.ts +0 -43
  276. package/dist/form/validation-schema.d.ts +0 -106
  277. package/dist/form/validation-strategy.d.ts +0 -55
  278. package/dist/form/validators.d.ts +0 -6
  279. package/dist/form-model-DYCzGGKF.js +0 -286
  280. package/dist/state/index.d.ts +0 -31
  281. package/dist/state/subscription-manager.d.ts +0 -180
  282. package/dist/state/types.d.ts +0 -144
  283. package/dist/state.d.ts +0 -2
  284. package/dist/state.js +0 -21
  285. package/dist/subscription-manager-705bls97.js +0 -171
package/dist/index.js CHANGED
@@ -1,2931 +1,1084 @@
1
- import { c as Ie } from "./form-model-DYCzGGKF.js";
2
- import { c as Re, a as Be, r as $e, b as Ge, d as je, e as Ue, f as We, s as qe, t as He, w as Ke } from "./behaviors-value-C7dSEhCc.js";
3
- import { i as B } from "./derived-registry-CluZCQ7U.js";
4
- import { m as Ye, u as ze } from "./derived-registry-CluZCQ7U.js";
5
- import { S as I } from "./subscription-manager-705bls97.js";
6
- import { default as Xe } from "./validators.js";
7
- import { signal as g, computed as c, effect as E, batch as X, Signal as $ } from "@preact/signals-core";
8
- import { r as Z } from "./signal-node-registry-tiwel-TV.js";
9
- import { g as et } from "./signal-node-registry-tiwel-TV.js";
10
- import { d as st, e as it } from "./behaviors-node-DGEFMTQu.js";
11
- import K, { useRef as O, useCallback as _, useMemo as ee, useEffect as te } from "react";
12
- import { createFormValidation as se } from "./validation.js";
13
- class M {
14
- // ============================================================================
15
- // Protected состояние (для Template Method паттерна)
16
- // ============================================================================
17
- /**
18
- * Пользователь взаимодействовал с узлом (touched)
19
- * Protected: наследники могут читать/изменять через методы
20
- */
21
- _touched = g(!1);
22
- /**
23
- * Значение узла было изменено (dirty)
24
- * Protected: наследники могут читать/изменять через методы
25
- */
26
- _dirty = g(!1);
27
- /**
28
- * Текущий статус узла
29
- * Protected: наследники могут читать/изменять через методы
30
- */
31
- _status = g("valid");
32
- // ============================================================================
33
- // Публичные computed signals (readonly для внешнего мира)
34
- // ============================================================================
35
- /**
36
- * Пользователь взаимодействовал с узлом (touched)
37
- * Computed из _touched для предоставления readonly интерфейса
38
- */
39
- touched = c(() => this._touched.value);
40
- /**
41
- * Пользователь не взаимодействовал с узлом (untouched)
42
- */
43
- untouched = c(() => !this._touched.value);
44
- /**
45
- * Значение узла было изменено (dirty)
46
- * Computed из _dirty для предоставления readonly интерфейса
47
- */
48
- dirty = c(() => this._dirty.value);
49
- /**
50
- * Значение узла не было изменено (pristine)
51
- */
52
- pristine = c(() => !this._dirty.value);
53
- /**
54
- * Текущий статус узла
55
- * Computed из _status для предоставления readonly интерфейса
56
- */
57
- status = c(() => this._status.value);
58
- /**
59
- * Узел отключен (disabled)
60
- */
61
- disabled = c(() => this._status.value === "disabled");
62
- /**
63
- * Узел включен (enabled)
64
- */
65
- enabled = c(() => this._status.value !== "disabled");
66
- /**
67
- * Получить ошибки валидации с фильтрацией
68
- *
69
- * Позволяет фильтровать ошибки по различным критериям:
70
- * - По коду ошибки
71
- * - По сообщению (частичное совпадение)
72
- * - По параметрам
73
- * - Через кастомный предикат
74
- *
75
- * Без параметров возвращает все ошибки (эквивалент errors.value)
76
- *
77
- * @param options - Опции фильтрации ошибок
78
- * @returns Отфильтрованный массив ошибок валидации
79
- *
80
- * @example
81
- * ```typescript
82
- * // Все ошибки
83
- * const allErrors = form.getErrors();
84
- *
85
- * // Ошибки с конкретным кодом
86
- * const requiredErrors = form.getErrors({ code: 'required' });
87
- *
88
- * // Ошибки с несколькими кодами
89
- * const errors = form.getErrors({ code: ['required', 'email'] });
90
- *
91
- * // Ошибки по сообщению
92
- * const passwordErrors = form.getErrors({ message: 'Password' });
93
- *
94
- * // Ошибки по параметрам
95
- * const minLengthErrors = form.getErrors({
96
- * params: { minLength: 8 }
97
- * });
98
- *
99
- * // Кастомная фильтрация
100
- * const customErrors = form.getErrors({
101
- * predicate: (err) => err.code.startsWith('custom_')
102
- * });
103
- * ```
104
- */
105
- getErrors(e) {
106
- const t = this.errors.value;
107
- return e ? t.filter((s) => {
108
- if (e.code !== void 0 && !(Array.isArray(e.code) ? e.code : [e.code]).includes(s.code) || e.message !== void 0 && !s.message.toLowerCase().includes(e.message.toLowerCase()))
109
- return !1;
110
- if (e.params !== void 0) {
111
- if (!s.params)
112
- return !1;
113
- for (const [i, a] of Object.entries(e.params))
114
- if (s.params[i] !== a)
115
- return !1;
116
- }
117
- return !(e.predicate !== void 0 && !e.predicate(s));
118
- }) : t;
119
- }
120
- // ============================================================================
121
- // Методы управления состоянием (Template Method)
122
- // ============================================================================
123
- /**
124
- * Отметить узел как touched (пользователь взаимодействовал)
125
- *
126
- * Template Method: обновляет signal в базовом классе,
127
- * вызывает hook для кастомной логики в наследниках
128
- */
129
- markAsTouched() {
130
- this._touched.value = !0, this.onMarkAsTouched();
131
- }
132
- /**
133
- * Отметить узел как untouched
134
- *
135
- * Template Method: обновляет signal в базовом классе,
136
- * вызывает hook для кастомной логики в наследниках
137
- */
138
- markAsUntouched() {
139
- this._touched.value = !1, this.onMarkAsUntouched();
140
- }
141
- /**
142
- * Отметить узел как dirty (значение изменено)
143
- *
144
- * Template Method: обновляет signal в базовом классе,
145
- * вызывает hook для кастомной логики в наследниках
146
- */
147
- markAsDirty() {
148
- this._dirty.value = !0, this.onMarkAsDirty();
149
- }
150
- /**
151
- * Отметить узел как pristine (значение не изменено)
152
- *
153
- * Template Method: обновляет signal в базовом классе,
154
- * вызывает hook для кастомной логики в наследниках
155
- */
156
- markAsPristine() {
157
- this._dirty.value = !1, this.onMarkAsPristine();
158
- }
159
- /**
160
- * Пометить все поля (включая вложенные) как touched
161
- * Алиас для markAsTouched(), но более явно показывает намерение
162
- * пометить ВСЕ поля рекурсивно
163
- *
164
- * Полезно для:
165
- * - Показа всех ошибок валидации перед submit
166
- * - Принудительного отображения ошибок при нажатии "Validate All"
167
- * - Отображения невалидных полей в wizard/step form
168
- *
169
- * @example
170
- * ```typescript
171
- * // Показать все ошибки перед submit
172
- * form.touchAll();
173
- * const isValid = await form.validate();
174
- * if (!isValid) {
175
- * // Все ошибки теперь видны пользователю
176
- * }
177
- *
178
- * // Или использовать submit() который уже вызывает touchAll
179
- * await form.submit(async (values) => {
180
- * await api.save(values);
181
- * });
182
- * ```
183
- */
184
- touchAll() {
185
- this.markAsTouched();
186
- }
187
- // ============================================================================
188
- // Методы управления доступностью (Template Method)
189
- // ============================================================================
190
- /**
191
- * Отключить узел
192
- *
193
- * Template Method: обновляет статус в базовом классе,
194
- * вызывает hook для кастомной логики в наследниках
195
- *
196
- * Отключенные узлы не проходят валидацию и не включаются в getValue()
197
- */
198
- disable() {
199
- this._status.value = "disabled", this.onDisable();
200
- }
201
- /**
202
- * Включить узел
203
- *
204
- * Template Method: обновляет статус в базовом классе,
205
- * вызывает hook для кастомной логики в наследниках
206
- */
207
- enable() {
208
- this._status.value = "valid", this.onEnable();
209
- }
210
- // ============================================================================
211
- // Protected hooks (для переопределения в наследниках)
212
- // ============================================================================
213
- /**
214
- * Hook: вызывается после markAsTouched()
215
- *
216
- * Переопределите в наследниках для дополнительной логики:
217
- * - GroupNode: пометить все дочерние узлы как touched
218
- * - ArrayNode: пометить все элементы массива как touched
219
- * - FieldNode: пустая реализация (нет дочерних узлов)
220
- *
221
- * @example
222
- * ```typescript
223
- * // GroupNode
224
- * protected onMarkAsTouched(): void {
225
- * this.fields.forEach(field => field.markAsTouched());
226
- * }
227
- * ```
228
- */
229
- onMarkAsTouched() {
230
- }
231
- /**
232
- * Hook: вызывается после markAsUntouched()
233
- *
234
- * Переопределите в наследниках для дополнительной логики:
235
- * - GroupNode: пометить все дочерние узлы как untouched
236
- * - ArrayNode: пометить все элементы массива как untouched
237
- * - FieldNode: пустая реализация (нет дочерних узлов)
238
- */
239
- onMarkAsUntouched() {
240
- }
241
- /**
242
- * Hook: вызывается после markAsDirty()
243
- *
244
- * Переопределите в наследниках для дополнительной логики:
245
- * - GroupNode: может обновить родительскую форму
246
- * - ArrayNode: может обновить родительскую форму
247
- * - FieldNode: пустая реализация
248
- */
249
- onMarkAsDirty() {
250
- }
251
- /**
252
- * Hook: вызывается после markAsPristine()
253
- *
254
- * Переопределите в наследниках для дополнительной логики:
255
- * - GroupNode: пометить все дочерние узлы как pristine
256
- * - ArrayNode: пометить все элементы массива как pristine
257
- * - FieldNode: пустая реализация
258
- */
259
- onMarkAsPristine() {
260
- }
261
- /**
262
- * Hook: вызывается после disable()
263
- *
264
- * Переопределите в наследниках для дополнительной логики:
265
- * - GroupNode: отключить все дочерние узлы
266
- * - ArrayNode: отключить все элементы массива
267
- * - FieldNode: очистить ошибки валидации
268
- *
269
- * @example
270
- * ```typescript
271
- * // GroupNode
272
- * protected onDisable(): void {
273
- * this.fields.forEach(field => field.disable());
274
- * }
275
- * ```
276
- */
277
- onDisable() {
278
- }
279
- /**
280
- * Hook: вызывается после enable()
281
- *
282
- * Переопределите в наследниках для дополнительной логики:
283
- * - GroupNode: включить все дочерние узлы
284
- * - ArrayNode: включить все элементы массива
285
- * - FieldNode: пустая реализация
286
- */
287
- onEnable() {
288
- }
1
+ import { c as ue } from "./create-model-DPSG3I0O.js";
2
+ import { e as vt, a as pt } from "./create-model-DPSG3I0O.js";
3
+ import { d as T, q as H, t as de, o as he, n as fe, b as J, m as _, i as X, v as ve, w as pe, p as me, f as we, g as ye, x as be } from "./safe-effect-BazLOv1g.js";
4
+ import { a as wt, c as yt, r as bt, s as gt, e as kt, u as St } from "./safe-effect-BazLOv1g.js";
5
+ import { signal as w, computed as d, batch as D, effect as ge, untracked as A } from "@preact/signals-core";
6
+ import { N as j, R as ke, k as Se, r as k } from "./node-kind-C-r6ZMdo.js";
7
+ import { r as O } from "./signal-node-registry-tiwel-TV.js";
8
+ import { g as Et } from "./signal-node-registry-tiwel-TV.js";
9
+ import { c as $e, u as Ee } from "./schema-controller-CpiO2JoK.js";
10
+ import { a as At } from "./schema-controller-CpiO2JoK.js";
11
+ import { e as Pe, h as Ae, i as Oe, c as Fe, j as Ie } from "./strategy-DzvnsWLh.js";
12
+ import { c as Ft, a as It, b as Nt, i as Vt } from "./type-guards-D-N_5Ddh.js";
13
+ function Ne() {
14
+ const t = w(!1), e = w(!1), s = w(0);
15
+ return {
16
+ touched: d(() => t.value),
17
+ dirty: d(() => e.value),
18
+ touches: d(() => s.value),
19
+ setTouched: (i) => {
20
+ D(() => {
21
+ t.value = i, i && (s.value = s.peek() + 1);
22
+ });
23
+ },
24
+ setDirty: (i) => {
25
+ e.value = i;
26
+ }
27
+ };
289
28
  }
290
- const w = {
291
- /** FieldNode.watch() */
292
- Watch: "watch",
293
- /** FieldNode.computeFrom() */
294
- ComputeFrom: "computeFrom",
295
- /** GroupNode.linkFields() */
296
- LinkFields: "linkFields",
297
- /** GroupNode.watchField() */
298
- WatchField: "watchField",
299
- /** ArrayNode.watchItems() */
300
- WatchItems: "watchItems",
301
- /** ArrayNode.watchLength() */
302
- WatchLength: "watchLength"
303
- };
304
- let ie = 0;
305
- function V(r) {
306
- return `${r}-${++ie}`;
29
+ function R() {
30
+ const t = w([]);
31
+ return {
32
+ list: d(() => t.value),
33
+ blocking: d(() => t.value.some((e) => e.severity !== "warning")),
34
+ set: (e) => {
35
+ t.value = e;
36
+ },
37
+ clear: () => {
38
+ t.value = [];
39
+ }
40
+ };
307
41
  }
308
- var A = /* @__PURE__ */ ((r) => (r.THROW = "throw", r.LOG = "log", r.CONVERT = "convert", r))(A || {});
309
- class F {
310
- static handle(e, t, s = "throw") {
311
- const i = this.extractMessage(e);
312
- switch (process.env.NODE_ENV !== "production" && console.error(`[${t}]`, e), s) {
313
- case "throw":
314
- throw e;
315
- case "log":
316
- return;
317
- case "convert":
318
- return {
319
- code: "validator_error",
320
- message: i,
321
- params: { field: t }
322
- };
42
+ function K(t = !1) {
43
+ const e = w(t);
44
+ return {
45
+ disabled: d(() => e.value),
46
+ setDisabled: (s) => {
47
+ e.value = s;
323
48
  }
324
- }
325
- /**
326
- * Извлечь сообщение из ошибки
327
- *
328
- * Обрабатывает различные типы ошибок:
329
- * - Error объекты → error.message
330
- * - Строки → возвращает как есть
331
- * - Объекты с message → извлекает message
332
- * - Другое → String(error)
333
- *
334
- * @param error Ошибка для извлечения сообщения
335
- * @returns Сообщение ошибки
336
- * @private
337
- *
338
- * @example
339
- * ```typescript
340
- * FormErrorHandler.extractMessage(new Error('Test'));
341
- * // 'Test'
342
- *
343
- * FormErrorHandler.extractMessage('String error');
344
- * // 'String error'
345
- *
346
- * FormErrorHandler.extractMessage({ message: 'Object error' });
347
- * // 'Object error'
348
- *
349
- * FormErrorHandler.extractMessage(null);
350
- * // 'null'
351
- * ```
352
- */
353
- static extractMessage(e) {
354
- return e instanceof Error ? e.message : typeof e == "string" ? e : typeof e == "object" && e !== null && "message" in e ? String(e.message) : String(e);
355
- }
356
- /**
357
- * Создать ValidationError с заданными параметрами
358
- *
359
- * Утилитная функция для создания ValidationError объектов
360
- *
361
- * @param code Код ошибки
362
- * @param message Сообщение ошибки
363
- * @param field Поле (опционально)
364
- * @returns ValidationError объект
365
- *
366
- * @example
367
- * ```typescript
368
- * const error = FormErrorHandler.createValidationError(
369
- * 'required',
370
- * 'This field is required',
371
- * 'email'
372
- * );
373
- * // { code: 'required', message: 'This field is required', field: 'email' }
374
- * ```
375
- */
376
- static createValidationError(e, t, s) {
377
- return {
378
- code: e,
379
- message: t,
380
- params: s ? { field: s } : void 0
381
- };
382
- }
383
- /**
384
- * Проверить, является ли объект ValidationError
385
- *
386
- * Type guard для ValidationError
387
- *
388
- * @param value Значение для проверки
389
- * @returns true если value является ValidationError
390
- *
391
- * @example
392
- * ```typescript
393
- * if (FormErrorHandler.isValidationError(result)) {
394
- * console.log(result.code); // OK, типобезопасно
395
- * }
396
- * ```
397
- */
398
- static isValidationError(e) {
399
- return typeof e == "object" && e !== null && "code" in e && "message" in e && typeof e.code == "string" && typeof e.message == "string";
400
- }
49
+ };
401
50
  }
402
- class re {
403
- /** Внутренний сигнал статуса */
404
- _status;
405
- /** Публичный read-only сигнал статуса */
406
- status;
407
- /** Поле валидно */
408
- valid;
409
- /** Поле невалидно */
410
- invalid;
411
- /** Идет валидация */
412
- pending;
413
- /** Поле отключено */
414
- disabled;
415
- /**
416
- * @param initial - Начальный статус (по умолчанию 'valid')
417
- */
418
- constructor(e = "valid") {
419
- this._status = g(e), this.status = c(() => this._status.value), this.valid = c(() => this._status.value === "valid"), this.invalid = c(() => this._status.value === "invalid"), this.pending = c(() => this._status.value === "pending"), this.disabled = c(() => this._status.value === "disabled");
420
- }
421
- /**
422
- * Начать валидацию
423
- *
424
- * Переводит статус в 'pending' если поле не отключено
425
- *
426
- * @example
427
- * ```typescript
428
- * statusMachine.startValidation();
429
- * // status: 'pending'
430
- * ```
431
- */
432
- startValidation() {
433
- this._status.value !== "disabled" && (this._status.value = "pending");
434
- }
435
- /**
436
- * Завершить валидацию
437
- *
438
- * @param hasErrors - Есть ли ошибки валидации
439
- *
440
- * @example
441
- * ```typescript
442
- * // Валидация успешна
443
- * statusMachine.completeValidation(false);
444
- * // status: 'valid'
445
- *
446
- * // Есть ошибки
447
- * statusMachine.completeValidation(true);
448
- * // status: 'invalid'
449
- * ```
450
- */
451
- completeValidation(e) {
452
- this._status.value !== "disabled" && (this._status.value = e ? "invalid" : "valid");
453
- }
454
- /**
455
- * Установить ошибки напрямую (без перехода через pending)
456
- *
457
- * Используется для синхронной валидации или установки ошибок извне
458
- *
459
- * @param hasErrors - Есть ли ошибки
460
- *
461
- * @example
462
- * ```typescript
463
- * statusMachine.setErrors(true);
464
- * // status: 'invalid'
465
- * ```
466
- */
467
- setErrors(e) {
468
- this._status.value !== "disabled" && (this._status.value = e ? "invalid" : "valid");
469
- }
470
- /**
471
- * Отключить поле
472
- *
473
- * Переводит статус в 'disabled'
474
- *
475
- * @example
476
- * ```typescript
477
- * statusMachine.disable();
478
- * // status: 'disabled'
479
- * ```
480
- */
481
- disable() {
482
- this._status.value = "disabled";
483
- }
484
- /**
485
- * Включить поле
486
- *
487
- * @param hasErrors - Есть ли ошибки (определяет valid/invalid)
488
- *
489
- * @example
490
- * ```typescript
491
- * statusMachine.enable(false);
492
- * // status: 'valid'
493
- *
494
- * statusMachine.enable(true);
495
- * // status: 'invalid'
496
- * ```
497
- */
498
- enable(e = !1) {
499
- this._status.value = e ? "invalid" : "valid";
500
- }
501
- /**
502
- * Обработать событие (альтернативный API)
503
- *
504
- * @param event - Событие для обработки
505
- *
506
- * @example
507
- * ```typescript
508
- * statusMachine.dispatch({ type: 'START_VALIDATION' });
509
- * statusMachine.dispatch({ type: 'VALIDATION_FAILURE' });
510
- * ```
511
- */
512
- dispatch(e) {
513
- switch (e.type) {
514
- case "START_VALIDATION":
515
- this.startValidation();
516
- break;
517
- case "VALIDATION_SUCCESS":
518
- this.completeValidation(!1);
519
- break;
520
- case "VALIDATION_FAILURE":
521
- this.completeValidation(!0);
522
- break;
523
- case "DISABLE":
524
- this.disable();
525
- break;
526
- case "ENABLE":
527
- this.enable(e.hasErrors);
528
- break;
529
- case "SET_ERRORS":
530
- this.setErrors(e.hasErrors);
531
- break;
51
+ function W() {
52
+ const t = w(0);
53
+ return {
54
+ active: d(() => t.value > 0),
55
+ mark: (e) => {
56
+ t.value = Math.max(0, t.peek() + (e ? 1 : -1));
532
57
  }
533
- }
534
- /**
535
- * Получить текущий статус
536
- */
537
- getStatus() {
538
- return this._status.value;
539
- }
540
- /**
541
- * Проверить, можно ли начать валидацию
542
- */
543
- canValidate() {
544
- return this._status.value !== "disabled";
545
- }
58
+ };
546
59
  }
547
- class ae extends M {
548
- // ============================================================================
549
- // Приватные сигналы
550
- // ============================================================================
551
- _value;
552
- _errors;
553
- // _touched, _dirty наследуются от FormNode (protected)
554
- // _status управляется через statusMachine
555
- _componentProps;
556
- /**
557
- * State machine для управления статусом поля
558
- * Централизует логику переходов между valid/invalid/pending/disabled
559
- */
560
- statusMachine;
561
- // ============================================================================
562
- // Публичные computed signals
563
- // ============================================================================
60
+ function Ve(t = {}) {
61
+ const e = w(t);
62
+ return {
63
+ props: d(() => e.value),
64
+ update: (s) => {
65
+ e.value = { ...e.peek(), ...s };
66
+ }
67
+ };
68
+ }
69
+ class Ce {
70
+ [j] = "control";
564
71
  value;
565
- // valid, invalid, pending, status, disabled берутся из statusMachine
566
72
  valid;
567
73
  invalid;
568
74
  pending;
569
- // Override status и disabled из базового класса
570
- status;
571
75
  disabled;
572
- // touched, dirty наследуются от FormNode
573
- errors;
76
+ touched;
77
+ /** Уходы из поля, включая повторные, — триггер стратегии `blur`. @internal */
78
+ touches;
79
+ dirty;
80
+ issues;
81
+ ownIssues;
82
+ component;
574
83
  componentProps;
575
- /**
576
- * Вычисляемое свойство: нужно ли показывать ошибку
577
- * Ошибка показывается если поле невалидно И (touched ИЛИ dirty)
578
- */
579
84
  shouldShowError;
580
- // ============================================================================
581
- // Конфигурация
582
- // ============================================================================
583
- validators;
584
- asyncValidators;
585
- updateOn;
586
- initialValue;
587
- currentAbortController;
588
- debounceMs;
589
- validateDebounceTimer;
85
+ shouldShowIssues;
86
+ source;
590
87
  /**
591
- * Pending debounced validation state
592
- * Contains resolve function and AbortController for cancellation
88
+ * Значение на момент построения. К нему возвращает `resetToCreation` (`resetOnDisable`), а
89
+ * {@link reset} — только поле над собственным сигналом, без модели за ним.
593
90
  */
594
- pendingValidation;
91
+ initialValue;
92
+ interaction = Ne();
93
+ issueState = R();
94
+ availability;
595
95
  /**
596
- * Менеджер подписок для централизованного cleanup
597
- * Использует SubscriptionManager вместо массива для управления подписками
96
+ * Блокировка из схемы (`disabled: true` узла — вычисляемые `total`, `fullName`). Постоянная:
97
+ * `enable()` контейнера её не снимает, иначе «выключить форму на время отправки → включить»
98
+ * разблокировало бы такие поля. Снимает её только явный `enable()` этого поля.
598
99
  */
599
- disposers = new I();
600
- component;
601
- // ============================================================================
602
- // Конструктор
603
- // ============================================================================
100
+ locked;
101
+ pendingState = W();
102
+ componentState;
604
103
  constructor(e) {
605
- super(), this.validators = e.validators || [], this.asyncValidators = e.asyncValidators || [], this.updateOn = e.updateOn || "blur", this.debounceMs = e.debounce || 0, this.component = e.component, this._value = e.valueSignal ?? g(e.value), this.initialValue = this._value.peek(), this._errors = g([]), this._componentProps = g(e.componentProps || {}), this.statusMachine = new re(e.disabled ? "disabled" : "valid"), this.value = c(() => this._value.value), this.valid = c(() => this.statusMachine.valid.value), this.invalid = c(() => this.statusMachine.invalid.value), this.pending = c(() => this.statusMachine.pending.value), this.status = c(() => this.statusMachine.status.value), this.disabled = c(() => this.statusMachine.disabled.value), this.errors = c(() => this._errors.value), this.componentProps = c(() => this._componentProps.value), this.shouldShowError = c(
606
- () => this.statusMachine.invalid.value && (this._touched.value || this._dirty.value)
104
+ this.source = e.valueSignal, this.initialValue = this.source.peek(), this.component = e.component, this.availability = K(e.disabled === !0), this.locked = e.disabled === !0, this.componentState = Ve(e.componentProps ?? {});
105
+ const { disabled: s } = this.availability, i = this.issueState.blocking;
106
+ this.value = d(() => this.source.value), this.disabled = s, this.pending = d(() => !s.value && this.pendingState.active.value), this.invalid = d(() => !s.value && !this.pending.value && i.value), this.valid = d(() => !s.value && !this.pending.value && !i.value), this.touched = this.interaction.touched, this.touches = this.interaction.touches, this.dirty = this.interaction.dirty, this.issues = this.issueState.list, this.ownIssues = this.issueState.list, this.componentProps = this.componentState.props;
107
+ const n = d(() => this.touched.value || this.dirty.value);
108
+ this.shouldShowError = d(() => this.invalid.value && n.value), this.shouldShowIssues = d(
109
+ () => !s.value && !this.pending.value && this.issues.value.length > 0 && n.value
607
110
  );
608
111
  }
609
- // ============================================================================
610
- // Реализация абстрактных методов FormNode
611
- // ============================================================================
612
112
  getValue() {
613
- return this._value.peek();
614
- }
615
- setValue(e, t) {
616
- if (this._value.value = e, this._dirty.value = !0, t?.emitEvent === !1)
617
- return;
618
- const s = this.validators.length > 0 || this.asyncValidators.length > 0, i = this._errors.value.length > 0;
619
- if (this.updateOn === "change") {
620
- this.validate();
621
- return;
622
- }
623
- i && s && this.validate();
113
+ return this.source.peek();
624
114
  }
625
- patchValue(e) {
626
- this.setValue(e);
115
+ setValue(e) {
116
+ this.source.value = e, this.interaction.setDirty(!0);
627
117
  }
628
118
  /**
629
- * Сбросить поле к указанному значению (или к initialValue)
630
- *
631
- * @param value - опциональное значение для сброса. Если не указано, используется initialValue
632
- *
633
- * @remarks
634
- * Этот метод:
635
- * - Устанавливает значение в value или initialValue
636
- * - Очищает ошибки валидации
637
- * - Сбрасывает touched/dirty флаги
638
- * - Устанавливает статус в 'valid'
639
- *
640
- * Если вам нужно сбросить к исходному значению, используйте resetToInitial()
119
+ * Сбросить поле: значение, замечания, `touched` и `dirty`. Без аргумента — к точке отсчёта модели,
120
+ * той же, к которой возвращает `model.reset()`: значению создания модели либо последнему
121
+ * `captureInitial()` (загруженные данные). Поле над собственным сигналом, без модели, — к значению,
122
+ * с которым нода создана.
641
123
  *
642
- * @example
643
- * ```typescript
644
- * // Сброс к initialValue
645
- * field.reset();
646
- *
647
- * // Сброс к новому значению
648
- * field.reset('new value');
649
- * ```
124
+ * У производного поля (цель `compute`) без аргумента сбрасывается только состояние: значением
125
+ * владеет поведение, а значение создания ноды снято до первого расчёта — запись вернула бы
126
+ * «сырое» значение, которое `compute` пересчитает лишь при смене источников.
650
127
  */
651
128
  reset(e) {
652
- this._value.value = e !== void 0 ? e : this.initialValue, this._errors.value = [], this._touched.value = !1, this._dirty.value = !1, this.statusMachine.setErrors(!1);
129
+ e !== void 0 ? this.source.value = e : T(this.source) || (this.source.value = this.referenceValue()), this.resetState();
653
130
  }
654
- /**
655
- * Сбросить поле к исходному значению (initialValue)
656
- *
657
- * @remarks
658
- * Алиас для reset() без параметров, но более явный:
659
- * - resetToInitial() - явно показывает намерение вернуться к начальному значению
660
- * - reset() - может принимать новое значение
661
- *
662
- * Полезно когда:
663
- * - Пользователь нажал "Cancel" - вернуть форму в исходное состояние
664
- * - Форма была изменена через reset(newValue), но нужно вернуться к самому началу
665
- * - Явное намерение показать "отмену всех изменений"
666
- *
667
- * @example
668
- * ```typescript
669
- * const field = new FieldNode({ value: 'initial', component: Input });
670
- *
671
- * field.setValue('changed');
672
- * field.reset('temp value');
673
- * console.log(field.value.value); // 'temp value'
674
- *
675
- * field.resetToInitial();
676
- * console.log(field.value.value); // 'initial'
677
- * ```
678
- */
679
- resetToInitial() {
680
- this.reset(this.initialValue);
131
+ /** Точка отсчёта модели за полем; у поля без модели — значение создания. */
132
+ referenceValue() {
133
+ const e = H(this.source);
134
+ return e === void 0 ? this.initialValue : e.value;
681
135
  }
682
- /**
683
- * Cancel any pending validation (debounced or running)
684
- * @private
685
- * @remarks
686
- * Centralizes all cancellation logic:
687
- * - Aborts pending debounced validation and resolves its promise
688
- * - Clears debounce timer
689
- * - Aborts currently running async validation
690
- */
691
- cancelPendingValidation() {
692
- this.pendingValidation && (this.pendingValidation.abortController.abort(), this.pendingValidation.resolve(!1), this.pendingValidation = void 0), this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0), this.currentAbortController && (this.currentAbortController.abort(), this.currentAbortController = void 0);
136
+ /** Значение создания и чистое состояние, у цели `compute` тоже — для `resetOnDisable`. @internal */
137
+ resetToCreation() {
138
+ this.source.value = this.initialValue, this.resetState();
693
139
  }
694
- /**
695
- * Запустить валидацию поля
696
- * @param options - опции валидации
697
- * @returns `Promise<boolean>` - true если поле валидно
698
- *
699
- * @remarks
700
- * Метод защищен от race conditions через AbortController.
701
- * При быстром вводе только последняя валидация применяет результаты.
702
- *
703
- * @example
704
- * ```typescript
705
- * // Обычная валидация
706
- * await field.validate();
707
- *
708
- * // С debounce
709
- * await field.validate({ debounce: 300 });
710
- * ```
711
- */
712
- async validate(e) {
713
- const t = e?.debounce ?? this.debounceMs;
714
- if (this.cancelPendingValidation(), t <= 0 || this.asyncValidators.length === 0)
715
- return this.validateImmediate();
716
- const s = new AbortController();
717
- return new Promise((i) => {
718
- this.pendingValidation = { resolve: i, abortController: s }, this.validateDebounceTimer = setTimeout(async () => {
719
- if (this.validateDebounceTimer = void 0, s.signal.aborted) {
720
- i(!1);
721
- return;
722
- }
723
- this.pendingValidation = void 0;
724
- const a = await this.validateImmediate(s);
725
- i(a);
726
- }, t), s.signal.addEventListener(
727
- "abort",
728
- () => {
729
- this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0), i(!1);
730
- },
731
- { once: !0 }
732
- );
733
- });
140
+ resetState() {
141
+ this.issueState.clear(), this.interaction.setTouched(!1), this.interaction.setDirty(!1);
734
142
  }
735
- /**
736
- * Немедленная валидация без debounce
737
- * @private
738
- * @param providedController - AbortController from debounced validate()
739
- * @remarks
740
- * Защищена от race conditions через AbortController:
741
- * - Отменяет предыдущую валидацию при запуске новой (if no controller provided)
742
- * - Передаёт AbortSignal в async валидаторы для отмены операций (например, fetch)
743
- * - Проверяет signal.aborted в ключевых точках
744
- */
745
- async validateImmediate(e) {
746
- const t = e ?? new AbortController();
747
- e || this.currentAbortController?.abort(), this.currentAbortController = t;
748
- const { signal: s } = t, i = [];
749
- for (const n of this.validators) {
750
- const l = n(this._value.value);
751
- l && i.push(l);
752
- }
753
- if (s.aborted)
754
- return !1;
755
- if (i.length > 0) {
756
- this._errors.value = i;
757
- const n = i.some((l) => l.severity !== "warning");
758
- if (this.statusMachine.setErrors(n), n)
759
- return !1;
760
- }
761
- if (this.asyncValidators.length > 0) {
762
- if (s.aborted)
763
- return !1;
764
- this.statusMachine.startValidation();
765
- try {
766
- const n = await Promise.all(
767
- this.asyncValidators.map(async (d) => {
768
- if (s.aborted)
769
- throw new DOMException("Validation aborted", "AbortError");
770
- try {
771
- const u = await d(this._value.value, { signal: s });
772
- if (s.aborted)
773
- throw new DOMException("Validation aborted", "AbortError");
774
- return u;
775
- } catch (u) {
776
- if (u instanceof DOMException && u.name === "AbortError")
777
- throw u;
778
- return F.handle(
779
- u,
780
- "FieldNode AsyncValidator",
781
- A.CONVERT
782
- );
783
- }
784
- })
785
- );
786
- if (s.aborted)
787
- return !1;
788
- const l = n.filter((d) => d !== null);
789
- if (l.length > 0) {
790
- this._errors.value = l;
791
- const d = l.some((u) => u.severity !== "warning");
792
- if (this.statusMachine.completeValidation(d), d)
793
- return !1;
794
- }
795
- } catch (n) {
796
- if (n instanceof DOMException && n.name === "AbortError")
797
- return !1;
798
- throw n;
799
- }
800
- }
801
- return s.aborted ? !1 : ((this.validators.length > 0 || this.asyncValidators.length > 0) && (this._errors.value = [], this.statusMachine.completeValidation(!1)), !this._errors.value.some((n) => n.severity !== "warning"));
143
+ markAsTouched() {
144
+ this.interaction.setTouched(!0);
145
+ }
146
+ markAsUntouched() {
147
+ this.interaction.setTouched(!1);
802
148
  }
803
- setErrors(e) {
804
- this._errors.value = e;
805
- const t = e.some((s) => s.severity !== "warning");
806
- this.statusMachine.setErrors(t);
149
+ disable() {
150
+ this.availability.setDisabled(!0), this.issueState.clear();
807
151
  }
808
- clearErrors() {
809
- this._errors.value = [], this.statusMachine.setErrors(!1);
152
+ enable() {
153
+ this.locked = !1, this.availability.setDisabled(!1);
810
154
  }
811
- // ============================================================================
812
- // Protected hooks (Template Method pattern)
813
- // ============================================================================
814
- /**
815
- * Hook: вызывается после markAsTouched()
816
- *
817
- * Для FieldNode: если updateOn === 'blur', запускаем валидацию
818
- */
819
- onMarkAsTouched() {
820
- this.updateOn === "blur" && this.validate();
155
+ /** Каскад от контейнера: блокировку из схемы не снимает. @internal */
156
+ enableFromParent() {
157
+ this.locked || this.availability.setDisabled(!1);
821
158
  }
822
- /**
823
- * Hook: вызывается после disable()
824
- *
825
- * Для FieldNode: синхронизируем statusMachine и очищаем ошибки
826
- */
827
- onDisable() {
828
- this.statusMachine.disable(), this._errors.value = [];
159
+ setIssues(e) {
160
+ this.issueState.set(e);
829
161
  }
830
- /**
831
- * Hook: вызывается после enable()
832
- *
833
- * Для FieldNode: синхронизируем statusMachine и запускаем валидацию
834
- */
835
- onEnable() {
836
- this.statusMachine.enable(this._errors.value.length > 0), this.validate();
162
+ clearIssues() {
163
+ this.issueState.clear();
837
164
  }
838
- /**
839
- * Обновляет свойства компонента (например, опции для Select)
840
- *
841
- * @example
842
- * ```typescript
843
- * // Обновление опций для Select после загрузки справочников
844
- * form.registrationAddress.city.updateComponentProps({
845
- * options: cities
846
- * });
847
- * ```
848
- */
849
165
  updateComponentProps(e) {
850
- this._componentProps.value = {
851
- ...this._componentProps.value,
852
- ...e
853
- };
854
- }
855
- /**
856
- * Динамически изменяет триггер валидации (updateOn)
857
- * Полезно для адаптивной валидации - например, переключиться на instant feedback после первого submit
858
- *
859
- * @param updateOn - новый триггер валидации: 'change' | 'blur' | 'submit'
860
- *
861
- * @example
862
- * ```typescript
863
- * // Сценарий 1: Instant feedback после submit
864
- * const form = createForm({
865
- * email: {
866
- * value: '',
867
- * component: Input,
868
- * updateOn: 'submit', // Изначально валидация только при submit
869
- * validators: [required, email],
870
- * },
871
- * });
872
- *
873
- * await form.submit(async (values) => {
874
- * // После submit переключаем на instant feedback
875
- * form.email.setUpdateOn('change');
876
- * await api.save(values);
877
- * });
878
- *
879
- * // Теперь валидация происходит при каждом изменении
880
- *
881
- * // Сценарий 2: Прогрессивное улучшение
882
- * form.email.setUpdateOn('blur'); // Сначала только при blur
883
- * // ... пользователь начинает вводить ...
884
- * form.email.setUpdateOn('change'); // Переключаем на change для real-time feedback
885
- * ```
886
- */
887
- setUpdateOn(e) {
888
- this.updateOn = e;
166
+ this.componentState.update(e);
889
167
  }
890
- getUpdateOn() {
891
- return this.updateOn;
168
+ /** Раннер валидации: начало (`true`) и конец (`false`) async-правил поля. @internal */
169
+ setPending(e) {
170
+ this.pendingState.mark(e);
892
171
  }
893
- // ============================================================================
894
- // Методы-помощники для реактивности (Фаза 1)
895
- // ============================================================================
896
- /**
897
- * Подписка на изменения значения поля
898
- * Автоматически отслеживает изменения через @preact/signals effect
899
- *
900
- * @param callback - Функция, вызываемая при изменении значения.
901
- * Для async операций передается AbortSignal во втором параметре.
902
- * @returns Функция отписки для cleanup
903
- *
904
- * @example
905
- * ```typescript
906
- * // Синхронный callback
907
- * const unsubscribe = form.email.watch((value) => {
908
- * console.log('Email changed:', value);
909
- * });
910
- *
911
- * // Асинхронный callback с поддержкой отмены
912
- * const unsubscribe = form.email.watch(async (value, signal) => {
913
- * const result = await fetch('/api/validate', { signal });
914
- * // ...
915
- * });
916
- *
917
- * // Cleanup
918
- * useEffect(() => unsubscribe, []);
919
- * ```
920
- */
921
- watch(e) {
922
- const t = new AbortController(), s = E(() => {
923
- const a = this.value.value;
924
- e(a, t.signal);
925
- }), i = V(w.Watch);
926
- return this.disposers.add(i, () => {
927
- t.abort(), s();
928
- });
929
- }
930
- /**
931
- * Вычисляемое значение из других полей
932
- * Автоматически обновляет текущее поле при изменении источников
933
- *
934
- * @param sources - Массив ReadonlySignal для отслеживания
935
- * @param computeFn - Функция вычисления нового значения
936
- * @returns Функция отписки для cleanup
937
- *
938
- * @example
939
- * ```typescript
940
- * // Автоматический расчет первоначального взноса (20% от стоимости)
941
- * const dispose = form.initialPayment.computeFrom(
942
- * [form.propertyValue.value],
943
- * (propertyValue) => {
944
- * return propertyValue ? propertyValue * 0.2 : null;
945
- * }
946
- * );
947
- *
948
- * // Cleanup
949
- * useEffect(() => dispose, []);
950
- * ```
951
- */
952
- computeFrom(e, t) {
953
- const s = E(() => {
954
- const a = e.map((l) => l.value), n = t(...a);
955
- this.setValue(n, { emitEvent: !1 });
956
- }), i = V(w.ComputeFrom);
957
- return this.disposers.add(i, s);
958
- }
959
- /**
960
- * Очистить все ресурсы и таймеры
961
- * Должен вызываться при unmount компонента
962
- *
963
- * @remarks
964
- * Освобождает все ресурсы:
965
- * - Отписывает все subscriptions через SubscriptionManager
966
- * - Отменяет pending/running валидации через cancelPendingValidation()
967
- *
968
- * Использует try-finally для гарантированного cleanup даже при ошибках.
969
- *
970
- * @example
971
- * ```typescript
972
- * useEffect(() => {
973
- * return () => {
974
- * field.dispose();
975
- * };
976
- * }, []);
977
- * ```
978
- */
979
- dispose() {
980
- try {
981
- this.disposers.dispose();
982
- } finally {
983
- this.cancelPendingValidation();
984
- }
172
+ /** У поля подписок нет. @internal */
173
+ dispose() {
985
174
  }
986
175
  }
987
- function L(r) {
988
- const { getChildren: e, ownErrors: t, disabled: s } = r, i = c(() => t.value.length > 0 ? !1 : e().every((o) => o.disabled.value || o.valid.value)), a = c(() => !i.value), n = c(() => e().some((o) => o.pending.value)), l = c(() => e().some((o) => o.touched.value)), d = c(() => e().some((o) => o.dirty.value)), u = c(() => {
989
- const o = [...t.value];
990
- for (const p of e())
991
- o.push(...p.errors.value);
992
- return o;
993
- }), v = c(() => s?.value ? "disabled" : n.value ? "pending" : a.value ? "invalid" : "valid");
176
+ function Q(t) {
177
+ const { getChildren: e, ownIssues: s, ownPending: i } = t, n = () => t.disabled?.value ?? !1, a = d(() => s.value.some((l) => l.severity !== "warning")), r = d(
178
+ () => !n() && ((i?.value ?? !1) || e().some((l) => l.pending.value))
179
+ ), o = d(
180
+ () => !n() && !r.value && !a.value && e().every((l) => l.disabled.value || l.valid.value)
181
+ ), c = d(
182
+ () => !n() && !r.value && (a.value || e().some((l) => !l.disabled.value && l.invalid.value))
183
+ );
994
184
  return {
995
- valid: i,
996
- invalid: a,
997
- pending: n,
998
- touched: l,
999
- dirty: d,
1000
- errors: u,
1001
- status: v
185
+ valid: o,
186
+ invalid: c,
187
+ pending: r,
188
+ touched: d(() => e().some((l) => l.touched.value)),
189
+ dirty: d(() => e().some((l) => l.dirty.value)),
190
+ issues: d(() => [
191
+ ...s.value,
192
+ ...e().flatMap((l) => l.issues.value)
193
+ ])
1002
194
  };
1003
195
  }
1004
- function G(r) {
1005
- return typeof r == "object" && r !== null && "getProxy" in r && typeof r.getProxy == "function";
1006
- }
1007
- const j = "$";
1008
- function ne(r, e) {
1009
- let t;
1010
- const s = () => {
1011
- if (!t) {
1012
- const i = {};
1013
- e.forEach((a, n) => {
1014
- i[n] = a && G(a) ? a.getProxy() : a;
1015
- }), t = i;
1016
- }
1017
- return t;
1018
- };
1019
- return new Proxy(r, {
1020
- get: (i, a) => {
1021
- if (a === j)
1022
- return s();
1023
- if (a in i)
1024
- return i[a];
1025
- if (typeof a == "string" && e.has(a)) {
1026
- const n = e.get(a);
1027
- return n && G(n) ? n.getProxy() : n;
1028
- }
1029
- },
1030
- set: (i, a, n) => typeof a == "string" && e.has(a) ? !1 : (i[a] = n, !0),
1031
- has: (i, a) => a === j || typeof a == "string" && e.has(a) ? !0 : a in i,
1032
- ownKeys: (i) => {
1033
- const a = Reflect.ownKeys(i), n = Array.from(e.keys());
1034
- return [.../* @__PURE__ */ new Set([...a, ...n])];
1035
- },
1036
- getOwnPropertyDescriptor: (i, a) => typeof a == "string" && e.has(a) ? { enumerable: !0, configurable: !0 } : Reflect.getOwnPropertyDescriptor(i, a)
1037
- });
1038
- }
1039
- class oe {
1040
- /**
1041
- * @param form - Форма для отправки
1042
- */
1043
- constructor(e) {
1044
- this.form = e;
1045
- }
1046
- /** Внутренний сигнал состояния отправки */
1047
- _submitting = g(!1);
1048
- /** Публичный read-only сигнал состояния отправки */
1049
- submitting = c(() => this._submitting.value);
1050
- /**
1051
- * Отправить форму
1052
- *
1053
- * Процесс:
1054
- * 1. Помечает все поля как touched (для отображения ошибок)
1055
- * 2. Валидирует форму
1056
- * 3. Если валидация успешна - вызывает onSubmit
1057
- * 4. Управляет состоянием submitting
1058
- *
1059
- * @param onSubmit - Callback для отправки данных
1060
- * @param options - Опции submit
1061
- * @returns Результат от onSubmit или `null` если валидация не пройдена
1062
- *
1063
- * @remarks
1064
- * Возвращаемый `null` неоднозначен: он сигналит и провал валидации, и легитимный `null`,
1065
- * который вернул `onSubmit` (или void-обработчик). Чтобы различать эти случаи, используйте
1066
- * {@link submitWithResult} — он отдаёт явный флаг `success`.
1067
- *
1068
- * @example
1069
- * ```typescript
1070
- * const result = await submitter.submit(async (values) => {
1071
- * const response = await fetch('/api/form', {
1072
- * method: 'POST',
1073
- * body: JSON.stringify(values)
1074
- * });
1075
- * return response.json();
1076
- * });
1077
- *
1078
- * if (result === null) {
1079
- * console.log('Форма не прошла валидацию');
1080
- * }
1081
- * ```
1082
- */
1083
- async submit(e, t) {
1084
- const { skipValidation: s = !1, skipTouch: i = !1 } = t || {};
1085
- if (i || this.form.markAsTouched(), !s && !await this.form.validate())
1086
- return null;
1087
- this._submitting.value = !0;
1088
- try {
1089
- return await e(this.form.getValue());
1090
- } finally {
1091
- this._submitting.value = !1;
1092
- }
1093
- }
1094
- /**
1095
- * Отправить форму с расширенным результатом
1096
- *
1097
- * В отличие от submit(), возвращает объект с информацией об успехе/ошибке
1098
- *
1099
- * @param onSubmit - Callback для отправки данных
1100
- * @param options - Опции submit
1101
- * @returns Объект SubmitResult с данными и статусом
1102
- *
1103
- * @example
1104
- * ```typescript
1105
- * const result = await submitter.submitWithResult(async (values) => {
1106
- * return await api.saveForm(values);
1107
- * });
1108
- *
1109
- * if (result.success) {
1110
- * console.log('Сохранено:', result.data);
1111
- * } else if (result.error) {
1112
- * console.error('Ошибка:', result.error.message);
1113
- * } else {
1114
- * console.log('Валидация не пройдена');
1115
- * }
1116
- * ```
1117
- */
1118
- async submitWithResult(e, t) {
1119
- const { skipValidation: s = !1, skipTouch: i = !1 } = t || {};
1120
- if (i || this.form.markAsTouched(), !s && !await this.form.validate())
1121
- return { success: !1, data: null };
1122
- this._submitting.value = !0;
1123
- try {
1124
- return { success: !0, data: await e(this.form.getValue()) };
1125
- } catch (a) {
1126
- return {
1127
- success: !1,
1128
- data: null,
1129
- error: a instanceof Error ? a : new Error(String(a))
1130
- };
1131
- } finally {
1132
- this._submitting.value = !1;
1133
- }
1134
- }
1135
- /**
1136
- * Проверить, идет ли отправка формы
1137
- */
1138
- isSubmitting() {
1139
- return this._submitting.value;
1140
- }
1141
- }
1142
- function le(r) {
1143
- const e = [];
1144
- for (const t of r.split(".")) {
1145
- const s = /^([^[\]]+)(?:\[(\d+)\])?$/.exec(t);
1146
- if (!s) return [];
1147
- e.push(s[2] !== void 0 ? { key: s[1], index: Number(s[2]) } : { key: s[1] });
1148
- }
1149
- return e;
1150
- }
1151
- class S extends M {
1152
- // ============================================================================
1153
- // Приватные поля
1154
- // ============================================================================
1155
- id = crypto.randomUUID();
1156
- /**
1157
- * Коллекция полей формы (упрощённый Map вместо FieldRegistry)
1158
- */
1159
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1160
- _fields = /* @__PURE__ */ new Map();
1161
- /**
1162
- * Менеджер подписок для централизованного cleanup
1163
- */
1164
- disposers = new I();
1165
- /**
1166
- * Ссылка на Proxy-инстанс
1167
- */
1168
- _proxyInstance;
1169
- /**
1170
- * Cleanup декларативной схемы поведения (createForm({ behavior })). Вызывается в dispose().
1171
- */
1172
- _behaviorCleanup;
1173
- /**
1174
- * Фабрика для создания узлов формы
1175
- */
1176
- nodeFactory = new ue();
1177
- // ============================================================================
1178
- // Приватные сигналы состояния (inline из StateManager)
1179
- // ============================================================================
1180
- /** Управление отправкой формы */
1181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1182
- formSubmitter;
1183
- /** Флаг disabled состояния */
1184
- _disabled = g(!1);
1185
- /** Form-level validation errors */
1186
- _formErrors = g([]);
1187
- /**
1188
- * M1: связь листовой ноды-ребёнка → её сигнал модели (тот, что помечает `markDerived`).
1189
- * Нужна bulk-сеттерам (`setValue`/`patchValue`), чтобы корректно определять derived-поля:
1190
- * `field.value` — computed-обёртка, отличная от записываемого сигнала модели. @internal
1191
- */
1192
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1193
- _fieldSignals = /* @__PURE__ */ new WeakMap();
1194
- // ============================================================================
1195
- // Публичные computed signals
1196
- // ============================================================================
1197
- value;
1198
- valid;
1199
- invalid;
1200
- touched;
1201
- dirty;
1202
- pending;
1203
- errors;
1204
- status;
1205
- submitting;
1206
- constructor(e) {
1207
- super(), this.formSubmitter = new oe(this);
1208
- const t = e.form, s = t != null && typeof t == "object" && ("valueSignal" in t || "value" in t), a = "form" in e && !s ? e.form : e;
1209
- for (const [l, d] of Object.entries(a)) {
1210
- const u = this.createNode(d);
1211
- this._fields.set(l, u);
1212
- }
1213
- this.value = c(() => {
1214
- const l = {};
1215
- return this._fields.forEach((d, u) => {
1216
- l[u] = d.value.value;
1217
- }), l;
1218
- });
1219
- const n = L({
1220
- getChildren: () => Array.from(this._fields.values()),
1221
- ownErrors: this._formErrors,
1222
- disabled: this._disabled
1223
- });
1224
- this.valid = n.valid, this.invalid = n.invalid, this.pending = n.pending, this.touched = n.touched, this.dirty = n.dirty, this.errors = n.errors, this.status = n.status, this.submitting = this.formSubmitter.submitting;
1225
- }
1226
- // ============================================================================
1227
- // Приватный метод для создания Proxy
1228
- // ============================================================================
1229
- /**
1230
- * Создать Proxy для типобезопасного доступа к полям
1231
- * @see buildFormProxy
1232
- */
1233
- buildProxy() {
1234
- return ne(this, this._fields);
1235
- }
1236
- // ============================================================================
1237
- // Реализация абстрактных методов FormNode
1238
- // ============================================================================
1239
- getValue() {
1240
- const e = {};
1241
- return this._fields.forEach((t, s) => {
1242
- e[s] = t.getValue();
1243
- }), e;
1244
- }
1245
- setValue(e, t) {
1246
- for (const [s, i] of Object.entries(e)) {
1247
- const a = this._fields.get(s);
1248
- if (a) {
1249
- const n = this._fieldSignals.get(a) ?? a.value;
1250
- if (B(n)) continue;
1251
- a.setValue(i, t);
1252
- }
1253
- }
1254
- }
1255
- patchValue(e) {
1256
- X(() => {
1257
- for (const [t, s] of Object.entries(e)) {
1258
- const i = this._fields.get(t);
1259
- if (i && s !== void 0) {
1260
- const a = this._fieldSignals.get(i) ?? i.value;
1261
- if (B(a)) continue;
1262
- i.setValue(s, { emitEvent: !1 });
1263
- }
1264
- }
1265
- });
1266
- }
1267
- /**
1268
- * Сбросить форму к указанным значениям (или к initialValues)
1269
- *
1270
- * @param value - опциональный объект со значениями для сброса
1271
- *
1272
- * @remarks
1273
- * Рекурсивно вызывает reset() для всех полей формы
1274
- *
1275
- * @example
1276
- * ```typescript
1277
- * // Сброс к initialValues
1278
- * form.reset();
1279
- *
1280
- * // Сброс к новым значениям
1281
- * form.reset({ email: 'new@mail.com', password: '' });
1282
- * ```
1283
- */
1284
- reset(e) {
1285
- this._formErrors.value = [], this._fields.forEach((t, s) => {
1286
- const i = e?.[s];
1287
- t.reset(i);
1288
- });
1289
- }
1290
- /**
1291
- * Сбросить форму к исходным значениям (initialValues)
1292
- */
1293
- resetToInitial() {
1294
- this._formErrors.value = [], this._fields.forEach((e) => {
1295
- "resetToInitial" in e && typeof e.resetToInitial == "function" ? e.resetToInitial() : e.reset();
1296
- });
1297
- }
1298
- async validate() {
1299
- return this.clearErrors(), await Promise.all(Array.from(this._fields.values()).map((t) => t.validate())), Array.from(this._fields.values()).every(
1300
- (t) => t.valid.value || t.disabled.value
1301
- );
1302
- }
1303
- /**
1304
- * Установить form-level validation errors
1305
- */
1306
- setErrors(e) {
1307
- this._formErrors.value = e;
1308
- }
1309
- /**
1310
- * Очистить все errors (form-level + field-level)
1311
- */
1312
- clearErrors() {
1313
- this._formErrors.value = [], this._fields.forEach((e) => e.clearErrors());
1314
- }
1315
- /**
1316
- * Получить поле по ключу
1317
- */
1318
- getField(e) {
1319
- return this._fields.get(e);
1320
- }
1321
- /**
1322
- * Получить Map всех полей формы (для совместимости)
1323
- */
1324
- get fields() {
1325
- return this._fields;
1326
- }
1327
- /**
1328
- * Получить Proxy-инстанс для прямого доступа к полям
1329
- *
1330
- * Proxy позволяет обращаться к полям формы напрямую через точечную нотацию:
1331
- * - form.email вместо form.fields.get('email')
1332
- * - form.address.city вместо form.fields.get('address').fields.get('city')
1333
- *
1334
- * Используется в:
1335
- * - BehaviorApplicator для доступа к полям в behavior functions
1336
- * - ValidationApplicator для доступа к форме в tree validators
1337
- *
1338
- * @returns Proxy-инстанс с типобезопасным доступом к полям или сама форма, если proxy не доступен
1339
- *
1340
- * @example
1341
- * ```typescript
1342
- * const form = new GroupNode({
1343
- * email: { value: '', component: Input },
1344
- * name: { value: '', component: Input },
1345
- * });
1346
- *
1347
- * const proxy = form.getProxy();
1348
- * console.log(proxy.email.value.value); // Прямой доступ к полю
1349
- * ```
1350
- */
1351
- getProxy() {
1352
- return this._proxyInstance || (this._proxyInstance = this.buildProxy()), this._proxyInstance;
1353
- }
1354
- /**
1355
- * Получить все поля формы как итератор
1356
- */
1357
- getAllFields() {
1358
- return this._fields.values();
1359
- }
1360
- // ============================================================================
1361
- // Protected hooks (Template Method pattern)
1362
- // ============================================================================
1363
- onMarkAsTouched() {
1364
- this._fields.forEach((e) => e.markAsTouched());
1365
- }
1366
- onMarkAsUntouched() {
1367
- this._fields.forEach((e) => e.markAsUntouched());
1368
- }
1369
- onMarkAsDirty() {
1370
- this._fields.forEach((e) => e.markAsDirty());
1371
- }
1372
- onMarkAsPristine() {
1373
- this._fields.forEach((e) => e.markAsPristine());
1374
- }
1375
- // ============================================================================
1376
- // Дополнительные методы (из FormStore)
1377
- // ============================================================================
1378
- /**
1379
- * Отправить форму
1380
- *
1381
- * @param onSubmit - Callback для отправки данных
1382
- * @param options - Опции submit (skipValidation, skipTouch)
1383
- * @returns Результат от onSubmit или `null` если валидация не пройдена
1384
- *
1385
- * @remarks
1386
- * `null` перегружен: он означает и «валидация не пройдена», и легитимный `null`-результат
1387
- * `onSubmit` (или void-обработчик). Если вызывающей стороне нужно различать эти случаи —
1388
- * используйте {@link submitWithResult}, который возвращает явный флаг `success`.
1389
- */
1390
- async submit(e, t) {
1391
- return this.formSubmitter.submit(e, t);
1392
- }
1393
- /**
1394
- * Отправить форму с расширенным результатом
1395
- *
1396
- * @param onSubmit - Callback для отправки данных
1397
- * @param options - Опции submit
1398
- * @returns Объект SubmitResult с данными, статусом и возможной ошибкой
1399
- */
1400
- async submitWithResult(e, t) {
1401
- return this.formSubmitter.submitWithResult(e, t);
1402
- }
1403
- /**
1404
- * Получить вложенное поле по пути
1405
- *
1406
- * Поддерживаемые форматы путей:
1407
- * - Simple: "email" - получить поле верхнего уровня
1408
- * - Nested: "address.city" - получить вложенное поле
1409
- * - Array index: "items[0]" - получить элемент массива по индексу
1410
- * - Combined: "items[0].name" - получить поле элемента массива
1411
- *
1412
- * @param path - Путь к полю
1413
- * @returns FormNode если найдено, undefined если путь не существует
1414
- *
1415
- * @example
1416
- * ```typescript
1417
- * const form = new GroupNode({
1418
- * email: { value: '', component: Input },
1419
- * address: {
1420
- * city: { value: '', component: Input }
1421
- * },
1422
- * items: [{ name: { value: '', component: Input } }]
1423
- * });
1424
- *
1425
- * form.getFieldByPath('email'); // FieldNode
1426
- * form.getFieldByPath('address.city'); // FieldNode
1427
- * form.getFieldByPath('items[0]'); // GroupNode
1428
- * form.getFieldByPath('items[0].name'); // FieldNode
1429
- * form.getFieldByPath('invalid.path'); // undefined
1430
- * ```
1431
- */
1432
- getFieldByPath(e) {
1433
- if (e.startsWith(".") || e.endsWith("."))
1434
- return;
1435
- const t = le(e);
1436
- if (t.length === 0)
1437
- return;
1438
- let s = this;
1439
- for (const i of t) {
1440
- if (!(s instanceof S) || (s = s.getField(i.key), !s)) return;
1441
- if (i.index !== void 0)
1442
- if ("at" in s && "length" in s && typeof s.at == "function") {
1443
- const a = s.at(i.index);
1444
- if (!a) return;
1445
- s = a;
1446
- } else
1447
- return;
1448
- }
1449
- return s;
1450
- }
1451
- // ============================================================================
1452
- // Private методы для создания узлов
1453
- // ============================================================================
1454
- /**
1455
- * Создать узел на основе конфигурации
1456
- *
1457
- * ✅ РЕФАКТОРИНГ: Полное делегирование NodeFactory
1458
- *
1459
- * NodeFactory теперь обрабатывает:
1460
- * - Массивы [schema, ...items]
1461
- * - FieldConfig
1462
- * - GroupConfig
1463
- * - ArrayConfig
1464
- *
1465
- * @param config Конфигурация узла
1466
- * @returns Созданный узел формы
1467
- * @private
1468
- */
1469
- createNode(e) {
1470
- return this.nodeFactory.createNode(e);
1471
- }
1472
- // ============================================================================
1473
- // Методы-помощники для реактивности (Фаза 1)
1474
- // ============================================================================
1475
- /**
1476
- * Связывает два поля: при изменении source автоматически обновляется target
1477
- */
1478
- linkFields(e, t, s) {
1479
- const i = this._fields.get(e), a = this._fields.get(t);
1480
- if (!i || !a) {
1481
- const d = i ? t : e;
1482
- throw new Error(`GroupNode.linkFields: field "${String(d)}" not found`);
1483
- }
1484
- const n = E(() => {
1485
- const d = i.value.value, u = s ? s(d) : d;
1486
- a.setValue(u, { emitEvent: !1 });
1487
- }), l = V(w.LinkFields);
1488
- return this.disposers.add(l, n);
1489
- }
1490
- /**
1491
- * Подписка на изменения вложенного поля по строковому пути
1492
- * Поддерживает вложенные пути типа "address.city"
1493
- *
1494
- * @param fieldPath - Строковый путь к полю (например, "address.city")
1495
- * @param callback - Функция, вызываемая при изменении поля
1496
- * @returns Функция отписки для cleanup
1497
- *
1498
- * @example
1499
- * ```typescript
1500
- * // Подписка на изменение страны для загрузки городов
1501
- * const dispose = form.watchField(
1502
- * 'registrationAddress.country',
1503
- * async (countryCode) => {
1504
- * if (countryCode) {
1505
- * const cities = await fetchCitiesByCountry(countryCode);
1506
- * form.registrationAddress.city.updateComponentProps({
1507
- * options: cities
1508
- * });
1509
- * }
1510
- * }
1511
- * );
1512
- *
1513
- * // Cleanup
1514
- * useEffect(() => dispose, []);
1515
- * ```
1516
- */
1517
- /** Подписка на top-level поле — value типизирован как `T[K]`. */
1518
- watchField(e, t) {
1519
- const s = this.getFieldByPath(e);
1520
- if (!s)
1521
- throw new Error(`GroupNode.watchField: field "${e}" not found`);
1522
- const i = E(() => {
1523
- t(s.value.value);
1524
- }), a = V(w.WatchField);
1525
- return this.disposers.add(a, i);
1526
- }
1527
- /**
1528
- * Подписка на вложенное поле по строковому пути ("address.city").
1529
- * Путь нельзя выразить в типах узла → value честно `unknown`, потребитель сужает.
1530
- */
1531
- watchFieldByPath(e, t) {
1532
- const s = this.getFieldByPath(e);
1533
- if (!s)
1534
- throw new Error(`GroupNode.watchFieldByPath: field "${e}" not found`);
1535
- const i = E(() => {
1536
- t(s.value.value);
1537
- }), a = V(w.WatchField);
1538
- return this.disposers.add(a, i);
1539
- }
1540
- /**
1541
- * Hook: вызывается после disable()
1542
- */
1543
- onDisable() {
1544
- this._disabled.value = !0, this._fields.forEach((e) => e.disable());
1545
- }
1546
- /**
1547
- * Hook: вызывается после enable()
1548
- */
1549
- onEnable() {
1550
- this._disabled.value = !1, this._fields.forEach((e) => e.enable());
1551
- }
1552
- /**
1553
- * Прикрепить cleanup декларативной схемы поведения (вызывается createForm).
1554
- * @internal
1555
- */
1556
- attachBehaviorCleanup(e) {
1557
- this._behaviorCleanup = e;
1558
- }
1559
- /**
1560
- * Связать листовую ноду-ребёнка с её сигналом модели (M1). Вызывается `createForm` для каждого
1561
- * листового поля на его владеющей группе — используется bulk-сеттерами для derived-guard (F9).
1562
- * @internal
1563
- */
1564
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1565
- registerFieldSignal(e, t) {
1566
- this._fieldSignals.set(e, t);
1567
- }
1568
- /**
1569
- * Очистить все ресурсы узла
1570
- */
1571
- dispose() {
1572
- this._behaviorCleanup?.(), this._behaviorCleanup = void 0, this.disposers.dispose(), this._fields.forEach((e) => {
1573
- "dispose" in e && typeof e.dispose == "function" && e.dispose();
1574
- });
1575
- }
196
+ const Me = [
197
+ "value",
198
+ "valid",
199
+ "invalid",
200
+ "pending",
201
+ "disabled",
202
+ "touched",
203
+ "dirty",
204
+ "issues",
205
+ "ownIssues",
206
+ "getValue",
207
+ "setValue",
208
+ "reset",
209
+ "markAsTouched",
210
+ "markAsUntouched",
211
+ "enable",
212
+ "disable",
213
+ "setIssues",
214
+ "clearIssues"
215
+ ], L = new Set(Me), N = "$";
216
+ function Te(t) {
217
+ return Se(t) === "group" ? t.proxy : t;
1576
218
  }
1577
- class U extends M {
1578
- // ============================================================================
1579
- // Приватные поля
1580
- // ============================================================================
1581
- items;
1582
- itemSchema;
1583
- initialItems;
1584
- /**
1585
- * Менеджер подписок для централизованного cleanup
1586
- * Использует SubscriptionManager вместо массива для управления подписками
1587
- */
1588
- disposers = new I();
1589
- /** Array-level validation errors (e.g., "минимум 1 элемент") */
1590
- _arrayErrors = g([]);
1591
- // ============================================================================
1592
- // Приватные поля для сохранения схем
1593
- // ============================================================================
1594
- validationSchemaFn;
1595
- behaviorSchemaFn;
1596
- // ============================================================================
1597
- // Публичные computed signals
1598
- // ============================================================================
1599
- value;
1600
- valid;
1601
- invalid;
1602
- touched;
1603
- dirty;
1604
- pending;
1605
- errors;
1606
- status;
1607
- length;
1608
- // ============================================================================
1609
- // Конструктор
1610
- // ============================================================================
1611
- constructor(e, t = []) {
1612
- super(), this.itemSchema = e, this.initialItems = t, this.items = g([]);
1613
- for (const i of t)
1614
- this.push(i);
1615
- this.length = c(() => this.items.value.length), this.value = c(() => this.items.value.map((i) => i.value.value));
1616
- const s = L({
1617
- getChildren: () => this.items.value,
1618
- ownErrors: this._arrayErrors
1619
- });
1620
- this.valid = s.valid, this.invalid = s.invalid, this.pending = s.pending, this.touched = s.touched, this.dirty = s.dirty, this.errors = s.errors, this.status = s.status;
1621
- }
1622
- // ============================================================================
1623
- // CRUD операции
1624
- // ============================================================================
1625
- /**
1626
- * Добавить элемент в конец массива
1627
- * @param initialValue - Начальные значения для нового элемента
1628
- */
1629
- push(e) {
1630
- const t = this.createItem(e);
1631
- this.items.value = [...this.items.value, t];
1632
- }
1633
- /**
1634
- * Удалить элемент по индексу
1635
- * @param index - Индекс элемента для удаления
1636
- *
1637
- * @remarks
1638
- * Вызывает dispose() на удаляемом элементе для очистки подписок
1639
- */
1640
- removeAt(e) {
1641
- if (e < 0 || e >= this.items.value.length) {
1642
- F.handle(
1643
- new Error(
1644
- `ArrayNode.removeAt: index ${e} out of bounds (length: ${this.items.value.length})`
1645
- ),
1646
- "ArrayNode.removeAt",
1647
- A.LOG
1648
- );
1649
- return;
1650
- }
1651
- const t = this.items.value[e];
1652
- this.items.value = this.items.value.filter((s, i) => i !== e), t && "dispose" in t && typeof t.dispose == "function" && t.dispose();
1653
- }
1654
- /**
1655
- * Вставить элемент в массив
1656
- * @param index - Индекс для вставки
1657
- * @param initialValue - Начальные значения для нового элемента
1658
- */
1659
- insert(e, t) {
1660
- if (e < 0 || e > this.items.value.length) {
1661
- F.handle(
1662
- new Error(
1663
- `ArrayNode.insert: index ${e} out of bounds (length: ${this.items.value.length})`
1664
- ),
1665
- "ArrayNode.insert",
1666
- A.LOG
1667
- );
1668
- return;
1669
- }
1670
- const s = this.createItem(t), i = [...this.items.value];
1671
- i.splice(e, 0, s), this.items.value = i;
1672
- }
1673
- /**
1674
- * Переместить элемент массива с позиции `from` на позицию `to`
1675
- *
1676
- * @param from - Индекс перемещаемого элемента
1677
- * @param to - Целевой индекс
1678
- *
1679
- * @remarks
1680
- * Реордер не уничтожает элемент (dispose НЕ вызывается) — тот же инстанс под-формы
1681
- * переезжает на новую позицию, состояние/валидация сохраняются. Длина не меняется.
1682
- *
1683
- * @example
1684
- * ```typescript
1685
- * // Поднять элемент #2 на одну позицию вверх
1686
- * form.items.move(2, 1);
1687
- * ```
1688
- */
1689
- move(e, t) {
1690
- const s = this.items.value.length;
1691
- if (e < 0 || e >= s || t < 0 || t >= s) {
1692
- F.handle(
1693
- new Error(`ArrayNode.move: index out of bounds (from=${e}, to=${t}, length=${s})`),
1694
- "ArrayNode.move",
1695
- A.LOG
1696
- );
1697
- return;
219
+ function De(t, e) {
220
+ let s;
221
+ const i = () => {
222
+ if (!s) {
223
+ const r = {};
224
+ e.forEach((o, c) => {
225
+ r[c] = Te(o);
226
+ }), s = Object.freeze(r);
1698
227
  }
1699
- if (e === t) return;
1700
- const i = [...this.items.value], [a] = i.splice(e, 1);
1701
- i.splice(t, 0, a), this.items.value = i;
1702
- }
1703
- /**
1704
- * Поменять местами два элемента массива
1705
- *
1706
- * @param a - Индекс первого элемента
1707
- * @param b - Индекс второго элемента
1708
- *
1709
- * @remarks
1710
- * Как и {@link move}, не уничтожает элементы — меняет только их порядок.
1711
- *
1712
- * @example
1713
- * ```typescript
1714
- * form.items.swap(0, 2);
1715
- * ```
1716
- */
1717
- swap(e, t) {
1718
- const s = this.items.value.length;
1719
- if (e < 0 || e >= s || t < 0 || t >= s) {
1720
- F.handle(
1721
- new Error(`ArrayNode.swap: index out of bounds (a=${e}, b=${t}, length=${s})`),
1722
- "ArrayNode.swap",
1723
- A.LOG
1724
- );
1725
- return;
1726
- }
1727
- if (e === t) return;
1728
- const i = [...this.items.value];
1729
- [i[e], i[t]] = [i[t], i[e]], this.items.value = i;
1730
- }
1731
- /**
1732
- * Удалить все элементы массива
1733
- *
1734
- * @remarks
1735
- * Вызывает dispose() на всех элементах для очистки подписок
1736
- */
1737
- clear() {
1738
- const e = [...this.items.value];
1739
- this.items.value = [], e.forEach((t) => {
1740
- "dispose" in t && typeof t.dispose == "function" && t.dispose();
1741
- });
1742
- }
1743
- /**
1744
- * Получить элемент по индексу
1745
- * @param index - Индекс элемента
1746
- * @returns Типизированный GroupNode proxy или undefined если индекс вне границ
1747
- */
1748
- at(e) {
1749
- const t = this.items.value[e];
1750
- if (t)
1751
- return t.getProxy();
1752
- }
1753
- // ============================================================================
1754
- // Реализация абстрактных методов
1755
- // ============================================================================
1756
- getValue() {
1757
- return this.items.value.map((e) => e.getValue());
1758
- }
1759
- setValue(e, t) {
1760
- this.clear(), e.forEach((s) => this.push(s)), t?.emitEvent !== !1 && this.validate().catch((s) => {
1761
- F.handle(s, "ArrayNode.setValue", A.LOG);
1762
- });
1763
- }
1764
- patchValue(e) {
1765
- e.forEach((t, s) => {
1766
- this.items.value[s] && t !== void 0 && this.items.value[s].patchValue(t);
1767
- });
1768
- }
1769
- /**
1770
- * Сбросить массив к указанным значениям (или очистить)
1771
- *
1772
- * @param values - опциональный массив значений для сброса
1773
- *
1774
- * @remarks
1775
- * Очищает текущий массив и заполняет новыми элементами
1776
- *
1777
- * @example
1778
- * ```typescript
1779
- * // Очистить массив
1780
- * arrayNode.reset();
1781
- *
1782
- * // Сбросить к новым значениям
1783
- * arrayNode.reset([{ name: 'Item 1' }, { name: 'Item 2' }]);
1784
- * ```
1785
- */
1786
- reset(e) {
1787
- this._arrayErrors.value = [], this.clear(), e && e.forEach((t) => this.push(t));
1788
- }
1789
- /**
1790
- * Сбросить массив к исходным значениям (initialItems)
1791
- *
1792
- * @remarks
1793
- * Восстанавливает массив в состояние, которое было при создании ArrayNode.
1794
- * Более явный способ сброса к начальным значениям по сравнению с reset()
1795
- *
1796
- * Полезно когда:
1797
- * - Пользователь нажал "Cancel" - вернуть массив к исходным элементам
1798
- * - Массив был изменен через reset(newValues), но нужно вернуться к началу
1799
- * - Явное намерение показать "отмена всех изменений"
1800
- *
1801
- * @example
1802
- * ```typescript
1803
- * const arrayNode = new ArrayNode(
1804
- * { name: { value: '', component: Input } },
1805
- * [{ name: 'Initial 1' }, { name: 'Initial 2' }]
1806
- * );
1807
- *
1808
- * arrayNode.push({ name: 'New Item' });
1809
- * arrayNode.reset([{ name: 'Temp' }]);
1810
- * console.log(arrayNode.length.value); // 1
1811
- *
1812
- * arrayNode.resetToInitial();
1813
- * console.log(arrayNode.length.value); // 2
1814
- * console.log(arrayNode.at(0)?.name.value.value); // 'Initial 1'
1815
- * ```
1816
- */
1817
- resetToInitial() {
1818
- this._arrayErrors.value = [], this.clear(), this.initialItems.forEach((e) => this.push(e));
1819
- }
1820
- async validate() {
1821
- return (await Promise.all(this.items.value.map((t) => t.validate()))).every(Boolean) && this._arrayErrors.value.length === 0;
1822
- }
1823
- /**
1824
- * Установить array-level validation errors
1825
- *
1826
- * @param errors - Массив ошибок валидации уровня массива
1827
- *
1828
- * @example
1829
- * ```typescript
1830
- * arrayNode.setErrors([{
1831
- * code: 'minItems',
1832
- * message: 'Минимум 1 элемент обязателен',
1833
- * }]);
1834
- * ```
1835
- */
1836
- setErrors(e) {
1837
- this._arrayErrors.value = e;
1838
- }
1839
- /**
1840
- * Очистить все errors (array-level + item-level)
1841
- *
1842
- * @example
1843
- * ```typescript
1844
- * arrayNode.clearErrors();
1845
- * console.log(arrayNode.errors.value); // []
1846
- * ```
1847
- */
1848
- clearErrors() {
1849
- this._arrayErrors.value = [], this.items.value.forEach((e) => e.clearErrors());
1850
- }
1851
- // ============================================================================
1852
- // Protected hooks (Template Method pattern)
1853
- // ============================================================================
1854
- /**
1855
- * Hook: вызывается после markAsTouched()
1856
- *
1857
- * Для ArrayNode: рекурсивно помечаем все элементы массива как touched
1858
- */
1859
- onMarkAsTouched() {
1860
- this.items.value.forEach((e) => e.markAsTouched());
1861
- }
1862
- /**
1863
- * Hook: вызывается после markAsUntouched()
1864
- *
1865
- * Для ArrayNode: рекурсивно помечаем все элементы массива как untouched
1866
- */
1867
- onMarkAsUntouched() {
1868
- this.items.value.forEach((e) => e.markAsUntouched());
1869
- }
1870
- /**
1871
- * Hook: вызывается после markAsDirty()
1872
- *
1873
- * Для ArrayNode: рекурсивно помечаем все элементы массива как dirty
1874
- */
1875
- onMarkAsDirty() {
1876
- this.items.value.forEach((e) => e.markAsDirty());
1877
- }
1878
- /**
1879
- * Hook: вызывается после markAsPristine()
1880
- *
1881
- * Для ArrayNode: рекурсивно помечаем все элементы массива как pristine
1882
- */
1883
- onMarkAsPristine() {
1884
- this.items.value.forEach((e) => e.markAsPristine());
1885
- }
1886
- // ============================================================================
1887
- // Итерация
1888
- // ============================================================================
1889
- /**
1890
- * Итерировать по элементам массива
1891
- * @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
228
+ return s;
229
+ }, n = /* @__PURE__ */ new Map(), a = (r) => {
230
+ const o = Reflect.get(t, r, t);
231
+ if (typeof o != "function") return o;
232
+ let c = n.get(r);
233
+ return c || (c = o.bind(t), n.set(r, c)), c;
234
+ };
235
+ return new Proxy(t, {
236
+ get: (r, o) => o === ke ? t : o === N ? i() : L.has(o) ? a(o) : typeof o == "string" && e.has(o) ? i()[o] : a(o),
237
+ set: (r, o, c) => o === N || L.has(o) || typeof o == "string" && e.has(o) ? !1 : Reflect.set(t, o, c, t),
238
+ has: (r, o) => o === N || typeof o == "string" && e.has(o) || o in t,
239
+ ownKeys: () => [.../* @__PURE__ */ new Set([...Reflect.ownKeys(t), ...e.keys()])],
240
+ getOwnPropertyDescriptor: (r, o) => typeof o == "string" && e.has(o) ? { enumerable: !0, configurable: !0, value: i()[o] } : Reflect.getOwnPropertyDescriptor(t, o)
241
+ });
242
+ }
243
+ class _e {
244
+ [j] = "group";
245
+ value;
246
+ valid;
247
+ invalid;
248
+ pending;
249
+ disabled;
250
+ touched;
251
+ dirty;
252
+ issues;
253
+ ownIssues;
254
+ /** Сырые ноды детей по именам полей данных. */
255
+ children;
256
+ /** Замечания самой группы (ответ сервера на форму целиком). */
257
+ own = R();
258
+ availability = K();
259
+ /** Ожидание async-правила на ручке самой группы. */
260
+ ownPending = W();
261
+ /**
262
+ * Лист → его записываемый сигнал модели: по нему `setValue` узнаёт цели `compute` (их
263
+ * помечает `markDerived`), а не по computed-обёртке `value` ноды.
264
+ */
265
+ leafSignals = /* @__PURE__ */ new WeakMap();
266
+ behaviorCleanup;
267
+ face;
268
+ /**
269
+ * @param children - Готовые ноды детей по именам полей данных. Вид ребёнка задан нодой: по
270
+ * именам и значениям группа ничего не угадывает.
1892
271
  */
1893
- forEach(e) {
1894
- this.items.value.forEach((t, s) => {
1895
- const i = t.getProxy();
1896
- e(i, s);
272
+ constructor(e) {
273
+ this.children = new Map(e);
274
+ const s = [...this.children.values()];
275
+ this.value = d(() => {
276
+ const a = {};
277
+ return this.children.forEach((r, o) => {
278
+ a[o] = r.value.value;
279
+ }), a;
1897
280
  });
1898
- }
1899
- /**
1900
- * Маппинг элементов массива
1901
- * @param callback - Функция преобразования с типизированным GroupNode proxy
1902
- * @returns Новый массив результатов
1903
- */
1904
- map(e) {
1905
- return this.items.value.map((t, s) => {
1906
- const i = t.getProxy();
1907
- return e(i, s);
281
+ const { disabled: i } = this.availability, n = Q({
282
+ getChildren: () => s,
283
+ ownIssues: this.own.list,
284
+ ownPending: d(() => !i.value && this.ownPending.active.value),
285
+ disabled: i
1908
286
  });
287
+ this.valid = n.valid, this.invalid = n.invalid, this.pending = n.pending, this.touched = n.touched, this.dirty = n.dirty, this.issues = n.issues, this.ownIssues = this.own.list, this.disabled = this.availability.disabled;
1909
288
  }
1910
- // ============================================================================
1911
- // Private методы
1912
- // ============================================================================
1913
- /**
1914
- * Создать новый элемент массива на основе схемы
1915
- * @param initialValue - Начальные значения
1916
- */
1917
- createItem(e) {
1918
- if (this.isGroupSchema(this.itemSchema)) {
1919
- const t = new S(this.itemSchema);
1920
- return e && t.patchValue(e), this.validationSchemaFn && "applyValidationSchema" in t && t.applyValidationSchema(this.validationSchemaFn), this.behaviorSchemaFn && "applyBehaviorSchema" in t && t.applyBehaviorSchema(this.behaviorSchemaFn), t;
1921
- }
1922
- throw new Error(
1923
- "ArrayNode поддерживает только GroupNode элементы. Для массива примитивов используйте обычное поле с типом массива."
1924
- );
289
+ /** Лицо группы: прокси с нодами полей. Строится один раз. */
290
+ get proxy() {
291
+ return this.face ??= De(this, this.children), this.face;
1925
292
  }
1926
- /**
1927
- * Проверить, является ли схема групповой (объект полей)
1928
- * @param schema - Схема для проверки
1929
- */
1930
- isGroupSchema(e) {
1931
- return typeof e == "object" && e !== null && !("component" in e) && !Array.isArray(e);
293
+ getValue() {
294
+ const e = {};
295
+ return this.children.forEach((s, i) => {
296
+ e[i] = s.getValue();
297
+ }), e;
1932
298
  }
1933
- // ============================================================================
1934
- // Validation Schema
1935
- // ============================================================================
1936
- /**
1937
- * Применить validation schema ко всем элементам массива
1938
- *
1939
- * Validation schema будет применена к:
1940
- * - Всем существующим элементам
1941
- * - Всем новым элементам, добавляемым через push/insert
1942
- *
1943
- * @param schemaFn - Функция валидации для элемента массива
1944
- *
1945
- * @example
1946
- * ```typescript
1947
- * import { propertyValidation } from './validation/property-validation';
1948
- *
1949
- * form.properties.applyValidationSchema(propertyValidation);
1950
- * ```
1951
- */
1952
- applyValidationSchema(e) {
1953
- this.validationSchemaFn = e, this.items.value.forEach((t) => {
1954
- "applyValidationSchema" in t && typeof t.applyValidationSchema == "function" && t.applyValidationSchema(e);
1955
- });
299
+ /** Записать значения полей; цели `compute` пропускаются — ими владеет поведение. */
300
+ setValue(e) {
301
+ if (!(e === null || typeof e != "object"))
302
+ for (const [s, i] of Object.entries(e)) {
303
+ const n = this.children.get(s);
304
+ if (!n) continue;
305
+ const a = this.leafSignals.get(n) ?? n.value;
306
+ T(a) || n.setValue(i);
307
+ }
1956
308
  }
1957
309
  /**
1958
- * Применить behavior schema ко всем элементам ArrayNode
1959
- *
1960
- * Автоматически применяется к новым элементам при push/insert.
1961
- *
1962
- * @param schemaFn - Behavior schema функция
1963
- *
1964
- * @example
1965
- * ```typescript
1966
- * import { addressBehavior } from './behaviors/address-behavior';
1967
- *
1968
- * form.addresses.applyBehaviorSchema(addressBehavior);
1969
- * ```
310
+ * Сбросить группу: замечания группы и всех полей, `touched`, `dirty` и значения. Без аргумента —
311
+ * к точке отсчёта модели, той же, к которой возвращает `model.reset()` (значения создания модели
312
+ * либо последнего `captureInitial()`), — и форма после сброса не изменена.
313
+ * Цели `compute` значения не получают, как и в `setValue`: ими владеет поведение.
1970
314
  */
1971
- applyBehaviorSchema(e) {
1972
- this.behaviorSchemaFn = e, this.items.value.forEach((t) => {
1973
- "applyBehaviorSchema" in t && typeof t.applyBehaviorSchema == "function" && t.applyBehaviorSchema(e);
315
+ reset(e) {
316
+ this.own.clear(), this.children.forEach((s, i) => {
317
+ const n = this.leafSignals.get(s) ?? s.value, a = T(n) ? void 0 : e?.[i];
318
+ s.reset(a);
1974
319
  });
1975
320
  }
1976
- // ============================================================================
1977
- // Методы-помощники для реактивности (Фаза 1)
1978
- // ============================================================================
1979
- /**
1980
- * Подписка на изменения конкретного поля во всех элементах массива
1981
- * Срабатывает при изменении значения поля в любом элементе
1982
- *
1983
- * @param fieldKey - Ключ поля для отслеживания
1984
- * @param callback - Функция, вызываемая при изменении, получает массив всех значений и индекс измененного элемента
1985
- * @returns Функция отписки для cleanup
1986
- *
1987
- * @example
1988
- * ```typescript
1989
- * // Автоматический пересчет общей стоимости при изменении цен
1990
- * const dispose = form.existingLoans.watchItems(
1991
- * 'remainingAmount',
1992
- * (amounts) => {
1993
- * const totalDebt = amounts.reduce((sum, amount) => sum + (amount || 0), 0);
1994
- * form.totalDebt.setValue(totalDebt);
1995
- * }
1996
- * );
1997
- *
1998
- * // При изменении любого remainingAmount → пересчитается totalDebt
1999
- * form.existingLoans.at(0)?.remainingAmount.setValue(500000);
2000
- *
2001
- * // Cleanup
2002
- * useEffect(() => dispose, []);
2003
- * ```
2004
- */
2005
- watchItems(e, t) {
2006
- const s = E(() => {
2007
- const a = this.items.value.map((n) => {
2008
- if (n instanceof S)
2009
- return n.getFieldByPath(e)?.value.value;
2010
- });
2011
- t(a);
2012
- }), i = V(w.WatchItems);
2013
- return this.disposers.add(i, s);
321
+ /** Сброс всех полей группы к значениям создания, у целей `compute` тоже. @internal */
322
+ resetToCreation() {
323
+ this.own.clear(), this.children.forEach((e) => k(e).resetToCreation());
2014
324
  }
2015
- /**
2016
- * Подписка на изменение длины массива
2017
- * Срабатывает при добавлении/удалении элементов
2018
- *
2019
- * @param callback - Функция, вызываемая при изменении длины, получает новую длину
2020
- * @returns Функция отписки для cleanup
2021
- *
2022
- * @example
2023
- * ```typescript
2024
- * // Обновление счетчика элементов в UI
2025
- * const dispose = form.properties.watchLength((length) => {
2026
- * console.log(`Количество объектов недвижимости: ${length}`);
2027
- * form.propertyCount.setValue(length);
2028
- * });
2029
- *
2030
- * form.properties.push({ title: 'Квартира', value: 5000000 });
2031
- * // Выведет: "Количество объектов недвижимости: 1"
2032
- *
2033
- * // Cleanup
2034
- * useEffect(() => dispose, []);
2035
- * ```
2036
- */
2037
- watchLength(e) {
2038
- const t = E(() => {
2039
- const i = this.length.value;
2040
- e(i);
2041
- }), s = V(w.WatchLength);
2042
- return this.disposers.add(s, t);
325
+ markAsTouched() {
326
+ this.children.forEach((e) => e.markAsTouched());
2043
327
  }
2044
- /**
2045
- * Очистить все ресурсы узла
2046
- * Рекурсивно очищает все subscriptions и элементы массива
2047
- *
2048
- * @example
2049
- * ```typescript
2050
- * useEffect(() => {
2051
- * return () => {
2052
- * arrayNode.dispose();
2053
- * };
2054
- * }, []);
2055
- * ```
2056
- */
2057
- dispose() {
2058
- this.disposers.dispose(), this.items.value.forEach((e) => {
2059
- "dispose" in e && typeof e.dispose == "function" && e.dispose();
2060
- });
328
+ markAsUntouched() {
329
+ this.children.forEach((e) => e.markAsUntouched());
2061
330
  }
2062
- /**
2063
- * Hook: вызывается после disable()
2064
- *
2065
- * Для ArrayNode: рекурсивно отключаем все элементы массива
2066
- *
2067
- * @example
2068
- * ```typescript
2069
- * // Отключить весь массив полей
2070
- * form.items.disable();
2071
- *
2072
- * // Все элементы становятся disabled
2073
- * form.items.forEach(item => {
2074
- * console.log(item.status.value); // 'disabled'
2075
- * });
2076
- * ```
2077
- */
2078
- onDisable() {
2079
- this.items.value.forEach((e) => {
2080
- e.disable();
2081
- });
331
+ disable() {
332
+ this.availability.setDisabled(!0), this.own.clear(), this.children.forEach((e) => e.disable());
2082
333
  }
2083
- /**
2084
- * Hook: вызывается после enable()
2085
- *
2086
- * Для ArrayNode: рекурсивно включаем все элементы массива
2087
- *
2088
- * @example
2089
- * ```typescript
2090
- * // Включить весь массив полей
2091
- * form.items.enable();
2092
- *
2093
- * // Все элементы становятся enabled
2094
- * form.items.forEach(item => {
2095
- * console.log(item.status.value); // 'valid' или 'invalid'
2096
- * });
2097
- * ```
2098
- */
2099
- onEnable() {
2100
- this.items.value.forEach((e) => {
2101
- e.enable();
2102
- });
334
+ /** Включить группу и детей; поля с блокировкой из схемы (`disabled: true`) остаются выключенными. */
335
+ enable() {
336
+ this.availability.setDisabled(!1), this.children.forEach((e) => k(e).enableFromParent());
2103
337
  }
2104
- }
2105
- class ue {
2106
- /**
2107
- * Создает узел формы на основе конфигурации
2108
- *
2109
- * ✅ ОБНОВЛЕНО: Теперь поддерживает массивы напрямую
2110
- *
2111
- * Автоматически определяет тип узла:
2112
- * - FieldNode: имеет value и component
2113
- * - ArrayNode: массив [schema, ...items] или { schema, initialItems }
2114
- * - GroupNode: объект без value, component, schema
2115
- *
2116
- * @param config Конфигурация узла
2117
- * @returns Экземпляр FieldNode, GroupNode или ArrayNode
2118
- * @throws Error если конфиг не соответствует ни одному типу
2119
- *
2120
- * @example
2121
- * ```typescript
2122
- * const factory = new NodeFactory();
2123
- *
2124
- * // FieldNode
2125
- * const field = factory.createNode({
2126
- * value: 'test@mail.com',
2127
- * component: Input,
2128
- * validators: [required, email]
2129
- * });
2130
- *
2131
- * // GroupNode
2132
- * const group = factory.createNode({
2133
- * email: { value: '', component: Input },
2134
- * password: { value: '', component: Input }
2135
- * });
2136
- *
2137
- * // ArrayNode (объект)
2138
- * const array = factory.createNode({
2139
- * schema: { title: { value: '', component: Input } },
2140
- * initialItems: [{ title: 'Item 1' }]
2141
- * });
2142
- *
2143
- * // ArrayNode (массив) - новый формат
2144
- * const array2 = factory.createNode([
2145
- * { title: { value: '', component: Input } }, // schema
2146
- * { title: 'Item 1' }, // initial item 1
2147
- * { title: 'Item 2' } // initial item 2
2148
- * ]);
2149
- * ```
2150
- */
2151
- createNode(e) {
2152
- if (Array.isArray(e) && e.length >= 1)
2153
- return this.createArrayNodeFromArray(e);
2154
- if (this.isFieldConfig(e))
2155
- return new ae(e);
2156
- if (this.isArrayConfig(e))
2157
- return new U(
2158
- e.schema,
2159
- e.initialItems
2160
- );
2161
- if (this.isGroupConfig(e))
2162
- return new S(e);
2163
- throw new Error(
2164
- `NodeFactory: Unknown node config. Expected FieldConfig, GroupConfig, or ArrayConfig, but got: ${JSON.stringify(
2165
- e
2166
- )}`
2167
- );
338
+ /** Каскад от контейнера — то же, что `enable()`: своей блокировки из схемы у группы нет. @internal */
339
+ enableFromParent() {
340
+ this.enable();
2168
341
  }
2169
- /**
2170
- * Создать ArrayNode из массива [schema, ...initialItems]
2171
- *
2172
- * ✅ НОВОЕ: Извлечено из GroupNode для централизации логики
2173
- *
2174
- * Формат: [itemSchema, ...initialItems]
2175
- * - Первый элемент - схема элемента массива
2176
- * - Остальные элементы - начальные значения
2177
- *
2178
- * @param config Массив с схемой и начальными элементами
2179
- * @returns ArrayNode
2180
- *
2181
- * @example
2182
- * ```typescript
2183
- * const factory = new NodeFactory();
2184
- *
2185
- * // Массив с начальными элементами
2186
- * const array = factory.createArrayNodeFromArray([
2187
- * { title: { value: '', component: Input } }, // schema
2188
- * { title: 'Item 1' }, // initial value
2189
- * { title: 'Item 2' } // initial value
2190
- * ]);
2191
- * ```
2192
- * @private
2193
- */
2194
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2195
- createArrayNodeFromArray(e) {
2196
- const [t, ...s] = e, i = [];
2197
- this.isGroupConfig(t) && i.push(this.extractValues(t));
2198
- for (const a of s)
2199
- this.isGroupConfig(a) ? i.push(this.extractValues(a)) : i.push(a);
2200
- return new U(t, i);
342
+ setIssues(e) {
343
+ this.own.set(e);
2201
344
  }
2202
- /**
2203
- * Извлечь значения из схемы (рекурсивно)
2204
- *
2205
- * ✅ НОВОЕ: Извлечено из GroupNode для централизации логики
2206
- *
2207
- * Преобразует схему формы в объект со значениями:
2208
- * - `{ name: { value: 'John', component: Input } } → { name: 'John' }`
2209
- * - Поддерживает вложенные группы
2210
- * - Поддерживает массивы
2211
- *
2212
- * @param schema Схема формы
2213
- * @returns Объект со значениями полей
2214
- *
2215
- * @example
2216
- * ```typescript
2217
- * const factory = new NodeFactory();
2218
- *
2219
- * const schema = {
2220
- * name: { value: 'John', component: Input },
2221
- * age: { value: 30, component: Input },
2222
- * address: {
2223
- * city: { value: 'Moscow', component: Input }
2224
- * }
2225
- * };
2226
- *
2227
- * factory.extractValues(schema);
2228
- * // { name: 'John', age: 30, address: { city: 'Moscow' } }
2229
- * ```
2230
- */
2231
- extractValues(e) {
2232
- if (this.isFieldConfig(e))
2233
- return e.value;
2234
- if (Array.isArray(e))
2235
- return e.map((t) => this.extractValues(t));
2236
- if (this.isGroupConfig(e)) {
2237
- const t = {};
2238
- for (const [s, i] of Object.entries(e))
2239
- t[s] = this.extractValues(i);
2240
- return t;
2241
- }
2242
- return e;
345
+ clearIssues() {
346
+ this.own.clear(), this.children.forEach((e) => e.clearIssues());
2243
347
  }
2244
- /**
2245
- * Проверяет, является ли конфиг конфигурацией поля (FieldConfig)
2246
- *
2247
- * FieldConfig имеет обязательные свойства:
2248
- * - value: начальное значение поля
2249
- * - component: React-компонент для отображения
2250
- *
2251
- * @param config Проверяемая конфигурация
2252
- * @returns true если config является FieldConfig
2253
- *
2254
- * @example
2255
- * ```typescript
2256
- * const factory = new NodeFactory();
2257
- *
2258
- * factory.isFieldConfig({ value: '', component: Input }); // true
2259
- * factory.isFieldConfig({ email: { value: '' } }); // false
2260
- * factory.isFieldConfig(null); // false
2261
- * ```
2262
- */
2263
- isFieldConfig(e) {
2264
- return e == null || typeof e != "object" ? !1 : "valueSignal" in e ? !0 : "value" in e ? "component" in e || "componentProps" in e || "validators" in e || "asyncValidators" in e || "updateOn" in e || "debounce" in e : !1;
348
+ /** Своё ожидание async-правила на ручке группы; ожидание детей агрегат добавит сам. @internal */
349
+ setPending(e) {
350
+ this.ownPending.mark(e);
2265
351
  }
2266
- /**
2267
- * Проверяет, является ли конфиг конфигурацией массива (ArrayConfig)
2268
- *
2269
- * ArrayConfig имеет обязательное свойство:
2270
- * - schema: схема для элементов массива
2271
- *
2272
- * И НЕ имеет:
2273
- * - value (отличие от FieldConfig)
2274
- *
2275
- * @param config Проверяемая конфигурация
2276
- * @returns true если config является ArrayConfig
2277
- *
2278
- * @example
2279
- * ```typescript
2280
- * const factory = new NodeFactory();
2281
- *
2282
- * factory.isArrayConfig({ schema: {}, initialItems: [] }); // true
2283
- * factory.isArrayConfig({ value: '', component: Input }); // false
2284
- * factory.isArrayConfig({ email: { value: '' } }); // false
2285
- * ```
2286
- */
2287
- isArrayConfig(e) {
2288
- return e != null && typeof e == "object" && "schema" in e && !("value" in e);
352
+ /** Прикрепить cleanup поведения формы (`createForm({ behavior })`). @internal */
353
+ attachBehaviorCleanup(e) {
354
+ this.behaviorCleanup = e;
2289
355
  }
2290
- /**
2291
- * Проверяет, является ли конфиг конфигурацией группы (GroupConfig)
2292
- *
2293
- * GroupConfig - это объект, который:
2294
- * - НЕ является FieldConfig (нет value/component)
2295
- * - НЕ является ArrayConfig (нет schema)
2296
- * - Содержит вложенные конфиги полей/групп/массивов
2297
- *
2298
- * @param config Проверяемая конфигурация
2299
- * @returns true если config является GroupConfig
2300
- *
2301
- * @example
2302
- * ```typescript
2303
- * const factory = new NodeFactory();
2304
- *
2305
- * factory.isGroupConfig({
2306
- * email: { value: '', component: Input },
2307
- * password: { value: '', component: Input }
2308
- * }); // true
2309
- *
2310
- * factory.isGroupConfig({ value: '', component: Input }); // false
2311
- * factory.isGroupConfig({ schema: {} }); // false
2312
- * factory.isGroupConfig(null); // false
2313
- * ```
2314
- */
2315
- isGroupConfig(e) {
2316
- return e != null && typeof e == "object" && !this.isFieldConfig(e) && !this.isArrayConfig(e);
356
+ /** Связать лист с его сигналом модели для `setValue`. @internal */
357
+ registerLeafSignal(e, s) {
358
+ this.leafSignals.set(e, s);
359
+ }
360
+ /** Снять поведение и подписки всех детей. @internal */
361
+ dispose() {
362
+ this.behaviorCleanup?.(), this.behaviorCleanup = void 0, this.children.forEach((e) => e.dispose());
2317
363
  }
2318
364
  }
2319
- class de extends M {
2320
- /**
2321
- * @param control Реактивный массив модели (`model.<path>`).
2322
- * @param buildItem Строитель формы элемента по его под-модели (инъекция, чтобы избежать цикла
2323
- * импорта с `create-form`). Обычно `(item) => createForm({ model: item, schema: itemSchema(item) })`.
2324
- */
2325
- constructor(e, t) {
2326
- super(), this.control = e, this.buildItem = t, this.initial = e.toArray(), this.disposeSync = E(() => {
2327
- const i = e.length, a = [], n = [];
2328
- for (let u = 0; u < i; u++) {
2329
- const v = e.at(u);
2330
- if (!v) continue;
2331
- let o = this.cache.get(v);
2332
- o || (o = t(v), this.cache.set(v, o)), a.push(v), n.push(o);
365
+ const z = /* @__PURE__ */ new WeakMap();
366
+ let je = 0;
367
+ function Z(t) {
368
+ let e = z.get(t);
369
+ return e === void 0 && (e = je++, z.set(t, e)), e;
370
+ }
371
+ class Re {
372
+ /**
373
+ * @param control - Массив модели (`model.<путь>`).
374
+ * @param buildRow - Форма строки по её под-модели; инъекция снимает цикл импорта со сборкой.
375
+ */
376
+ constructor(e, s) {
377
+ this.control = e, this.initial = e.toArray(), this.stopSync = ge(() => {
378
+ const a = e.length, r = [], o = [];
379
+ for (let h = 0; h < a; h++) {
380
+ const u = e.at(h);
381
+ if (!u) continue;
382
+ let f = this.cache.get(u);
383
+ f || (f = A(() => {
384
+ const p = s(u);
385
+ return this.availability.disabled.peek() && p.disable(), p;
386
+ }), this.cache.set(u, f)), r.push(u), o.push(f);
2333
387
  }
2334
- const l = new Set(a), d = [];
2335
- for (const u of this.mountedModels) {
2336
- if (l.has(u)) continue;
2337
- const v = this.cache.get(u);
2338
- this.cache.delete(u), v && d.push(v);
388
+ const c = new Set(r), l = [];
389
+ for (const h of this.mounted) {
390
+ if (c.has(h)) continue;
391
+ const u = this.cache.get(h);
392
+ this.cache.delete(h), u && l.push(u);
2339
393
  }
2340
- this.mountedModels = a, this.itemNodes.value = n, d.forEach((u) => this.disposeItemNode(u));
2341
- }), this.length = c(() => this.itemNodes.value.length), this.value = c(() => this.itemNodes.value.map((i) => i.value.value));
2342
- const s = L({
2343
- getChildren: () => this.itemNodes.value,
2344
- ownErrors: this._arrayErrors
394
+ this.mounted = r, this.rows.value = o, A(() => l.forEach((h) => k(h).dispose()));
395
+ }), this.value = d(() => this.rows.value.map((a) => a.value.value));
396
+ const { disabled: i } = this.availability, n = Q({
397
+ getChildren: () => this.rows.value,
398
+ ownIssues: this.own.list,
399
+ ownPending: d(() => !i.value && this.ownPending.active.value),
400
+ disabled: i
2345
401
  });
2346
- this.valid = s.valid, this.invalid = s.invalid, this.pending = s.pending, this.touched = s.touched, this.dirty = s.dirty, this.errors = s.errors, this.status = s.status;
2347
- }
2348
- itemNodes = g([]);
2349
- // Кэш per-item формы по идентичности фасада под-модели (фасады кэшируются в core).
2350
- cache = /* @__PURE__ */ new WeakMap();
2351
- // Фасады под-моделей, для которых сейчас смонтированы формы — «предыдущий» набор для
2352
- // реконсиляции в disposeSync. Выпавшие из массива элементы диспозятся и вытесняются из кэша.
2353
- mountedModels = [];
2354
- initial;
2355
- _arrayErrors = g([]);
2356
- disposeSync;
2357
- /**
2358
- * Сигнал per-item форм. Публичен в т.ч. чтобы `isArrayNode` распознавал узел как массив
2359
- * (duck-typing проверяет наличие `items`).
2360
- */
2361
- get items() {
2362
- return this.itemNodes;
402
+ this.valid = n.valid, this.invalid = n.invalid, this.pending = n.pending, this.touched = n.touched, this.dirty = d(() => this.reshaped.value || n.dirty.value), this.issues = n.issues, this.ownIssues = this.own.list, this.disabled = this.availability.disabled;
2363
403
  }
404
+ [j] = "array";
2364
405
  value;
2365
406
  valid;
2366
407
  invalid;
2367
408
  pending;
409
+ disabled;
2368
410
  touched;
2369
411
  dirty;
2370
- errors;
2371
- status;
2372
- length;
2373
- // ── Доступ к элементам ─────────────────────────────────────────────────────
412
+ issues;
413
+ ownIssues;
414
+ rows = w([]);
415
+ /**
416
+ * Состав массива меняли методами ноды после создания или сброса. Как `dirty` поля: ставит запись
417
+ * через форму (`push`, `removeAt`, `move`, `setValue`…), а не запись в модель — загрузка данных
418
+ * массив изменённым не делает. Операция, которая состав не поменяла (`clear` пустого массива,
419
+ * `move(i, i)`), — тоже: поведение «очистить список при снятом флаге» после сброса формы её не
420
+ * испачкает. Снимает `reset`.
421
+ */
422
+ reshaped = w(!1);
423
+ /** Форма строки по фасаду под-модели элемента. */
424
+ cache = /* @__PURE__ */ new WeakMap();
425
+ /** Под-модели строк, для которых сейчас есть формы, — «прежний» набор для сверки. */
426
+ mounted = [];
427
+ initial;
428
+ own = R();
429
+ availability = K();
430
+ /** Ожидание async-правила на ручке самого массива. */
431
+ ownPending = W();
432
+ stopSync;
433
+ get length() {
434
+ return this.rows.value.length;
435
+ }
2374
436
  at(e) {
2375
- return this.itemNodes.value[e];
437
+ return this.rows.value[e];
2376
438
  }
2377
439
  map(e) {
2378
- return this.itemNodes.value.map((t, s) => e(t, s));
440
+ return this.rows.value.map(e);
2379
441
  }
2380
442
  forEach(e) {
2381
- this.itemNodes.value.forEach((t, s) => e(t, s));
443
+ this.rows.value.forEach(e);
444
+ }
445
+ keyAt(e) {
446
+ const s = e < this.rows.value.length ? this.mounted[e] : void 0;
447
+ if (s === void 0)
448
+ throw new RangeError(`[@reformer/core] FormArray.keyAt: no row at index ${e}`);
449
+ return String(Z(s));
2382
450
  }
2383
- // ── Мутации (делегируют массиву модели; effect пересоберёт itemNodes) ──────
2384
451
  push(e) {
2385
- this.control.push(e ?? {});
452
+ this.reshape(() => this.control.push(e));
2386
453
  }
2387
- insert(e, t) {
2388
- this.control.insertAt(e, t ?? {});
454
+ insert(e, s) {
455
+ this.reshape(() => this.control.insertAt(this.modelIndex(e), s));
2389
456
  }
2390
457
  removeAt(e) {
2391
- this.control.removeAt(e);
458
+ this.reshape(() => this.control.removeAt(this.modelIndex(e)));
2392
459
  }
2393
- /**
2394
- * Переместить элемент модель-массива. Делегирует мутацию массиву модели; внутренний `effect`
2395
- * пересоберёт `itemNodes` в новом порядке (per-item формы сохраняются по кешу идентичности).
2396
- */
2397
- move(e, t) {
2398
- this.control.move(e, t);
460
+ move(e, s) {
461
+ this.reshape(() => this.control.move(this.modelIndex(e), this.modelIndex(s)));
2399
462
  }
2400
- /** Поменять местами два элемента модель-массива (см. {@link move}). */
2401
- swap(e, t) {
2402
- this.control.swap(e, t);
463
+ swap(e, s) {
464
+ this.reshape(() => this.control.swap(this.modelIndex(e), this.modelIndex(s)));
2403
465
  }
2404
466
  clear() {
2405
- this.control.clear();
467
+ this.reshape(() => this.control.clear());
2406
468
  }
2407
- // ── Абстрактные методы FormNode ────────────────────────────────────────────
2408
469
  getValue() {
2409
470
  return this.control.toArray();
2410
471
  }
2411
472
  setValue(e) {
2412
- this.control.clear(), (e ?? []).forEach((t) => this.control.push(t));
2413
- }
2414
- patchValue(e) {
2415
- this.setValue(e);
473
+ this.reshape(() => {
474
+ this.control.clear(), (e ?? []).forEach((s) => this.control.push(s));
475
+ });
2416
476
  }
477
+ /**
478
+ * Сбросить массив: строки, замечания и `dirty`. Без аргумента — к точке отсчёта модели, той же, к
479
+ * которой возвращает `model.reset()`: элементам создания модели либо последнего
480
+ * `captureInitial()`.
481
+ */
2417
482
  reset(e) {
2418
- this._arrayErrors.value = [], this.setValue(e ?? this.initial);
483
+ this.own.clear(), this.setValue(e ?? this.referenceItems()), this.reshaped.value = !1;
2419
484
  }
2420
- resetToInitial() {
2421
- this.reset(this.initial);
485
+ /** Строки пересобираются из значений создания — их поведение считает заново. @internal */
486
+ resetToCreation() {
487
+ this.own.clear(), this.setValue(this.initial), this.reshaped.value = !1;
2422
488
  }
2423
- async validate() {
2424
- return (await Promise.all(
2425
- this.itemNodes.value.map(
2426
- (t) => t.validate()
2427
- )
2428
- )).every(Boolean) && this._arrayErrors.value.length === 0;
489
+ /** Точка отсчёта модели за массивом; без модели — элементы создания. */
490
+ referenceItems() {
491
+ const e = H(this.control);
492
+ return e === void 0 ? this.initial : e.value;
2429
493
  }
2430
- setErrors(e) {
2431
- this._arrayErrors.value = e;
494
+ markAsTouched() {
495
+ this.rows.value.forEach((e) => e.markAsTouched());
2432
496
  }
2433
- clearErrors() {
2434
- this._arrayErrors.value = [], this.itemNodes.value.forEach(
2435
- (e) => e.clearErrors?.()
2436
- );
497
+ markAsUntouched() {
498
+ this.rows.value.forEach((e) => e.markAsUntouched());
2437
499
  }
2438
- // ── Hooks (forward к элементам) ────────────────────────────────────────────
2439
- // Агрегатное состояние (touched/dirty/status) выводится из детей (createAggregateSignals), поэтому
2440
- // базовые сигналы бесполезны для чтения — состояние меняется ТОЛЬКО через per-item формы. Форвардим
2441
- // ВСЕ hook'и (как ArrayNode), иначе markAsUntouched/Pristine/Dirty/disable/enable молча no-op.
2442
- onMarkAsTouched() {
2443
- this.itemNodes.value.forEach(
2444
- (e) => e.markAsTouched?.()
2445
- );
500
+ disable() {
501
+ this.availability.setDisabled(!0), this.own.clear(), this.rows.value.forEach((e) => e.disable());
2446
502
  }
2447
- onMarkAsUntouched() {
2448
- this.itemNodes.value.forEach(
2449
- (e) => e.markAsUntouched?.()
2450
- );
503
+ /** Включить массив и строки; поля с блокировкой из схемы (`disabled: true`) остаются выключенными. */
504
+ enable() {
505
+ this.availability.setDisabled(!1), this.rows.value.forEach((e) => k(e).enableFromParent());
2451
506
  }
2452
- onMarkAsDirty() {
2453
- this.itemNodes.value.forEach(
2454
- (e) => e.markAsDirty?.()
2455
- );
507
+ /** Каскад от контейнера — то же, что `enable()`: своей блокировки из схемы у массива нет. @internal */
508
+ enableFromParent() {
509
+ this.enable();
2456
510
  }
2457
- onMarkAsPristine() {
2458
- this.itemNodes.value.forEach(
2459
- (e) => e.markAsPristine?.()
2460
- );
511
+ setIssues(e) {
512
+ this.own.set(e);
2461
513
  }
2462
- onDisable() {
2463
- this.itemNodes.value.forEach((e) => e.disable?.());
514
+ clearIssues() {
515
+ this.own.clear(), this.rows.value.forEach((e) => e.clearIssues());
2464
516
  }
2465
- onEnable() {
2466
- this.itemNodes.value.forEach((e) => e.enable?.());
517
+ /** Своё ожидание async-правила на ручке массива; ожидание строк агрегат добавит сам. @internal */
518
+ setPending(e) {
519
+ this.ownPending.mark(e);
2467
520
  }
2468
- /** Диспозит форму элемента (эффекты/поведения/вложенные подписки); терпима к не-GroupNode. */
2469
- disposeItemNode(e) {
2470
- e.dispose?.();
521
+ /** Выполнить мутацию массива модели и отметить `dirty`, если состав строк изменился. */
522
+ reshape(e) {
523
+ const s = this.composition();
524
+ e();
525
+ const i = this.composition();
526
+ (i.length !== s.length || i.some((n, a) => n !== s[a])) && (this.reshaped.value = !0);
2471
527
  }
2472
528
  /**
2473
- * Очистка подписки синхронизации И детерминированный teardown всех форм элементов.
2474
- * Без пробрасывания dispose в itemNodes эффекты/поведения каждой формы элемента (в т.ч. disposeSync
2475
- * вложенных ModelArrayNode) остались бы жить после teardown формы — утечка подписок.
529
+ * Индекс модели для индекса строки формы. Без `null`-элементов они совпадают; с ними строка `i` —
530
+ * `i`-й непустой элемент, а индекс за последней строкой — конец массива модели. Читает модель, а не
531
+ * `mounted`: внутри `batch` синхронизация ещё не прошла.
2476
532
  */
533
+ modelIndex(e) {
534
+ return A(() => {
535
+ const s = [];
536
+ for (let i = 0; i < this.control.length; i++)
537
+ this.control.at(i) && s.push(i);
538
+ return s.length === this.control.length ? e : e >= 0 && e < s.length ? s[e] : e === s.length ? this.control.length : e;
539
+ });
540
+ }
541
+ /** Под-модели строк по порядку — без подписки: мутацию могут вызвать из effect'а поведения. */
542
+ composition() {
543
+ return A(
544
+ () => Array.from({ length: this.control.length }, (e, s) => this.control.at(s))
545
+ );
546
+ }
547
+ /** Остановить синхронизацию и диспозить формы всех строк. @internal */
2477
548
  dispose() {
2478
- this.disposeSync(), this.itemNodes.value.forEach((e) => this.disposeItemNode(e)), this.mountedModels = [];
549
+ this.stopSync(), this.rows.peek().forEach((e) => k(e).dispose()), this.mounted = [];
2479
550
  }
2480
551
  }
2481
- const J = (r) => r !== null && typeof r == "object" && !Array.isArray(r) && !(r instanceof Date) && !(typeof Blob < "u" && r instanceof Blob) && !(typeof File < "u" && r instanceof File);
2482
- function D(r, e, t) {
2483
- if (r == null || typeof r != "object" || r instanceof $) return;
2484
- if (Array.isArray(r)) {
2485
- for (const a of r) D(a, e, t);
2486
- return;
2487
- }
2488
- const s = r, i = s.array;
2489
- if (i && typeof i.__path == "string" && typeof s.item == "function") {
2490
- t.set(i.__path, s.item);
2491
- return;
2492
- }
2493
- s.value instanceof $ ? e.set(s.value, {
2494
- component: s.component,
2495
- componentProps: s.componentProps,
2496
- validators: s.validators,
2497
- asyncValidators: s.asyncValidators,
2498
- updateOn: s.updateOn,
2499
- disabled: s.disabled,
2500
- debounce: s.debounce
2501
- }) : process.env.NODE_ENV !== "production" && s.component !== void 0 && ("valueSignal" in s || s.value != null) && console.warn(
2502
- "[reformer] createForm({ schema }): узел с `component` не распознан как поле — `value` не является сигналом модели. Ожидается `value: model.$.<path>`; проверьте, что не передан `value: model.<path>` (value-прокси) или `valueSignal:`."
2503
- );
2504
- for (const [a, n] of Object.entries(s))
2505
- a !== "value" && D(n, e, t);
552
+ const q = /* @__PURE__ */ new WeakMap();
553
+ function ee(t, e) {
554
+ let s = q.get(e);
555
+ return s || (s = /* @__PURE__ */ new Map(), q.set(e, s)), s.has(t) || s.set(t, t(e)), s.get(t);
2506
556
  }
2507
- function Y(r, e, t, s) {
2508
- const i = {};
2509
- for (const [a, n] of Object.entries(e)) {
2510
- const l = t === "" ? a : `${t}.${a}`;
2511
- if (Array.isArray(n))
2512
- continue;
2513
- if (J(n)) {
2514
- i[a] = Y(r, n, l, s);
2515
- continue;
557
+ const b = "[@reformer/core] compileSchema", Ke = /* @__PURE__ */ new Set([
558
+ "model",
559
+ "item",
560
+ "part",
561
+ "children",
562
+ "component",
563
+ "componentProps",
564
+ "selector",
565
+ "disabled",
566
+ "initialValue"
567
+ ]), x = {
568
+ value: "привязка поля — `model: model.$.<поле>`",
569
+ array: "привязка массива под-форм — `model: model.$.<массив>`"
570
+ }, We = Object.freeze({}), te = /* @__PURE__ */ new WeakSet();
571
+ function lt(t) {
572
+ return t !== null && typeof t == "object" && te.has(t);
573
+ }
574
+ function se(t) {
575
+ return de(t)?.path;
576
+ }
577
+ function ie(t, e, s) {
578
+ const i = se(t);
579
+ if (i === void 0) return s;
580
+ const n = e.basePath;
581
+ return n === void 0 || n === "" ? i : i.startsWith(`${n}.`) ? i.slice(n.length + 1) : i;
582
+ }
583
+ function Be(t) {
584
+ return typeof t == "string" || typeof t == "number" || J(t);
585
+ }
586
+ function Ue(t) {
587
+ return t == null || typeof t == "boolean";
588
+ }
589
+ function g(t) {
590
+ process.env.NODE_ENV !== "production" && typeof console < "u" && console.warn(t);
591
+ }
592
+ function F(t, e, s) {
593
+ const i = typeof t.selector == "string" && t.selector !== "" ? t.selector : void 0;
594
+ if (i !== void 0) {
595
+ const n = s.selectorPaths.get(i);
596
+ if (n !== void 0)
597
+ throw new Error(
598
+ `${b}: в области «${s.scope}» селектор «${i}» повторяется — узлы ${n} и ${e}. По \`selector\` узел адресуют поведение и шаги, поэтому в области он уникален. Дайте узлам разные селекторы; в строках массива и подформах селекторы живут в своих областях и с корнем не пересекаются.`
599
+ );
600
+ s.selectorPaths.set(i, e);
601
+ }
602
+ return {
603
+ id: i ?? e,
604
+ path: e,
605
+ selector: i,
606
+ scope: s.scope,
607
+ component: t.component,
608
+ props: t.componentProps ?? We,
609
+ source: t
610
+ };
611
+ }
612
+ function I(t, e) {
613
+ const s = e.nodes.get(t.id);
614
+ if (s !== void 0)
615
+ throw new Error(
616
+ `${b}: в области «${e.scope}» идентификатор «${t.id}» занят — узлы ${s.path} и ${t.path}. Селектор не может совпадать с позиционным путём узла (\`#/…\`): дайте узлу другой селектор.`
617
+ );
618
+ return te.add(t), e.nodes.set(t.id, t), t;
619
+ }
620
+ function oe(t, e, s) {
621
+ const i = t.get(e);
622
+ i !== void 0 && g(
623
+ `${b}: ручка «${s.handle.__path}» привязана к двум полям — узлы ${i.path} и ${s.path}. Нода формы у ручки одна и возьмёт конфиг последнего узла (component, componentProps, disabled). Значение второй раз показывают текстом — сигналом ручки в \`children\`.`
624
+ ), t.set(e, s);
625
+ }
626
+ function V(t, e, s) {
627
+ const i = F(t, e, s), n = [], a = t.children;
628
+ if (Array.isArray(a)) {
629
+ let o = null;
630
+ for (let c = 0; c < a.length; c++) {
631
+ const l = a[c], h = `${e}/${c}`;
632
+ if (!Ue(l)) {
633
+ if (Be(l)) {
634
+ o === null && (o = { id: h, parts: [] }, n.push({ kind: "text", id: o.id, parts: o.parts })), o.parts.push(l);
635
+ continue;
636
+ }
637
+ if (o = null, typeof l != "object" || X(l) || ve(l)) {
638
+ g(
639
+ `${b}: ребёнок ${h} — не узел и не текст (группа модели, под-модель либо значение другого типа); он пропущен. Текст — строка, число или сигнал; группу подключают узлом \`{ model: model.$.<группа>, part }\`.`
640
+ );
641
+ continue;
642
+ }
643
+ n.push(ne(l, h, s));
644
+ }
2516
645
  }
2517
- const d = r.signalAt(l);
2518
- if (!d) throw new Error(`createForm({ model }): не найден сигнал для пути "${l}"`);
2519
- const u = s.get(d) ?? {}, { validators: v, asyncValidators: o, ...p } = u;
2520
- i[a] = { ...p, valueSignal: d };
2521
646
  }
2522
- return i;
647
+ const r = Object.keys(t).every((o) => o === "selector" || o === "componentProps");
648
+ return i.component === void 0 && a === void 0 && r && g(
649
+ `${b}: узел ${i.id} — без \`component\` и без \`children\`: он ничего не рисует, и \`componentProps\` ему некому отдать. Пустую секцию объявляют с \`children: []\`, узел с разметкой — с \`component\`.`
650
+ ), I({ ...i, kind: "container", children: n }, s);
2523
651
  }
2524
- function z(r, e, t) {
2525
- for (const [s, i] of Object.entries(r)) {
2526
- const a = e === "" ? s : `${e}.${s}`;
2527
- Array.isArray(i) || (J(i) ? z(i, a, t) : t.push(a));
652
+ function ne(t, e, s) {
653
+ if (s.ancestors.has(t))
654
+ throw new Error(`${b}: узел ${e} содержит сам себя — дерево схемы зациклено.`);
655
+ s.ancestors.add(t);
656
+ try {
657
+ return Le(t, e, s);
658
+ } finally {
659
+ s.ancestors.delete(t);
2528
660
  }
2529
661
  }
2530
- function Q(r) {
2531
- const { model: e, schema: t, behavior: s } = r, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
2532
- t !== void 0 && D(t, i, a);
2533
- const n = Y(e, e.get(), "", i), l = new S(n), d = e.get();
2534
- for (const [o, p] of Object.entries(d)) {
2535
- if (!Array.isArray(p)) continue;
2536
- const h = a.get(o);
2537
- if (!h) continue;
2538
- const f = e[o], m = (y) => Q({ model: y, schema: h(y) }), b = new de(f, m);
2539
- l.fields.set(o, b);
2540
- }
2541
- const u = l.getProxy(), v = [];
2542
- z(e.get(), "", v);
2543
- for (const o of v) {
2544
- const p = e.signalAt(o), h = l.getFieldByPath(o);
2545
- if (p && h) {
2546
- Z(p, h);
2547
- const f = o.lastIndexOf("."), m = f === -1 ? l : l.getFieldByPath(o.slice(0, f));
2548
- m instanceof S && m.registerFieldSignal(h, p);
2549
- }
662
+ function Le(t, e, s) {
663
+ if (process.env.NODE_ENV !== "production")
664
+ for (const i of Object.keys(t)) Ke.has(i) || s.unknownKeys.add(i);
665
+ if (typeof t.item == "function") {
666
+ const i = he(t.model);
667
+ return i ? ze(t, i, e, s) : (g(
668
+ `${b}: у узла с \`item\` привязка — не массив модели (узел ${e}); он стал контейнером без строк. Ожидается \`model: model.$.<массив>\`; начальное значение поля должно быть массивом, а не \`null\`.`
669
+ ), V(t, e, s));
670
+ }
671
+ if (typeof t.part == "function") {
672
+ const i = fe(t.model);
673
+ return i ? qe(t, i, e, s) : (g(
674
+ `${b}: у узла с \`part\` привязка — не группа модели (узел ${e}); он стал контейнером. Ожидается \`model: model.$.<группа>\`.`
675
+ ), V(t, e, s));
676
+ }
677
+ if (J(t.model)) {
678
+ const i = t.model, n = I(
679
+ { ...F(t, e, s), kind: "field", handle: i, disabled: t.disabled === !0 },
680
+ s
681
+ );
682
+ return oe(s.fields, i, n), n;
2550
683
  }
2551
- return s && l.attachBehaviorCleanup(s.__run(e, u)), u;
684
+ return t.model != null && g(
685
+ `${b}: привязка узла не распознана (узел ${e}) — он не стал полем. Ожидается \`model: model.$.<path>\` (лист или массив); проверьте, что не передан \`model: model.<path>\` (value-фасад) или группа без \`part\`.`
686
+ ), V(t, e, s);
2552
687
  }
2553
- const he = (r) => r != null && typeof r == "object" && "model" in r && typeof r.model?.signalAt == "function";
2554
- function Oe(r) {
2555
- return he(r) ? Q(r) : new S(r).getProxy();
688
+ function ze(t, e, s, i) {
689
+ const n = t.item, a = ie(e, i, s), r = `${i.scope}/${a}`, o = /* @__PURE__ */ new WeakMap(), c = /* @__PURE__ */ new Set(), l = I(
690
+ {
691
+ ...F(t, s, i),
692
+ kind: "array",
693
+ handle: e,
694
+ control: _(e),
695
+ initialValue: t.initialValue,
696
+ row(h) {
697
+ let u = o.get(h);
698
+ if (!u) {
699
+ const f = /* @__PURE__ */ new Set();
700
+ u = B(
701
+ ee(n, h),
702
+ h,
703
+ `${r}[${Z(h)}]`,
704
+ f
705
+ );
706
+ const p = [...f].filter((y) => !c.has(y));
707
+ if (p.length > 0) {
708
+ for (const y of p) c.add(y);
709
+ re(new Set(p));
710
+ }
711
+ o.set(h, u);
712
+ }
713
+ return u;
714
+ }
715
+ },
716
+ i
717
+ );
718
+ return i.arrays.set(e, l), l;
2556
719
  }
2557
- function x(r) {
2558
- return r == null ? !1 : typeof r == "object" && "value" in r && "setValue" in r && "getValue" in r && "validate" in r;
720
+ function qe(t, e, s, i) {
721
+ const n = F(t, s, i), a = _(e), r = `${i.scope}/${ie(e, i, s)}`, o = B(
722
+ ee(t.part, a),
723
+ a,
724
+ r,
725
+ i.unknownKeys,
726
+ i.ancestors
727
+ );
728
+ for (const [c, l] of o.fields) oe(i.fields, c, l);
729
+ for (const [c, l] of o.arrays) i.arrays.set(c, l);
730
+ return I({ ...n, kind: "part", handle: e, model: a, content: o }, i);
2559
731
  }
2560
- function ce(r) {
2561
- return r == null ? !1 : x(r) && "validators" in r && "asyncValidators" in r && // FieldNode имеет markAsTouched метод
2562
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2563
- typeof r.markAsTouched == "function" && // У FieldNode нет fields или items
2564
- !("fields" in r) && !("items" in r);
732
+ function B(t, e, s, i, n = /* @__PURE__ */ new Set()) {
733
+ if (t === null || typeof t != "object")
734
+ throw new Error(`${b}: корень области «${s}» — не узел схемы: ${String(t)}.`);
735
+ const a = e !== void 0 ? e.$ : void 0, r = {
736
+ scope: s,
737
+ basePath: se(a),
738
+ nodes: /* @__PURE__ */ new Map(),
739
+ fields: /* @__PURE__ */ new Map(),
740
+ arrays: /* @__PURE__ */ new Map(),
741
+ selectorPaths: /* @__PURE__ */ new Map(),
742
+ ancestors: n,
743
+ unknownKeys: i
744
+ }, o = ne(t, "#", r);
745
+ return {
746
+ scope: s,
747
+ model: e,
748
+ root: o,
749
+ nodes: r.nodes,
750
+ fields: r.fields,
751
+ arrays: r.arrays
752
+ };
2565
753
  }
2566
- function fe(r) {
2567
- return r == null ? !1 : x(r) && "getFieldByPath" in r && "fields" in r && // GroupNode НЕ имеет items/push/removeAt (это ArrayNode/ModelArrayNode)
2568
- !("items" in r) && !("push" in r) && !("removeAt" in r);
754
+ function xe(t, e, s) {
755
+ const i = /* @__PURE__ */ new Set(), n = B(t, e, s, i);
756
+ return i.size > 0 && re(i), n;
2569
757
  }
2570
- function ve(r) {
2571
- return r == null ? !1 : x(r) && "items" in r && "length" in r && "push" in r && "removeAt" in r && "at" in r && // eslint-disable-next-line @typescript-eslint/no-explicit-any
2572
- typeof r.push == "function" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
2573
- typeof r.removeAt == "function";
758
+ function re(t) {
759
+ const e = [...t], s = e.filter((i) => i in x).map((i) => `\`${i}\`: ${x[i]}`);
760
+ g(
761
+ `${b}: у узлов схемы есть ключи, которые сборка не читает: ${e.map((i) => `\`${i}\``).join(", ")}. Узел описывают \`model\`, \`item\`, \`part\`, \`children\`, \`component\`, \`componentProps\`, \`selector\`, \`disabled\` и \`initialValue\`; вложенные узлы читаются только из \`children\`.` + (s.length > 0 ? ` ${s.join("; ")}.` : "")
762
+ );
2574
763
  }
2575
- function Ne(r) {
2576
- return ce(r) ? "FieldNode" : fe(r) ? "GroupNode" : ve(r) ? "ArrayNode" : x(r) ? "FormNode" : "Unknown";
764
+ function ae(t, e, s) {
765
+ return xe(t, e, s?.scope ?? "#");
2577
766
  }
2578
- var N = { exports: {} }, P = {};
2579
- var W;
2580
- function pe() {
2581
- if (W) return P;
2582
- W = 1;
2583
- var r = K;
2584
- function e(o, p) {
2585
- return o === p && (o !== 0 || 1 / o === 1 / p) || o !== o && p !== p;
2586
- }
2587
- var t = typeof Object.is == "function" ? Object.is : e, s = r.useState, i = r.useEffect, a = r.useLayoutEffect, n = r.useDebugValue;
2588
- function l(o, p) {
2589
- var h = p(), f = s({ inst: { value: h, getSnapshot: p } }), m = f[0].inst, b = f[1];
2590
- return a(
2591
- function() {
2592
- m.value = h, m.getSnapshot = p, d(m) && b({ inst: m });
2593
- },
2594
- [o, h, p]
2595
- ), i(
2596
- function() {
2597
- return d(m) && b({ inst: m }), o(function() {
2598
- d(m) && b({ inst: m });
2599
- });
2600
- },
2601
- [o]
2602
- ), n(h), h;
2603
- }
2604
- function d(o) {
2605
- var p = o.getSnapshot;
2606
- o = o.value;
2607
- try {
2608
- var h = p();
2609
- return !t(o, h);
2610
- } catch {
2611
- return !0;
767
+ const Ge = { fields: /* @__PURE__ */ new Map(), arrays: /* @__PURE__ */ new Map() }, Ye = (t) => (
768
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
769
+ ((e) => U({ model: e, compiled: t.row(e) }))
770
+ );
771
+ function le(t, e) {
772
+ const s = /* @__PURE__ */ new Map(), i = [], n = (r, o) => {
773
+ const c = e.fields.get(o), l = c ? {
774
+ component: c.component,
775
+ componentProps: c.source.componentProps,
776
+ disabled: c.disabled
777
+ } : {}, h = new Ce({ ...l, valueSignal: o });
778
+ s.set(r, h), O(o, h), i.push([h, o]);
779
+ };
780
+ for (const r of Object.keys(t)) {
781
+ const o = t[r];
782
+ if (me(o)) {
783
+ const c = e.arrays.get(o);
784
+ if (c) {
785
+ const l = _(o), h = new Re(l, Ye(c));
786
+ s.set(r, h), O(o, h);
787
+ } else e.fields.has(o) && n(r, o);
788
+ continue;
2612
789
  }
790
+ if (X(o)) {
791
+ const c = le(o, e);
792
+ s.set(r, c), O(o, c.proxy);
793
+ continue;
794
+ }
795
+ n(r, o);
2613
796
  }
2614
- function u(o, p) {
2615
- return p();
2616
- }
2617
- var v = typeof window > "u" || typeof window.document > "u" || typeof window.document.createElement > "u" ? u : l;
2618
- return P.useSyncExternalStore = r.useSyncExternalStore !== void 0 ? r.useSyncExternalStore : v, P;
797
+ const a = new _e(s);
798
+ for (const [r, o] of i) a.registerLeafSignal(r, o);
799
+ return a;
2619
800
  }
2620
- var T = {};
2621
- var q;
2622
- function me() {
2623
- return q || (q = 1, process.env.NODE_ENV !== "production" && (function() {
2624
- function r(h, f) {
2625
- return h === f && (h !== 0 || 1 / h === 1 / f) || h !== h && f !== f;
801
+ function ct(t) {
802
+ const { schema: e, ...s } = t;
803
+ return U({
804
+ ...s,
805
+ ...e !== void 0 ? { compiled: ae(e, t.model) } : {}
806
+ });
807
+ }
808
+ function He(t) {
809
+ k(t).dispose();
810
+ }
811
+ function U(t) {
812
+ const { model: e, compiled: s, behavior: i } = t, n = s ?? Ge;
813
+ for (const [o, c] of n.arrays)
814
+ c.initialValue !== void 0 && pe(o, c.initialValue);
815
+ const a = le(
816
+ e.$,
817
+ n
818
+ ), r = a.proxy;
819
+ if (O(e.$, r), i)
820
+ try {
821
+ a.attachBehaviorCleanup(
822
+ i.__run(e, r, t.schemaController, t.flow)
823
+ );
824
+ } catch (o) {
825
+ throw a.dispose(), o;
2626
826
  }
2627
- function e(h, f) {
2628
- v || i.startTransition === void 0 || (v = !0, console.error(
2629
- "You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
2630
- ));
2631
- var m = f();
2632
- if (!o) {
2633
- var b = f();
2634
- a(m, b) || (console.error(
2635
- "The result of getSnapshot should be cached to avoid an infinite loop"
2636
- ), o = !0);
827
+ return r;
828
+ }
829
+ const C = "[@reformer/core] FormFlow";
830
+ function Je(t) {
831
+ const e = [...t.steps];
832
+ if (e.length === 0) throw new Error(`${C}: у потока нет шагов.`);
833
+ const s = e.filter((u, f) => e.indexOf(u) !== f);
834
+ if (s.length > 0)
835
+ throw new Error(`${C}: шаги повторяются: ${[...new Set(s)].join(", ")}.`);
836
+ const i = w(0), n = w(/* @__PURE__ */ new Set()), a = w(0);
837
+ let r = 0;
838
+ const o = (u) => e.indexOf(u), c = (u) => {
839
+ const f = o(u);
840
+ if (f < 0)
841
+ throw new Error(`${C}: шага «${u}» нет. Шаги потока: ${e.join(", ")}.`);
842
+ return f;
843
+ }, l = (u) => {
844
+ const f = e[i.peek()];
845
+ return r++, D(() => {
846
+ u < i.peek() && (n.value = new Set([...n.peek()].filter((p) => o(p) < u))), i.value = u;
847
+ }), { status: "moved", from: f, to: e[u] };
848
+ }, h = (u) => {
849
+ const f = o(u);
850
+ return f < 0 ? !1 : f === 0 || n.value.has(e[f - 1]);
851
+ };
852
+ return {
853
+ steps: e,
854
+ current: d(() => e[i.value]),
855
+ // Наружу — только чтение: шаг меняют переходы потока.
856
+ index: d(() => i.value),
857
+ completed: d(() => n.value),
858
+ isFirst: d(() => i.value === 0),
859
+ isLast: d(() => i.value === e.length - 1),
860
+ validating: d(() => a.value > 0),
861
+ canGoTo: h,
862
+ async next() {
863
+ const u = i.peek(), f = r, p = e[u];
864
+ let y;
865
+ if (t.validateStep) {
866
+ a.value++;
867
+ try {
868
+ y = await t.validateStep(p);
869
+ } finally {
870
+ a.value--;
871
+ }
2637
872
  }
2638
- b = n({
2639
- inst: { value: m, getSnapshot: f }
873
+ const S = y !== void 0 ? { validation: y } : {};
874
+ return r !== f ? { status: "stayed", step: e[i.peek()], reason: "cancelled", ...S } : y !== void 0 && y.status !== "valid" ? { status: "stayed", step: p, reason: y.status, ...S } : (n.value = /* @__PURE__ */ new Set([...n.peek(), p]), u === e.length - 1 ? { status: "stayed", step: p, reason: "edge", ...S } : (r++, i.value = u + 1, { status: "moved", from: p, to: e[u + 1] }));
875
+ },
876
+ previous() {
877
+ const u = i.peek();
878
+ return u === 0 ? { status: "stayed", step: e[0], reason: "edge" } : l(u - 1);
879
+ },
880
+ goTo(u) {
881
+ const f = c(u);
882
+ return f === i.peek() ? { status: "stayed", step: u, reason: "current" } : h(u) ? l(f) : { status: "stayed", step: e[i.peek()], reason: "locked" };
883
+ },
884
+ reset() {
885
+ r++, D(() => {
886
+ i.value = 0, n.value = /* @__PURE__ */ new Set();
2640
887
  });
2641
- var y = b[0].inst, k = b[1];
2642
- return d(
2643
- function() {
2644
- y.value = m, y.getSnapshot = f, t(y) && k({ inst: y });
2645
- },
2646
- [h, m, f]
2647
- ), l(
2648
- function() {
2649
- return t(y) && k({ inst: y }), h(function() {
2650
- t(y) && k({ inst: y });
2651
- });
2652
- },
2653
- [h]
2654
- ), u(m), m;
2655
888
  }
2656
- function t(h) {
2657
- var f = h.getSnapshot;
2658
- h = h.value;
889
+ };
890
+ }
891
+ function Xe(t) {
892
+ const e = w(0);
893
+ return {
894
+ submitting: d(() => e.value > 0),
895
+ async submit(s) {
896
+ const i = await t.check?.();
897
+ if (i) {
898
+ const { result: a } = i;
899
+ if (a.status !== "valid") return { status: a.status, validation: a };
900
+ if (i.stale)
901
+ return { status: "cancelled", validation: { ...a, status: "cancelled" } };
902
+ }
903
+ const n = i ? i.values : t.values();
904
+ e.value++;
2659
905
  try {
2660
- var m = f();
2661
- return !a(h, m);
2662
- } catch {
2663
- return !0;
906
+ return { status: "submitted", result: await s(n) };
907
+ } catch (a) {
908
+ return { status: "failed", error: a };
909
+ } finally {
910
+ e.value--;
2664
911
  }
2665
912
  }
2666
- function s(h, f) {
2667
- return f();
2668
- }
2669
- typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ < "u" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart == "function" && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
2670
- var i = K, a = typeof Object.is == "function" ? Object.is : r, n = i.useState, l = i.useEffect, d = i.useLayoutEffect, u = i.useDebugValue, v = !1, o = !1, p = typeof window > "u" || typeof window.document > "u" || typeof window.document.createElement > "u" ? s : e;
2671
- T.useSyncExternalStore = i.useSyncExternalStore !== void 0 ? i.useSyncExternalStore : p, typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ < "u" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop == "function" && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
2672
- })()), T;
2673
- }
2674
- var H;
2675
- function ye() {
2676
- return H || (H = 1, process.env.NODE_ENV === "production" ? N.exports = pe() : N.exports = me()), N.exports;
2677
- }
2678
- var C = ye();
2679
- function ge(r, e) {
2680
- if (r === e) return !0;
2681
- if (r.length !== e.length) return !1;
2682
- for (let t = 0; t < r.length; t++)
2683
- if (r[t] !== e[t]) return !1;
2684
- return !0;
913
+ };
2685
914
  }
2686
- function R(r, e, t, s) {
2687
- const i = O(null), a = O(r);
2688
- a.current = r;
2689
- const n = O(e);
2690
- n.current = e;
2691
- const l = O(t);
2692
- if (l.current = t, i.current === null) {
2693
- const v = {};
2694
- for (const o in r)
2695
- v[o] = r[o].value;
2696
- i.current = { ...v, __snapshot: null };
2697
- }
2698
- const d = _(
2699
- (v) => {
2700
- let o = !0;
2701
- return E(() => {
2702
- const h = a.current;
2703
- for (const f in h)
2704
- h[f].value;
2705
- if (o) {
2706
- o = !1;
2707
- return;
2708
- }
2709
- v();
2710
- });
2711
- },
2712
- [s]
2713
- ), u = _(() => {
2714
- const v = i.current, o = a.current, p = {};
2715
- for (const f in o)
2716
- p[f] = o[f].value;
2717
- let h = !1;
2718
- for (const f of n.current) {
2719
- const { key: m, useShallowArrayEqual: b } = f, y = p[m], k = v[m];
2720
- if (b) {
2721
- if (!ge(k, y)) {
2722
- h = !0;
2723
- break;
2724
- }
2725
- } else if (k !== y) {
2726
- h = !0;
2727
- break;
2728
- }
915
+ const M = Object.freeze({
916
+ status: "valid",
917
+ issues: /* @__PURE__ */ new Map(),
918
+ failures: []
919
+ }), G = "[@reformer/core] createForm";
920
+ function Qe(t, e) {
921
+ const s = typeof e.validation == "function" ? { rules: e.validation } : e.validation ?? {}, i = /* @__PURE__ */ new Map();
922
+ for (const v of e.flow?.steps ?? []) {
923
+ if (i.has(v.id)) throw new Error(`${G}: шаг потока «${v.id}» повторяется.`);
924
+ i.set(v.id, v.rules ?? null);
925
+ }
926
+ const n = [...i.values()].filter(
927
+ (v) => v != null
928
+ ), a = s.rules ?? (n.length > 0 || s.extras ? Pe(
929
+ () => Fe(...n, ...s.extras ? [s.extras] : [])
930
+ ) : void 0), r = a !== void 0 ? Ae(t, a, {
931
+ strategy: s.strategy,
932
+ debounce: s.debounce,
933
+ liveAfterSubmit: s.liveAfterSubmit
934
+ }) : void 0, o = w(0), c = (v) => {
935
+ const m = we();
936
+ return m ? (o.value++, m.then(v).finally(() => {
937
+ o.value--;
938
+ })) : v();
939
+ }, l = async (v) => {
940
+ let m;
941
+ const $ = await c(() => (m = t.get(), v())), P = t.get();
942
+ return { result: $, values: P, stale: $.status === "valid" && !ye(m, P) };
943
+ }, h = () => r ? c(() => r.run()) : Promise.resolve(M), u = (v) => {
944
+ if (!i.has(v)) {
945
+ const m = [...i.keys()];
946
+ throw new Error(
947
+ `${G}: шага «${v}» нет в потоке. ` + (m.length > 0 ? `Шаги потока: ${m.join(", ")}.` : "Поток не задан: `flow`.")
948
+ );
949
+ }
950
+ return i.get(v) ?? null;
951
+ }, f = async (v) => {
952
+ o.value++;
953
+ try {
954
+ return await Ie(t, v, { touch: !0, skipDisabled: !0 });
955
+ } finally {
956
+ o.value--;
957
+ }
958
+ }, p = async (v) => {
959
+ const m = u(v);
960
+ return m ? c(() => f(m)) : M;
961
+ }, y = async (v) => {
962
+ const m = u(v);
963
+ if (!m) return M;
964
+ const { result: $, stale: P } = await l(() => f(m));
965
+ return P ? { ...$, status: "cancelled" } : $;
966
+ }, S = e.flow !== void 0 ? Je({ steps: [...i.keys()], validateStep: y }) : void 0, ce = Xe({
967
+ check: r ? () => l(() => r.run({ touch: "all", submit: !0 })) : void 0,
968
+ values: () => t.get()
969
+ });
970
+ return {
971
+ flow: S,
972
+ validating: d(() => (r?.validating.value ?? !1) || o.value > 0),
973
+ validation: a !== void 0 && r ? { rules: a, controller: r } : void 0,
974
+ submission: ce,
975
+ validate: h,
976
+ validateStep: p,
977
+ dispose() {
978
+ r?.dispose();
979
+ for (const v of n) Oe(t, v);
2729
980
  }
2730
- if (!h && v.__snapshot)
2731
- return v.__snapshot;
2732
- for (const f in o)
2733
- v[f] = p[f];
2734
- return v.__snapshot = l.current(p), v.__snapshot;
2735
- }, [s]);
2736
- return C.useSyncExternalStore(d, u, u);
981
+ };
2737
982
  }
2738
- function be(r) {
2739
- const e = {
2740
- value: r.value,
2741
- disabled: r.disabled,
2742
- errors: r.errors,
2743
- pending: r.pending,
2744
- valid: r.valid,
2745
- invalid: r.invalid,
2746
- touched: r.touched,
2747
- dirty: r.dirty,
2748
- shouldShowError: r.shouldShowError,
2749
- componentProps: r.componentProps
2750
- }, t = [
2751
- { key: "value" },
2752
- { key: "disabled" },
2753
- { key: "errors", useShallowArrayEqual: !0 },
2754
- { key: "pending" },
2755
- { key: "valid" },
2756
- { key: "invalid" },
2757
- { key: "touched" },
2758
- { key: "dirty" },
2759
- { key: "shouldShowError" },
2760
- { key: "componentProps" }
2761
- ], s = _(
2762
- (i) => ({
2763
- value: i.value,
2764
- pending: i.pending,
2765
- disabled: i.disabled,
2766
- errors: i.errors,
2767
- valid: i.valid,
2768
- invalid: i.invalid,
2769
- touched: i.touched,
2770
- dirty: i.dirty,
2771
- shouldShowError: i.shouldShowError,
2772
- componentProps: i.componentProps
2773
- }),
2774
- []
2775
- );
2776
- return R(e, t, s, r);
983
+ const E = "[@reformer/core] createForm", Y = (t) => t.map((e) => `"${e}"`).join(", ");
984
+ function Ze(t, e, s) {
985
+ if (t === void 0) return;
986
+ const i = Ee(e, t);
987
+ if (i.length > 0 && console.warn(
988
+ `${E}: поведение обращается к узлам схемы, которых нет в корневом дереве: ${Y(i)}. Правило ничего не сделает. Узлы строки массива и подформы адресуются из их поведения — через \`applyEach\` / \`apply\`.`
989
+ ), s) {
990
+ const n = s.steps.map((r) => r.id), a = n.filter((r) => t.nodes.get(r)?.selector !== r);
991
+ a.length > 0 && a.length < n.length && console.warn(
992
+ `${E}: в \`flow.steps\` есть шаги, для которых в дереве схемы нет узла с таким \`selector\`: ${Y(a)}. Визард не свяжет их с разметкой шага.`
993
+ );
994
+ }
2777
995
  }
2778
- function Ee(r) {
2779
- const e = {
2780
- value: r.value,
2781
- length: r.length,
2782
- errors: r.errors,
2783
- pending: r.pending,
2784
- valid: r.valid,
2785
- invalid: r.invalid,
2786
- touched: r.touched,
2787
- dirty: r.dirty,
2788
- disabled: r.disabled
2789
- }, t = [
2790
- { key: "value" },
2791
- { key: "length" },
2792
- { key: "errors", useShallowArrayEqual: !0 },
2793
- { key: "pending" },
2794
- { key: "valid" },
2795
- { key: "invalid" },
2796
- { key: "touched" },
2797
- { key: "dirty" },
2798
- { key: "disabled" }
2799
- ], s = _(
2800
- (i) => ({
2801
- value: i.value,
2802
- length: i.length,
2803
- pending: i.pending,
2804
- errors: i.errors,
2805
- valid: i.valid,
2806
- invalid: i.invalid,
2807
- touched: i.touched,
2808
- dirty: i.dirty,
2809
- disabled: i.disabled
2810
- }),
2811
- []
2812
- );
2813
- return R(e, t, s, r);
996
+ function et(t, e) {
997
+ const { schema: s, registry: i } = t;
998
+ if (s == null) return {};
999
+ if (typeof s == "function")
1000
+ return { tree: s(e) };
1001
+ if (i == null)
1002
+ throw new Error(
1003
+ `${E}: \`schema\` — готовое дерево, а не билдер \`(model) => узел\`. Передайте билдер; документу схемы (JSON) нужен \`registry\`. Форму из модели и готового дерева без сборки собирает \`createFormFromModel({ model, schema })\`.`
1004
+ );
1005
+ if (typeof i.resolveSchema != "function")
1006
+ throw new Error(
1007
+ `${E}: \`registry\` не умеет собирать дерево из документа схемы — у него нет метода \`resolveSchema\`. Возьмите реестр \`@reformer/renderer-json\` (\`createComponentRegistry\`).`
1008
+ );
1009
+ return i.resolveSchema(s, e);
2814
1010
  }
2815
- function _e() {
2816
- const r = _(
2817
- () => ({
2818
- value: [],
2819
- length: 0,
2820
- pending: !1,
2821
- errors: [],
2822
- valid: !0,
2823
- invalid: !1,
2824
- touched: !1,
2825
- dirty: !1,
2826
- disabled: !1
2827
- }),
2828
- []
1011
+ function ut(t) {
1012
+ if (typeof t?.model?.signalAt != "function" && t?.initial === void 0)
1013
+ throw new Error(
1014
+ `${E}: нужна модель — \`model\` либо \`initial\`. Значения формы принадлежат модели: \`createForm({ initial: { email: '' }, schema })\`.`
1015
+ );
1016
+ const s = t.model ?? ue(t.initial);
1017
+ t.seed?.(s);
1018
+ const { tree: i, fieldWrapper: n, errorBoundary: a } = et(t, s), r = i !== void 0 ? ae(i, s) : void 0, o = $e(), c = o.scopeOf(s), l = Qe(s, { validation: t.validation, flow: t.flow }), h = be(
1019
+ () => U({
1020
+ model: s,
1021
+ ...r !== void 0 ? { compiled: r } : {},
1022
+ ...t.behavior ? { behavior: t.behavior, schemaController: o } : {},
1023
+ ...l.flow !== void 0 ? { flow: l.flow } : {}
1024
+ })
2829
1025
  );
2830
- return R({}, [], r);
2831
- }
2832
- function Me(r) {
2833
- const e = r && "length" in r && "map" in r;
2834
- return r ? e ? Ee(r) : be(r) : _e();
2835
- }
2836
- function xe(r) {
2837
- const e = O({ value: r.value.value }), t = _(
2838
- (i) => {
2839
- let a = !0;
2840
- return E(() => {
2841
- if (r.value.value, a) {
2842
- a = !1;
2843
- return;
2844
- }
2845
- i();
2846
- });
2847
- },
2848
- [r]
2849
- ), s = _(() => {
2850
- const i = r.value.value;
2851
- return e.current.value === i ? e.current.value : (e.current.value = i, i);
2852
- }, [r]);
2853
- return C.useSyncExternalStore(t, s, s);
2854
- }
2855
- function Ce(r) {
2856
- const e = O({ length: r.length.value }), t = _(
2857
- (i) => {
2858
- let a = !0;
2859
- return E(() => {
2860
- if (r.length.value, a) {
2861
- a = !1;
2862
- return;
2863
- }
2864
- i();
2865
- });
1026
+ t.model === void 0 && s.captureInitial(), process.env.NODE_ENV !== "production" && Ze(r, c.__overrideMaps, t.flow);
1027
+ const u = {
1028
+ ...l,
1029
+ model: s,
1030
+ form: h,
1031
+ render: {
1032
+ tree: r,
1033
+ controller: o,
1034
+ node: (f) => c.node(f),
1035
+ ...n !== void 0 ? { fieldWrapper: n } : {},
1036
+ ...a !== void 0 ? { errorBoundary: a } : {}
2866
1037
  },
2867
- [r]
2868
- ), s = _(() => {
2869
- const i = r.length.value;
2870
- return e.current.length === i ? e.current.length : (e.current.length = i, i);
2871
- }, [r]);
2872
- return C.useSyncExternalStore(t, s, s);
2873
- }
2874
- function Pe(r) {
2875
- const { model: e, schema: t, strategy: s, debounce: i, liveAfterSubmit: a } = r, n = ee(
2876
- () => se(e, t, { strategy: s, debounce: i, liveAfterSubmit: a }),
2877
- [e, t, s, i, a]
2878
- );
2879
- te(() => n.start(), [n]);
2880
- const l = C.useSyncExternalStore(
2881
- _((u) => n.validating.subscribe(u), [n]),
2882
- () => n.validating.peek(),
2883
- () => !1
2884
- // SSR: прогон не идёт
2885
- ), d = _(() => n.validate(), [n]);
2886
- return { submit: d, validate: d, isValidating: l };
1038
+ // После `...level`: уровень снимает только себя, сборка — ещё и форму.
1039
+ dispose() {
1040
+ He(h), l.dispose();
1041
+ }
1042
+ };
1043
+ try {
1044
+ t.setup?.(u);
1045
+ } catch (f) {
1046
+ throw u.dispose(), f;
1047
+ }
1048
+ return u;
2887
1049
  }
1050
+ const dt = Object.freeze({ runtime: "reformer-core", v: 1 });
2888
1051
  export {
2889
- U as ArrayNode,
2890
- A as ErrorStrategy,
2891
- ae as FieldNode,
2892
- F as FormErrorHandler,
2893
- M as FormNode,
2894
- re as FormStatusMachine,
2895
- oe as FormSubmitter,
2896
- S as GroupNode,
2897
- de as ModelArrayNode,
2898
- ue as NodeFactory,
2899
- I as SubscriptionManager,
2900
- Re as computeFrom,
2901
- Be as copyFrom,
2902
- Oe as createForm,
2903
- Q as createFormFromModel,
2904
- Ie as createModel,
2905
- st as disableWhen,
2906
- it as enableWhen,
2907
- et as getNodeForSignal,
2908
- Ne as getNodeType,
2909
- ve as isArrayNode,
2910
- B as isDerived,
2911
- ce as isFieldNode,
2912
- x as isFormNode,
2913
- fe as isGroupNode,
2914
- Ye as markDerived,
2915
- Z as registerSignalNode,
2916
- $e as resetWhen,
2917
- Ge as revalidateWhen,
2918
- je as runOutsideEffect,
2919
- Ue as safeCallback,
2920
- We as safeDebouncedCallback,
2921
- qe as syncFields,
2922
- He as transformValue,
2923
- V as uniqueId,
2924
- ze as unmarkDerived,
2925
- Ce as useArrayLength,
2926
- Me as useFormControl,
2927
- xe as useFormControlValue,
2928
- Pe as useFormValidation,
2929
- Xe as validators,
2930
- Ke as watchField
1052
+ dt as CORE_RUNTIME_TOKEN,
1053
+ wt as arrayOf,
1054
+ ae as compileSchema,
1055
+ ut as createForm,
1056
+ Je as createFormFlow,
1057
+ ct as createFormFromModel,
1058
+ Qe as createFormLevel,
1059
+ ue as createModel,
1060
+ $e as createSchemaController,
1061
+ At as createSchemaScope,
1062
+ Xe as createSubmission,
1063
+ He as disposeForm,
1064
+ vt as eachLeafSignal,
1065
+ pt as eachValueSignal,
1066
+ Et as getNodeForSignal,
1067
+ lt as isCompiledNode,
1068
+ T as isDerived,
1069
+ Ft as isFormArray,
1070
+ It as isFormControl,
1071
+ Nt as isFormGroup,
1072
+ Vt as isFormNode,
1073
+ X as isModelContainerSignal,
1074
+ J as isValueSignal,
1075
+ yt as markDerived,
1076
+ _ as modelOf,
1077
+ O as registerSignalNode,
1078
+ bt as runOutsideEffect,
1079
+ gt as safeCallback,
1080
+ kt as safeDebouncedCallback,
1081
+ ee as schemaSubtree,
1082
+ Ee as unknownSchemaSelectors,
1083
+ St as unmarkDerived
2931
1084
  };