@reformer/core 11.0.0-beta.8 → 11.0.0-beta.9

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 (224) hide show
  1. package/README.md +91 -73
  2. package/dist/behaviors-value-HA_Q03hc.js +108 -0
  3. package/dist/behaviors.js +370 -242
  4. package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
  5. package/dist/date-utils-90-qZbse.js +43 -0
  6. package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
  7. package/dist/form/aggregate-signals.d.ts +21 -63
  8. package/dist/form/behaviors/collections.d.ts +28 -7
  9. package/dist/form/behaviors/context.d.ts +19 -2
  10. package/dist/form/behaviors/index.d.ts +3 -3
  11. package/dist/form/behaviors/internals.d.ts +9 -2
  12. package/dist/form/behaviors/node.d.ts +25 -2
  13. package/dist/form/behaviors/operators.d.ts +17 -9
  14. package/dist/form/behaviors/schema-operators.d.ts +6 -4
  15. package/dist/form/behaviors/types.d.ts +12 -4
  16. package/dist/form/create-form.d.ts +47 -35
  17. package/dist/form/flow/flow.d.ts +87 -0
  18. package/dist/form/form-bundle.d.ts +45 -28
  19. package/dist/form/form-config-base.d.ts +46 -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 +16 -21
  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 +30 -14
  37. package/dist/form/signal-node-registry.d.ts +6 -7
  38. package/dist/form/submission.d.ts +93 -0
  39. package/dist/form/type-guards.d.ts +14 -73
  40. package/dist/form/types/contracts.d.ts +19 -92
  41. package/dist/form/types/field-config.d.ts +27 -0
  42. package/dist/form/types/index.d.ts +5 -57
  43. package/dist/form/types/nodes.d.ts +159 -0
  44. package/dist/form/types/schema-node.d.ts +139 -63
  45. package/dist/form/types/validation-schema.d.ts +23 -27
  46. package/dist/form/validation/context.d.ts +42 -7
  47. package/dist/form/validation/index.d.ts +9 -8
  48. package/dist/form/validation/operators.d.ts +22 -30
  49. package/dist/form/validation/run.d.ts +93 -18
  50. package/dist/form/validation/strategy.d.ts +41 -13
  51. package/dist/form/validation/types.d.ts +74 -20
  52. package/dist/form/validators/date-utils.d.ts +13 -1
  53. package/dist/form/validators/email.d.ts +8 -7
  54. package/dist/form/validators/file-type.d.ts +8 -7
  55. package/dist/form/validators/file-utils.d.ts +2 -1
  56. package/dist/form/validators/future-date.d.ts +8 -7
  57. package/dist/form/validators/index.d.ts +1 -1
  58. package/dist/form/validators/integer.d.ts +8 -7
  59. package/dist/form/validators/is-date.d.ts +11 -9
  60. package/dist/form/validators/is-number.d.ts +11 -9
  61. package/dist/form/validators/max-age.d.ts +13 -9
  62. package/dist/form/validators/max-date.d.ts +8 -7
  63. package/dist/form/validators/max-file-size.d.ts +8 -7
  64. package/dist/form/validators/max-files.d.ts +8 -7
  65. package/dist/form/validators/max-length.d.ts +14 -7
  66. package/dist/form/validators/max-total-file-size.d.ts +8 -7
  67. package/dist/form/validators/max.d.ts +10 -8
  68. package/dist/form/validators/min-age.d.ts +12 -9
  69. package/dist/form/validators/min-date.d.ts +8 -7
  70. package/dist/form/validators/min-file-size.d.ts +8 -7
  71. package/dist/form/validators/min-files.d.ts +11 -9
  72. package/dist/form/validators/min-length.d.ts +14 -7
  73. package/dist/form/validators/min.d.ts +10 -8
  74. package/dist/form/validators/multiple-of.d.ts +10 -8
  75. package/dist/form/validators/non-negative.d.ts +8 -7
  76. package/dist/form/validators/non-zero.d.ts +8 -7
  77. package/dist/form/validators/past-date.d.ts +8 -7
  78. package/dist/form/validators/pattern.d.ts +9 -8
  79. package/dist/form/validators/phone.d.ts +8 -7
  80. package/dist/form/validators/required.d.ts +11 -9
  81. package/dist/form/validators/url.d.ts +20 -13
  82. package/dist/form/validators/validation-issue.d.ts +8 -0
  83. package/dist/i18n/descriptor.d.ts +1 -1
  84. package/dist/i18n/en.json.d.ts +5 -1
  85. package/dist/i18n/locale-store.d.ts +7 -0
  86. package/dist/i18n/message-format.d.ts +24 -4
  87. package/dist/i18n/ru.json.d.ts +5 -1
  88. package/dist/i18n/translator.d.ts +11 -0
  89. package/dist/i18n/validation-message.d.ts +42 -7
  90. package/dist/i18n.d.ts +7 -12
  91. package/dist/i18n.js +66 -283
  92. package/dist/index.d.ts +16 -7
  93. package/dist/index.js +924 -2855
  94. package/dist/loader-BWkigpVe.js +52 -0
  95. package/dist/locale/en.js +1 -1
  96. package/dist/locale/en.json +4 -1
  97. package/dist/locale/ru.js +4 -1
  98. package/dist/locale/ru.json +4 -1
  99. package/dist/locale-CG54AU2Q.js +219 -0
  100. package/dist/locale.js +1 -1
  101. package/dist/model/behaviors-value.d.ts +24 -6
  102. package/dist/model/create-model.d.ts +2 -2
  103. package/dist/model/derived-registry.d.ts +1 -1
  104. package/dist/model/index.d.ts +11 -5
  105. package/dist/model/model-nodes.d.ts +79 -12
  106. package/dist/model/model-signals-proxy.d.ts +5 -6
  107. package/dist/model/model-value-proxy.d.ts +20 -15
  108. package/dist/model/safe-effect.d.ts +33 -1
  109. package/dist/model/types.d.ts +63 -13
  110. package/dist/model.js +20 -18
  111. package/dist/node-kind-C-r6ZMdo.js +15 -0
  112. package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
  113. package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
  114. package/dist/platforms/react/form-bundle-context.d.ts +2 -2
  115. package/dist/platforms/react/hooks/types.d.ts +57 -38
  116. package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
  117. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  118. package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
  119. package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
  120. package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
  121. package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
  122. package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
  123. package/dist/platforms/react/i18n/context.d.ts +6 -1
  124. package/dist/platforms/react/i18n/hooks.d.ts +16 -10
  125. package/dist/platforms/react/i18n/provider.d.ts +9 -2
  126. package/dist/platforms/react/index.d.ts +6 -4
  127. package/dist/react.d.ts +22 -0
  128. package/dist/react.js +505 -0
  129. package/dist/runtime-token.d.ts +1 -2
  130. package/dist/safe-effect-BbtX3KuN.js +643 -0
  131. package/dist/schema-controller-CpiO2JoK.js +143 -0
  132. package/dist/signals.d.ts +15 -9
  133. package/dist/signals.js +11 -6
  134. package/dist/strategy-CoFSemuP.js +375 -0
  135. package/dist/type-guards-D-N_5Ddh.js +19 -0
  136. package/dist/validation-issue-B9mjI8jz.js +7 -0
  137. package/dist/validation-message-Ced9MGxs.js +194 -0
  138. package/dist/validation.js +9 -9
  139. package/dist/validators/email.js +5 -5
  140. package/dist/validators/future-date.js +8 -8
  141. package/dist/validators/integer.js +3 -3
  142. package/dist/validators/is-date.js +4 -4
  143. package/dist/validators/is-number.js +4 -4
  144. package/dist/validators/max-age.js +8 -9
  145. package/dist/validators/max-date.js +8 -8
  146. package/dist/validators/max-length.js +7 -7
  147. package/dist/validators/max.js +4 -4
  148. package/dist/validators/min-age.js +10 -8
  149. package/dist/validators/min-date.js +3 -3
  150. package/dist/validators/min-length.js +5 -5
  151. package/dist/validators/min.js +4 -4
  152. package/dist/validators/multiple-of.js +11 -7
  153. package/dist/validators/non-negative.js +3 -3
  154. package/dist/validators/non-zero.js +3 -3
  155. package/dist/validators/past-date.js +8 -8
  156. package/dist/validators/pattern.js +5 -5
  157. package/dist/validators/phone.js +11 -9
  158. package/dist/validators/required.js +2 -2
  159. package/dist/validators/url.js +48 -12
  160. package/dist/validators.js +17 -17
  161. package/docs/llms/01-api-reference.md +60 -34
  162. package/docs/llms/02-quick-start.md +111 -80
  163. package/docs/llms/03-api-signatures.md +368 -153
  164. package/docs/llms/04-common-patterns.md +58 -56
  165. package/docs/llms/05-common-mistakes.md +69 -44
  166. package/docs/llms/06-troubleshooting.md +14 -9
  167. package/docs/llms/07-complete-import.md +74 -43
  168. package/docs/llms/08-form-types.md +11 -4
  169. package/docs/llms/09-formschema.md +129 -81
  170. package/docs/llms/10-arrays.md +106 -75
  171. package/docs/llms/11-async-watchfield.md +2 -2
  172. package/docs/llms/13-multi-step.md +134 -63
  173. package/docs/llms/14-extended-mistakes.md +20 -17
  174. package/docs/llms/15-project-structure.md +221 -74
  175. package/docs/llms/16-ui-components.md +55 -42
  176. package/docs/llms/17-nonexistent-api.md +199 -32
  177. package/docs/llms/18-conditional-fields.md +66 -32
  178. package/docs/llms/19-reading-values.md +15 -3
  179. package/docs/llms/20-compute-vs-watch.md +16 -10
  180. package/docs/llms/21-array-operations.md +51 -27
  181. package/docs/llms/22-cycle-detection.md +1 -1
  182. package/docs/llms/23-copy-from.md +2 -2
  183. package/docs/llms/24-sync-fields.md +6 -5
  184. package/docs/llms/25-reset-when.md +14 -7
  185. package/docs/llms/26-transform-value.md +4 -4
  186. package/docs/llms/27-revalidate-when.md +16 -15
  187. package/docs/llms/28-submit-and-reset.md +276 -128
  188. package/docs/llms/29-async-preload.md +39 -29
  189. package/docs/llms/30-type-safety-recipes.md +105 -51
  190. package/docs/llms/31-async-validator-debounce.md +96 -53
  191. package/docs/llms/33-validation-strategy.md +177 -65
  192. package/docs/llms/34-i18n.md +69 -43
  193. package/docs/llms/35-unified-contract.md +100 -39
  194. package/docs/llms/36-issue-messages.md +284 -0
  195. package/docs/llms/37-compiled-schema.md +332 -0
  196. package/docs/llms/38-form-flow.md +397 -0
  197. package/llms-index.json +1 -1
  198. package/llms.txt +6084 -3420
  199. package/package.json +23 -4
  200. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  201. package/dist/date-utils-dTT_x_Be.js +0 -31
  202. package/dist/form/create-core-form.d.ts +0 -72
  203. package/dist/form/factories/index.d.ts +0 -6
  204. package/dist/form/factories/node-factory.d.ts +0 -189
  205. package/dist/form/form-submitter.d.ts +0 -121
  206. package/dist/form/nodes/array-node.d.ts +0 -276
  207. package/dist/form/nodes/field-node.d.ts +0 -297
  208. package/dist/form/nodes/form-node.d.ts +0 -326
  209. package/dist/form/nodes/group-node.d.ts +0 -284
  210. package/dist/form/nodes/model-array-node.d.ts +0 -126
  211. package/dist/form/nodes/subscription-manager.d.ts +0 -180
  212. package/dist/form/status-machine.d.ts +0 -115
  213. package/dist/form/types/deep-schema.d.ts +0 -107
  214. package/dist/form/types/form-proxy.d.ts +0 -141
  215. package/dist/form/unique-id.d.ts +0 -45
  216. package/dist/form/validation/config.d.ts +0 -73
  217. package/dist/form/validation/error-handler.d.ts +0 -166
  218. package/dist/form/validators/validation-error.d.ts +0 -8
  219. package/dist/index-BrNkQj1y.js +0 -105
  220. package/dist/loader-Dv_Hl7JC.js +0 -216
  221. package/dist/model-value-proxy-caTmAiUd.js +0 -419
  222. package/dist/node-BeV_lV5i.js +0 -100
  223. package/dist/strategy-DgzELWno.js +0 -204
  224. package/dist/validation-error-B-U3RsV0.js +0 -11
