@reformer/core 10.2.0 → 11.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +90 -68
  2. package/dist/behaviors-value-D05z3my6.js +109 -0
  3. package/dist/behaviors.d.ts +1 -1
  4. package/dist/behaviors.js +376 -211
  5. package/dist/create-model-DPSG3I0O.js +30 -0
  6. package/dist/date-utils-90-qZbse.js +43 -0
  7. package/dist/en-HP8XEych.js +44 -0
  8. package/dist/form/aggregate-signals.d.ts +21 -63
  9. package/dist/form/behaviors/collections.d.ts +84 -0
  10. package/dist/form/behaviors/context.d.ts +56 -0
  11. package/dist/form/behaviors/index.d.ts +30 -0
  12. package/dist/form/behaviors/internals.d.ts +41 -0
  13. package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +28 -5
  14. package/dist/form/behaviors/operators.d.ts +59 -0
  15. package/dist/form/behaviors/schema-operators.d.ts +83 -0
  16. package/dist/form/behaviors/types.d.ts +37 -0
  17. package/dist/form/create-form.d.ts +59 -70
  18. package/dist/form/flow/flow.d.ts +87 -0
  19. package/dist/form/form-bundle.d.ts +153 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +23 -27
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +159 -0
  37. package/dist/form/schema-subtree.d.ts +28 -0
  38. package/dist/form/signal-node-registry.d.ts +27 -22
  39. package/dist/form/submission.d.ts +93 -0
  40. package/dist/form/type-guards.d.ts +14 -73
  41. package/dist/form/types/contracts.d.ts +21 -91
  42. package/dist/form/types/field-config.d.ts +27 -0
  43. package/dist/form/types/index.d.ts +5 -91
  44. package/dist/form/types/nodes.d.ts +159 -0
  45. package/dist/form/types/schema-node.d.ts +148 -50
  46. package/dist/form/types/validation-schema.d.ts +24 -41
  47. package/dist/form/validation/context.d.ts +67 -0
  48. package/dist/form/validation/index.d.ts +24 -28
  49. package/dist/form/validation/operators.d.ts +101 -0
  50. package/dist/form/validation/run.d.ts +120 -0
  51. package/dist/form/validation/strategy.d.ts +89 -0
  52. package/dist/form/validation/types.d.ts +83 -0
  53. package/dist/form/validators/date-utils.d.ts +42 -0
  54. package/dist/form/validators/email.d.ts +23 -0
  55. package/dist/form/{validation/validators → validators}/file-type.d.ts +10 -10
  56. package/dist/form/{validation/validators → validators}/file-utils.d.ts +3 -2
  57. package/dist/form/validators/future-date.d.ts +23 -0
  58. package/dist/form/{validation/validators → validators}/index.d.ts +7 -4
  59. package/dist/form/validators/integer.d.ts +22 -0
  60. package/dist/form/validators/is-date.d.ts +24 -0
  61. package/dist/form/validators/is-number.d.ts +25 -0
  62. package/dist/form/validators/max-age.d.ts +28 -0
  63. package/dist/form/validators/max-date.d.ts +25 -0
  64. package/dist/form/{validation/validators → validators}/max-file-size.d.ts +10 -10
  65. package/dist/form/validators/max-files.d.ts +26 -0
  66. package/dist/form/validators/max-length.d.ts +33 -0
  67. package/dist/form/validators/max-total-file-size.d.ts +26 -0
  68. package/dist/form/validators/max.d.ts +26 -0
  69. package/dist/form/validators/min-age.d.ts +27 -0
  70. package/dist/form/validators/min-date.d.ts +25 -0
  71. package/dist/form/{validation/validators → validators}/min-file-size.d.ts +10 -10
  72. package/dist/form/validators/min-files.d.ts +27 -0
  73. package/dist/form/validators/min-length.d.ts +33 -0
  74. package/dist/form/validators/min.d.ts +26 -0
  75. package/dist/form/validators/multiple-of.d.ts +25 -0
  76. package/dist/form/validators/non-negative.d.ts +22 -0
  77. package/dist/form/validators/non-zero.d.ts +22 -0
  78. package/dist/form/validators/past-date.d.ts +23 -0
  79. package/dist/form/validators/pattern.d.ts +26 -0
  80. package/dist/form/{validation/validators → validators}/phone.d.ts +11 -11
  81. package/dist/form/validators/required.d.ts +29 -0
  82. package/dist/form/validators/url.d.ts +45 -0
  83. package/dist/form/validators/validation-issue.d.ts +8 -0
  84. package/dist/i18n/descriptor.d.ts +54 -0
  85. package/dist/i18n/en.json.d.ts +49 -0
  86. package/dist/i18n/loader.d.ts +71 -0
  87. package/dist/i18n/locale-store.d.ts +46 -0
  88. package/dist/i18n/locale.d.ts +90 -0
  89. package/dist/i18n/message-format.d.ts +136 -0
  90. package/dist/i18n/resolve-localized.d.ts +17 -0
  91. package/dist/i18n/ru.json.d.ts +49 -0
  92. package/dist/i18n/translator.d.ts +61 -0
  93. package/dist/i18n/validation-message.d.ts +53 -0
  94. package/dist/i18n.d.ts +26 -0
  95. package/dist/i18n.js +86 -0
  96. package/dist/index.d.ts +18 -6
  97. package/dist/index.js +932 -2779
  98. package/dist/loader-BWkigpVe.js +52 -0
  99. package/dist/locale/en.d.ts +2 -0
  100. package/dist/locale/en.js +5 -0
  101. package/dist/locale/en.json +45 -0
  102. package/dist/locale/index.d.ts +18 -0
  103. package/dist/locale/ru.d.ts +2 -0
  104. package/dist/locale/ru.js +44 -0
  105. package/dist/locale/ru.json +45 -0
  106. package/dist/locale-CG54AU2Q.js +219 -0
  107. package/dist/locale.d.ts +2 -0
  108. package/dist/locale.js +9 -0
  109. package/dist/{state → model}/behaviors-value.d.ts +24 -6
  110. package/dist/{state/form-model.d.ts → model/create-model.d.ts} +18 -4
  111. package/dist/{state → model}/derived-registry.d.ts +1 -1
  112. package/dist/model/index.d.ts +42 -0
  113. package/dist/model/model-nodes.d.ts +190 -0
  114. package/dist/model/model-signals-proxy.d.ts +63 -0
  115. package/dist/model/model-value-proxy.d.ts +84 -0
  116. package/dist/{state → model}/safe-effect.d.ts +50 -2
  117. package/dist/model/types.d.ts +239 -0
  118. package/dist/model.d.ts +2 -0
  119. package/dist/model.js +27 -0
  120. package/dist/node-kind-C-r6ZMdo.js +15 -0
  121. package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
  122. package/dist/platforms/react/field-binding/field-handle.d.ts +53 -0
  123. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  124. package/dist/{form → platforms/react}/hooks/types.d.ts +60 -38
  125. package/dist/platforms/react/hooks/use-form-bundle.d.ts +41 -0
  126. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  127. package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +13 -7
  128. package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +6 -6
  129. package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +7 -9
  130. package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +18 -18
  131. package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +9 -1
  132. package/dist/platforms/react/i18n/context.d.ts +18 -0
  133. package/dist/platforms/react/i18n/hooks.d.ts +65 -0
  134. package/dist/platforms/react/i18n/provider.d.ts +59 -0
  135. package/dist/platforms/react/index.d.ts +30 -0
  136. package/dist/react.d.ts +22 -0
  137. package/dist/react.js +505 -0
  138. package/dist/runtime-token.d.ts +22 -0
  139. package/dist/safe-effect-BazLOv1g.js +669 -0
  140. package/dist/schema-controller-CpiO2JoK.js +143 -0
  141. package/dist/signals.d.ts +15 -9
  142. package/dist/signals.js +11 -6
  143. package/dist/strategy-DzvnsWLh.js +375 -0
  144. package/dist/type-guards-D-N_5Ddh.js +19 -0
  145. package/dist/validation-issue-B9mjI8jz.js +7 -0
  146. package/dist/validation-message-Ced9MGxs.js +194 -0
  147. package/dist/validation.d.ts +1 -1
  148. package/dist/validation.js +11 -165
  149. package/dist/validators/email.d.ts +1 -1
  150. package/dist/validators/email.js +5 -8
  151. package/dist/validators/future-date.d.ts +1 -1
  152. package/dist/validators/future-date.js +10 -13
  153. package/dist/validators/integer.d.ts +1 -1
  154. package/dist/validators/integer.js +4 -7
  155. package/dist/validators/is-date.d.ts +1 -1
  156. package/dist/validators/is-date.js +5 -8
  157. package/dist/validators/is-number.d.ts +1 -1
  158. package/dist/validators/is-number.js +4 -7
  159. package/dist/validators/max-age.d.ts +1 -1
  160. package/dist/validators/max-age.js +8 -12
  161. package/dist/validators/max-date.d.ts +1 -1
  162. package/dist/validators/max-date.js +10 -13
  163. package/dist/validators/max-length.d.ts +1 -1
  164. package/dist/validators/max-length.js +7 -10
  165. package/dist/validators/max.d.ts +1 -1
  166. package/dist/validators/max.js +4 -7
  167. package/dist/validators/min-age.d.ts +1 -1
  168. package/dist/validators/min-age.js +10 -11
  169. package/dist/validators/min-date.d.ts +1 -1
  170. package/dist/validators/min-date.js +8 -11
  171. package/dist/validators/min-length.d.ts +1 -1
  172. package/dist/validators/min-length.js +4 -7
  173. package/dist/validators/min.d.ts +1 -1
  174. package/dist/validators/min.js +4 -7
  175. package/dist/validators/multiple-of.d.ts +1 -1
  176. package/dist/validators/multiple-of.js +11 -10
  177. package/dist/validators/non-negative.d.ts +1 -1
  178. package/dist/validators/non-negative.js +2 -5
  179. package/dist/validators/non-zero.d.ts +1 -1
  180. package/dist/validators/non-zero.js +4 -7
  181. package/dist/validators/past-date.d.ts +1 -1
  182. package/dist/validators/past-date.js +10 -13
  183. package/dist/validators/pattern.d.ts +1 -1
  184. package/dist/validators/pattern.js +5 -8
  185. package/dist/validators/phone.d.ts +1 -1
  186. package/dist/validators/phone.js +10 -11
  187. package/dist/validators/required.d.ts +1 -1
  188. package/dist/validators/required.js +4 -7
  189. package/dist/validators/url.d.ts +1 -1
  190. package/dist/validators/url.js +48 -17
  191. package/dist/validators.d.ts +1 -5
  192. package/dist/validators.js +98 -154
  193. package/docs/llms/01-api-reference.md +89 -0
  194. package/docs/llms/02-quick-start.md +181 -0
  195. package/docs/llms/03-api-signatures.md +494 -0
  196. package/docs/llms/04-common-patterns.md +204 -0
  197. package/docs/llms/05-common-mistakes.md +192 -0
  198. package/docs/llms/06-troubleshooting.md +16 -0
  199. package/docs/llms/07-complete-import.md +130 -0
  200. package/docs/llms/08-form-types.md +54 -0
  201. package/docs/llms/09-formschema.md +191 -0
  202. package/docs/llms/10-arrays.md +241 -0
  203. package/docs/llms/11-async-watchfield.md +53 -0
  204. package/docs/llms/12-array-cleanup.md +31 -0
  205. package/docs/llms/13-multi-step.md +162 -0
  206. package/docs/llms/14-extended-mistakes.md +101 -0
  207. package/docs/llms/15-project-structure.md +279 -0
  208. package/docs/llms/16-ui-components.md +229 -0
  209. package/docs/llms/17-nonexistent-api.md +278 -0
  210. package/docs/llms/18-conditional-fields.md +222 -0
  211. package/docs/llms/19-reading-values.md +100 -0
  212. package/docs/llms/20-compute-vs-watch.md +153 -0
  213. package/docs/llms/21-array-operations.md +163 -0
  214. package/docs/llms/22-cycle-detection.md +86 -0
  215. package/docs/llms/23-copy-from.md +141 -0
  216. package/docs/llms/24-sync-fields.md +115 -0
  217. package/docs/llms/25-reset-when.md +122 -0
  218. package/docs/llms/26-transform-value.md +136 -0
  219. package/docs/llms/27-revalidate-when.md +133 -0
  220. package/docs/llms/28-submit-and-reset.md +409 -0
  221. package/docs/llms/29-async-preload.md +203 -0
  222. package/docs/llms/30-type-safety-recipes.md +213 -0
  223. package/docs/llms/31-async-validator-debounce.md +178 -0
  224. package/docs/llms/32-async-options-loading.md +108 -0
  225. package/docs/llms/33-validation-strategy.md +305 -0
  226. package/docs/llms/34-i18n.md +298 -0
  227. package/docs/llms/35-unified-contract.md +309 -0
  228. package/docs/llms/36-issue-messages.md +284 -0
  229. package/docs/llms/37-compiled-schema.md +332 -0
  230. package/docs/llms/38-form-flow.md +396 -0
  231. package/llms-index.json +1 -0
  232. package/llms.txt +10014 -3826
  233. package/package.json +77 -15
  234. package/dist/behaviors-node-DGEFMTQu.js +0 -18
  235. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  236. package/dist/date-utils-dTT_x_Be.js +0 -31
  237. package/dist/derived-registry-CluZCQ7U.js +0 -16
  238. package/dist/form/behaviors.d.ts +0 -133
  239. package/dist/form/error-handler.d.ts +0 -166
  240. package/dist/form/factories/index.d.ts +0 -6
  241. package/dist/form/factories/node-factory.d.ts +0 -188
  242. package/dist/form/form-submitter.d.ts +0 -125
  243. package/dist/form/nodes/array-node.d.ts +0 -360
  244. package/dist/form/nodes/field-node.d.ts +0 -294
  245. package/dist/form/nodes/form-node.d.ts +0 -340
  246. package/dist/form/nodes/group-node.d.ts +0 -288
  247. package/dist/form/nodes/model-array-node.d.ts +0 -121
  248. package/dist/form/status-machine.d.ts +0 -153
  249. package/dist/form/types/deep-schema.d.ts +0 -116
  250. package/dist/form/types/form-proxy.d.ts +0 -141
  251. package/dist/form/unique-id.d.ts +0 -53
  252. package/dist/form/validation/validators/date-utils.d.ts +0 -30
  253. package/dist/form/validation/validators/email.d.ts +0 -23
  254. package/dist/form/validation/validators/future-date.d.ts +0 -23
  255. package/dist/form/validation/validators/integer.d.ts +0 -22
  256. package/dist/form/validation/validators/is-date.d.ts +0 -23
  257. package/dist/form/validation/validators/is-number.d.ts +0 -24
  258. package/dist/form/validation/validators/max-age.d.ts +0 -25
  259. package/dist/form/validation/validators/max-date.d.ts +0 -25
  260. package/dist/form/validation/validators/max-files.d.ts +0 -26
  261. package/dist/form/validation/validators/max-length.d.ts +0 -27
  262. package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
  263. package/dist/form/validation/validators/max.d.ts +0 -25
  264. package/dist/form/validation/validators/min-age.d.ts +0 -25
  265. package/dist/form/validation/validators/min-date.d.ts +0 -25
  266. package/dist/form/validation/validators/min-files.d.ts +0 -26
  267. package/dist/form/validation/validators/min-length.d.ts +0 -27
  268. package/dist/form/validation/validators/min.d.ts +0 -25
  269. package/dist/form/validation/validators/multiple-of.d.ts +0 -24
  270. package/dist/form/validation/validators/non-negative.d.ts +0 -22
  271. package/dist/form/validation/validators/non-zero.d.ts +0 -22
  272. package/dist/form/validation/validators/past-date.d.ts +0 -23
  273. package/dist/form/validation/validators/pattern.d.ts +0 -33
  274. package/dist/form/validation/validators/required.d.ts +0 -32
  275. package/dist/form/validation/validators/url.d.ts +0 -43
  276. package/dist/form/validation-schema.d.ts +0 -106
  277. package/dist/form/validation-strategy.d.ts +0 -55
  278. package/dist/form/validators.d.ts +0 -6
  279. package/dist/form-model-DYCzGGKF.js +0 -286
  280. package/dist/state/index.d.ts +0 -31
  281. package/dist/state/subscription-manager.d.ts +0 -180
  282. package/dist/state/types.d.ts +0 -144
  283. package/dist/state.d.ts +0 -2
  284. package/dist/state.js +0 -21
  285. package/dist/subscription-manager-705bls97.js +0 -171
