@reformer/core 11.0.0-beta.1 → 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 (283) 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 -24
  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 -85
  42. package/dist/form/types/field-config.d.ts +27 -0
  43. package/dist/form/types/index.d.ts +5 -57
  44. package/dist/form/types/nodes.d.ts +159 -0
  45. package/dist/form/types/schema-node.d.ts +147 -46
  46. package/dist/form/types/validation-schema.d.ts +25 -17
  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 +17 -5
  97. package/dist/index.js +942 -2601
  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 +58 -36
  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 +1 -2
  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 -1
  192. package/dist/validators.js +86 -108
  193. package/docs/llms/01-api-reference.md +60 -34
  194. package/docs/llms/02-quick-start.md +112 -74
  195. package/docs/llms/03-api-signatures.md +369 -148
  196. package/docs/llms/04-common-patterns.md +58 -56
  197. package/docs/llms/05-common-mistakes.md +69 -44
  198. package/docs/llms/06-troubleshooting.md +14 -9
  199. package/docs/llms/07-complete-import.md +74 -43
  200. package/docs/llms/08-form-types.md +11 -4
  201. package/docs/llms/09-formschema.md +132 -84
  202. package/docs/llms/10-arrays.md +141 -61
  203. package/docs/llms/11-async-watchfield.md +2 -2
  204. package/docs/llms/13-multi-step.md +134 -63
  205. package/docs/llms/14-extended-mistakes.md +20 -17
  206. package/docs/llms/15-project-structure.md +224 -70
  207. package/docs/llms/16-ui-components.md +86 -46
  208. package/docs/llms/17-nonexistent-api.md +217 -32
  209. package/docs/llms/18-conditional-fields.md +71 -32
  210. package/docs/llms/19-reading-values.md +45 -4
  211. package/docs/llms/20-compute-vs-watch.md +25 -10
  212. package/docs/llms/21-array-operations.md +76 -26
  213. package/docs/llms/22-cycle-detection.md +1 -1
  214. package/docs/llms/23-copy-from.md +2 -2
  215. package/docs/llms/24-sync-fields.md +6 -5
  216. package/docs/llms/25-reset-when.md +14 -7
  217. package/docs/llms/26-transform-value.md +4 -4
  218. package/docs/llms/27-revalidate-when.md +16 -15
  219. package/docs/llms/28-submit-and-reset.md +278 -128
  220. package/docs/llms/29-async-preload.md +41 -31
  221. package/docs/llms/30-type-safety-recipes.md +105 -51
  222. package/docs/llms/31-async-validator-debounce.md +96 -53
  223. package/docs/llms/33-validation-strategy.md +178 -66
  224. package/docs/llms/34-i18n.md +298 -0
  225. package/docs/llms/35-unified-contract.md +309 -0
  226. package/docs/llms/36-issue-messages.md +284 -0
  227. package/docs/llms/37-compiled-schema.md +332 -0
  228. package/docs/llms/38-form-flow.md +396 -0
  229. package/llms-index.json +1 -0
  230. package/llms.txt +10690 -3089
  231. package/package.json +48 -9
  232. package/dist/behaviors-node-DGEFMTQu.js +0 -18
  233. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  234. package/dist/date-utils-dTT_x_Be.js +0 -31
  235. package/dist/derived-registry-CluZCQ7U.js +0 -16
  236. package/dist/form/behaviors.d.ts +0 -133
  237. package/dist/form/error-handler.d.ts +0 -166
  238. package/dist/form/factories/index.d.ts +0 -6
  239. package/dist/form/factories/node-factory.d.ts +0 -188
  240. package/dist/form/form-submitter.d.ts +0 -121
  241. package/dist/form/nodes/array-node.d.ts +0 -276
  242. package/dist/form/nodes/field-node.d.ts +0 -294
  243. package/dist/form/nodes/form-node.d.ts +0 -326
  244. package/dist/form/nodes/group-node.d.ts +0 -284
  245. package/dist/form/nodes/model-array-node.d.ts +0 -121
  246. package/dist/form/status-machine.d.ts +0 -115
  247. package/dist/form/types/deep-schema.d.ts +0 -107
  248. package/dist/form/types/form-proxy.d.ts +0 -141
  249. package/dist/form/unique-id.d.ts +0 -45
  250. package/dist/form/validation/validators/date-utils.d.ts +0 -30
  251. package/dist/form/validation/validators/email.d.ts +0 -23
  252. package/dist/form/validation/validators/future-date.d.ts +0 -23
  253. package/dist/form/validation/validators/integer.d.ts +0 -22
  254. package/dist/form/validation/validators/is-date.d.ts +0 -23
  255. package/dist/form/validation/validators/is-number.d.ts +0 -24
  256. package/dist/form/validation/validators/max-age.d.ts +0 -25
  257. package/dist/form/validation/validators/max-date.d.ts +0 -25
  258. package/dist/form/validation/validators/max-files.d.ts +0 -26
  259. package/dist/form/validation/validators/max-length.d.ts +0 -27
  260. package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
  261. package/dist/form/validation/validators/max.d.ts +0 -25
  262. package/dist/form/validation/validators/min-age.d.ts +0 -25
  263. package/dist/form/validation/validators/min-date.d.ts +0 -25
  264. package/dist/form/validation/validators/min-files.d.ts +0 -26
  265. package/dist/form/validation/validators/min-length.d.ts +0 -27
  266. package/dist/form/validation/validators/min.d.ts +0 -25
  267. package/dist/form/validation/validators/multiple-of.d.ts +0 -24
  268. package/dist/form/validation/validators/non-negative.d.ts +0 -22
  269. package/dist/form/validation/validators/non-zero.d.ts +0 -22
  270. package/dist/form/validation/validators/past-date.d.ts +0 -23
  271. package/dist/form/validation/validators/pattern.d.ts +0 -33
  272. package/dist/form/validation/validators/required.d.ts +0 -32
  273. package/dist/form/validation/validators/url.d.ts +0 -43
  274. package/dist/form/validation-schema.d.ts +0 -106
  275. package/dist/form/validation-strategy.d.ts +0 -55
  276. package/dist/form/validators.d.ts +0 -11
  277. package/dist/form-model-DYCzGGKF.js +0 -286
  278. package/dist/state/index.d.ts +0 -31
  279. package/dist/state/subscription-manager.d.ts +0 -180
  280. package/dist/state/types.d.ts +0 -144
  281. package/dist/state.d.ts +0 -2
  282. package/dist/state.js +0 -21
  283. package/dist/subscription-manager-705bls97.js +0 -171