@@ -1,10 +1,12 @@
1
- import { FormProxy, GroupNodeConfig, FormSchema } from './types/index';
1
+ import { CompiledSchema } from './schema/compiled';
2
+ import { FormGroup } from './types/index';
2
3
  import { FormSchemaNode } from './types/schema-node';
3
4
  import { FormModel } from '../model/types';
4
5
  import { FormBehavior } from './behaviors';
5
6
  import { SchemaController } from './schema-controller';
7
+ import { FormFlow } from './flow/flow';
6
8
  /**
7
- * Аргументы createForm под архитектуру M1: данные приходят из {@link FormModel},
9
+ * Аргументы сборки формы из модели: данные приходят из {@link FormModel},
8
10
  * конфиг полей (component/componentProps) — из единой схемы.
9
11
  *
10
12
  * @group Utilities
@@ -13,13 +15,13 @@ export interface CreateFormFromModelArgs<T> {
13
15
  /** Реактивная модель данных (источник истины значений). */
14
16
  model: FormModel<T>;
15
17
  /**
16
- * Единая Schema (дерево узлов {@link FormSchemaNode}). createForm обходит её и привязывает конфиг
17
- * поля к ноде по идентичности сигнала (`node.value === model.$.path`). Опциональна.
18
+ * Схема — дерево узлов {@link FormSchemaNode}. Сборка обходит её и привязывает конфиг поля к
19
+ * ноде по идентичности ручки (`node.model === model.$.<path>`). Опциональна.
18
20
  */
