@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,284 +0,0 @@
1
- import { Signal, ReadonlySignal } from '@preact/signals-core';
2
- import { FormNode, SetValueOptions } from './form-node';
3
- import { ValidationError, FieldStatus, FormSchema, GroupNodeConfig, FormValue } from '../types/index';
4
- import { FormProxy } from '../types/form-proxy';
5
- import { SubmitOptions, SubmitResult } from '../form-submitter';
6
- /**
7
- * GroupNode - узел для группы полей
8
- *
9
- * Создаётся из {@link FormSchema} (дерево field-конфигов). Обычно строится через `createForm`
10
- * (M1: `createForm({ model, schema })`); schema-валидация/behavior живут на слое модели
11
- * (`validateModel` из `@reformer/core/validation`, `computeFrom`/`enableWhen`/…), а не на ноде.
12
- *
13
- * @group Nodes
14
- *
15
- * @example
16
- * ```typescript
17
- * const form = new GroupNode({
18
- * email: { valueSignal: model.$.email, component: Input },
19
- * password: { valueSignal: model.$.password, component: Input },
20
- * });
21
- *
22
- * // Прямой доступ к полям через Proxy
23
- * const proxy = form.getProxy();
24
- * proxy.email.setValue('test@mail.com');
25
- * await proxy.validate();
26
- * console.log(proxy.valid.value);
27
- * ```
28
- */
29
- export declare class GroupNode<T> extends FormNode<T> {
30
- id: `${string}-${string}-${string}-${string}-${string}`;
31
- /**
32
- * Коллекция полей формы (упрощённый Map вместо FieldRegistry)
33
- */
34
- private readonly _fields;
35
- /**
36
- * Менеджер подписок для централизованного cleanup
37
- */
38
- private disposers;
39
- /**
40
- * Ссылка на Proxy-инстанс
41
- */
42
- private _proxyInstance?;
43
- /**
44
- * Cleanup декларативной схемы поведения (createForm({ behavior })). Вызывается в dispose().
45
- */
46
- private _behaviorCleanup?;
47
- /**
48
- * Фабрика для создания узлов формы
49
- */
50
- private readonly nodeFactory;
51
- /** Управление отправкой формы */
52
- private readonly formSubmitter;
53
- /** Флаг disabled состояния */
54
- private readonly _disabled;
55
- /** Form-level validation errors */
56
- private readonly _formErrors;
57
- /**
58
- * M1: связь листовой ноды-ребёнка → её сигнал модели (тот, что помечает `markDerived`).
59
- * Нужна bulk-сеттерам (`setValue`/`patchValue`), чтобы корректно определять derived-поля:
60
- * `field.value` — computed-обёртка, отличная от записываемого сигнала модели. @internal
61
- */
62
- private readonly _fieldSignals;
63
- readonly value: ReadonlySignal<T>;
64
- readonly valid: ReadonlySignal<boolean>;
65
- readonly invalid: ReadonlySignal<boolean>;
66
- readonly touched: ReadonlySignal<boolean>;
67
- readonly dirty: ReadonlySignal<boolean>;
68
- readonly pending: ReadonlySignal<boolean>;
69
- readonly errors: ReadonlySignal<ValidationError[]>;
70
- readonly status: ReadonlySignal<FieldStatus>;
71
- readonly submitting: ReadonlySignal<boolean>;
72
- /**
73
- * Создать GroupNode только со схемой формы (обратная совместимость)
74
- */
75
- constructor(schema: FormSchema<T>);
76
- /**
77
- * Создать GroupNode с полной конфигурацией (form, behavior, validation)
78
- */
79
- constructor(config: GroupNodeConfig<T>);
80
- /**
81
- * Создать Proxy для типобезопасного доступа к полям
82
- * @see buildFormProxy
83
- */
84
- private buildProxy;
85
- getValue(): T;
86
- setValue(value: T, options?: SetValueOptions): void;
87
- patchValue(value: Partial<T>): void;
88
- /**
89
- * Сбросить форму к указанным значениям (или к initialValues)
90
- *
91
- * @param value - опциональный объект со значениями для сброса
92
- *
93
- * @remarks
94
- * Рекурсивно вызывает reset() для всех полей формы
95
- *
96
- * @example
97
- * ```typescript
98
- * // Сброс к initialValues
99
- * form.reset();
100
- *
101
- * // Сброс к новым значениям
102
- * form.reset({ email: 'new@mail.com', password: '' });
103
- * ```
104
- */
105
- reset(value?: T): void;
106
- /**
107
- * Сбросить форму к исходным значениям (initialValues)
108
- */
109
- resetToInitial(): void;
110
- validate(): Promise<boolean>;
111
- /**
112
- * Установить form-level validation errors
113
- */
114
- setErrors(errors: ValidationError[]): void;
115
- /**
116
- * Очистить все errors (form-level + field-level)
117
- */
118
- clearErrors(): void;
119
- /**
120
- * Получить поле по ключу
121
- */
122
- getField<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
123
- /**
124
- * Получить Map всех полей формы (для совместимости)
125
- */
126
- get fields(): Map<keyof T, FormNode<FormValue>>;
127
- /**
128
- * Получить Proxy-инстанс для прямого доступа к полям
129
- *
130
- * Proxy позволяет обращаться к полям формы напрямую через точечную нотацию:
131
- * - form.email вместо form.fields.get('email')
132
- * - form.address.city вместо form.fields.get('address').fields.get('city')
133
- *
134
- * @returns Proxy-инстанс с типобезопасным доступом к полям или сама форма, если proxy не доступен
135
- *
136
- * @example
137
- * ```typescript
138
- * const form = new GroupNode({
139
- * email: { value: '', component: Input },
140
- * name: { value: '', component: Input },
141
- * });
142
- *
143
- * const proxy = form.getProxy();
144
- * console.log(proxy.email.value.value); // Прямой доступ к полю
145
- * ```
146
- */
147
- getProxy(): FormProxy<T>;
148
- /**
149
- * Получить все поля формы как итератор
150
- */
151
- getAllFields(): IterableIterator<FormNode<FormValue>>;
152
- protected onMarkAsTouched(): void;
153
- protected onMarkAsUntouched(): void;
154
- protected onMarkAsDirty(): void;
155
- protected onMarkAsPristine(): void;
156
- /**
157
- * Отправить форму
158
- *
159
- * @param onSubmit - Callback для отправки данных
160
- * @param options - Опции submit (skipValidation, skipTouch)
161
- * @returns Результат от onSubmit или `null` если валидация не пройдена
162
- *
163
- * @remarks
164
- * `null` перегружен: он означает и «валидация не пройдена», и легитимный `null`-результат
165
- * `onSubmit` (или void-обработчик). Если вызывающей стороне нужно различать эти случаи —
166
- * используйте {@link submitWithResult}, который возвращает явный флаг `success`.
167
- */
168
- submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
169
- /**
170
- * Отправить форму с расширенным результатом
171
- *
172
- * @param onSubmit - Callback для отправки данных
173
- * @param options - Опции submit
174
- * @returns Объект SubmitResult с данными, статусом и возможной ошибкой
175
- */
176
- submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
177
- /**
178
- * Получить вложенное поле по пути
179
- *
180
- * Поддерживаемые форматы путей:
181
- * - Simple: "email" - получить поле верхнего уровня
182
- * - Nested: "address.city" - получить вложенное поле
183
- * - Array index: "items[0]" - получить элемент массива по индексу
184
- * - Combined: "items[0].name" - получить поле элемента массива
185
- *
186
- * @param path - Путь к полю
187
- * @returns FormNode если найдено, undefined если путь не существует
188
- *
189
- * @example
190
- * ```typescript
191
- * const form = new GroupNode({
192
- * email: { value: '', component: Input },
193
- * address: {
194
- * city: { value: '', component: Input }
195
- * },
196
- * items: [{ name: { value: '', component: Input } }]
197
- * });
198
- *
199
- * form.getFieldByPath('email'); // FieldNode
200
- * form.getFieldByPath('address.city'); // FieldNode
201
- * form.getFieldByPath('items[0]'); // GroupNode
202
- * form.getFieldByPath('items[0].name'); // FieldNode
203
- * form.getFieldByPath('invalid.path'); // undefined
204
- * ```
205
- */
206
- getFieldByPath(path: string): FormNode<FormValue> | undefined;
207
- /**
208
- * Создать узел на основе конфигурации
209
- *
210
- * ✅ РЕФАКТОРИНГ: Полное делегирование NodeFactory
211
- *
212
- * NodeFactory теперь обрабатывает:
213
- * - Массивы [schema, ...items]
214
- * - FieldConfig
215
- * - GroupConfig
216
- * - ArrayConfig
217
- *
218
- * @param config Конфигурация узла
219
- * @returns Созданный узел формы
220
- * @private
221
- */
222
- private createNode;
223
- /**
224
- * Связывает два поля: при изменении source автоматически обновляется target
225
- */
226
- linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
227
- /**
228
- * Подписка на изменения вложенного поля по строковому пути
229
- * Поддерживает вложенные пути типа "address.city"
230
- *
231
- * @param fieldPath - Строковый путь к полю (например, "address.city")
232
- * @param callback - Функция, вызываемая при изменении поля
233
- * @returns Функция отписки для cleanup
234
- *
235
- * @example
236
- * ```typescript
237
- * // Подписка на изменение страны для загрузки городов
238
- * const dispose = form.watchField(
239
- * 'registrationAddress.country',
240
- * async (countryCode) => {
241
- * if (countryCode) {
242
- * const cities = await fetchCitiesByCountry(countryCode);
243
- * form.registrationAddress.city.updateComponentProps({
244
- * options: cities
245
- * });
246
- * }
247
- * }
248
- * );
249
- *
250
- * // Cleanup
251
- * useEffect(() => dispose, []);
252
- * ```
253
- */
254
- /** Подписка на top-level поле — value типизирован как `T[K]`. */
255
- watchField<K extends keyof T & string>(fieldPath: K, callback: (value: T[K]) => void | Promise<void>): () => void;
256
- /**
257
- * Подписка на вложенное поле по строковому пути ("address.city").
258
- * Путь нельзя выразить в типах узла → value честно `unknown`, потребитель сужает.
259
- */
260
- watchFieldByPath(path: string, callback: (value: unknown) => void | Promise<void>): () => void;
261
- /**
262
- * Hook: вызывается после disable()
263
- */
264
- protected onDisable(): void;
265
- /**
266
- * Hook: вызывается после enable()
267
- */
268
- protected onEnable(): void;
269
- /**
270
- * Прикрепить cleanup декларативной схемы поведения (вызывается createForm).
271
- * @internal
272
- */
273
- attachBehaviorCleanup(cleanup: () => void): void;
274
- /**
275
- * Связать листовую ноду-ребёнка с её сигналом модели (M1). Вызывается `createForm` для каждого
276
- * листового поля на его владеющей группе — используется bulk-сеттерами для derived-guard (F9).
277
- * @internal
278
- */
279
- registerFieldSignal(node: FormNode<any>, signal: Signal<unknown>): void;
280
- /**
281
- * Очистить все ресурсы узла
282
- */
283
- dispose(): void;
284
- }
@@ -1,121 +0,0 @@
1
- import { Signal, ReadonlySignal } from '@preact/signals-core';
2
- import { FormNode } from './form-node';
3
- import { FieldStatus, ValidationError } from '../types/index';
4
- import { FormModel } from '../../state/types';
5
- import { FormProxy } from '../types/form-proxy';
6
- /** Минимальный контракт реактивного массива модели, используемый узлом. */
7
- export interface ModelArrayControl<TItem extends object> {
8
- readonly length: number;
9
- at(index: number): FormModel<TItem> | undefined;
10
- push(item: TItem): void;
11
- insertAt(index: number, item: TItem): void;
12
- removeAt(index: number): void;
13
- move(from: number, to: number): void;
14
- swap(a: number, b: number): void;
15
- clear(): void;
16
- toArray(): TItem[];
17
- }
18
- /**
19
- * Узел массива, делегирующий данные массиву {@link FormModel} (архитектура M1).
20
- *
21
- * В отличие от {@link ArrayNode} (владеет элементами сам), `ModelArrayNode` НЕ владеет данными:
22
- * массив принадлежит модели, а узел держит per-item формы элементов (привязанные к сигналам
23
- * под-моделей) и синхронизирует их с длиной массива модели. Мутации (`push`/`removeAt`/`move`/…)
24
- * делегируются массиву модели; per-item формы кэшируются по идентичности под-модели, поэтому при
25
- * reorder/повторном рендере не пересоздаются (состояние и валидация сохраняются). Реализует тот же
26
- * контракт, что ждут секции массива и `useFormControl` (`length`/`value`/`valid`/`errors`/`at`/`push`/…).
27
- *
28
- * Обычно создаётся не напрямую, а `createForm({ model, schema })`: когда в схеме встречается узел
29
- * массива `{ array: model.<field>, item: (item) => itemSchema }`, форма материализует его как
30
- * `ModelArrayNode` и кладёт под `form.<field>` (совместим с `FormArraySection`).
31
- *
32
- * @group Nodes
33
- *
34
- * @example Массив как часть формы (через createForm)
35
- * ```typescript
36
- * const model = createModel<{ rows: { name: string; qty: number }[] }>({ rows: [] });
37
- *
38
- * const rowItem = (item: FormModel<{ name: string; qty: number }>) => ({
39
- * name: { value: item.$.name, component: Input },
40
- * qty: { value: item.$.qty, component: Input },
41
- * });
42
- *
43
- * const form = createForm({
44
- * model,
45
- * schema: {
46
- * children: [{ array: model.rows, item: rowItem }],
47
- * },
48
- * });
49
- *
50
- * const rows = form.rows as unknown as ModelArrayNode<{ name: string; qty: number }>;
51
- * rows.push({ name: 'A', qty: 1 }); // мутация уезжает в model.rows
52
- * rows.at(0)?.name.setValue('B'); // правка поля элемента доезжает в под-модель
53
- * rows.length.value; // 1 (реактивная длина)
54
- * ```
55
- */
56
- export declare class ModelArrayNode<T extends object> extends FormNode<T[]> {
57
- private readonly control;
58
- private readonly buildItem;
59
- private readonly itemNodes;
60
- private readonly cache;
61
- private mountedModels;
62
- private readonly initial;
63
- private readonly _arrayErrors;
64
- private readonly disposeSync;
65
- /**
66
- * Сигнал per-item форм. Публичен в т.ч. чтобы `isArrayNode` распознавал узел как массив
67
- * (duck-typing проверяет наличие `items`).
68
- */
69
- get items(): Signal<FormProxy<T>[]>;
70
- readonly value: ReadonlySignal<T[]>;
71
- readonly valid: ReadonlySignal<boolean>;
72
- readonly invalid: ReadonlySignal<boolean>;
73
- readonly pending: ReadonlySignal<boolean>;
74
- readonly touched: ReadonlySignal<boolean>;
75
- readonly dirty: ReadonlySignal<boolean>;
76
- readonly errors: ReadonlySignal<ValidationError[]>;
77
- readonly status: ReadonlySignal<FieldStatus>;
78
- readonly length: ReadonlySignal<number>;
79
- /**
80
- * @param control Реактивный массив модели (`model.<path>`).
81
- * @param buildItem Строитель формы элемента по его под-модели (инъекция, чтобы избежать цикла
82
- * импорта с `create-form`). Обычно `(item) => createForm({ model: item, schema: itemSchema(item) })`.
83
- */
84
- constructor(control: ModelArrayControl<T>, buildItem: (item: FormModel<T>) => FormProxy<T>);
85
- at(index: number): FormProxy<T> | undefined;
86
- map<R>(fn: (item: FormProxy<T>, index: number) => R): R[];
87
- forEach(fn: (item: FormProxy<T>, index: number) => void): void;
88
- push(item?: Partial<T>): void;
89
- insert(index: number, item?: Partial<T>): void;
90
- removeAt(index: number): void;
91
- /**
92
- * Переместить элемент модель-массива. Делегирует мутацию массиву модели; внутренний `effect`
93
- * пересоберёт `itemNodes` в новом порядке (per-item формы сохраняются по кешу идентичности).
94
- */
95
- move(from: number, to: number): void;
96
- /** Поменять местами два элемента модель-массива (см. {@link move}). */
97
- swap(a: number, b: number): void;
98
- clear(): void;
99
- getValue(): T[];
100
- setValue(values: T[]): void;
101
- patchValue(values: Partial<T[]>): void;
102
- reset(values?: T[]): void;
103
- resetToInitial(): void;
104
- validate(): Promise<boolean>;
105
- setErrors(errors: ValidationError[]): void;
106
- clearErrors(): void;
107
- protected onMarkAsTouched(): void;
108
- protected onMarkAsUntouched(): void;
109
- protected onMarkAsDirty(): void;
110
- protected onMarkAsPristine(): void;
111
- protected onDisable(): void;
112
- protected onEnable(): void;
113
- /** Диспозит форму элемента (эффекты/поведения/вложенные подписки); терпима к не-GroupNode. */
114
- private disposeItemNode;
115
- /**
116
- * Очистка подписки синхронизации И детерминированный teardown всех форм элементов.
117
- * Без пробрасывания dispose в itemNodes эффекты/поведения каждой формы элемента (в т.ч. disposeSync
118
- * вложенных ModelArrayNode) остались бы жить после teardown формы — утечка подписок.
119
- */
120
- dispose(): void;
121
- }
@@ -1,115 +0,0 @@
1
- import { ReadonlySignal } from '@preact/signals-core';
2
- import { FieldStatus } from './types/index';
3
- /**
4
- * FormStatusMachine - управляет состоянием поля формы
5
- *
6
- * Предоставляет:
7
- * - Единый источник истины для статуса
8
- * - Computed signals для derived состояний (valid, invalid, pending, disabled)
9
- * - Валидацию переходов между состояниями
10
- *
11
- * @example
12
- * ```typescript
13
- * const statusMachine = new FormStatusMachine('valid');
14
- *
15
- * // Начало валидации
16
- * statusMachine.startValidation();
17
- * console.log(statusMachine.pending.value); // true
18
- *
19
- * // Завершение валидации с ошибками
20
- * statusMachine.completeValidation(true);
21
- * console.log(statusMachine.invalid.value); // true
22
- *
23
- * // Отключение поля
24
- * statusMachine.disable();
25
- * console.log(statusMachine.disabled.value); // true
26
- * ```
27
- */
28
- export declare class FormStatusMachine {
29
- /** Внутренний сигнал статуса */
30
- private readonly _status;
31
- /** Публичный read-only сигнал статуса */
32
- readonly status: ReadonlySignal<FieldStatus>;
33
- /** Поле валидно */
34
- readonly valid: ReadonlySignal<boolean>;
35
- /** Поле невалидно */
36
- readonly invalid: ReadonlySignal<boolean>;
37
- /** Идет валидация */
38
- readonly pending: ReadonlySignal<boolean>;
39
- /** Поле отключено */
40
- readonly disabled: ReadonlySignal<boolean>;
41
- /**
42
- * @param initial - Начальный статус (по умолчанию 'valid')
43
- */
44
- constructor(initial?: FieldStatus);
45
- /**
46
- * Начать валидацию
47
- *
48
- * Переводит статус в 'pending' если поле не отключено
49
- *
50
- * @example
51
- * ```typescript
52
- * statusMachine.startValidation();
53
- * // status: 'pending'
54
- * ```
55
- */
56
- startValidation(): void;
57
- /**
58
- * Завершить валидацию
59
- *
60
- * @param hasErrors - Есть ли ошибки валидации
61
- *
62
- * @example
63
- * ```typescript
64
- * // Валидация успешна
65
- * statusMachine.completeValidation(false);
66
- * // status: 'valid'
67
- *
68
- * // Есть ошибки
69
- * statusMachine.completeValidation(true);
70
- * // status: 'invalid'
71
- * ```
72
- */
73
- completeValidation(hasErrors: boolean): void;
74
- /**
75
- * Установить ошибки напрямую (без перехода через pending)
76
- *
77
- * Используется для синхронной валидации или установки ошибок извне
78
- *
79
- * @param hasErrors - Есть ли ошибки
80
- *
81
- * @example
82
- * ```typescript
83
- * statusMachine.setErrors(true);
84
- * // status: 'invalid'
85
- * ```
86
- */
87
- setErrors(hasErrors: boolean): void;
88
- /**
89
- * Отключить поле
90
- *
91
- * Переводит статус в 'disabled'
92
- *
93
- * @example
94
- * ```typescript
95
- * statusMachine.disable();
96
- * // status: 'disabled'
97
- * ```
98
- */
99
- disable(): void;
100
- /**
101
- * Включить поле
102
- *
103
- * @param hasErrors - Есть ли ошибки (определяет valid/invalid)
104
- *
105
- * @example
106
- * ```typescript
107
- * statusMachine.enable(false);
108
- * // status: 'valid'
109
- *
110
- * statusMachine.enable(true);
111
- * // status: 'invalid'
112
- * ```
113
- */
114
- enable(hasErrors?: boolean): void;
115
- }
@@ -1,107 +0,0 @@
1
- import { ComponentType } from 'react';
2
- import { Signal } from '@preact/signals-core';
3
- import { ValidatorFn, AsyncValidatorFn, AnyFunction } from './index';
4
- /**
5
- * Конфигурация поля
6
- * @group Types
7
- * @category Configuration Types
8
- * @remarks
9
- * В схеме `createForm({ model, schema })` лист связывают через **`value: model.$.field`**: harvest
10
- * ловит узел по идентичности `Signal` (`node.value instanceof Signal` — см. `create-form.ts`) и
11
- * нормализует его в `valueSignal`. Писать `valueSignal:` **в самой схеме `createForm` нельзя** —
12
- * harvest его не подхватит (проверка идёт по `node.value`), и `component`/`componentProps` этого
13
- * узла молча отбросятся. `value`/`valueSignal` на прямом FieldNode-конфиге — это
14
- * post-normalization / legacy-слой, а не форма записи в схеме `createForm`.
15
- */
16
- export interface FieldConfig<T> {
17
- /**
18
- * Начальное значение-литерал (legacy-путь). Под архитектурой M1 значение приходит из
19
- * {@link FieldConfig.valueSignal} (сигнал {@link FormModel}); тогда `value` не требуется.
20
- */
21
- value?: T | null;
22
- /**
23
- * Сигнал значения из {@link FormModel} (M1). Если задан — служит источником истины значения
24
- * поля (нода не владеет значением, а ссылается на этот сигнал). Имеет приоритет над `value`.
25
- */
26
- valueSignal?: Signal<T>;
27
- /**
28
- * UI-компонент поля. Опционален: core-часть можно использовать без ссылки на компонент
29
- * (значение/валидация работают без UI; компонент нужен только для рендеринга).
30
- */
31
- component?: ComponentType<any>;
32
- componentProps?: any;
33
- validators?: ValidatorFn<T>[];
34
- asyncValidators?: AsyncValidatorFn<T>[];
35
- disabled?: boolean;
36
- updateOn?: 'change' | 'blur' | 'submit';
37
- /** Задержка (в мс) перед запуском асинхронной валидации */
38
- debounce?: number;
39
- }
40
- /**
41
- * «Хотя бы одно поле обязано присутствовать»: превращает объект со всеми опциональными
42
- * свойствами в объединение вариантов, где ровно один ключ обязателен, а остальные опциональны.
43
- * Пустой объект `{}` не assignable ни к одному варианту и потому отклоняется.
44
- *
45
- * @internal
46
- */
47
- type RequireAtLeastOne<T> = {
48
- [K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
49
- }[keyof T];
50
- /**
51
- * {@link FieldConfig}, из которого нельзя сделать пустой `{}`.
52
- *
53
- * Все свойства {@link FieldConfig} опциональны, поэтому в {@link FormSchema} опечатка вида
54
- * `{ email: {} }` молча проходит проверку типов и в рантайме становится пустой подгруппой
55
- * вместо поля. `NonEmptyFieldConfig` требует наличия хотя бы одного свойства (value/valueSignal/
56
- * component/validators/…), оставляя каждое из них опциональным по отдельности.
57
- *
58
- * @internal
59
- */
60
- type NonEmptyFieldConfig<T> = RequireAtLeastOne<FieldConfig<T>>;
61
- /**
62
- * **Data-shaped** конфиг формы: ключи повторяют структуру данных `T`, значения — {@link FieldConfig}
63
- * (или вложенный `FormSchema`). Форма конфига, из которой строится {@link GroupNode}.
64
- * - `T[] -> [FormSchema<T>]` (массив с одним элементом)
65
- * - `object -> FormSchema<T>` (группа)
66
- * - `primitive -> FieldConfig<T>` (поле)
67
- *
68
- * Использует NonNullable для корректной обработки опциональных полей.
69
- *
70
- * ⚠️ Не путать с {@link FormSchemaNode} — тот описывает **узел дерева** M1-схемы (лист/массив/
71
- * контейнер), передаваемой в `createForm({ model, schema })`. `FormSchema` — это data-shaped конфиг
72
- * (ключи = поля данных), а не узел дерева.
73
- *
74
- * @group Types
75
- * @category Configuration Types
76
- *
77
- * @example
78
- * ```typescript
79
- * interface Form {
80
- * name: string; // → FieldConfig<string>
81
- * address: { // → FormSchema<Address>
82
- * city: string;
83
- * street: string;
84
- * };
85
- * items?: Array<{ // → [FormSchema<Item>] (опциональный)
86
- * title: string;
87
- * price: number;
88
- * }>;
89
- * }
90
- *
91
- * const schema: FormSchema<Form> = {
92
- * name: { value: '', component: Input },
93
- * address: {
94
- * city: { value: '', component: Input },
95
- * street: { value: '', component: Input },
96
- * },
97
- * items: [{
98
- * title: { value: '', component: Input },
99
- * price: { value: 0, component: Input },
100
- * }],
101
- * };
102
- * ```
103
- */
104
- export type FormSchema<T> = {
105
- [K in keyof T]: NonNullable<T[K]> extends string | number | boolean ? NonEmptyFieldConfig<T[K]> : NonNullable<T[K]> extends Array<infer U> ? U extends string | number | boolean ? NonEmptyFieldConfig<T[K]> : U extends Date | File | Blob | AnyFunction ? NonEmptyFieldConfig<T[K]> : [FormSchema<U>] : NonNullable<T[K]> extends Date | File | Blob | AnyFunction ? NonEmptyFieldConfig<T[K]> : FormSchema<NonNullable<T[K]>>;
106
- };
107
- export {};