@@ -2,52 +2,78 @@
2
2
 
3
3
  ### Imports (CRITICALLY IMPORTANT)
4
4
 
5
- Архитектура M1: значения живут в **модели** (`createModel`), а форма (`createForm`) строит ноды поверх сигналов модели. Behaviors работают на сигналах (`model.$.field`), а не на строковых путях.
6
-
7
- | What | Where |
8
- | ------------------------------------------------------------------------------------------------------ | --------------------------- |
9
- | `createModel`, `createForm` | `@reformer/core` |
10
- | `validateModel`, `defineValidationSchema`, `validate`, `validateAsync`, `validateWhen`, `cross`, `each`, `apply` | `@reformer/core/validation` |
11
- | `Rule`, `AsyncRule`, `ValidationSchema` (типы) | `@reformer/core/validation` |
12
- | `useFormControl`, `useFormControlValue`, `useArrayLength` | `@reformer/core` |
13
- | `FormModel`, `FormProxy`, `FieldNode`, `GroupNode`, `ArrayNode`, `ModelArrayNode` | `@reformer/core` |
14
- | `ModelSignals`, `ModelArray`, `ModelValue`, `ModelObject`, `PathAwareSignal` | `@reformer/core` |
15
- | `ValidationError`, `FieldConfig`, `FormSchema`, `FieldControlState` | `@reformer/core` |
16
- | `computeFrom`, `copyFrom`, `watchField`, `enableWhen`, `disableWhen` | `@reformer/core` (примитивы) |
17
- | `transformValue`, `resetWhen`, `syncFields`, `revalidateWhen` | `@reformer/core` (примитивы) |
18
- | `required`, `min`, `max`, `minLength`, `maxLength`, `email`, `pattern`, `url`, `phone` | `@reformer/core/validators` |
19
- | `isNumber`, `integer`, `multipleOf`, `nonNegative`, `nonZero` | `@reformer/core/validators` |
20
- | `isDate`, `minDate`, `maxDate`, `pastDate`, `futureDate`, `minAge`, `maxAge` | `@reformer/core/validators` |
21
- | `defineFormBehavior`, `compute`, `computeFrom`, `copyFrom`, `onChange`, `enableWhen`, `disableWhen` | `@reformer/core/behaviors` |
22
- | `transformValue`, `resetWhen`, `syncFields`, `revalidateWhen`, `apply`, `applyEach`, `aggregateInto` | `@reformer/core/behaviors` |
23
- | `exclusiveFlag`, `onDispose`, `getScope`, `effect`, `defer` | `@reformer/core/behaviors` |
24
-
25
- > **Два способа писать behaviors.** Низкоуровневые примитивы (`computeFrom`, `copyFrom`, `watchField`,
26
- > `enableWhen`, …) экспортируются из `@reformer/core`, принимают **сигналы** (`model.$.x`), возвращают
27
- > **cleanup-функцию** и вызываются императивно (например, в `useEffect`). Декларативный DSL
28
- > (`defineFormBehavior` + операторы) экспортируется из `@reformer/core/behaviors`, регистрирует cleanup
29
- > сам и передаётся в `createForm({ behavior })`. См. `20-compute-vs-watch.md`.
30
-
31
- > **Валидация — отдельный слой.** Layout-схема `createForm` НЕ несёт валидаторов. Правила живут в
32
- > `defineValidationSchema<T>(({ model }) => { validate(model.$.x, [required(), min(50000)]); ... })`
5
+ Значения живут в **модели** (`createModel`), а форма (`createForm`) строит ноды поверх сигналов модели. Схема, правила и поведение привязываются к ручкам модели (`model.$.field`), а не к строковым путям.
6
+
7
+ | What | Where |
8
+ | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
9
+ | `createModel`, `createForm`, `createFormFromModel`, `disposeForm`, `arrayOf`, `modelOf` | `@reformer/core` |
10
+ | `compileSchema`, `createFormFlow`, `createFormLevel`, `createSubmission`, `getNodeForSignal` | `@reformer/core` |
11
+ | `isFormNode`, `isFormControl`, `isFormGroup`, `isFormArray` (гарды нод) | `@reformer/core` |
12
+ | `FormModel`, `FormBundle`, `FormNode`, `FormControl`, `FormGroup`, `FormArray`, `FormFields` | `@reformer/core` |
13
+ | `ModelSignals`, `ModelArray`, `ModelValue`, `ModelObject`, `PathAwareSignal` | `@reformer/core` |
14
+ | `ValidationIssue`, `RuleOptions`, `FormSchemaNode`, `ComponentRef`, `CompiledSchema` | `@reformer/core` |
15
+ | `FormValidation`, `FormFlowConfig`, `FormFlow`, `FlowTransition`, `SubmitResult` | `@reformer/core` |
16
+ | `FormSubmission`, `SubmissionDependencies`, `ActionCheck` (отправка) | `@reformer/core` |
17
+ | `defineValidation`, `validate`, `validateAsync`, `validateWhen`, `apply`, `applyEach` | `@reformer/core/validation` |
18
+ | `validateModel`, `runValidation`, `applyValidationResult` (раннер), `createFormValidation` | `@reformer/core/validation` |
19
+ | `Rule`, `AsyncRule`, `ValidationDefinition`, `ValidationScope`, `ValidationResult` (типы) | `@reformer/core/validation` |
20
+ | `FormValidationController`, `FormValidationRunOptions`, `ValidationStrategyKind` (типы стратегии) | `@reformer/core/validation` |
21
+ | `useFormBundle`, `useFormControl`, `useFormControlValue`, `useArrayLength`, `useFormValidation` | `@reformer/core/react` |
22
+ | `useFormFlow`, `FormBundleContext`, `useFormBundleContext` | `@reformer/core/react` |
23
+ | `bindFieldProps`, `getFieldAdapter`, `FieldAdapter`, `FieldHandle`, `FieldControlState` (связка поля) | `@reformer/core/react` |
24
+ | `I18nProvider`, `useI18n`, `useMessages`, `useIssueMessage` | `@reformer/core/react` |
25
+ | `defineIssueMessages`, `resolveIssueMessage`, `createI18n`, `extendLocale`, `createLocaleLoader` | `@reformer/core/i18n` |
26
+ | `computeFrom`, `copyFrom`, `watchField`, `transformValue`, `resetWhen`, `syncFields`, `revalidateWhen` | `@reformer/core/model` (примитивы над сигналами) |
27
+ | `deepEqual`, `deferredWritesSettled` (сравнение значений, ожидание отложенных записей поведения) | `@reformer/core/model` |
28
+ | `required`, `min`, `max`, `minLength`, `maxLength`, `email`, `pattern`, `url`, `phone` | `@reformer/core/validators` |
29
+ | `isNumber`, `integer`, `multipleOf`, `nonNegative`, `nonZero` | `@reformer/core/validators` |
30
+ | `isDate`, `minDate`, `maxDate`, `pastDate`, `futureDate`, `minAge`, `maxAge` | `@reformer/core/validators` |
31
+ | `defineFormBehavior`, `compute`, `computeFrom`, `copyFrom`, `onChange`, `enableWhen`, `disableWhen` | `@reformer/core/behaviors` |
32
+ | `transformValue`, `resetWhen`, `syncFields`, `revalidateWhen`, `apply`, `applyEach`, `aggregateInto` | `@reformer/core/behaviors` |
33
+ | `exclusiveFlag`, `onDispose`, `getScope`, `effect`, `defer` | `@reformer/core/behaviors` |
34
+ | `hideWhen`, `onComponentEvent`, `renderEffect`, `onInit`, `onMount`, `onUnmount` (узлы схемы) | `@reformer/core/behaviors` |
35
+
36
+ > **Корень `@reformer/core` без React.** Хуки, контекст сборки, связка поля с контролом и
37
+ > провайдер локали — только из `@reformer/core/react`. Корень работает на сервере и с любым слоем
38
+ > представления.
39
+
40
+ > **Поведение формы — `@reformer/core/behaviors`.** Декларативный DSL (`defineFormBehavior` +
41
+ > операторы) регистрирует cleanup сам и передаётся в `createForm({ behavior })`. Примитивы над
42
+ > сигналами (`computeFrom`, `copyFrom`, `watchField`, …) в корень `@reformer/core` не входят: они
43
+ > лежат в сабпате `@reformer/core/model`, принимают **сигналы** (`model.$.x`), возвращают
44
+ > **cleanup-функцию** и нужны вне формы. `enableWhen` / `disableWhen` существуют только как
45
+ > операторы поведения. См. `20-compute-vs-watch.md`.
46
+
47
+ > **Валидация — отдельный слой.** Схема формы правил НЕ несёт. Правила живут в
48
+ > `defineValidation<T>(({ model, cross }) => { validate(model.$.x, [required(), min(50000)]); ... })`
33
49
  > из `@reformer/core/validation`; фабрики `required()`, `min(50000)`, `email()` возвращают