19
21
  schema?: FormSchemaNode;
20
22
  /**
21
23
  * Декларативная схема поведения ({@link defineFormBehavior}). Запускается ПОСЛЕ построения нод и
22
- * заполнения реестра сигнал→нода; cleanup живёт на форме и вызывается в `form.dispose()`.
24
+ * заполнения реестра сигнал→нода; cleanup живёт на форме и снимается вместе с ней.
23
25
  */
24
26
  behavior?: FormBehavior<T>;
25
27
  /**
@@ -28,14 +30,16 @@ export interface CreateFormFromModelArgs<T> {
28
30
  * Без него поведение пишет правила в собственный контроллер, который никто не читает.
29
31
  */
30
32
  schemaController?: SchemaController;
33
+ /** Поток шагов сборки: его получает поведение (`defineFormBehavior(({ flow }) => …)`). */
34
+ flow?: FormFlow;
31
35
  }
32
36
  /**
33
- * Собрать форму из {@link FormModel} и единой схемы (низкоуровневая фабрика архитектуры M1).
37
+ * Собрать форму из {@link FormModel} и единой схемы (низкоуровневая фабрика).
34
38
  *
35
39
  * Значения принадлежат модели (источник истины), ноды формы держат UI/валидационное состояние и
36
- * ссылаются на сигналы модели по идентичности (`node.value === model.$.path`). Обходит структуру
37
- * модели, привязывает конфиг поля (component/componentProps) из схемы, материализует массивы
38
- * под-форм как {@link ModelArrayNode} (на любой глубине), заполняет реестр ручка→нода (для
40
+ * ссылаются на сигналы модели по идентичности. Обходит структуру модели и строит ноды по виду её
41
+ * узлов, привязывает конфиг поля (component/componentProps) из схемы, материализует массивы
42
+ * под-форм ({@link FormArray}, на любой глубине), заполняет реестр ручка→нода (для
39
43
  * `enableWhen`, `apply`/`applyEach` и роутинга ошибок) и, при наличии, запускает декларативное
40
44
  * поведение (cleanup живёт на форме).
41
45
  *
@@ -45,9 +49,9 @@ export interface CreateFormFromModelArgs<T> {
45
49
  *
46
50
  * @typeParam T - Тип модели данных формы
47
51
  * @param args - Модель, единая схема и (опционально) декларативное поведение {@link CreateFormFromModelArgs}
48
- * @returns Типизированная форма с Proxy-доступом к полям {@link FormProxy}
52
+ * @returns Группа формы {@link FormGroup} с нодами полей по именам
49
53
  *
50
- * @example Форма из модели + схемы (эквивалент `createFormFromModel({ model, schema })`)
54
+ * @example Форма из модели и схемы
51
55
  * ```typescript
52
56
  * import { createModel, createFormFromModel } from '@reformer/core';
53
57
  *
@@ -60,10 +64,10 @@ export interface CreateFormFromModelArgs<T> {
60
64
  * const schema = {
61
65
  * component: Section,
62
66
  * children: [
63
- * // Layout несёт только component/componentProps; правила — в отдельной ValidationSchema.
64
- * { value: model.$.email, component: Input },
67
+ * // Layout несёт только component/componentProps; правила — в отдельной ValidationDefinition.
68
+ * { model: model.$.email, component: Input },
65
69
  * // вложенная группа: `model.$.profile.name` (≡ под-модель `model.profile.$.name` — тот же сигнал)
66
- * { value: model.$.profile.name, component: Input },
70
+ * { model: model.$.profile.name, component: Input },
67
71
  * ],
68
72
  * };
69
73
  *
@@ -77,32 +81,40 @@ export interface CreateFormFromModelArgs<T> {
77
81
  * @see `createForm` — сборка одним вызовом: модель, форма, валидация и дерево для рендера
78
82
  * @group Utilities
79
83
  */
