@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
@@ -0,0 +1,229 @@
1
+ ## 14.5 UI COMPONENT PATTERNS
2
+
3
+ > **Default rule (read first)**: для UI используй `FormField` из
4
+ > [`@reformer/ui-kit`](../../reformer-ui-kit/) — он покрывает 95% случаев одной
5
+ > строкой `<FormField control={form.x} />`. Свои обёртки поля пиши ТОЛЬКО если
6
+ > ui-kit не подходит (другая design system, особый low-level input).
7
+ >
8
+ > Канонический schema-driven подход:
9
+ >
10
+ > - **компонент** объявляется в схеме как `component: Input` (или `SelectAsync`, `CheckboxWithLabel`, …) —
11
+ > сам компонент кита, отдельных «field-версий» нет: связывает его с полем обёртка (`FormField`)
12
+ > - **пропсы** компонента — в `componentProps: { label, placeholder, options, type, ... }`
13
+ > - **JSX рендерит**: `<FormField control={form.x} />` БЕЗ дополнительных props
14
+ >
15
+ > См. `find_recipe(package="@reformer/ui-kit", topic="form-field-integration")`
16
+ > для полного руководства.
17
+
18
+ ### Default — FormField из ui-kit (canonical)
19
+
20
+ ```tsx
21
+ import { createForm, type FormModel, type FormSchemaNode } from '@reformer/core';
22
+ import { useFormBundle } from '@reformer/core/react';
23
+ import { FormField, Input, SelectAsync, CheckboxWithLabel, Button } from '@reformer/ui-kit';
24
+
25
+ interface RegistrationForm {
26
+ email: string;
27
+ country: string;
28
+ agree: boolean;
29
+ }
30
+
31
+ function RegistrationPage() {
32
+ // useFormBundle вызывает фабрику ровно один раз и держит сборку стабильной между рендерами
33
+ const { form } = useFormBundle(() =>
34
+ createForm<RegistrationForm>({
35
+ initial: { email: '', country: 'ru', agree: false },
36
+ schema: (model: FormModel<RegistrationForm>): FormSchemaNode => ({
37
+ children: [
38
+ {
39
+ model: model.$.email,
40
+ component: Input,
41
+ componentProps: { label: 'Email', type: 'email', placeholder: 'you@example.com' },
42
+ },
43
+ {
44
+ model: model.$.country,
45
+ component: SelectAsync,
46
+ componentProps: {
47
+ label: 'Country',
48
+ options: [
49
+ { value: 'ru', label: 'Россия' },
50
+ { value: 'by', label: 'Беларусь' },
51
+ ],
52
+ },
53
+ },
54
+ {
55
+ model: model.$.agree,
56
+ component: CheckboxWithLabel,
57
+ componentProps: { label: 'I agree to terms' },
58
+ },
59
+ ],
60
+ }),
61
+ })
62
+ );
63
+
64
+ return (
65
+ <form>
66
+ <FormField control={form.email} testId="email" />
67
+ <FormField control={form.country} testId="country" />
68
+ <FormField control={form.agree} testId="agree" />
69
+ <Button type="submit">Register</Button>
70
+ </form>
71
+ );
72
+ }
73
+ ```
74
+
75
+ `FormField` сам читает `componentProps.label`, `componentProps.placeholder`,
76
+ `componentProps.options` через `useFormControl(...).componentProps` и применяет
77
+ их к нужному `<input>`/`<select>`/etc. Текст замечания (по коду — словарь формы из
78
+ `ValidationMessagesProvider messages`, затем локаль), `pending` для async-валидаций,
79
+ `data-testid` для e2e — всё из коробки.
80
+
81
+ ### Anti-patterns (не делай так)
82
+
83
+ ❌ **Свои field-компоненты с label-prop'ами в JSX**:
84
+
85
+ ```tsx
86
+ // WRONG — дублирует логику FormField, ломает schema-driven архитектуру
87
+ <Input control={form.email} label="Email" placeholder="..." />
88
+ <Select control={form.country} options={[...]} />
89
+ ```
90
+
91
+ ❌ **Передача компонент-пропсов через JSX вместо схемы**:
92
+
93
+ ```tsx
94
+ // WRONG — нарушает single source of truth (схема)
95
+ <FormField control={form.email} label="Email" />
96
+ ```
97
+
98
+ ✅ Всё это в схеме:
99
+
100
+ ```ts
101
+ { model: model.$.email, component: Input, componentProps: { label: 'Email' } }
102
+ ```
103
+
104
+ ```tsx
105
+ <FormField control={form.email} />
106
+ ```
107
+
108
+ ### Advanced — кастомный input через `children` slot
109
+
110
+ Когда нужен низкоуровневый input, которого нет в ui-kit (маска, особый combobox):
111
+
112
+ ```tsx
113
+ import { FormField } from '@reformer/ui-kit';
114
+ import { InputMask } from 'react-input-mask';
115
+
116
+ <FormField control={form.phone} testId="phone">
117
+ <InputMask mask="+7 (999) 999-99-99" />
118
+ </FormField>;
119
+ ```
120
+
121
+ `children` оборачивается в `CdkFormField.Control asChild` и получает все нужные
122
+ props (`value`, `onChange`, `onBlur`, `aria-invalid`) — в диалекте ребёнка, если тот объявил
123
+ статику `reformerAdapter` (`defineFieldControl` из `@reformer/ui-kit/fields`), иначе value-based.
124
+
125
+ ### Advanced — write your own from scratch (rare)
126
+
127
+ Если ты не хочешь подключать `@reformer/ui-kit`, пиши свои компоненты на основе
128
+ `useFormControl` — но **сохраняй schema-driven подход**: читай label/placeholder
129
+ из `componentProps`, не из JSX-props.
130
+
131
+ ```tsx
132
+ import type { FormControl } from '@reformer/core';
133
+ import { useFormControl, useIssueMessage } from '@reformer/core/react';
134
+ import { formMessages } from './form.validation'; // defineIssueMessages — константа модуля
135
+
136
+ type MyFormFieldProps<T> = { control: FormControl<T> }; // ← ОДИН prop
137
+
138
+ function MyFormField<T>({ control }: MyFormFieldProps<T>) {
139
+ const { value, issues, disabled, shouldShowIssues, componentProps } = useFormControl(control);
140
+ const message = useIssueMessage(formMessages); // текст замечания по коду
141
+ // componentProps = { label, placeholder, type, options, ... } — из СХЕМЫ
142
+ const cp = (componentProps ?? {}) as Record<string, unknown>;
143
+
144
+ return (
145
+ <label>
146
+ {cp.label && <span>{cp.label as string}</span>}
147
+ <input
148
+ type={(cp.type as string) ?? 'text'}
149
+ value={(value ?? '') as string}
150
+ placeholder={cp.placeholder as string | undefined}
151
+ disabled={disabled}
152
+ onChange={(e) => (control.setValue as (v: unknown) => void)(e.target.value)}
153
+ onBlur={() => control.markAsTouched()}
154
+ />
155
+ {/* shouldShowIssues — ошибки и предупреждения; shouldShowError — для aria-invalid */}
156
+ {shouldShowIssues && issues[0] && <span>{message(issues[0])}</span>}
157
+ </label>
158
+ );
159
+ }
160
+ ```
161
+
162
+ Внутри дерева с `ValidationMessagesProvider` (cdk) вместо `useIssueMessage(formMessages)` берут
163
+ `useIssueMessageResolver()` из `@reformer/cdk` — он учитывает словари всех провайдеров выше. См.
164
+ `36-issue-messages.md`.
165
+
166
+ Использование — как у `FormField`:
167
+
168
+ ```tsx
169
+ <MyFormField control={form.email} /> // ← без label-prop
170
+ ```
171
+
172
+ ### Связывание поля с контролом — `FieldAdapter` (`@reformer/core/react`)
173
+
174
+ Контролы говорят на разных диалектах (`checked` + `onCheckedChange`, `onValueChange`, DOM-событие
175
+ в `onChange`), форма — на value-based seam (`value` + `onChange(value)` + `onBlur`). Перевод
176
+ описывает `FieldAdapter` (`valueProp`, `changeProp`, `fromEmit`, `toValue`, `bindBlur`, `strip`,
177
+ `passControl` — все необязательны). Компонент объявляет его статикой `reformerAdapter`, а обёртка
178
+ поля (`FormField.Control` из `@reformer/cdk`, рендерер `@reformer/renderer-react`) читает статику
179
+ и связывает поле сама — отдельные «field-версии» компонентов не нужны.
180
+
181
+ | Символ | Назначение |
182
+ | ----------------------------- | --------------------------------------------------------------------------- |
183
+ | `FieldAdapter` | Тип адаптера: диалект контрола |
184
+ | `getFieldAdapter(component)` | Статика `component.reformerAdapter` либо `undefined` |
185
+ | `bindFieldProps(adapter, seam, props)` | Props контрола под адаптер (без адаптера — seam как есть) |
186
+ | `FieldHandle` | Императивный handle поля: `focus` / `blur` / `scrollIntoView` / `getElement` |
187
+ | `makeElementFieldHandle(ref)` | Базовый `FieldHandle` из DOM-узла |
188
+ | `useFieldHandleRef(outerRef)` | Ref для контрола + публикация handle потребителю (строит ОБЁРТКА поля) |
189
+
190
+ В приложении обычно достаточно `defineFieldControl(MyControl, { adapter })` из
191
+ `@reformer/ui-kit/fields` (плюс готовые пресеты `checkedAdapter`, `valueChangeAdapter`, …); для
192
+ компонентов чужой библиотеки без статики — `RendererSettings.resolveFieldAdapter`.
193
+
194
+ ### Integration with UI libraries (shadcn etc.)
195
+
196
+ > **Через рендерер — без обёртки.** Если ты рендеришь форму через `@reformer/renderer-react` /
197
+ > `@reformer/renderer-json`, вместо ручной обёртки на каждый контрол можно зарегистрировать сырой
198
+ > компонент и передать `settings.resolveFieldAdapter` — рендерер сам сведёт value-seam к диалекту
199
+ > контрола. Свой компонент можно подготовить и один раз — статикой
200
+ > `defineFieldControl(MyInput, { adapter })` из `@reformer/ui-kit/fields`: его поймут и рендерер,
201
+ > и `FormField.Control` из `@reformer/cdk`. Ручная обёртка на `useFormControl` (ниже) нужна для
202
+ > прямого JSX без `FormField` и рендерера.
203
+
204
+ Если есть существующая design system — оборачивай её компоненты в один
205
+ `MyFormField` (как выше) и используй один прop `control`. Не множь обёртки на
206
+ тип input'а — пусть `componentProps.type` диспатчит внутри.
207
+
208
+ ```tsx
209
+ import { Input } from '@/components/ui/input';
210
+ import { Label } from '@/components/ui/label';
211
+
212
+ function ShadcnFormField({ control }: { control: FormControl<string> }) {
213
+ const { value, issues, disabled, shouldShowIssues, componentProps } = useFormControl(control);
214
+ const message = useIssueMessage(formMessages);
215
+ const cp = (componentProps ?? {}) as Record<string, unknown>;
216
+
217
+ return (
218
+ <div className="space-y-2">
219
+ {cp.label && <Label>{cp.label as string}</Label>}
220
+ <Input
221
+ value={(value ?? '') as string}
222
+ onChange={(e) => control.setValue(e.target.value)}
223
+ disabled={disabled}
224
+ />
225
+ {shouldShowIssues && issues[0] && <p className="text-red-500">{message(issues[0])}</p>}
226
+ </div>
227
+ );
228
+ }
229
+ ```
@@ -0,0 +1,278 @@
1
+ ## 15. NON-EXISTENT API (DO NOT USE)
2
+
3
+ **Следующего API НЕТ в @reformer/core** (наследие старой path-based архитектуры и прежних
4
+ контрактов модели, валидации, схемы, визарда и нод):
5
+
6
+ > ⚠️ Операторы валидации `validate`/`validateAsync`/`validateWhen`/`apply`/`applyEach`
7
+ > **существуют** — но живут в сабпути `@reformer/core/validation` (схема
8
+ > `defineValidation(({ model, cross }) => …)`, раннер `validateModel`), а НЕ в корне и НЕ
9
+ > в `@reformer/core/validators`. Ниже перечислено то, чего действительно нет.
10
+
11
+ | Wrong | Correct | Notes |
12
+ | -------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
13
+ | `useForm` | `createForm` + `useFormBundle` | Хука useForm нет |
14
+ | `FormValue` | тип значения самого поля либо `unknown` | Тип удалён: модель обобщённая (`createModel<T>`), параметры замечания — `Record<string, unknown>` |
15
+ | `value instanceof Signal` | `isSignal(value)` из `@reformer/core/signals` | Проверка по классу не узнаёт сигнал другой копии рантайма; ручку поля проверяет `isValueSignal` |
16
+ | `import { useFormControl, useFormBundle, … } from '@reformer/core'` | те же имена из `@reformer/core/react` | Корень ядра без React: хуки (`useFormControl`, `useFormControlValue`, `useArrayLength`, `useFormValidation`, `useFormBundle`, `useFormFlow`), `FormBundleContext`, связка поля (`bindFieldProps`, `getFieldAdapter`, `FieldAdapter`, `FieldHandle`) и типы снимков (`FieldControlState`, `ArrayControlState`) отдаёт только сабпат `/react` |
17
+ | `import { I18nProvider, useI18n, … } from '@reformer/core/i18n'` | те же имена из `@reformer/core/react` | `/i18n` — локаль, перевод и тексты замечаний без React; провайдер и хуки (`I18nProvider`, `useI18n`, `useMessages`, `useIssueMessage`) — в `/react` |
18
+ | `ElementType` / `ComponentType` в типе узла схемы | `ComponentRef` из `@reformer/core` | Типы схемы и ноды не импортируют типы React: `component` — непрозрачная ссылка (`string | object`), к типу своего UI-слоя её приводит рендерер |
19
+ | `validateForm`, `validateFormModel` | `validateModel(model, schema)` из `@reformer/core/validation` | Legacy-движок дерева `{ value, validators }` удалён; внешний раннер — `validateModel` |
20
+ | `validateModelSync` | `await validateModel(model, schema)` | Синхронного раннера нет — прогон асинхронный (`Promise<boolean>`) |
21
+ | `applyWhen` | `validateWhen(() => cond, () => { … })` | Условная валидация — оператором `validateWhen`, не узлом `{ when, children }` |
22
+ | `validateItems`, `validateGroup`, `validateTree` | `applyEach(model.$.arr, rowRules)` / `apply(model.$.group, groupRules)` | Правила строк и подформы — отдельные схемы, подключённые привязкой |
23
+ | `ValidationSchemaFn`, `BehaviorSchemaFn` | `ValidationDefinition<T>` (`defineValidation`) / `defineFormBehavior` | Типы path-схем удалены |
24
+ | `equalTo`, `custom`, `notEmpty` (validators) | inline `Rule<T>` `(value) => issue \| null` в `validate(sig, [...])`; сравнение полей — `cross(sig, fn)` | Таких фабрик нет; кастомное правило — обычная функция значения, cross-field — `cross` |
25
+ | `FieldPath`, `FieldPathNode` | `model.$.field` (`PathAwareSignal`) | Пути заменены сигналами |
26
+ | `ctx.form.x.value.value` | `model.x` / `model.$.x.value` | В behaviors читаем модель напрямую |
27
+ | `ctx.setFieldValue(name, value)` | `model.x = value` / `compute(...)` | Не существует |
28
+ | `transformers`, `createTransformer` | `transformValue(signal, fn)` | Готового набора трансформеров нет |
29
+ | `useHiddenCondition` | `useFormControlValue` + условный рендер в JSX | Хука нет |
30
+ | `FormProvider`, `control` prop, `register()` | `<Component form={form} />`, `useFormControl(form.field)` | Форма передаётся через props |
31
+ | `getFieldValue()` | `model.field` / `useFormControlValue(form.field)`| Не существует |
32
+
33
+ ### Удалено: модель (этап 1)
34
+
35
+ | Wrong | Correct | Notes |
36
+ | -------------------------------------- | ---------------------------------------------------- | --------------------------------------- |
37
+ | `model.set(partial)` для части полей | `model.patch(partial)` | `set` — полная установка: недостающие ключи в dev дают предупреждение и не меняются; `patch` сливает переданные ключи, вложенные группы — тоже частично, массив — целиком |
38
+ | `FormValue`, `T extends FormValue` | `T` без ограничения / `Record<string, unknown>` | Тип удалён |
39
+
40
+ ### Удалено: компонент массива (этап 2)
41
+
42
+ Имя `FormArray` теперь занято **типом ноды** массива под-форм в `@reformer/core`; компонент массива
43
+ в cdk и ui-kit называется `FieldArray`.
44
+
45
+ | Wrong | Correct | Notes |
46
+ | ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
47
+ | компонент `FormArray` (`@reformer/cdk`, `@reformer/ui-kit`) | `FieldArray` | `{ model: model.$.items, component: FieldArray, item }` |
48
+ | `FormArraySection` | `FieldArraySection` | ui-kit |
49
+ | `useFormArray` | `useFieldArray` | cdk |
50
+ | `@reformer/cdk/form-array`, `@reformer/ui-kit/form-array` | `@reformer/cdk/field-array`, `@reformer/ui-kit/field-array` | сабпаты |
51
+ | ключи локали `kit.formArray.*` | `kit.fieldArray.*` | ui-kit |
52
+
53
+ JSON-документ схемы со старыми именами `FormArray` / `FormArraySection` реестр renderer-json ещё
54
+ принимает как псевдонимы (dev-предупреждение).
55
+
56
+ ### Удалено: замечание валидации без текста (этап 4)
57
+
58
+ Замечание несёт код и параметры; текст по коду даёт словарь формы — см. `36-issue-messages.md`.
59
+
60
+ | Wrong | Correct | Notes |
61
+ | ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
62
+ | `required({ message: '…' })`, `{ messageKey }` | `required({ code: 'phone.required' })` + `defineIssueMessages({ 'phone.required': '…' })` | У `RuleOptions` только `code` и `params` |
63
+ | `ValidationError` (`{ code, message, … }`) | `ValidationIssue` (`{ code, params?, severity? }`) | Поля `message` нет — текст через резолвер |
64
+ | `ValidateOptions` | `RuleOptions` | `{ code?, params? }` |
65
+ | `ValidationSchema`, `defineValidationSchema` | `ValidationDefinition`, `defineValidation` | `@reformer/core/validation` |
66
+ | `resolveValidationError` | `resolveIssueMessage(issue, i18n, messages?)` | `@reformer/core/i18n` |
67
+ | `useValidationMessage` | `useIssueMessage(messages?)` | `@reformer/core/react` |
68
+ | `node.errors`, `setErrors`, `clearErrors` | `node.issues`, `setIssues`, `clearIssues` | И в снимке `useFormControl` — `issues` |
69
+ | `result.errors` у `ValidationResult` | `result.issues` | Замечания по ручке поля |
70
+ | `getErrors(filter)`, `ErrorFilterOptions` | `node.issues.value.filter(…)` | Фильтр — обычный `filter` |
71
+ | `errors[0].message` в разметке | `useIssueMessage(messages)(issues[0])` | Строку даёт резолвер при показе |
72
+ | cdk `createMessageResolver`, `defaultErrorResolver`, `ValidationMessageTable` | `<ValidationMessagesProvider messages={defineIssueMessages({…})}>` | Словарь формы поверх локали |
73
+ | cdk `ValidationErrorResolver`, `useValidationErrorResolver` | `IssueMessageResolver`, `useIssueMessageResolver` | `@reformer/cdk` |
74
+
75
+ ### Удалено: схема (этап 5)
76
+
77
+ | Wrong | Correct | Notes |
78
+ | ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
79
+ | `bundle.render.tree` как авторское дерево | `bundle.render.tree` — `CompiledSchema`; авторское дерево — `bundle.render.tree.root.source` | См. `37-compiled-schema.md` |
80
+ | повтор `selector` в одной области «с предупреждением» | разные селекторы | Повтор — исключение при сборке |
81
+ | свой разбор ключей `model` / `item` / `part` у узла | `compileSchema(tree, model)` — `kind` узла | `field \| array \| part \| container \| text` |
82
+
83
+ ### Удалено: уровень формы, поток шагов, визард (этап 6)
84
+
85
+ | Wrong | Correct | Notes |
86
+ | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
87
+ | `bundle.validation.validateAll()` / `.runAll()` | `bundle.validate()` → `ValidationResult` | Поле `bundle.validation` осталось служебным (`{ rules, controller }` для `useFormBundle`) — методов проверки на нём нет |
88
+ | `bundle.validation.validateStep(n)` / `.runStep(n)` | `bundle.validateStep(id)` → `ValidationResult` | Идентификатор шага — `selector`, не номер |
89
+ | `createForm({ validation: { steps: { loan: rules } } })` | `createForm({ flow: { steps: [{ id: 'loan', rules }] } })` | Правила вне шагов — `validation: { extras }` |
90
+ | `buildValidation`, `FormValidationBundle` | `createForm({ validation, flow })` / `createFormLevel` | Уровень формы собирает сборка |
91
+ | `form.submit(handler)`, `SubmitOptions`, `form.submitting` | `bundle.submission.submit(handler)` → `SubmitResult`, `bundle.submission.submitting` | Один прогон правил; обработчик — только при `valid` |
92
+ | `bundle.submit(handler)`, `bundle.submitting` | `bundle.submission.submit(handler)` → `SubmitResult`, `bundle.submission.submitting` | Отправка — отдельный объект сборки; псевдонимов нет. То же у `level` в `renderBehavior` и бандлов `createReactForm` / `createJsonForm`: `level.submission.submit(handler)` |
93
+ | `form.validate()` | `bundle.validate()` | У нод методов проверки нет |
94
+ | `<FormWizard config={…}>`, `FormWizardConfig` | `<FormWizard flow={bundle.flow}>` | Без `flow` и сборки в контексте — позиционный поток без правил |
95
+ | `<FormWizard ref={…}>`, `FormWizardHandle`, `ref.current.goToStep(n)` | `bundle.flow.goTo(id)` / `next()` / `previous()` | Навигация — методами потока |
96
+ | `defineSteps(model, { steps, extras, strategy })` | `createForm({ validation: { extras, strategy }, flow })` | Живая проверка — стратегия формы |
97
+ | `useWizardStepValidation(config)` | `validation: { strategy }` сборки | Стратегию армирует `useFormBundle` |
98
+ | `makeValidationConfig(model)` (`validateStep(n)` / `validateAll()`) | `flow` сборки | Визард берёт поток, а не конфиг |
99
+
100
+ ### Удалено: ноды формы — контракты вместо классов (этап 7)
101
+
102
+ | Wrong | Correct | Notes |
103
+ | ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
104
+ | классы `FormNode`, `FieldNode`, `GroupNode`, `ModelArrayNode` | интерфейсы `FormNode`, `FormControl`, `FormGroup`, `FormArray` | Классы реализации внутренние; ноды строит сборка |
105
+ | `FormProxy<T>` | `FormGroup<T>` | Тип группы |
106
+ | `isFieldNode`, `isGroupNode`, `isArrayNode` | `isFormControl`, `isFormGroup`, `isFormArray` | Гарды по метке вида; `isFormNode` остался |
107
+ | `node instanceof FieldNode` | `isFormControl(node)` | `instanceof` по интерфейсу невозможен |
108
+ | `node.status.value`, `FieldStatus` | `node.valid` / `invalid` / `pending` / `disabled` | Отдельного статуса нет |
109
+ | `node.patchValue(v)` | `node.setValue(v)` / `model.patch(partial)` | Частичное обновление — у модели |
110
+ | `markAsDirty()`, `markAsPristine()` | `dirty` ставит `setValue(v)`, снимает `reset()` | Изменились ли значения модели — `model.isDirty()` |
111
+ | `node.validate()` | `bundle.validate()` / `bundle.validateStep(id)` | |
112
+ | `group.fields` (Map), `group.getProxy()` | `form.<поле>`, `form.$` (все поля), `Object.values(form.$)` | `fields` теперь обычное поле данных |
113
+ | `node.id` | ключ строки — `array.keyAt(index)` | `id` теперь обычное поле данных |
114
+ | `array.items` | `array.at(i)` / `map` / `forEach` | |
115
+ | `array.length.value` | `array.length` (число; в эффекте подписывает) | Не сигнал |
116
+ | `key={row.id}` / `key={index}` у строк массива | `key={array.keyAt(index)}` | Ключ не меняется при `move` / `swap` |
117
+
118
+ Затеняют поле группы только члены `FormNode` (`value`, `issues`, `reset`, …): такое поле берут через
119
+ `form.$.value`. Имена `id`, `status`, `fields`, `submit` — обычные поля данных.
120
+
121
+ ### Удалено вместе со старым путём сборки формы
122
+
123
+ Форма собирается только из модели: `createForm({ initial | model, schema })`. Ноды строятся по
124
+ виду узла модели, правила живут в схеме валидации.
125
+
126
+ | Wrong | Correct | Notes |
127
+ | -------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
128
+ | `createLegacyForm(schema)` | `createForm({ initial, schema })` | Формы без модели нет |
129
+ | `new FormGroup({ email: { value, component } })` | `createFormFromModel({ model, schema })` | `FormGroup` — тип, ноды строит сборка |
130
+ | `new FormControl({ value: '' })` | `createForm({ initial: { email: '' } }).form.email` | `FormControl` — тип; значение принадлежит модели |
131
+ | `FieldConfig.validators` / `asyncValidators` | `validate` / `validateAsync` в `defineValidation` | Собственных валидаторов у ноды нет |
132
+ | `updateOn`, `debounce` узла поля; `field.setUpdateOn()` / `getUpdateOn()` | `validation: { strategy, debounce }` в `createForm` | Когда запускать проверку, решает стратегия валидации формы |
133
+ | `field.setValue(value, { emitEvent: false })` | `field.setValue(value)` | Опций у `setValue` нет |
134
+ | `ArrayNode` | `FormArray` | Нода массива одна — над массивом модели |
135
+ | `NodeFactory` | — | Сборка по ключам конфига удалена |
136
+ | `FormErrorHandler`, `ErrorStrategy` | — | Обслуживали валидаторы ноды |
137
+ | `FormSchema<T>`, `GroupNodeConfig<T>` | `FormSchemaNode` | Схема — дерево узлов, а не запись по полям |
138
+ | `ValidatorFn`, `AsyncValidatorFn`, `Validator<TForm, TField>` | `Rule<T>`, `AsyncRule<T>` из `@reformer/core/validation` | Правило — функция одного аргумента |
139
+
140
+ ### Удалено: подписки на нодах, доступ по строковому пути, псевдонимы
141
+
142
+ Реакции живут на слое модели (`defineFormBehavior`), поле берётся через форму, у каждой
143
+ операции одно имя.
144
+
145
+ | Wrong | Correct | Notes |
146
+ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
147
+ | `field.watch(cb)`, `form.watchField('email', cb)`, `form.watchFieldByPath(path, cb)` | `onChange(model.$.email, cb)` в `defineFormBehavior`; вне поведения — `watchField(model.$.email, cb)` из `@reformer/core/model` | Подписок на ноде нет |
148
+ | `field.computeFrom(sources, fn)`, `form.linkFields('a', 'b', fn)` | `compute(model.$.b, () => fn(model.a))` в `defineFormBehavior` | Вычисляемое значение — оператор поведения |
149
+ | `field.dispose()` | сборку — `bundle.dispose()`; форму из `createFormFromModel` — `disposeForm(form)` | В контракте нод `dispose` нет |
150
+ | `form.getFieldByPath('address.city')`, `form.getField('email')` | `form.address.city`, `form.email`; имя, занятое членом ноды, — `form.$.value` | Строковых путей к нодам нет |
151
+ | `form.getAllFields()` | `Object.values(form.$)` | `form.$` — ноды всех полей группы по имени |
152
+ | `form.touchAll()` | `form.markAsTouched()` | Отметка каскадная: уходит во все вложенные поля |
153
+ | `form.resetToInitial()`, `field.resetToInitial()` | `form.reset()` — к значениям создания формы; `model.reset()` — к точке отсчёта модели | См. `28-submit-and-reset.md` |
154
+ | `form.submitWithResult(handler)` | `bundle.submission.submit(handler)` → `SubmitResult` | Проверка и отправка одним вызовом |
155
+ | `getNodeType(node)` | `isFormControl` / `isFormGroup` / `isFormArray` | Вид ноды — гардами |
156
+ | `each(model.items, (item) => …)` | `applyEach(model.$.items, itemRules)` | Правила строки — отдельная схема над элементом |
157
+ | `import { cross } from '@reformer/core/validation'` | `defineValidation(({ model, cross }) => …)` | `cross` — только из аргумента схемы |
158
+ | `createCoreForm`, `CreateCoreFormConfig` | `createForm`, `CreateFormConfig` | Сборка одна |
159
+ | `createReactForm`, `useReactForm` (`@reformer/renderer-react`); `createJsonForm`, `useJsonForm` (`@reformer/renderer-json`) | `createForm` + `useFormBundle`; документ JSON — `createForm({ schema: document, registry })` | Фабрики слоёв удалены — сборка одна |
160
+ | `JsonFormRenderer`, `JsonRendererProvider` | `<FormRenderer form={bundle} />` из `@reformer/renderer-react` | Реестр уходит в сборку, а не в контекст |
161
+ | `renderBehavior`, `RenderBehaviorFn` | одно поведение `defineFormBehavior(({ model, form, schema, flow }) => …)` | Правила узлов — в поведении формы |
162
+ | `CoreForm`, `CreateFormConfigBase` | `FormBundle`, `CreateFormConfig` | Общие типы удалённых фабрик |
163
+ | `FormArrayProxy<T>` | `FormArray<T>` | Псевдоним типа удалён |
164
+ | `FormSubmitter`, `SubmittableForm`, `SubscriptionManager`, `createSchemaOverrideMaps`, `ModelArrayControl`, `FormControlsProxy` | — | Внутренние детали, из пакета не экспортируются |
165
+
166
+ ### Прежние ключи узла схемы
167
+
168
+ | Wrong | Correct | Notes |
169
+ | -------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
170
+ | `{ value: model.$.email, component }` | `{ model: model.$.email, component }` | Привязка поля — ключ `model` |
171
+ | `{ array: model.items, item }` | `{ model: model.$.items, item }` | Привязка массива под-форм — ручка, а не фасад |
172
+ | `{ email: { model, component } }` (запись «имя → узел») | `{ children: [{ model, component }] }` | Вложенные узлы читаются только из `children` |
173
+ | узел внутри `componentProps` | узел в `children` | В пропсы обход схемы не заглядывает |
174
+ | `testId` ключом узла | `componentProps: { testId }` | Ключа узла `testId` нет |
175
+
176
+ ### Операторы над сигналами — не в корне
177
+
178
+ `computeFrom`, `copyFrom`, `watchField`, `transformValue`, `resetWhen`, `syncFields`,
179
+ `revalidateWhen`, `enableWhen`, `disableWhen` из `@reformer/core` не импортируются.
180
+
181
+ | Wrong | Correct |
182
+ | -------------------------------- | ------------------------------------------------ |
183
+ | `import { enableWhen, copyFrom } from '@reformer/core'` | `import { enableWhen, copyFrom } from '@reformer/core/behaviors'` — внутри `defineFormBehavior` |
184
+ | `import { watchField } from '@reformer/core'` | `onChange` из `@reformer/core/behaviors`; сам примитив — `@reformer/core/model` |
185
+ | `import { computeFrom } from '@reformer/core'` | `compute` / `computeFrom` из `@reformer/core/behaviors` |
186
+
187
+ ### UI: удалённые field-версии компонентов (`@reformer/ui-kit`)
188
+
189
+ Отдельных «field-версий» у компонентов кита больше нет: в `component` поля кладётся сам
190
+ компонент, диалект он объявляет статикой `reformerAdapter`, связывает поле обёртка
191
+ (`FormField.Control` / рендерер).
192
+
193
+ | Wrong | Correct |
194
+ | ---------------------------------------------- | --------------------------------------------------------- |
195
+ | `InputField`, `SelectField`, `CheckboxField`, … (любой `*Field`) | `Input`, `SelectAsync`, `CheckboxWithLabel`, … — таблица в ui-kit `01-overview.md` |
196
+ | `InputField` + `type: 'number'` | `InputNumber` |
197
+ | `InputField` + `suggestions` | `InputSuggest` |
198
+ | `FileUploadField` + `variant: 'dropzone' \| 'input'` | `FileUploadDropzone` / `FileUploadInput` (без `variant`) |
199
+ | `withFormControl(MyControl, adapter)` | `defineFieldControl(MyControl, { adapter })` из `@reformer/ui-kit/fields` |
200
+
201
+ ### Common Import Errors
202
+
203
+ ```typescript
204
+ // WRONG - этих символов / путей НЕ существует
205
+ import { useForm, validateForm, validateFormModel } from '@reformer/core'; // NO!
206
+ import { useFormControl, useFormBundle } from '@reformer/core'; // NO! хуки — из /react
207
+ import { validate, applyWhen, equalTo } from '@reformer/core/validators'; // NO! операторов тут нет
208
+ import { defineValidationSchema, type ValidationSchema } from '@reformer/core/validation'; // NO!
209
+ import { resolveValidationError, useValidationMessage } from '@reformer/core/i18n'; // NO!
210
+ import { FieldNode, GroupNode, isFieldNode, type FormProxy } from '@reformer/core'; // NO!
211
+ import { defineSteps, useWizardStepValidation } from '@reformer/cdk/form-wizard'; // NO!
212
+ import { FormArray, useFormArray } from '@reformer/cdk/form-array'; // NO! — field-array
213
+ import { transformers } from '@reformer/core/behaviors'; // NO!
214
+ import type { FieldPath, ValidationSchemaFn, ValidationError } from '@reformer/core'; // NO!
215
+
216
+ // CORRECT
217
+ import { createModel, createForm, isFormControl } from '@reformer/core';
218
+ import { useFormBundle, useFormControl, useIssueMessage } from '@reformer/core/react';
219
+ import type { FormModel, FormGroup, FormSchemaNode, ValidationIssue } from '@reformer/core';
220
+ // Операторы валидации + раннер + defineValidation — отдельный сабпуть:
221
+ import {
222
+ validate, validateAsync, validateWhen, apply, applyEach,
223
+ defineValidation, validateModel, runValidation, applyValidationResult,
224
+ type Rule, type AsyncRule, type ValidationDefinition, type ValidationResult,
225
+ } from '@reformer/core/validation';
226
+ // Фабрики-валидаторы — правила значения `Rule<T>`, пустое значение пропускают:
227
+ import { required, email, min, minLength, pattern } from '@reformer/core/validators';
228
+ // Тексты замечаний — словарь формы:
229
+ import { defineIssueMessages } from '@reformer/core/i18n';
230
+ // Поведение (отдельный слой):
231
+ import { defineFormBehavior, compute, onChange, revalidateWhen } from '@reformer/core/behaviors';
232
+ // Компонент массива — FieldArray:
233
+ import { FieldArray, useFieldArray } from '@reformer/cdk/field-array';
234
+ ```
235
+
236
+ ### Schema Common Mistakes
237
+
238
+ Валидация и разметка — **разные** контракты. Узел схемы привязывает поле к ручке модели и
239
+ правил не несёт; правила живут в отдельной `defineValidation`.
240
+
241
+ ```typescript
242
+ // WRONG - запись «имя → узел», литерал вместо ручки и validators прямо в узле
243
+ const schema = {
244
+ name: '', // нет привязки к модели
245
+ email: { value: '', validators: [required()] }, // ни ключа value, ни validators у узла нет
246
+ };
247
+
248
+ // CORRECT - схема только связывает поле с ручкой модели...
249
+ const schema: FormSchemaNode = {
250
+ children: [
251
+ { model: model.$.name, component: Input, componentProps: { label: 'Name' } },
252
+ { model: model.$.email, component: Input, componentProps: { label: 'Email' } },
253
+ ],
254
+ };
255
+
256
+ // ...а правила — отдельная схема (уходит в createForm({ validation }) либо в validateModel):
257
+ const validation = defineValidation<Form>(({ model }) => {
258
+ validate(model.$.email, [required({ code: 'email.required' }), email()]);
259
+ });
260
+ ```
261
+
262
+ ### Behaviors Common Mistakes
263
+
264
+ ```typescript
265
+ // WRONG - строковые пути и (form) => ... — старый API
266
+ enableWhen(path.city, (form) => Boolean(form.country));
267
+
268
+ // CORRECT - сигналы модели, условие читает model
269
+ enableWhen(model.$.city, () => Boolean(model.country), { resetOnDisable: true });
270
+ ```
271
+
272
+ Поведение **не владеет** валидацией. Чтобы поведение инициировало прогон схемы — мост
273
+ `revalidateWhen` (или живая стратегия сборки `validation: { strategy: 'change' }`):
274
+
275
+ ```typescript
276
+ // CORRECT - поведение дёргает внешний раннер валидации при изменении зависимости
277
+ revalidateWhen([model.$.password], () => void validateModel(model, validation));
278
+ ```