34
- > `Rule<T> = (value) => ValidationError | null` и передаются массивом в `validate(sig, [...])`.
35
- > Запуск — только внешним раннером `await validateModel(model, schema)` (`Promise<boolean>`, ошибки
36
- > сам роутит в ноды; `form.validate()`/`form.submit()` schema-валидацию НЕ гоняют).
50
+ > `Rule<T> = (value: T) => ValidationIssue | null` и передаются массивом в `validate(sig, [...])`.
51
+ > Правила отдают сборке: `createForm({ validation, flow })`. Запуск — уровнем формы:
52
+ > `bundle.validate()` (`ValidationResult` со статусом `valid` / `invalid` / `error` / `cancelled`),
53
+ > `bundle.validateStep(id)`, отправка `bundle.submission.submit(handler)` (`SubmitResult`). Без
54
+ > сборки — раннер `validateModel(model, rules)` (`Promise<boolean>`) либо `runValidation` (полный
55
+ > результат). Снять сборку — `bundle.dispose()`. См. `38-form-flow.md`.
56
+
57
+ > **Текста у замечания нет.** `ValidationIssue = { code, params?, severity? }`; строку по коду
58
+ > даёт словарь формы (`defineIssueMessages`) поверх словаря локали. Опции правил —
59
+ > `RuleOptions = { code?, params? }`. См. `36-issue-messages.md`.
37
60
 