80
- export declare function createFormFromModel<T>(args: CreateFormFromModelArgs<T>): FormProxy<T>;
84
+ export declare function createFormFromModel<T>(args: CreateFormFromModelArgs<T>): FormGroup<T>;
81
85
  /**
82
- * Создать форму из готового конфига группы.
86
+ * Снять форму, собранную {@link createFormFromModel}: поведение формы и подписки нод (массивы
87
+ * под-форм следят за моделью). Значения модели остаются. Идемпотентно.
83
88
  *
84
- * @param config - Конфиг {@link GroupNode}
85
- * @returns Типизированная форма с Proxy-доступом к полям
86
- * @group Utilities
87
- */
88
- export declare function createLegacyForm<T>(config: GroupNodeConfig<T>): FormProxy<T>;
89
- /**
90
- * Создать форму из плоской схемы полей с инлайн-значениями (`value: ''`) — путь ДО архитектуры M1.
89
+ * Сборку одним вызовом снимают целиком — `bundle.dispose()`: вместе с формой она снимает живую
90
+ * проверку и отменяет идущие проверки. Этот вызов нужен своим слоям сборки поверх
91
+ * {@link createFormFromModel}.
91
92
  *
92
- * Значения живут в нодах, модели нет: поведение (`defineFormBehavior`) и внешняя валидация
93
- * (`validateModel`) к такой форме не подключаются. Для нового кода — {@link createFormFromModel}
94
- * либо сборка одним вызовом.
95
- *
96
- * @param schema - Схема полей формы
97
- * @returns Типизированная форма с Proxy-доступом к полям
98
- * @group Utilities
93
+ * @param form - Форма из {@link createFormFromModel}.
99
94
  *
100
95
  * @example
101
96
  * ```typescript
102
- * const form = createLegacyForm<UserForm>({
103
- * email: { value: '', component: Input },
104
- * password: { value: '', component: Input },
105
- * });
97
+ * const form = createFormFromModel<Profile>({ model, schema, behavior });
98
+ * // … форма больше не нужна
99
+ * disposeForm(form);
106
100
  * ```
101
+ *
102
+ * @group Utilities
103
+ */
104
+ export declare function disposeForm<T>(form: FormGroup<T>): void;
105
+ /**
106
+ * Аргументы {@link createFormFromCompiled}: вместо авторского дерева — его компиляция.
107
+ *
108
+ * @internal
109
+ */
110
+ export interface CreateFormFromCompiledArgs<T> extends Omit<CreateFormFromModelArgs<T>, 'schema'> {
111
+ /** Скомпилированная схема области модели; без неё у полей нет конфига из схемы. */
112
+ compiled?: CompiledSchema;
113
+ }
114
+ /**
115
+ * Форма из модели и скомпилированной схемы. Её зовут {@link createFormFromModel}, сборка одним
116
+ * вызовом (`createForm`) и форма строки массива — каждая со своей компиляцией.
117
+ *
118
+ * @internal
107
119
  */
108
- export declare function createLegacyForm<T>(schema: FormSchema<T>): FormProxy<T>;
120
+ export declare function createFormFromCompiled<T>(args: CreateFormFromCompiledArgs<T>): FormGroup<T>;
@@ -0,0 +1,87 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { ValidationResult } from '../validation/types';
3
+ /**
4
+ * Почему поток остался на шаге: `invalid` — у шага блокирующие замечания, `error` — правило не
5
+ * вернуло результат, `cancelled` — прогон отменён либо результат проверки устарел (поток перевели,
6
+ * модель формы изменилась, пока шла проверка шага), `edge` — дальше (или раньше) шагов нет,
7
+ * `locked` — шаг недоступен, пока не пройден предыдущий, `current` — `goTo` на шаг, на котором поток
8
+ * уже стоит.
9
+ *
10
+ * @group Form
11
+ */
12
+ export type FlowStayReason = 'invalid' | 'error' | 'cancelled' | 'edge' | 'locked' | 'current';
13
+ /**
14
+ * Итог перехода: поток перешёл на другой шаг либо остался — с причиной и, если шаг проверялся,
15
+ * результатом проверки.
16
+ *
17
+ * @group Form
18
+ */
19
+ export type FlowTransition = {
20
+ readonly status: 'moved';
21
+ readonly from: string;
22
+ readonly to: string;
23
+ } | {
24
+ readonly status: 'stayed';
25
+ readonly step: string;
26
+ readonly reason: FlowStayReason;
27
+ readonly validation?: ValidationResult;
28
+ };
29
+ /**
30
+ * Поток шагов формы.
31
+ *
32
+ * @group Form
33
+ */
34
+ export interface FormFlow {
35
+ /** Идентификаторы шагов по порядку. */
36
+ readonly steps: readonly string[];
37
+ /** Текущий шаг. */
38
+ readonly current: ReadonlySignal<string>;
39
+ /** Номер текущего шага с нуля. */
40
+ readonly index: ReadonlySignal<number>;
41
+ /** Пройденные шаги: правила шага прошли при переходе дальше. */
42
+ readonly completed: ReadonlySignal<ReadonlySet<string>>;
43
+ readonly isFirst: ReadonlySignal<boolean>;
44
+ readonly isLast: ReadonlySignal<boolean>;
45
+ /** Идёт ли проверка шага в `next()`. */
46
+ readonly validating: ReadonlySignal<boolean>;
47
+ /** Можно ли перейти на шаг: первый — всегда, остальные — после прохождения предыдущего. */
48
+ canGoTo(stepId: string): boolean;
49
+ /** Проверить правила текущего шага и при успехе перейти к следующему. */
50
+ next(): Promise<FlowTransition>;
51
+ /** Вернуться на шаг назад; пройденными остаются только шаги до него. */
52
+ previous(): FlowTransition;
53
+ /** Перейти на шаг; неизвестный шаг — исключение. */
54
+ goTo(stepId: string): FlowTransition;
55
+ /** Вернуться на первый шаг и забыть пройденные. */
56
+ reset(): void;
57
+ }
58
+ /**
59
+ * Опции {@link createFormFlow}.
60
+ *
61
+ * @group Form
62
+ */
63
+ export interface FormFlowOptions {
64
+ /** Идентификаторы шагов по порядку; не пустой список без повторов. */
65
+ readonly steps: readonly string[];
66
+ /** Проверка шага при `next()`; без неё шаг проходится без проверки. */
67
+ readonly validateStep?: (stepId: string) => Promise<ValidationResult>;
68
+ }
69
+ /**
70
+ * Создать поток шагов.
71
+ *
72
+ * @param options - {@link FormFlowOptions}: шаги и проверка шага.
73
+ * @returns {@link FormFlow} на первом шаге.
74
+ * @throws Error если шагов нет или идентификатор повторяется.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const flow = createFormFlow({ steps: ['loan', 'contacts', 'confirm'] });
79
+ *
80
+ * await flow.next(); // { status: 'moved', from: 'loan', to: 'contacts' }
81
+ * flow.canGoTo('confirm'); // false — шаг contacts ещё не пройден
82
+ * flow.goTo('loan'); // { status: 'moved', from: 'contacts', to: 'loan' }
83
+ * ```
84
+ *
85
+ * @group Form
86
+ */
87
+ export declare function createFormFlow(options: FormFlowOptions): FormFlow;
@@ -1,10 +1,11 @@
1
1
  import { FormModel } from '../model/types';