@@ -1,288 +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
- * Используется в:
135
- * - BehaviorApplicator для доступа к полям в behavior functions
136
- * - ValidationApplicator для доступа к форме в tree validators
137
- *
138
- * @returns Proxy-инстанс с типобезопасным доступом к полям или сама форма, если proxy не доступен
139
- *
140
- * @example
141
- * ```typescript
142
- * const form = new GroupNode({
143
- * email: { value: '', component: Input },
144
- * name: { value: '', component: Input },
145
- * });
146
- *
147
- * const proxy = form.getProxy();
148
- * console.log(proxy.email.value.value); // Прямой доступ к полю
149
- * ```
150
- */
151
- getProxy(): FormProxy<T>;
152
- /**
153
- * Получить все поля формы как итератор
154
- */
155
- getAllFields(): IterableIterator<FormNode<FormValue>>;
156
- protected onMarkAsTouched(): void;
157
- protected onMarkAsUntouched(): void;
158
- protected onMarkAsDirty(): void;
159
- protected onMarkAsPristine(): void;
160
- /**
161
- * Отправить форму
162
- *
163
- * @param onSubmit - Callback для отправки данных
164
- * @param options - Опции submit (skipValidation, skipTouch)
165
- * @returns Результат от onSubmit или `null` если валидация не пройдена
166
- *
167
- * @remarks
168
- * `null` перегружен: он означает и «валидация не пройдена», и легитимный `null`-результат
169
- * `onSubmit` (или void-обработчик). Если вызывающей стороне нужно различать эти случаи —
170
- * используйте {@link submitWithResult}, который возвращает явный флаг `success`.
171
- */
172
- submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
173
- /**
174
- * Отправить форму с расширенным результатом
175
- *
176
- * @param onSubmit - Callback для отправки данных
177
- * @param options - Опции submit
178
- * @returns Объект SubmitResult с данными, статусом и возможной ошибкой
179
- */
180
- submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
181
- /**
182
- * Получить вложенное поле по пути
183
- *
184
- * Поддерживаемые форматы путей:
185
- * - Simple: "email" - получить поле верхнего уровня
186
- * - Nested: "address.city" - получить вложенное поле
187
- * - Array index: "items[0]" - получить элемент массива по индексу
188
- * - Combined: "items[0].name" - получить поле элемента массива
189
- *
190
- * @param path - Путь к полю
191
- * @returns FormNode если найдено, undefined если путь не существует
192
- *
193
- * @example
194
- * ```typescript
195
- * const form = new GroupNode({
196
- * email: { value: '', component: Input },
197
- * address: {
198
- * city: { value: '', component: Input }
199
- * },
200
- * items: [{ name: { value: '', component: Input } }]
201
- * });
202
- *
203
- * form.getFieldByPath('email'); // FieldNode
204
- * form.getFieldByPath('address.city'); // FieldNode
205
- * form.getFieldByPath('items[0]'); // GroupNode
206
- * form.getFieldByPath('items[0].name'); // FieldNode
207
- * form.getFieldByPath('invalid.path'); // undefined
208
- * ```
209
- */
210
- getFieldByPath(path: string): FormNode<FormValue> | undefined;
211
- /**
212
- * Создать узел на основе конфигурации
213
- *
214
- * ✅ РЕФАКТОРИНГ: Полное делегирование NodeFactory
215
- *
216
- * NodeFactory теперь обрабатывает:
217
- * - Массивы [schema, ...items]
218
- * - FieldConfig
219
- * - GroupConfig
220
- * - ArrayConfig
221
- *
222
- * @param config Конфигурация узла
223
- * @returns Созданный узел формы
224
- * @private
225
- */
226
- private createNode;
227
- /**
228
- * Связывает два поля: при изменении source автоматически обновляется target
229
- */
230
- linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
231
- /**
232
- * Подписка на изменения вложенного поля по строковому пути
233
- * Поддерживает вложенные пути типа "address.city"
234
- *
235
- * @param fieldPath - Строковый путь к полю (например, "address.city")
236
- * @param callback - Функция, вызываемая при изменении поля
237
- * @returns Функция отписки для cleanup
238
- *
239
- * @example
240
- * ```typescript
241
- * // Подписка на изменение страны для загрузки городов
242
- * const dispose = form.watchField(
243
- * 'registrationAddress.country',
244
- * async (countryCode) => {
245
- * if (countryCode) {
246
- * const cities = await fetchCitiesByCountry(countryCode);
247
- * form.registrationAddress.city.updateComponentProps({
248
- * options: cities
249
- * });
250
- * }
251
- * }
252
- * );
253
- *
254
- * // Cleanup
255
- * useEffect(() => dispose, []);
256
- * ```
257
- */
258
- /** Подписка на top-level поле — value типизирован как `T[K]`. */
259
- watchField<K extends keyof T & string>(fieldPath: K, callback: (value: T[K]) => void | Promise<void>): () => void;
260
- /**
261
- * Подписка на вложенное поле по строковому пути ("address.city").
262
- * Путь нельзя выразить в типах узла → value честно `unknown`, потребитель сужает.
263
- */
264
- watchFieldByPath(path: string, callback: (value: unknown) => void | Promise<void>): () => void;
265
- /**
266
- * Hook: вызывается после disable()
267
- */
268
- protected onDisable(): void;
269
- /**
270
- * Hook: вызывается после enable()
271
- */
272
- protected onEnable(): void;
273
- /**
274
- * Прикрепить cleanup декларативной схемы поведения (вызывается createForm).
275
- * @internal
276
- */
277
- attachBehaviorCleanup(cleanup: () => void): void;
278
- /**
279
- * Связать листовую ноду-ребёнка с её сигналом модели (M1). Вызывается `createForm` для каждого
280
- * листового поля на его владеющей группе — используется bulk-сеттерами для derived-guard (F9).
281
- * @internal
282
- */
283
- registerFieldSignal(node: FormNode<any>, signal: Signal<unknown>): void;
284
- /**
285
- * Очистить все ресурсы узла
286
- */
287
- dispose(): void;
288
- }
@@ -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,153 +0,0 @@
1
- import { ReadonlySignal } from '@preact/signals-core';
2
- import { FieldStatus } from './types/index';
3
- /**
4
- * События для State Machine
5
- */
6
- export type StatusEvent = {
7
- type: 'START_VALIDATION';
8
- } | {
9
- type: 'VALIDATION_SUCCESS';
10
- } | {
11
- type: 'VALIDATION_FAILURE';
12
- } | {
13
- type: 'DISABLE';
14
- } | {
15
- type: 'ENABLE';
16
- hasErrors?: boolean;
17
- } | {
18
- type: 'SET_ERRORS';
19
- hasErrors: boolean;
20
- };
21
- /**
22
- * FormStatusMachine - управляет состоянием поля формы
23
- *
24
- * Предоставляет:
25
- * - Единый источник истины для статуса
26
- * - Computed signals для derived состояний (valid, invalid, pending, disabled)
27
- * - Валидацию переходов между состояниями
28
- *
29
- * @example
30
- * ```typescript
31
- * const statusMachine = new FormStatusMachine('valid');
32
- *
33
- * // Начало валидации
34
- * statusMachine.startValidation();
35
- * console.log(statusMachine.pending.value); // true
36
- *
37
- * // Завершение валидации с ошибками
38
- * statusMachine.completeValidation(true);
39
- * console.log(statusMachine.invalid.value); // true
40
- *
41
- * // Отключение поля
42
- * statusMachine.disable();
43
- * console.log(statusMachine.disabled.value); // true
44
- * ```
45
- */
46
- export declare class FormStatusMachine {
47
- /** Внутренний сигнал статуса */
48
- private readonly _status;
49
- /** Публичный read-only сигнал статуса */
50
- readonly status: ReadonlySignal<FieldStatus>;
51
- /** Поле валидно */
52
- readonly valid: ReadonlySignal<boolean>;
53
- /** Поле невалидно */
54
- readonly invalid: ReadonlySignal<boolean>;
55
- /** Идет валидация */
56
- readonly pending: ReadonlySignal<boolean>;
57
- /** Поле отключено */
58
- readonly disabled: ReadonlySignal<boolean>;
59
- /**
60
- * @param initial - Начальный статус (по умолчанию 'valid')
61
- */
62
- constructor(initial?: FieldStatus);
63
- /**
64
- * Начать валидацию
65
- *
66
- * Переводит статус в 'pending' если поле не отключено
67
- *
68
- * @example
69
- * ```typescript
70
- * statusMachine.startValidation();
71
- * // status: 'pending'
72
- * ```
73
- */
74
- startValidation(): void;
75
- /**
76
- * Завершить валидацию
77
- *
78
- * @param hasErrors - Есть ли ошибки валидации
79
- *
80
- * @example
81
- * ```typescript
82
- * // Валидация успешна
83
- * statusMachine.completeValidation(false);
84
- * // status: 'valid'
85
- *
86
- * // Есть ошибки
87
- * statusMachine.completeValidation(true);
88
- * // status: 'invalid'
89
- * ```
90
- */
91
- completeValidation(hasErrors: boolean): void;
92
- /**
93
- * Установить ошибки напрямую (без перехода через pending)
94
- *
95
- * Используется для синхронной валидации или установки ошибок извне
96
- *
97
- * @param hasErrors - Есть ли ошибки
98
- *
99
- * @example
100
- * ```typescript
101
- * statusMachine.setErrors(true);
102
- * // status: 'invalid'
103
- * ```
104
- */
105
- setErrors(hasErrors: boolean): void;
106
- /**
107
- * Отключить поле
108
- *
109
- * Переводит статус в 'disabled'
110
- *
111
- * @example
112
- * ```typescript
113
- * statusMachine.disable();
114
- * // status: 'disabled'
115
- * ```
116
- */
117
- disable(): void;
118
- /**
119
- * Включить поле
120
- *
121
- * @param hasErrors - Есть ли ошибки (определяет valid/invalid)
122
- *
123
- * @example
124
- * ```typescript
125
- * statusMachine.enable(false);
126
- * // status: 'valid'
127
- *
128
- * statusMachine.enable(true);
129
- * // status: 'invalid'
130
- * ```
131
- */
132
- enable(hasErrors?: boolean): void;
133
- /**
134
- * Обработать событие (альтернативный API)
135
- *
136
- * @param event - Событие для обработки
137
- *
138
- * @example
139
- * ```typescript
140
- * statusMachine.dispatch({ type: 'START_VALIDATION' });
141
- * statusMachine.dispatch({ type: 'VALIDATION_FAILURE' });
142
- * ```
143
- */
144
- dispatch(event: StatusEvent): void;
145
- /**
146
- * Получить текущий статус
147
- */
148
- getStatus(): FieldStatus;
149
- /**
150
- * Проверить, можно ли начать валидацию
151
- */
152
- canValidate(): boolean;
153
- }
@@ -1,116 +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
- * Конфигурация массива
63
- * @group Types
64
- * @category Configuration Types
65
- */
66
- export interface ArrayConfig<T extends object> {
67
- itemSchema: FormSchema<T>;
68
- initial?: Partial<T>[];
69
- }
70
- /**
71
- * **Data-shaped** конфиг формы: ключи повторяют структуру данных `T`, значения — {@link FieldConfig}
72
- * (или вложенный `FormSchema`). Форма конфига, из которой строится {@link GroupNode}.
73
- * - `T[] -> [FormSchema<T>]` (массив с одним элементом)
74
- * - `object -> FormSchema<T>` (группа)
75
- * - `primitive -> FieldConfig<T>` (поле)
76
- *
77
- * Использует NonNullable для корректной обработки опциональных полей.
78
- *
79
- * ⚠️ Не путать с {@link FormSchemaNode} — тот описывает **узел дерева** M1-схемы (лист/массив/
80
- * контейнер), передаваемой в `createForm({ model, schema })`. `FormSchema` — это data-shaped конфиг
81
- * (ключи = поля данных), а не узел дерева.
82
- *
83
- * @group Types
84
- * @category Configuration Types
85
- *
86
- * @example
87
- * ```typescript
88
- * interface Form {
89
- * name: string; // → FieldConfig<string>
90
- * address: { // → FormSchema<Address>
91
- * city: string;
92
- * street: string;
93
- * };
94
- * items?: Array<{ // → [FormSchema<Item>] (опциональный)
95
- * title: string;
96
- * price: number;
97
- * }>;
98
- * }
99
- *
100
- * const schema: FormSchema<Form> = {
101
- * name: { value: '', component: Input },
102
- * address: {
103
- * city: { value: '', component: Input },
104
- * street: { value: '', component: Input },
105
- * },
106
- * items: [{
107
- * title: { value: '', component: Input },
108
- * price: { value: 0, component: Input },
109
- * }],
110
- * };
111
- * ```
112
- */
113
- export type FormSchema<T> = {
114
- [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]>>;
115
- };
116
- export {};