38
61
  ### Type Values
39
62
 
40
63
  - Опциональные числа: `number | null` (конвенция «пользователь очистил поле»)
41
64
  - Опциональные строки: `string` (по умолчанию пустая строка) или `string | null`
42
- - Form-shape тип объявляй как `type`-alias — см. `30-type-safety-recipes.md`
65
+ - Форму данных объявляй через `interface` или `type` — оба варианта работают, см.
66
+ `30-type-safety-recipes.md`
43
67
 
44
68
  ### React Hooks Comparison (CRITICALLY IMPORTANT)
45
69
 
46
70
  | Hook | Return Type | Subscribes To | Use Case |
47
71
  |------|-------------|---------------|----------|
48
- | `useFormControl(field)` | `{ value, errors, disabled, touched, valid, invalid, pending, shouldShowError, componentProps }` | Все сигналы поля | Полное состояние поля, инпуты |
72
+ | `useFormControl(field)` | `{ value, issues, disabled, touched, dirty, valid, invalid, pending, shouldShowError, shouldShowIssues, componentProps }` | Все сигналы поля | Полное состояние поля, инпуты |
49
73
  | `useFormControlValue(field)` | `T` (значение напрямую) | Только сигнал value | Условный рендеринг |
50
74
  | `useArrayLength(array)` | `number` | Только длина массива | Реактивная длина массива |