2
2
  import { FormBehavior } from './behaviors';
3
3
  import { SchemaController, SchemaNodeControl } from './schema-controller';
4
+ import { CompiledSchema } from './schema/compiled';
4
5
  import { FormSchemaNode } from './types/schema-node';
5
- import { FormProxy } from './types/index';
6
- import { FormValidation, FormValidationBundle } from './validation/config';
7
- import { ValidationSchema } from './validation';
6
+ import { FormGroup } from './types/index';
7
+ import { FormFlowConfig, FormLevel, FormValidation } from './form-level';
8
+ import { ValidationDefinition } from './validation';
8
9
  /**
9
10
  * Дерево, собранное реестром из документа схемы (JSON), и то, что реестр хочет донести до
10
11
  * рендерера вместе с ним.
@@ -29,13 +30,13 @@ export interface SchemaResolver {
29
30
  resolveSchema?(document: unknown, model: FormModel<never>): ResolvedSchema;
30
31
  }
31
32
  /**
32
- * Часть бандла для рендерера: готовое дерево и схема-контроллер сборки.
33
+ * Часть бандла для рендерера: скомпилированная схема и схема-контроллер сборки.
33
34
  *
34
35
  * @group Utilities
35
36
  */
36
37
  export interface FormRender {
37
- /** Корневой узел дерева схемы; `undefined`, если схема не задана. */
38
- readonly tree: FormSchemaNode | undefined;
38
+ /** Скомпилированная схема корневой области; `undefined`, если схема не задана. */
39
+ readonly tree: CompiledSchema | undefined;
39
40
  /** Схема-контроллер сборки: области корня, строк массивов и подформ. */
40
41
  readonly controller: SchemaController;
41
42
  /** Управление узлом КОРНЕВОЙ области по его `selector`. */
@@ -46,17 +47,25 @@ export interface FormRender {
46
47
  readonly errorBoundary?: unknown;
47
48
  }
48
49
  /**
49
- * Результат {@link createForm}.
50
+ * Результат {@link createForm}: модель, форма, часть для рендерера и уровень формы
51
+ * ({@link FormLevel}) — `validate()`, `validateStep(id)`, поток шагов и отправка
52
+ * `submission.submit(handler)`.
50
53
  *
51
54
  * @typeParam T - Форма данных модели.
52
55
  * @group Utilities
53
56
  */
54
- export interface FormBundle<T> {
55
- model: FormModel<T>;
56
- form: FormProxy<T>;
57
- /** Собранная валидация; есть, если в конфиге заданы правила. */
58
- validation?: FormValidationBundle<T>;
59
- render: FormRender;
57
+ export interface FormBundle<T> extends FormLevel<T> {
58
+ readonly model: FormModel<T>;
59
+ readonly form: FormGroup<T>;
60
+ readonly render: FormRender;
61
+ /**
62
+ * Снять сборку: поведение формы, подписки нод, живую проверку; идущие проверки — полная, шага,
63
+ * отправки — получают `cancelled`. Значения модели остаются. Идемпотентно.
64
+ *
65
+ * Владелец сборки — тот, кто вызвал `createForm`: вне React (тесты, сервер, своя оболочка)
66
+ * снимайте её этим вызовом, когда форма больше не нужна.
67
+ */
68
+ dispose(): void;
60
69
  }
61
70
  /**
62
71
  * Конфиг {@link createForm}.
@@ -79,28 +88,35 @@ export interface CreateFormConfig<T> {
79
88
  registry?: SchemaResolver;
80
89
  /** Поведение формы: значения, состояние нод и правила узлов схемы. */
81
90
  behavior?: FormBehavior<T>;
82
- /** Правила валидации: готовая схема либо {@link FormValidation} со стратегией и шагами. */
83
- validation?: FormValidation<T> | ValidationSchema<T>;
91
+ /**
92
+ * Правила валидации: готовый набор либо {@link FormValidation} — полный набор `rules`, правила
93
+ * вне шагов `extras` и стратегия живой проверки. Без `rules` полный набор собирается из правил
94
+ * шагов потока и `extras`.
95
+ */
96
+ validation?: FormValidation<T> | ValidationDefinition<T>;
97
+ /**
98
+ * Поток шагов: `{ steps: [{ id, rules }] }`. Идентификатор шага — `selector` узла шага в схеме;
99
+ * `next()` потока проверяет правила текущего шага.
100
+ */
101
+ flow?: FormFlowConfig<T>;
84
102
  /**
85
103
  * Правка модели ДО сборки формы. Нужна там, где значение обязано существовать к моменту
86
104
  * построения нод — например, массив, наполняемый из вычислений (реактивные `onChange` на
87
- * инициализации не срабатывают).
105
+ * инициализации не срабатывают). Модель, созданная из `initial`, снимает точку отсчёта после
106
+ * `seed` — сброс его правки не откатывает; точкой отсчёта переданной `model` управляет владелец.
88
107
  */
89
108
  seed?: (model: FormModel<T>) => void;
90
109
  /** Донастройка ПОСЛЕ сборки: получает готовый бандл. */
91
110
  setup?: (bundle: FormBundle<T>) => void;
92
111
  }
93
- /** Бандл формы, у которой заданы правила: `validation` в нём есть всегда. */
94
- type ValidatedFormBundle<T> = FormBundle<T> & {
95
- validation: FormValidationBundle<T>;
96
- };
97
112
  /**
98
- * Собрать модель, форму, валидацию и дерево для рендера за один проход.
113
+ * Собрать модель, форму, уровень формы (проверка, отправка, поток шагов) и дерево для рендера за
114
+ * один проход.
99
115
  *
100
116
  * @typeParam T - Форма данных модели.
101
117
  * @param config - {@link CreateFormConfig}: (`initial` | `model`) + опц. `schema`, `registry`,
102
- * `behavior`, `validation`, `seed`, `setup`.
103
- * @returns {@link FormBundle} — `{ model, form, validation?, render }`.
118
+ * `behavior`, `validation`, `flow`, `seed`, `setup`.
119
+ * @returns {@link FormBundle} — `{ model, form, render, flow, validate, validateStep, submission, … }`.
104
120
  *
105
121
  * @example Рендерер по TS-схеме
106
122
  * ```tsx
@@ -109,7 +125,8 @@ type ValidatedFormBundle<T> = FormBundle<T> & {
109
125
  * model: createCreditModel(),
110
126
  * schema: creditSchema, // (model) => узел
111
127
  * behavior: creditBehavior,
112
- * validation: { steps: { loan: loanRules }, extras: crossRules },
128
+ * validation: { extras: crossRules },
129
+ * flow: { steps: [{ id: 'loan', rules: loanRules }, { id: 'confirm' }] },
113
130
  * })
114
131
  * );
115
132
  * return <FormRenderer form={credit} settings={{ fieldWrapper: FormField }} />;
@@ -118,6 +135,10 @@ type ValidatedFormBundle<T> = FormBundle<T> & {
118
135
  * @example React-руками — та же сборка, разметка в JSX
119
136
  * ```tsx
120
137
  * const credit = useFormBundle(() => createForm<CreditForm>({ initial, schema: creditSchema }));
138
+ * const send = async () => {
139
+ * const result = await credit.submission.submit(api.send);
140
+ * if (result.status === 'invalid') showHint('Проверьте форму');
141
+ * };
121
142
  * return <FormField control={credit.form.loanType} />;
122
143
  * ```
123
144
  *
@@ -129,8 +150,4 @@ type ValidatedFormBundle<T> = FormBundle<T> & {
129
150
  * return <FormRenderer form={credit} />;
130
151
  * ```
131
152
  */
132
- export declare function createForm<T extends object>(config: CreateFormConfig<T> & {
133
- validation: FormValidation<T> | ValidationSchema<T>;
134
- }): ValidatedFormBundle<T>;
135
153
  export declare function createForm<T extends object>(config: CreateFormConfig<T>): FormBundle<T>;
136
- export {};
@@ -0,0 +1,46 @@
1
+ import { FormModel } from '../model/types';
2
+ import { FormBehavior } from './behaviors';
3
+ import { FormGroup } from './types/index';
4
+ import { FormFlowConfig, FormLevel, FormValidation } from './form-level';
5
+ import { ValidationDefinition } from './validation';
6
+ /**
7
+ * Общая часть конфига всех фабрик формы.
8
+ *
9
+ * @typeParam T - Форма данных модели.
10
+ * @typeParam B - Бандл конкретной фабрики (его получает {@link CreateFormConfigBase.setup}).
11
+ */
12
+ export interface CreateFormConfigBase<T, B> {
13
+ /** Начальные значения — из них создаётся модель. Взаимоисключимо с `model`. */
14
+ initial?: T;
15
+ /** Готовая модель (если создана отдельной фабрикой). Приоритетнее `initial`. */
16
+ model?: FormModel<T>;
17
+ /** Декларативное поведение модели (compute/copyFrom/enableWhen/onChange). */
18
+ behavior?: FormBehavior<T>;
19
+ /** Правила валидации: готовый набор либо {@link FormValidation} с `extras` и стратегией. */
20
+ validation?: FormValidation<T> | ValidationDefinition<T>;
21
+ /** Поток шагов: идентификатор шага и его правила — {@link FormFlowConfig}. */
22
+ flow?: FormFlowConfig<T>;
23
+ /**
24
+ * Правка модели ДО сборки формы. Нужна там, где значение обязано существовать к моменту
25
+ * построения нод — например, массив, наполняемый из вычислений (реактивные `onChange` на
26
+ * инициализации не срабатывают). Модель, созданная из `initial`, снимает точку отсчёта после
27
+ * `seed` — сброс его правки не откатывает; точкой отсчёта переданной `model` управляет владелец.
28
+ */
29
+ seed?: (model: FormModel<T>) => void;
30
+ /** Донастройка ПОСЛЕ сборки: получает готовый бандл фабрики. */
31
+ setup?: (bundle: B) => void;
32
+ }
33
+ /**
34
+ * Общая часть результата фабрик: модель, форма и уровень формы ({@link FormLevel}) — проверка,
35
+ * отправка и поток шагов.
36
+ */
37
+ export interface CoreForm<T> extends FormLevel<T> {
38
+ readonly model: FormModel<T>;
39
+ readonly form: FormGroup<T>;
40
+ /**
41
+ * Снять сборку: форму (`disposeForm` — поведение, подписки нод) и уровень формы (живая
42
+ * проверка, идущие проверки). Фабрика обязана задать его ПОСЛЕ `...level`: `dispose` уровня
43
+ * форму не снимает.
44
+ */
45
+ dispose(): void;
46
+ }
@@ -0,0 +1,121 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FormModel } from '../model/types';
3
+ import { FormFlow } from './flow/flow';
4
+ import { FormSubmission } from './submission';
5
+ import { FormValidationController, ValidationStrategyKind } from './validation/strategy';
6
+ import { ValidationDefinition, ValidationResult } from './validation/types';
7
+ /**
8
+ * Правила валидации формы и стратегия живой проверки.
9
+ *
10
+ * ⚠️ `rules` и `extras` обязаны быть **стабильными ссылками** (`defineValidation` в модуле): отмена
11
+ * устаревших прогонов ключится по паре «модель + правила».
12
+ *
13
+ * @group Form
14
+ */
15
+ export interface FormValidation<T> {
16
+ /** Полный набор правил. Без него собирается из правил шагов потока и `extras`. */
17
+ rules?: ValidationDefinition<T>;
18
+ /** Правила, которые проверяются только целиком (отправка), но не по шагам. */
19
+ extras?: ValidationDefinition<T>;
20
+ /** Когда запускать живую проверку. По умолчанию `'submit'` — живой проверки нет. */
21
+ strategy?: ValidationStrategyKind;
22
+ debounce?: number;
23
+ /** Режим живой фазы для `afterFirstSubmit`. По умолчанию `'change'`. */
24
+ liveAfterSubmit?: 'change' | 'blur';
25
+ }
26
+ /**
27
+ * Шаг потока: идентификатор (он же `selector` узла шага в схеме) и правила шага.
28
+ *
29
+ * @group Form
30
+ */
31
+ export interface FlowStep<T> {
32
+ readonly id: string;
33
+ /** Правила шага; `null` или без поля — шаг без правил. */
34
+ readonly rules?: ValidationDefinition<T> | null;
35
+ }
36
+ /**
37
+ * Поток шагов в конфиге сборки.
38
+ *
39
+ * @group Form
40
+ */
41
+ export interface FormFlowConfig<T> {
42
+ readonly steps: readonly FlowStep<T>[];
43
+ }
44
+ /**
45
+ * Правила формы и контроллер живой проверки — его армирует `useFormBundle`.
46
+ *
47
+ * @group Form
48
+ */
49
+ export interface FormValidationState<T> {
50
+ /** Полный набор правил (стабильная ссылка). */
51
+ readonly rules: ValidationDefinition<T>;
52
+ readonly controller: FormValidationController;
53
+ }
54
+ /**
55
+ * Уровень формы в бандле сборки.
56
+ *
57
+ * @group Form
58
+ */
59
+ export interface FormLevel<T> {
60
+ /** Поток шагов; без `flow` в конфиге — `undefined`. */
61
+ readonly flow: FormFlow | undefined;
62
+ /** Идёт ли проверка: полная или шага. */
63
+ readonly validating: ReadonlySignal<boolean>;
64
+ /** Правила и контроллер живой проверки; без правил — `undefined`. */
65
+ readonly validation: FormValidationState<T> | undefined;
66
+ /**
67
+ * Отправка: `submission.submit(handler)` — один прогон всех правил и обработчик со значениями,
68
+ * которые они видели; `submission.submitting` — идёт ли обработчик.
69
+ */
70
+ readonly submission: FormSubmission<T>;
71
+ /**
72
+ * Проверить всю форму и показать замечания. Поля выключенных нод (`enableWhen`, `disable()`)
73
+ * не учитываются — статус совпадает с `valid` формы.
74
+ *
75
+ * Правила читают модель в момент вызова. Исключение — вызов в одном тике со сборкой формы или
76
+ * правкой поля: поведение ещё пишет отложенно (колбэк `onChange`, сброс `resetOnDisable`), и
77
+ * проверка сначала дожидается этих записей; `validating` поднят сразу. Так же ведут себя
78
+ * `validateStep` и отправка.
79
+ *
80
+ * Попыткой отправки проверка не считается: стратегия `afterFirstSubmit` остаётся в тихой фазе.
81
+ */
82
+ validate(): Promise<ValidationResult>;
83
+ /**
84
+ * Проверить шаг потока (без выключенных полей); неизвестный шаг — исключение.
85
+ *
86
+ * Это отчёт о шаге, как `validate` — о форме. Переход делает `flow.next()`: он сверяет модель
87
+ * и не переходит, если её изменили, пока шла проверка шага.
88
+ */
89
+ validateStep(stepId: string): Promise<ValidationResult>;
90
+ /**
91
+ * Снять уровень формы: подписки и таймеры живой проверки; идущие проверки — полная, шага,
92
+ * отправки — получают `cancelled`, и их замечания на ноды не ложатся. Идемпотентно. Ноды формы
93
+ * уровень не снимает — это делает сборка (`bundle.dispose()`).
94
+ */
95
+ dispose(): void;
96
+ }
97
+ /**
98
+ * Собрать уровень формы: правила, контроллер живой проверки, поток шагов и отправку.
99
+ *
100
+ * @typeParam T - Форма данных модели.
101
+ * @param model - Модель формы.
102
+ * @param config - Правила (`validation`) и поток (`flow`).
103
+ * @returns {@link FormLevel}.
104
+ * @throws Error если в потоке повторяется шаг.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const level = createFormLevel(model, {
109
+ * validation: { extras: crossRules },
110
+ * flow: { steps: [{ id: 'loan', rules: loanRules }, { id: 'confirm' }] },
111
+ * });
112
+ * const result = await level.submission.submit(sendApplication);
113
+ * if (result.status === 'invalid') showErrors();
114
+ * ```
115
+ *
116
+ * @group Form
117
+ */
118
+ export declare function createFormLevel<T>(model: FormModel<T>, config: {
119
+ validation?: FormValidation<T> | ValidationDefinition<T>;
120
+ flow?: FormFlowConfig<T>;
121
+ }): FormLevel<T>;
@@ -1,25 +1,9 @@
1
- import { FormNode } from './nodes/form-node';
2
- import { FormProxy } from './types/form-proxy';
1
+ import { FormGroup } from './types/nodes';
2
+ import { InternalFormNode } from './nodes/node-kind';
3
3
  /**
4
- * Создать Proxy для типобезопасного доступа к полям GroupNode
4
+ * Прокси группы над её сырым узлом.
5
5
  *
6
- * Proxy обеспечивает:
7
- * - Доступ к полям через точечную нотацию (form.email, form.address.city)
8
- * - Приоритет собственных свойств GroupNode над полями
9
- * - Цепочку proxy для вложенных GroupNode
10
- * - Блокировку прямого присваивания полям (только через setValue/patchValue)
11
- *
12
- * @param target - GroupNode для которого создаётся proxy
13
- * @param fields - Map полей формы
14
- * @returns Типизированный Proxy
15
- *
16
- * @internal
17
- *
18
- * @example
19
- * ```typescript
20
- * const proxy = buildFormProxy(groupNode, groupNode._fields);
21
- * proxy.email.setValue('test@example.com');
22
- * console.log(proxy.email.value.value);
23
- * ```
6
+ * @param target - Сырой узел группы
7
+ * @param children - Сырые ноды полей по именам
24
8
  */
