@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
@@ -1,91 +1,162 @@
1
1
  ## 12. MULTI-STEP FORM VALIDATION
2
2
 
3
- Каждый шаг — своя `ValidationSchema<Form>` (обычная функция `({ model }) => void`, обёрнутая
4
- `defineValidationSchema`). Переход к следующему шагу проверяется `validateModel(model, stepSchema)`;
5
- полный submit — по общей схеме (композиция шагов через `apply(...)`). `validateModel` сам разносит
6
- ошибки по нодам формы (`getNodeForSignal(sig).setErrors(...)`), поэтому UI подсветит проблемные поля
7
- текущего шага автоматически. Валидация живёт ОТДЕЛЬНО от layout: сам `RenderNode`/JSON-узел валидаторов
8
- не несёт — схема инъектируется в рантайме и роутит ошибки по тем же нодам.
3
+ Многошаговая форма — **поток шагов** сборки: `createForm({ flow: { steps: [{ id, rules }] } })`.
4
+ Каждый шаг — идентификатор и своя `ValidationDefinition<Form>` (обычная функция
5
+ `({ model, cross }) => void`, обёрнутая `defineValidation`). Идентификатор шага — `selector` узла
6
+ шага в схеме. Правила вне шагов (межшаговые) — `validation: { extras }`: они проверяются только
7
+ целиком, при отправке. Валидация живёт ОТДЕЛЬНО от схемы формы: узел схемы (TS или JSON) правил не
8
+ несёт.
9
+
10
+ Сборка отдаёт поток `bundle.flow` (`next` / `previous` / `goTo`), проверку шага
11
+ `bundle.validateStep(id)`, полную проверку `bundle.validate()` и отправку
12
+ `bundle.submission.submit(handler)`. Замечания разносятся по нодам сами — UI подсветит поля
13
+ текущего шага. Полный справочник — [38-form-flow.md](./38-form-flow.md).
14
+
15
+ ### Поток шагов сборки
9
16
 
10
17
  ```typescript
11
- import { type FormModel } from '@reformer/core';
12
- import {
13
- validate,
14
- apply,
15
- defineValidationSchema,
16
- validateModel,
17
- type ValidationSchema,
18
- } from '@reformer/core/validation';
19
- import { required, min } from '@reformer/core/validators';
18
+ import { createForm } from '@reformer/core';
19
+ import { useFormBundle } from '@reformer/core/react';
20
20
 
21
- // Под-схема шага — обычная функция ({ model }) => void. Значения проверяет оператор validate(sig, [rules]).
22
- const step1Schema = defineValidationSchema<Form>(({ model }) => {
23
- validate(model.$.loanType, [required()]);
24
- validate(model.$.loanAmount, [required(), min(50000)]);
25
- });
21
+ const createLoanForm = () =>
22
+ createForm<Form>({
23
+ initial,
24
+ schema: formSchema, // узлы шагов несут selector: 'loan', 'applicant', 'confirm'
25
+ validation: { extras: crossStepRules }, // правила всей формы — только целиком
26
+ flow: {
27
+ steps: [
28
+ { id: 'loan', rules: loanRules },
29
+ { id: 'applicant', rules: applicantRules },
30
+ { id: 'confirm' }, // шаг без правил
31
+ ],
32
+ },
33
+ });
26
34
 
27
- const step2Schema = defineValidationSchema<Form>(({ model }) => {
28
- validate(model.$.personalData.firstName, [required()]);
29
- validate(model.$.personalData.lastName, [required()]);
30
- });
35
+ // в компоненте: фабрика вызывается один раз, сборка стабильна между рендерами
36
+ const loan = useFormBundle(createLoanForm);
31
37
 
32
- // Карта шагов + полная схема (композиция под-схем через apply — заменяет пошаговую группировку деревом)
33
- const STEP_SCHEMAS: readonly ValidationSchema<Form>[] = [step1Schema, step2Schema];
34
- const fullSchema = defineValidationSchema<Form>(() => apply(...STEP_SCHEMAS));
38
+ // в обработчиках событий
39
+ await loan.flow?.next(); // проверить правила текущего шага и перейти → FlowTransition
40
+ await loan.validateStep('loan'); // правила одного шага → ValidationResult
41
+ await loan.validate(); // все шаги + extras → ValidationResult
42
+ await loan.submission.submit(sendLoan); // один прогон всех правил; обработчик — только при valid
43
+ loan.validating.value; // идёт ли проверка — полная либо шага
35
44
  ```
36
45
 