75
+ | `useFormFlow(bundle.flow)` | `{ current, index, completed, isFirst, isLast, validating }` | Сигналы потока шагов | Свои кнопки и индикатор шагов |
76
+ | `useIssueMessage(messages?)` | `(issue) => string` | Локаль | Текст замечания в своём компоненте |
51
77
 
52
78
  **CRITICAL**: Не деструктурируй `useFormControlValue`! Он возвращает `T` напрямую, НЕ `{ value: T }`.
53
79
 
@@ -59,5 +85,5 @@ const { value: loanType } = useFormControlValue(control.loanType);
59
85
  const loanType = useFormControlValue(control.loanType);
60
86
 
61
87
  // CORRECT - useFormControl returns object, destructuring OK
62
- const { value, errors, disabled } = useFormControl(control.loanType);
88
+ const { value, issues, disabled } = useFormControl(control.loanType);
63
89
  ```
@@ -1,80 +1,99 @@
1
1
  ## 1.5 QUICK START - Minimal Working Form
2
2
 
3
3
  > **Schema-driven UI rule (read first)**: компонент И его пропсы (label, placeholder,
4
- > options, type) объявляются в **схеме поля** (`component` + `componentProps`).
4
+ > options, type) объявляются в **узле схемы** (`component` + `componentProps`).
5
5
  > В JSX рендерится один универсальный `<FormField control={form.x} />` из
6
6
  > `@reformer/ui-kit` БЕЗ дополнительных props. Не пиши свои `Input`/`Select`/
7
7
  > `Checkbox`-обёртки с `label`-prop'ами — это anti-pattern. См.
8
8
  > `find_recipe(package="@reformer/ui-kit", topic="form-field-integration")`.
9
9
 
10
- Архитектура M1: сначала создаётся **модель данных** (`createModel`), затем **форма**
11
- (`createForm({ model, schema })`), где схема привязывает поля к сигналам модели
12
- (`model.$.field`). Layout-схема НЕ несёт валидаторов — валидация живёт в отдельной
13
- схеме `defineValidationSchema` из `@reformer/core/validation` и запускается внешним
14
- раннером `validateModel(model, schema)`.
10
+ **Модель данных** — источник истины значений. Схема — дерево узлов, привязанных к ручкам модели
11
+ (`model: model.$.field`). Сборка идёт ОДНИМ вызовом `createForm`: он создаёт модель, строит форму,
12
+ запускает поведение и собирает уровень формы — проверку и отправку. Правил в схеме нет — они
13
+ живут в отдельной схеме `defineValidation` из `@reformer/core/validation` и уходят в сборку полем
14
+ `validation`; запускает их `bundle.submission.submit(handler)` / `bundle.validate()`. Текста у
15
+ правил нет: правило несёт код, строку по коду даёт словарь формы (`defineIssueMessages`).
15
16
 
16
17
  ```typescript