25
- export declare function buildFormProxy<T>(target: FormNode<T>, fields: Map<keyof T, FormNode<unknown>>): FormProxy<T>;
9
+ export declare function buildFormProxy<T>(target: InternalFormNode<T>, children: ReadonlyMap<string, InternalFormNode>): FormGroup<T>;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Модуль `form` — доменный слой формы поверх реактивной модели (`model`).
3
3
  *
4
- * Ноды (value/touched/dirty/status/errors/componentProps), сборка формы из модели по идентичности
5
- * сигнала (`createForm`), state-операции над нодами (`enableWhen`/`disableWhen`), submit/статус.
4
+ * Ноды (value/touched/dirty/disabled/issues/componentProps) — контракты FormNode, FormControl,
5
+ * FormGroup, FormArray; сборка формы из модели по идентичности сигнала (`createForm`), уровень формы
6
+ * (проверка, отправка, поток шагов). Классы нод — внутренние.
6
7
  * React-биндинги вынесены в `platforms/react` — этот слой от React не зависит.
7
8
  * Schema-валидация — внешний контракт `@reformer/core/validation` (`validateModel`),
8
9
  * роутит ошибки в ноды через реестр сигнал→нода. Зависит от `model` (form→model разрешено);
@@ -11,28 +12,22 @@
11
12
  * @group Form