37
- ```typescript
38
- // Переход к следующему шагу. validateModel возвращает Promise<boolean> (true = нет блокирующих ошибок;
39
- // severity:'warning' не блокирует). Устаревшие прогоны той же (model, schema) отменяются автоматически.
40
- const goToNextStep = async () => {
41
- const ok = await validateModel(model, STEP_SCHEMAS[currentStep - 1]);
42
- if (!ok) return; // ошибки уже проставлены в ноды текущего шага
43
- setCurrentStep(currentStep + 1);
44
- };
45
-
46
- // Полный submit — по общей схеме
47
- const handleSubmit = async () => {
48
- const ok = await validateModel(model, fullSchema);
49
- if (ok) {
50
- await onSubmit(model.get());
51
- }
52
- };
53
- ```
46
+ - Полный набор правил — правила всех шагов и `extras`; свой полный набор задаёт
47
+ `validation: { rules }` (тогда правила шагов работают только для `validateStep` / `next`).
48
+ - `next()` на шаге с замечаниями остаётся на месте: `{ status: 'stayed', reason: 'invalid',
49
+ validation }`; поля шага помечаются тронутыми. Переход — `{ status: 'moved', from, to }`.
50
+ - Поле правили, пока шла проверка шага (async-правило), — `next()` остаётся на шаге с
51
+ `reason: 'cancelled'`: правила этого значения не видели. Следующий `next()` проверит новое.
52
+ - Стратегия `afterFirstSubmit` переходит в живую фазу только от отправки
53
+ (`bundle.submission.submit`): `next()`, `validateStep` и `validate` её не переключают.
54
+ - `goTo(id)` открывает первый шаг и шаги после пройденных; прыжок вперёд через непройденный шаг —
55
+ `reason: 'locked'`. Назад — всегда.
56
+ - В dev сборка предупреждает о шаге `flow.steps`, для которого в схеме нет узла с таким `selector`.
57
+ - Правила (`loanRules`, `extras`) — стабильные ссылки: константы модуля через `defineValidation`.
54
58
 
55
- Слой-потребитель (`FormWizard`) обычно оборачивает это в конфиг с per-step и полной валидацией:
59
+ В большой форме шаг объявляют один раз — в списке шагов (`flow/`): из него строятся и узлы шагов
60
+ схемы, и `flow.steps` сборки. См. `15-project-structure.md`.
56
61
 
57
- ```typescript
58
- function makeValidationConfig(model: FormModel<Form>) {
59
- return {
60
- validateStep: (n: number): Promise<boolean> => validateModel(model, STEP_SCHEMAS[n - 1]),
61
- validateAll: (): Promise<boolean> => validateModel(model, fullSchema),
62
+ ### Визард — адаптер потока
63
+
64
+ `FormWizard` из `@reformer/cdk` и `@reformer/ui-kit` рисует поток и зовёт его переходы:
65
+
66
+ ```tsx
67
+ import { FormWizard } from '@reformer/ui-kit';
68
+
69
+ function LoanWizard() {
70
+ const loan = useFormBundle(createLoanForm);
71
+ const send = async () => {
72
+ const outcome = await loan.submission.submit(sendLoan);
73
+ if (outcome.status !== 'submitted') showHint('Проверьте форму');
62
74
  };
75
+ return <FormWizard form={loan.form} flow={loan.flow} steps={STEPS} onSubmit={send} />;
63
76
  }
64
77
  ```
65
78
 
66
- ### Multi-Step Component Example
79
+ - В JSX — `form`, `flow={bundle.flow}` и `steps` пропсами; отправку проверяет приложение через
80
+ `bundle.submission.submit(handler)`.
81
+ - В схеме формы — узел `{ selector: 'wizard', component: FormWizard, children: [шаги] }`, шаг —
82
+ `{ selector: 'loan', component: Step, componentProps: { title }, children: [...] }`. Внутри
83
+ рендерера визард берёт форму и поток из сборки сам, а его `onSubmit` зовётся через
84
+ `bundle.submission.submit` — только после успешной проверки всей формы.
85
+ - Без `flow` и без сборки в контексте визард строит позиционный поток **без правил**.
86
+ - Навигация снаружи визарда — через сам поток: `bundle.flow?.goTo('loan')`; в поведении — `flow` из
87
+ области (`defineFormBehavior(({ model, flow }) => …)`).
88
+
89
+ ### Шаги без визарда — `useFormFlow`
67
90
 
68
91
  ```tsx