17
- import { createModel, createForm, type FormProxy } from '@reformer/core';
18
- import { defineValidationSchema, validate, validateModel } from '@reformer/core/validation';
18
+ import { createForm, type FormModel, type FormGroup, type FormSchemaNode } from '@reformer/core';
19
+ import { useFormBundle } from '@reformer/core/react';
20
+ import { defineIssueMessages } from '@reformer/core/i18n';
21
+ import { defineValidation, validate } from '@reformer/core/validation';
19
22
  import { required, email } from '@reformer/core/validators';
23
+ import { ValidationMessagesProvider } from '@reformer/cdk';
20
24
  import { FormField, Input, Button } from '@reformer/ui-kit';
21
25
 
22
- // 1. Define form type as `type` alias (not `interface` — see Recipe 2)
23
- type ContactForm = {
26
+ // 1. Form data type — `interface` or `type`, both work
27
+ interface ContactForm {
24
28
  name: string;
25
29
  email: string;
26
- };
30
+ }
27
31
 
28
- // 2. Model (источник истины значений)
29
- const model = createModel<ContactForm>({ name: '', email: '' });
30
-
31
- // 3. Layout-schema: привязка поля к сигналу (model.$.field) + component/componentProps.
32
- // БЕЗ validators — валидация в отдельной схеме (шаг 4).
33
- const schema = {
34
- name: {
35
- value: model.$.name,
36
- component: Input,
37
- componentProps: { label: 'Name', placeholder: 'Your name' },
38
- },
39
- email: {
40
- value: model.$.email,
41
- component: Input,
42
- componentProps: { label: 'Email', type: 'email' },
43
- },
44
- };
32
+ // 2. Схема — билдер (model) => дерево узлов. Узел поля: привязка `model` + component/componentProps.
33
+ // Вложенные узлы лежат в `children`. Правил здесь нет — они в отдельной схеме (шаг 3).
34
+ const contactSchema = (model: FormModel<ContactForm>): FormSchemaNode => ({
35
+ children: [
36
+ {
37
+ model: model.$.name,
38
+ component: Input,
39
+ componentProps: { label: 'Name', placeholder: 'Your name' },
40
+ },
41
+ {
42
+ model: model.$.email,
43
+ component: Input,
44
+ componentProps: { label: 'Email', type: 'email' },
45
+ },
46
+ ],
47
+ });
48
+
49
+ // 3. Схема валидации — отдельный слой (@reformer/core/validation). Правило несёт код, не текст.
50
+ const contactValidation = defineValidation<ContactForm>(({ model }) => {
51
+ validate(model.$.name, [required({ code: 'name.required' })]);
52
+ validate(model.$.email, [required({ code: 'email.required' }), email()]);
53
+ });
45
54
 