12
13
  * @module form
13
14
  */
14
- export { FormNode } from './nodes/form-node';
15
- export type { SetValueOptions } from './nodes/form-node';
16
- export { FieldNode } from './nodes/field-node';
17
- export { GroupNode } from './nodes/group-node';
18
- export { ArrayNode } from './nodes/array-node';
19
- export { ModelArrayNode } from './nodes/model-array-node';
20
- export type { ModelArrayControl } from './nodes/model-array-node';
21
15
  export { createForm } from './form-bundle';
22
16
  export type { CreateFormConfig, FormBundle, FormRender, ResolvedSchema, SchemaResolver, } from './form-bundle';
23
- export { createFormFromModel, createLegacyForm } from './create-form';
17
+ export { createFormFromModel, disposeForm } from './create-form';
24
18
  export type { CreateFormFromModelArgs } from './create-form';
25
19
  export { schemaSubtree } from './schema-subtree';
26
- export { createSchemaController, createSchemaScope, createSchemaOverrideMaps, unknownSchemaSelectors, } from './schema-controller';
20
+ export { compileSchema, isCompiledNode } from './schema/compile';
21
+ export type { CompileSchemaOptions } from './schema/compile';
22
+ export type { CompiledSchema, CompiledNode, CompiledChild, CompiledFieldNode, CompiledArrayNode, CompiledPartNode, CompiledContainerNode, CompiledTextNode, SchemaNodeId, SchemaScopeId, SchemaTextPart, } from './schema/compiled';
23
+ export { createSchemaController, createSchemaScope, unknownSchemaSelectors, } from './schema-controller';
27
24
  export type { SchemaController, SchemaScope, SchemaNodeControl, SchemaOverrideMaps, NodeLifecycleHooks, } from './schema-controller';