69
- function MultiStepForm() {
70
- const [step, setStep] = useState(1);
92
+ import { useFormFlow } from '@reformer/core/react';
71
93
 
72
- const nextStep = async () => {
73
- const ok = await validateModel(model, STEP_SCHEMAS[step - 1]);
74
- if (ok) setStep(step + 1);
75
- };
94
+ function MultiStepForm() {
95
+ const loan = useFormBundle(createLoanForm);
96
+ const flow = loan.flow!;
97
+ const { current, isFirst, isLast, validating } = useFormFlow(flow);
76
98
 
77
99
  return (
78
100
  <div>
79
- {step === 1 && <Step1Fields form={form} />}
80
- {step === 2 && <Step2Fields form={form} />}
101
+ {current === 'loan' && <LoanFields form={loan.form} />}
102
+ {current === 'applicant' && <ApplicantFields form={loan.form} />}
103
+ {current === 'confirm' && <Summary form={loan.form} />}
81
104
 
82
- <button onClick={() => setStep(step - 1)} disabled={step === 1}>
105
+ <button onClick={() => flow.previous()} disabled={isFirst}>
83
106
  Back
84
107
  </button>
85
- <button onClick={step === 2 ? handleSubmit : nextStep}>
86
- {step === 2 ? 'Submit' : 'Next'}
87
- </button>
108
+ {isLast ? (
109
+ <button onClick={() => void loan.submission.submit(sendLoan)}>Submit</button>
110
+ ) : (
111
+ <button onClick={() => void flow.next()} disabled={validating}>
112
+ Next
113
+ </button>
114
+ )}
88
115
  </div>
89
116
  );
90
117
  }
91
118
  ```
119
+
120
+ ### Без сборки — `validateModel` по схеме шага
121
+
122
+ Тот же результат без `createForm`: переход проверяется `validateModel(model, stepSchema)`, полный
123
+ submit — по общей схеме (композиция шагов через `apply(...)`).
124
+
125
+ ```typescript
126
+ import {
127
+ validate,
128
+ apply,
129
+ defineValidation,
130
+ validateModel,
131
+ type ValidationDefinition,
132
+ } from '@reformer/core/validation';
133
+ import { required, min } from '@reformer/core/validators';
134
+
135
+ const loanRules = defineValidation<Form>(({ model }) => {
136
+ validate(model.$.loanType, [required()]);
137
+ validate(model.$.loanAmount, [required(), min(50000)]);
138
+ });
139
+
140
+ const applicantRules = defineValidation<Form>(({ model }) => {
141
+ validate(model.$.personalData.firstName, [required()]);
142
+ validate(model.$.personalData.lastName, [required()]);
143
+ });
144
+
145
+ const STEP_RULES: readonly ValidationDefinition<Form>[] = [loanRules, applicantRules];
146
+ const fullRules = defineValidation<Form>(() => apply(...STEP_RULES));
147
+
148
+ // true = нет блокирующих замечаний (severity:'warning' не блокирует); замечания уже в нодах
149
+ const stepOk = await validateModel(model, STEP_RULES[index], { touch: true });
150
+ const allOk = await validateModel(model, fullRules, { touch: true });
151
+ ```
152
+
153
+ Поток без сборки собирает `createFormFlow({ steps, validateStep })` — см. `38-form-flow.md`.
154
+
155
+ ### Удалено
156
+
157
+ | Прежний контракт | Сейчас |
158
+ | -------------------------------------------------------- | --------------------------------------------------------- |
159
+ | `validation: { steps: { loan: rules } }` | `flow: { steps: [{ id: 'loan', rules }] }` |
160
+ | `bundle.validation.validateStep(n)` / `.validateAll()` / `.runStep()` / `.runAll()` | `bundle.validateStep(id)` / `bundle.validate()` |
161
+ | `<FormWizard config={…} ref={…}>`, `FormWizardConfig`, `FormWizardHandle` | `<FormWizard flow={bundle.flow}>`, навигация — `bundle.flow` |
162
+ | `defineSteps`, `useWizardStepValidation` | `flow` сборки; живая проверка — `validation.strategy` |
@@ -58,41 +58,44 @@ compute(model.$.b, () => model.a + 1); // расходится → Cycle detecte
58
58
  compute(model.$.total, () => model.price * model.qty); // одно направление
59
59
  ```
60
60
 
61
- ### Cross-field валидация — через `cross(sig, fn)`
61
+ ### Cross-field валидация — через `cross` области схемы
62
62
 
63
- Cross-field живёт в схеме валидации (`@reformer/core/validation`), а не в layout: правило вешается на
64
- ПОЛЕ-НОСИТЕЛЬ ошибки оператором `cross(sig, fn)`, а `fn` читает снапшот текущего scope (`model.get()`).
63
+ Cross-field живёт в схеме валидации (`@reformer/core/validation`), а не в схеме формы: правило
64
+ вешается на ПОЛЕ-НОСИТЕЛЬ ошибки оператором `cross(sig, check)` из аргумента схемы, а `check`
65
+ получает снимок модели области.
65
66
 
66
67
  ```typescript
67
- import { defineValidationSchema, validate, cross, validateModel } from '@reformer/core/validation';
68
+ import { defineValidation, validate, validateModel } from '@reformer/core/validation';
68
69
  import { required } from '@reformer/core/validators';
69
- import { revalidateWhen, type ValidationError } from '@reformer/core';
70
+ import { type ValidationIssue } from '@reformer/core';
71
+ import { revalidateWhen } from '@reformer/core/model';
70
72
 
71
73
  // ❌ старое (УДАЛЕНО): дерево { value, validators }, ModelValidator (value, scope, root), validateFormModel
72
74
  const legacyRule: ModelValidator<number, unknown, Form> = (_value, _scope, root) =>
73
- root.field1 > root.field2 ? { code: 'error', message: 'Invalid' } : null;
75
+ root.field1 > root.field2 ? { code: 'error' } : null;
74
76
  const legacySchema = { field1: { value: model.$.field1, component: Input, validators: [legacyRule] } };
75
77
  validateFormModel(model, legacySchema);
76
78
 
77
- // ✅ новое: cross-правило — обычная функция над снапшотом (model.get()), не читает scope/root
78
- const field1LessThanField2 = (f: Form): ValidationError | null =>
79
- f.field1 > f.field2 ? { code: 'error', message: 'Invalid' } : null;
79
+ // ✅ новое: cross-правило — обычная функция над снимком модели
80
+ const field1LessThanField2 = (form: Form): ValidationIssue | null =>
81
+ form.field1 > form.field2 ? { code: 'field1.notLess' } : null;
80
82
 
81
- const schema = defineValidationSchema<Form>(({ model }) => {
83
+ const schema = defineValidation<Form>(({ model, cross }) => {
82
84
  validate(model.$.field1, [required()]);
83
- cross(model.$.field1, field1LessThanField2); // правило на поле-носителе ошибки
85
+ cross(model.$.field1, field1LessThanField2); // правило на поле-носителе замечания
84
86
  });
85
87
 
86
- // Прогон — ТОЛЬКО внешним раннером; ошибки сами доезжают до нод формы.
87
- // ⚠️ form.validate()/submit() схему валидации больше НЕ прогоняют.
88
+ // Прогон — уровнем формы (createForm({ …, validation: schema })): bundle.validate() /
89
+ // bundle.submission.submit(handler); без сборки — раннер. Замечания сами доезжают до нод формы.
88
90
  await validateModel(model, schema);
89
91
 
90
92
  // Перезапуск правила при изменении соседнего поля — мост «поведение → валидация»:
91
93
  revalidateWhen([model.$.field2], () => void validateModel(model, schema));
92
94
  ```
93
95
 
94
- > **Актуальный vs удалённый API.** Операторы валидации `validate`/`validateAsync`/`validateWhen`/`cross`/`each`/`apply`
95
- > ТЕПЕРЬ существуют — в `@reformer/core/validation` с новыми сигнатурами (ambient, активны только внутри прогона
96
- > `validateModel`). УДАЛЕНЫ: `applyWhen`, `validateGroup`/`validateTree`/`validateForm`/`validateFormModel`,
96
+ > **Актуальный vs удалённый API.** Операторы валидации `validate`/`validateAsync`/`validateWhen`/`apply`/`applyEach`
97
+ > существуют — в `@reformer/core/validation` (активны только внутри прогона схемы); `cross` — в аргументе
98
+ > схемы. УДАЛЕНЫ: `applyWhen`, `validateGroup`/`validateTree`/`validateForm`/`validateFormModel`,
97
99
  > типы `FieldPath`/`ValidationSchemaFn`/`BehaviorSchemaFn` и `ctx.form.*`/`ctx.setFieldValue` (наследие
98
- > path-based архитектуры). См. `17-nonexistent-api.md`.
100
+ > path-based архитектуры), а также текст в правиле (`message`), методы проверки на нодах
101
+ > (`form.validate()` / `form.submit()`) и `validation.steps` сборки. См. `17-nonexistent-api.md`.