46
- // 4. Validation-schema — отдельный слой (@reformer/core/validation)
47
- const contactValidation = defineValidationSchema<ContactForm>(({ model }) => {
48
- validate(model.$.name, [required({ message: 'Name is required' })]);
49
- validate(model.$.email, [required({ message: 'Email is required' }), email({ message: 'Invalid email' })]);
55
+ // 4. Тексты замечаний по кодам — словарь формы (встроенный код `email` берёт текст из локали)
56
+ const contactMessages = defineIssueMessages({
57
+ 'name.required': 'Укажите имя',
58
+ 'email.required': 'Укажите email',
50
59
  });
51
60
 
52
- // 5. Form — ноды поверх сигналов модели
53
- const form = createForm<ContactForm>({ model, schema });
61
+ // 5. Сборка ОДНИМ вызовом: модель из initial + ноды поверх её сигналов + уровень формы.
62
+ const createContactForm = () =>
63
+ createForm<ContactForm>({
64
+ initial: { name: '', email: '' },
65
+ schema: contactSchema,
66
+ validation: contactValidation,
67
+ });
54
68
 
55
69
  // 6. Use in React component — thin JSX, FormField does ALL heavy lifting
56
70
  function ContactFormComponent() {
57
- const handleSubmit = async (e: React.FormEvent) => {
58
- e.preventDefault();
59
- // form.submit()/validate() НЕ гоняют schema-валидацию — только внешний validateModel
60
- const ok = await validateModel(model, contactValidation); // Promise<boolean>, ошибки сам роутит в ноды
61
- if (ok) {
62
- console.log('Form submitted:', model.get());
63
- }
71
+ // useFormBundle зовёт фабрику ровно один раз и держит сборку стабильной между рендерами.
72
+ const contact = useFormBundle(createContactForm);
73
+ const { form } = contact;
74
+
75
+ const handleSubmit = async (event: React.FormEvent) => {
76
+ event.preventDefault();
77
+ // Один прогон всех правил: при замечаниях обработчик не вызывается, а вся форма помечается
78
+ // тронутой — FormField покажет замечания.
79
+ const outcome = await contact.submission.submit((values) => api.send(values));
80
+ if (outcome.status === 'submitted') console.log('Form submitted');
64
81
  };
65
82
 
66
83
  return (
67
- <form onSubmit={handleSubmit}>
68
- <FormField control={form.name} testId="name" />
69
- <FormField control={form.email} testId="email" />
70
- <Button type="submit">Send</Button>
71
- </form>
84
+ <ValidationMessagesProvider messages={contactMessages}>
85
+ <form onSubmit={handleSubmit}>
86
+ <FormField control={form.name} testId="name" />
87
+ <FormField control={form.email} testId="email" />
88
+ <Button type="submit">Send</Button>
89
+ </form>
90
+ </ValidationMessagesProvider>
72
91
  );
73
92
  }
74
93
 
75
94
  // 7. Pass form to child components via props (NOT context!)
76
95
  type FormStepProps = {
77
- form: FormProxy<ContactForm>;
96
+ form: FormGroup<ContactForm>;
78
97
  };
79
98
 
80
99
  function FormStep({ form }: FormStepProps) {
@@ -82,16 +101,27 @@ function FormStep({ form }: FormStepProps) {
82
101
  }
83
102
  ```
84
103
 
85
- > **Стабильность инстанса.** В React создавай model/schema/form ОДИН раз через `useMemo(() => { … }, [])`
86
- > — иначе форма пересоздаётся на каждый рендер. См. `28-submit-and-reset.md`, `29-async-preload.md`.
104
+ > **Стабильность сборки.** Форму создают ОДИН раз: в React — `useFormBundle(фабрика)`, вне React —
105
+ > константой модуля. Иначе форма пересоздаётся на каждый рендер. См. `28-submit-and-reset.md`,
106
+ > `29-async-preload.md`.
87
107
 
88
- ### Arrays of objects — `{ array, item }` schema node
108
+ > **Проверка и отправка — уровень формы.** Отправка — отдельный объект сборки:
109
+ > `bundle.submission.submit(handler)` прогоняет все правила один раз и зовёт обработчик только при
110
+ > статусе `valid`; результат — `SubmitResult` (`submitted` / `invalid` / `error` / `cancelled` /
111
+ > `failed`). `bundle.validate()` проверяет всю форму и отвечает `ValidationResult`. У нод методов
112
+ > `validate()` / `submit()` нет. Многошаговая форма — `createForm({ flow: { steps: [{ id, rules }] } })`,
113
+ > см. `38-form-flow.md`. Вне React сборку снимают `bundle.dispose()`, когда форма больше не нужна.
89
114
 
90
- Массивы объектов принадлежат модели (`model.arrayField`). В схеме объявляются узлом
91
- `{ array: model.<path>, item: (itemModel) => itemSchema }`, где `item` строит под-схему
92
- для каждого элемента из его под-модели (`FormModel<Item>`):
115
+ ### Arrays of objects — узел `{ model, item }`
116
+
117
+ Массивы объектов принадлежат модели (`model.arrayField`). В схеме массив под-форм объявляется узлом
118
+ `{ model: model.$.<path>, item: (itemModel) => узел }`, где `item` строит разметку одной строки из
119
+ её под-модели (`FormModel<Item>`):
93
120
 
94
121
  ```typescript
122
+ import { arrayOf, createForm, type FormModel, type FormSchemaNode } from '@reformer/core';
123
+ import { FieldArray, InputNumber, SelectAsync, Textarea } from '@reformer/ui-kit';
124
+
95
125
  type PropertyItem = {
96
126
  type: 'apartment' | 'house';
97
127
  description: string;
@@ -100,33 +130,41 @@ type PropertyItem = {
100
130
 
101
131
  type MyForm = { properties: PropertyItem[] };
102
132
 
103
- const model = createModel<MyForm>({ properties: [] });
104
-
105
- // под-схема одного элемента: item.$.field — сигнал под-модели элемента
106
- const propertyItem = (item: FormModel<PropertyItem>) => ({
107
- type: {
108
- value: item.$.type,
109
- component: Select,
110
- componentProps: { label: 'Тип', options: [/* ... */] },
111
- },
112
- description: { value: item.$.description, component: Textarea, componentProps: { label: 'Описание' } },
113
- estimatedValue: {
114
- value: item.$.estimatedValue,
115
- component: Input,
116
- componentProps: { label: 'Стоимость', type: 'number' },
117
- },
133
+ // шаблон новой строки — его кладёт `push()` без значения и кнопка «Добавить»
134
+ const blankProperty = (): PropertyItem => ({ type: 'apartment', description: '', estimatedValue: 0 });
135
+
136
+ // разметка одной строки: item.$.field — ручка под-модели строки
137
+ const propertyRow = (item: FormModel<PropertyItem>): FormSchemaNode => ({
138
+ children: [
139
+ {
140
+ model: item.$.type,
141
+ component: SelectAsync,
142
+ componentProps: { label: 'Тип', options: [/* ... */] },
143
+ },
144
+ { model: item.$.description, component: Textarea, componentProps: { label: 'Описание' } },
145
+ {
146
+ model: item.$.estimatedValue,
147
+ component: InputNumber,
148
+ componentProps: { label: 'Стоимость' },
149
+ },
150
+ ],
118
151
  });
119
152
 
120
- const schema = {
121
- properties: { array: model.properties, item: propertyItem },
122
- };
123
-
124
- const form = createForm<MyForm>({ model, schema });
153
+ const { model, form } = createForm<MyForm>({
154
+ initial: { properties: arrayOf(blankProperty) },
155
+ schema: (model) => ({
156
+ children: [{ model: model.$.properties, component: FieldArray, item: propertyRow }],
157
+ }),
158
+ });
125
159
 
126
160
  // Операции над массивом — на модели:
127
- model.properties.push({ type: 'apartment', description: '', estimatedValue: 0 });
161
+ model.properties.push(); // новая строка по шаблону
162
+ model.properties.push({ type: 'house', description: '', estimatedValue: 0 });
128
163
  model.properties.removeAt(0);
129
164
  model.properties.length; // реактивная длина
165
+ form.properties.at(0); // форма строки (FormGroup<PropertyItem>)
166
+ form.properties.length; // число строк (число, не сигнал)
167
+ form.properties.keyAt(0); // стабильный ключ строки — для React `key`
130
168
  ```
131
169
 
132
170
  Подробнее в `10-arrays.md`, `21-array-operations.md` и `find_recipe(topic="form-array")`.