28
- export { createCoreForm } from './create-core-form';
29
- export type { CoreForm, CreateCoreFormConfig, CreateFormConfigBase } from './create-core-form';
30
- export { buildValidation } from './validation/config';
31
- export type { FormValidation, FormValidationBundle } from './validation/config';
32
- export { enableWhen, disableWhen } from './behaviors/node';
33
- export { FormErrorHandler, ErrorStrategy } from './validation/error-handler';
25
+ export type { CoreForm, CreateFormConfigBase } from './form-config-base';
26
+ export { createFormLevel } from './form-level';
27
+ export type { FormLevel, FormValidation, FormValidationState, FormFlowConfig, FlowStep, } from './form-level';
28
+ export { createSubmission } from './submission';
29
+ export type { ActionCheck, FormSubmission, SubmissionDependencies, SubmitResult, } from './submission';
30
+ export { createFormFlow } from './flow/flow';
31
+ export type { FormFlow, FormFlowOptions, FlowTransition, FlowStayReason } from './flow/flow';
34
32
  export { registerSignalNode, getNodeForSignal } from './signal-node-registry';
35
- export { FormSubmitter } from './form-submitter';
36
- export { SubscriptionManager } from './nodes/subscription-manager';
37
- export type { SubmittableForm, SubmitOptions, SubmitResult } from './form-submitter';
38
- export { isFormNode, isFieldNode, isGroupNode, isArrayNode, getNodeType } from './type-guards';
33
+ export { isFormNode, isFormControl, isFormGroup, isFormArray } from './type-guards';