@reformer/core 11.0.0-beta.7 → 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 (226) hide show
  1. package/README.md +90 -68
  2. package/dist/behaviors-value-HA_Q03hc.js +108 -0
  3. package/dist/behaviors.js +382 -217
  4. package/dist/create-model-BvcbNPgL.js +30 -0
  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 +42 -8
  9. package/dist/form/behaviors/context.d.ts +37 -4
  10. package/dist/form/behaviors/index.d.ts +6 -4
  11. package/dist/form/behaviors/internals.d.ts +9 -3
  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 +78 -0
  15. package/dist/form/behaviors/types.d.ts +25 -5
  16. package/dist/form/create-form.d.ts +57 -70
  17. package/dist/form/flow/flow.d.ts +87 -0
  18. package/dist/form/form-bundle.d.ts +153 -0
  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 +20 -20
  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 +153 -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 +19 -92
  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 +23 -27
  47. package/dist/form/validation/context.d.ts +46 -6
  48. package/dist/form/validation/index.d.ts +9 -8
  49. package/dist/form/validation/operators.d.ts +71 -17
  50. package/dist/form/validation/run.d.ts +93 -18
  51. package/dist/form/validation/strategy.d.ts +41 -13
  52. package/dist/form/validation/types.d.ts +74 -19
  53. package/dist/form/validators/date-utils.d.ts +13 -1
  54. package/dist/form/validators/email.d.ts +8 -7
  55. package/dist/form/validators/file-type.d.ts +8 -7
  56. package/dist/form/validators/file-utils.d.ts +2 -1
  57. package/dist/form/validators/future-date.d.ts +8 -7
  58. package/dist/form/validators/index.d.ts +1 -1
  59. package/dist/form/validators/integer.d.ts +8 -7
  60. package/dist/form/validators/is-date.d.ts +11 -9
  61. package/dist/form/validators/is-number.d.ts +11 -9
  62. package/dist/form/validators/max-age.d.ts +13 -9
  63. package/dist/form/validators/max-date.d.ts +8 -7
  64. package/dist/form/validators/max-file-size.d.ts +8 -7
  65. package/dist/form/validators/max-files.d.ts +8 -7
  66. package/dist/form/validators/max-length.d.ts +14 -7
  67. package/dist/form/validators/max-total-file-size.d.ts +8 -7
  68. package/dist/form/validators/max.d.ts +10 -8
  69. package/dist/form/validators/min-age.d.ts +12 -9
  70. package/dist/form/validators/min-date.d.ts +8 -7
  71. package/dist/form/validators/min-file-size.d.ts +8 -7
  72. package/dist/form/validators/min-files.d.ts +11 -9
  73. package/dist/form/validators/min-length.d.ts +14 -7
  74. package/dist/form/validators/min.d.ts +10 -8
  75. package/dist/form/validators/multiple-of.d.ts +10 -8
  76. package/dist/form/validators/non-negative.d.ts +8 -7
  77. package/dist/form/validators/non-zero.d.ts +8 -7
  78. package/dist/form/validators/past-date.d.ts +8 -7
  79. package/dist/form/validators/pattern.d.ts +9 -8
  80. package/dist/form/validators/phone.d.ts +8 -7
  81. package/dist/form/validators/required.d.ts +11 -9
  82. package/dist/form/validators/url.d.ts +20 -13
  83. package/dist/form/validators/validation-issue.d.ts +8 -0
  84. package/dist/i18n/descriptor.d.ts +1 -1
  85. package/dist/i18n/en.json.d.ts +5 -1
  86. package/dist/i18n/locale-store.d.ts +7 -0
  87. package/dist/i18n/message-format.d.ts +24 -4
  88. package/dist/i18n/ru.json.d.ts +5 -1
  89. package/dist/i18n/translator.d.ts +11 -0
  90. package/dist/i18n/validation-message.d.ts +42 -7
  91. package/dist/i18n.d.ts +7 -12
  92. package/dist/i18n.js +66 -283
  93. package/dist/index.d.ts +16 -7
  94. package/dist/index.js +958 -2798
  95. package/dist/loader-BWkigpVe.js +52 -0
  96. package/dist/locale/en.js +1 -1
  97. package/dist/locale/en.json +4 -1
  98. package/dist/locale/ru.js +4 -1
  99. package/dist/locale/ru.json +4 -1
  100. package/dist/locale-CG54AU2Q.js +219 -0
  101. package/dist/locale.js +1 -1
  102. package/dist/model/behaviors-value.d.ts +24 -6
  103. package/dist/model/create-model.d.ts +2 -2
  104. package/dist/model/derived-registry.d.ts +1 -1
  105. package/dist/model/index.d.ts +16 -5
  106. package/dist/model/model-nodes.d.ts +138 -8
  107. package/dist/model/model-signals-proxy.d.ts +11 -5
  108. package/dist/model/model-value-proxy.d.ts +84 -3
  109. package/dist/model/safe-effect.d.ts +33 -1
  110. package/dist/model/types.d.ts +70 -17
  111. package/dist/model.js +21 -17
  112. package/dist/node-kind-C-r6ZMdo.js +15 -0
  113. package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
  114. package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
  115. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  116. package/dist/platforms/react/hooks/types.d.ts +57 -38
  117. package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
  118. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  119. package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
  120. package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
  121. package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
  122. package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
  123. package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
  124. package/dist/platforms/react/i18n/context.d.ts +6 -1
  125. package/dist/platforms/react/i18n/hooks.d.ts +16 -10
  126. package/dist/platforms/react/i18n/provider.d.ts +9 -2
  127. package/dist/platforms/react/index.d.ts +7 -4
  128. package/dist/react.d.ts +22 -0
  129. package/dist/react.js +505 -0
  130. package/dist/runtime-token.d.ts +1 -2
  131. package/dist/safe-effect-BbtX3KuN.js +643 -0
  132. package/dist/schema-controller-CpiO2JoK.js +143 -0
  133. package/dist/signals.d.ts +15 -9
  134. package/dist/signals.js +11 -6
  135. package/dist/strategy-CoFSemuP.js +375 -0
  136. package/dist/type-guards-D-N_5Ddh.js +19 -0
  137. package/dist/validation-issue-B9mjI8jz.js +7 -0
  138. package/dist/validation-message-Ced9MGxs.js +194 -0
  139. package/dist/validation.js +9 -8
  140. package/dist/validators/email.js +5 -5
  141. package/dist/validators/future-date.js +8 -8
  142. package/dist/validators/integer.js +3 -3
  143. package/dist/validators/is-date.js +4 -4
  144. package/dist/validators/is-number.js +4 -4
  145. package/dist/validators/max-age.js +8 -9
  146. package/dist/validators/max-date.js +8 -8
  147. package/dist/validators/max-length.js +7 -7
  148. package/dist/validators/max.js +4 -4
  149. package/dist/validators/min-age.js +10 -8
  150. package/dist/validators/min-date.js +3 -3
  151. package/dist/validators/min-length.js +5 -5
  152. package/dist/validators/min.js +4 -4
  153. package/dist/validators/multiple-of.js +11 -7
  154. package/dist/validators/non-negative.js +3 -3
  155. package/dist/validators/non-zero.js +3 -3
  156. package/dist/validators/past-date.js +8 -8
  157. package/dist/validators/pattern.js +5 -5
  158. package/dist/validators/phone.js +11 -9
  159. package/dist/validators/required.js +2 -2
  160. package/dist/validators/url.js +48 -12
  161. package/dist/validators.js +17 -17
  162. package/docs/llms/01-api-reference.md +60 -34
  163. package/docs/llms/02-quick-start.md +111 -80
  164. package/docs/llms/03-api-signatures.md +368 -153
  165. package/docs/llms/04-common-patterns.md +58 -56
  166. package/docs/llms/05-common-mistakes.md +69 -44
  167. package/docs/llms/06-troubleshooting.md +14 -9
  168. package/docs/llms/07-complete-import.md +74 -43
  169. package/docs/llms/08-form-types.md +11 -4
  170. package/docs/llms/09-formschema.md +131 -83
  171. package/docs/llms/10-arrays.md +119 -69
  172. package/docs/llms/11-async-watchfield.md +2 -2
  173. package/docs/llms/13-multi-step.md +134 -63
  174. package/docs/llms/14-extended-mistakes.md +20 -17
  175. package/docs/llms/15-project-structure.md +221 -74
  176. package/docs/llms/16-ui-components.md +55 -42
  177. package/docs/llms/17-nonexistent-api.md +199 -32
  178. package/docs/llms/18-conditional-fields.md +66 -32
  179. package/docs/llms/19-reading-values.md +15 -3
  180. package/docs/llms/20-compute-vs-watch.md +16 -10
  181. package/docs/llms/21-array-operations.md +76 -26
  182. package/docs/llms/22-cycle-detection.md +1 -1
  183. package/docs/llms/23-copy-from.md +2 -2
  184. package/docs/llms/24-sync-fields.md +6 -5
  185. package/docs/llms/25-reset-when.md +14 -7
  186. package/docs/llms/26-transform-value.md +4 -4
  187. package/docs/llms/27-revalidate-when.md +16 -15
  188. package/docs/llms/28-submit-and-reset.md +276 -128
  189. package/docs/llms/29-async-preload.md +40 -30
  190. package/docs/llms/30-type-safety-recipes.md +105 -51
  191. package/docs/llms/31-async-validator-debounce.md +96 -53
  192. package/docs/llms/33-validation-strategy.md +178 -66
  193. package/docs/llms/34-i18n.md +69 -43
  194. package/docs/llms/35-unified-contract.md +307 -0
  195. package/docs/llms/36-issue-messages.md +284 -0
  196. package/docs/llms/37-compiled-schema.md +332 -0
  197. package/docs/llms/38-form-flow.md +397 -0
  198. package/llms-index.json +1 -1
  199. package/llms.txt +6968 -3278
  200. package/package.json +23 -4
  201. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  202. package/dist/create-model-B8U-COv-.js +0 -103
  203. package/dist/date-utils-dTT_x_Be.js +0 -31
  204. package/dist/form/create-core-form.d.ts +0 -70
  205. package/dist/form/factories/index.d.ts +0 -6
  206. package/dist/form/factories/node-factory.d.ts +0 -189
  207. package/dist/form/form-submitter.d.ts +0 -121
  208. package/dist/form/nodes/array-node.d.ts +0 -276
  209. package/dist/form/nodes/field-node.d.ts +0 -297
  210. package/dist/form/nodes/form-node.d.ts +0 -326
  211. package/dist/form/nodes/group-node.d.ts +0 -284
  212. package/dist/form/nodes/model-array-node.d.ts +0 -121
  213. package/dist/form/nodes/subscription-manager.d.ts +0 -180
  214. package/dist/form/status-machine.d.ts +0 -115
  215. package/dist/form/types/deep-schema.d.ts +0 -107
  216. package/dist/form/types/form-proxy.d.ts +0 -141
  217. package/dist/form/unique-id.d.ts +0 -45
  218. package/dist/form/validation/config.d.ts +0 -73
  219. package/dist/form/validation/error-handler.d.ts +0 -166
  220. package/dist/form/validators/validation-error.d.ts +0 -8
  221. package/dist/index-BrNkQj1y.js +0 -105
  222. package/dist/loader-Dv_Hl7JC.js +0 -216
  223. package/dist/model-signals-proxy-2vqS243J.js +0 -250
  224. package/dist/node-DGEFMTQu.js +0 -18
  225. package/dist/strategy-BXS_gBfz.js +0 -170
  226. package/dist/validation-error-B-U3RsV0.js +0 -11
@@ -1,8 +1,70 @@
1
1
  import { Signal } from '@preact/signals-core';
2
2
  import { PathAwareSignal } from './types';
3
3
  export declare const isIndexKey: (key: string) => boolean;
4
- export type ModelNode = LeafNode | GroupNode | ArrayNode;
5
- declare class LeafNode {
4
+ /**
5
+ * Глубокое сравнение значений модели: объекты и массивы — по содержимому, даты — по времени,
6
+ * `NaN` равен себе, остальное (включая файлы) — по ссылке.
7
+ *
8
+ * То же сравнение стоит за `isDirty` модели и за сверкой «модель изменилась, пока шла проверка»
9
+ * при отправке и переходе шага: значение, изменённое и возвращённое, изменением не считается.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * deepEqual({ date: new Date(0), tags: ['a'] }, { date: new Date(0), tags: ['a'] }); // true
14
+ * ```
15
+ */
16
+ export declare const deepEqual: (a: unknown, b: unknown) => boolean;
17
+ /** Фабрика нового элемента массива. */
18
+ type Blank = () => unknown;
19
+ /**
20
+ * Образец того же места модели внутри шаблона родительского массива. Ленивый: шаблон родителя
21
+ * ради него вызывается, только когда образец действительно понадобился.
22
+ */
23
+ type Shape = () => unknown;
24
+ /**
25
+ * Массив модели с шаблоном нового элемента.
26
+ *
27
+ * Шаблон — фабрика значения, которое получает `push()` / `insertAt(i)` без аргумента: кнопка
28
+ * «Добавить» секции массива, `model.items.push()` в поведении. Частичное значение
29
+ * (`push({ type: 'house' })`) дополняется из шаблона. Объявляется там же, где остальные
30
+ * начальные значения, — в модели; схеме и JSX знать его не нужно.
31
+ *
32
+ * Возвращает обычный массив: тип поля остаётся `U[]`. Шаблон переживает `set` / `patch` / `reset`
33
+ * модели. Вложенные массивы объявляются внутри шаблона элемента — тогда и элементы, пришедшие
34
+ * обычными данными (загрузка с сервера), получают их шаблоны.
35
+ *
36
+ * Шаблон привязан к самому массиву, поэтому до `createModel` его нельзя копировать:
37
+ * `structuredClone`, `JSON.parse(JSON.stringify(…))` и `[...array]` отдают массив без шаблона, и
38
+ * `push()` без значения бросит ошибку. Начальные значения с `arrayOf` держат в фабрике
39
+ * (`const initial = () => ({ items: arrayOf(blank) })`), а не в константе, которую клонируют.
40
+ *
41
+ * @typeParam U - Тип элемента массива.
42
+ * @param blank - Фабрика нового элемента. Вызывается на каждое добавление — значение не делится
43
+ * между элементами.
44
+ * @param items - Начальные элементы. По умолчанию массив пуст.
45
+ * @returns Массив начальных значений с шаблоном.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const blankPhone = () => ({ number: '' });
50
+ * const blankCoBorrower = () => ({ name: '', phones: arrayOf(blankPhone) });
51
+ *
52
+ * const model = createModel({ coBorrowers: arrayOf(blankCoBorrower) });
53
+ *
54
+ * model.coBorrowers.push(); // { name: '', phones: [] }
55
+ * model.coBorrowers.at(0).phones.push(); // { number: '' }
56
+ * model.coBorrowers.push({ name: 'Анна' }); // { name: 'Анна', phones: [] } — остальное из шаблона
57
+ * ```
58
+ *
59
+ * @group Model
60
+ */
61
+ export declare function arrayOf<U>(blank: () => U, items?: readonly U[]): U[];
62
+ export type ModelNode = ModelLeafNode | ModelGroupNode | ModelArrayNode;
63
+ /** Лист модели, чей это сигнал; `undefined` — сигнал не из модели. @internal */
64
+ export declare function leafOfSignal(handle: object): {
65
+ reference(): unknown;
66
+ } | undefined;
67
+ declare class ModelLeafNode {
6
68
  path: string;
7
69
  readonly kind: "leaf";
8
70
  readonly signal: PathAwareSignal<unknown>;
@@ -14,34 +76,93 @@ declare class LeafNode {
14
76
  /** Нереактивный снимок. */
15
77
  peek(): unknown;
16
78
  set(value: unknown): void;
79
+ /**
80
+ * Вернуть значение к снимку. Производное поле (цель `compute`, поле сервера с `markDerived`)
81
+ * сброс не пишет — как `set` и `patch`: значением владеет поведение, а снимок такого поля снят
82
+ * до первого расчёта, и `compute` пересчитал бы его только при смене источников.
83
+ */
17
84
  resetToInitial(): void;
18
85
  captureInitial(): void;
86
+ /** Значение точки отсчёта — копия: правка полученного снимок не трогает. */
87
+ reference(): unknown;
19
88
  dirty(): boolean;
89
+ /**
90
+ * Отличается ли значение от снимка. Производное поле отличием не считается: его пишет поведение,
91
+ * а не пользователь — иначе форма без единой правки была бы «изменённой».
92
+ */
93
+ differsFrom(snapshot: unknown): boolean;
20
94
  }
21
- export declare class GroupNode {
95
+ export declare class ModelGroupNode {
22
96
  path: string;
23
97
  readonly kind: "group";
24
98
  readonly children: Map<string, ModelNode>;
25
- constructor(initial: Record<string, unknown>, path: string);
99
+ /**
100
+ * @param shape Образец той же группы из шаблона родительского массива: из него вложенные
101
+ * массивы берут шаблоны, если значение пришло обычными данными (см. {@link ModelArrayNode}).
102
+ */
103
+ constructor(initial: Record<string, unknown>, path: string, shape?: Shape);
26
104
  rebase(path: string): void;
27
105
  /** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
28
106
  read(): Record<string, unknown>;
29
107
  peek(): Record<string, unknown>;
108
+ /**
109
+ * Полная установка: значение ожидается целиком. Ключи, которых в нём нет, остаются прежними —
110
+ * как у {@link patch}, — но в dev о них сообщает предупреждение: неполный объект в `set` — почти
111
+ * всегда ошибка вызывающего кода, а не намерение.
112
+ */
30
113
  set(value: unknown): void;
114
+ /**
115
+ * Частичное обновление: записываются только переданные ключи. Вложенная группа сливается так
116
+ * же, массив заменяется целиком.
117
+ */
118
+ patch(value: unknown): void;
119
+ /**
120
+ * Запись значения по ключам группы. Лишние ключи значения игнорируются, производные поля не
121
+ * затираются.
122
+ *
123
+ * @param missing - Куда складывать пути ключей, для которых значение не пришло. Передаётся при
124
+ * полной установке ({@link set}); при частичной ({@link patch}) — `undefined`.
125
+ * @param partial - Частичное обновление: ключ со значением `undefined` — «не трогать», как в
126
+ * частичном значении элемента массива (`DeepPartial` допускает `{ ключ: undefined }`).
127
+ */
128
+ private write;
31
129
  resetToInitial(): void;
32
130
  captureInitial(): void;
33
131
  dirty(): boolean;
132
+ /** Отличается ли группа от снимка её значения (строка массива сверяется со снимком массива). */
133
+ differsFrom(snapshot: unknown): boolean;
34
134
  }
35
- export declare class ArrayNode {
135
+ export declare class ModelArrayNode {
36
136
  path: string;
137
+ private readonly shape?;
37
138
  readonly kind: "array";
38
139
  readonly items: Signal<ModelNode[]>;
39
140
  private initial;
40
- constructor(initial: unknown[], path: string);
141
+ private blank;
142
+ private fallback;
143
+ private sample;
144
+ /** @param shape Образец того же массива из шаблона родителя (см. {@link ModelGroupNode}). */
145
+ constructor(initial: unknown[], path: string, shape?: Shape | undefined);
146
+ /** Шаблон по старшинству: свой (`arrayOf`) → из шаблона родителя → запасной извне. */
147
+ private template;
148
+ private readonly itemShape;
149
+ private build;
150
+ /**
151
+ * Значение нового элемента: из шаблона; переданный объект накладывается поверх шаблона —
152
+ * недостающие ключи берутся из шаблона, поэтому `push({ type: 'house' })` даёт полный элемент.
153
+ */
154
+ private fresh;
155
+ /** Запасной шаблон извне (сборка формы передаёт `initialValue`): шаблон модели главнее. */
156
+ provideBlank(blank: Blank): void;
41
157
  rebase(path: string): void;
42
158
  private reindex;
43
- push(value: unknown): void;
44
- insertAt(index: number, value: unknown): void;
159
+ /**
160
+ * Индекс за концом массива — ошибка, а не «в конец»: `splice` молча сдвигал бы элемент на
161
+ * другой индекс, и вызывающий код получал бы путь, которого не просил.
162
+ */
163
+ private beyondEnd;
164
+ push(value?: unknown): void;
165
+ insertAt(index: number, value?: unknown): void;
45
166
  removeAt(index: number): void;
46
167
  move(from: number, to: number): void;
47
168
  swap(a: number, b: number): void;
@@ -55,6 +176,15 @@ export declare class ArrayNode {
55
176
  set(value: unknown): void;
56
177
  resetToInitial(): void;
57
178
  captureInitial(): void;
179
+ /** Элементы точки отсчёта — копия: правка полученного снимок не трогает. */
180
+ reference(): unknown[];
58
181
  dirty(): boolean;
182
+ /**
183
+ * Отличается ли массив от снимка: другая длина либо отличается элемент. Сверка идёт по узлам, а
184
+ * не по значению целиком — производные поля строк (цели `compute` поведения строки) не в счёт.
185
+ */
186
+ differsFrom(snapshot: unknown): boolean;
59
187
  }
188
+ /** Начальное значение элемента как шаблон: фабрика — как есть, значение — копией. */
189
+ export declare const blankFrom: (initialValue: unknown) => Blank;
60
190
  export {};
@@ -1,6 +1,6 @@
1
1
  import { Signal } from '@preact/signals-core';
2
2
  import { PathAwareSignal } from './types';
3
- import { ModelNode, GroupNode } from './model-nodes';
3
+ import { ModelNode, ModelGroupNode, ModelArrayNode } from './model-nodes';
4
4
  /**
5
5
  * Узел дерева `$`: лист → сам {@link PathAwareSignal}, группа/массив → контейнерный узел —
6
6
  * {@link ReadonlySignal} агрегированного значения ПЛЮС доступ к детям по имени/индексу.
@@ -20,6 +20,13 @@ import { ModelNode, GroupNode } from './model-nodes';
20
20
  * полей, а потребители, перечисляющие модель, не видят служебных ключей.
21
21
  */
22
22
  export declare function signalsProxy(node: ModelNode): any;
23
+ /**
24
+ * Узел модели за контейнерной ручкой дерева `$` (группа или массив). Для листа и для всего, что
25
+ * ручкой не является, — `undefined`.
26
+ *
27
+ * @internal
28
+ */
29
+ export declare function containerNodeOf(handle: unknown): ModelGroupNode | ModelArrayNode | undefined;
23
30
  /**
24
31
  * Узел дерева `model.$` — контейнер (группа/массив), а не лист?
25
32
  *
@@ -37,12 +44,11 @@ export declare function isModelContainerSignal(value: unknown): boolean;
37
44
  /** Узел-массив дерева `model.$` — ручка значения массива целиком. */
38
45
  export declare function isModelArraySignal(value: unknown): boolean;
39
46
  /**
40
- * «Ручка значения», к которой можно привязать поле: лист (`instanceof Signal`) либо узел-массив
47
+ * «Ручка значения», к которой можно привязать поле: лист (сигнал, не контейнер) либо узел-массив
41
48
  * дерева `model.$` (значение — массив целиком, `.value` читается и пишется).
42
49
  *
43
50
  * Группа сюда не входит: она читается целиком, но не пишется и полем не бывает. Этой проверкой
44
- * `createForm` и рендереры отличают узел-поле схемы (`{ value, component }`) от контейнера —
45
- * вместо голого `value instanceof Signal`, который узел-массив не проходит.
51
+ * `createForm` и рендереры отличают узел-поле схемы (`{ model, component }`) от контейнера.
46
52
  *
47
53
  * @group Model
48
54
  * @example
@@ -54,4 +60,4 @@ export declare function isModelArraySignal(value: unknown): boolean;
54
60
  * ```
55
61
  */
56
62
  export declare function isValueSignal(value: unknown): value is Signal<unknown>;
57
- export declare function resolveSignalAt(root: GroupNode, path: string): PathAwareSignal<unknown> | undefined;
63
+ export declare function resolveSignalAt(root: ModelGroupNode, path: string): PathAwareSignal<unknown> | undefined;
@@ -1,3 +1,84 @@
1
- import { GroupNode } from './model-nodes';
2
- export declare const rootByFacade: WeakMap<object, GroupNode>;
3
- export declare function makeFormModel(group: GroupNode): any;
1
+ import { ModelGroupNode } from './model-nodes';
2
+ import { FormModel, ModelArray } from './types';
3
+ export declare const rootByFacade: WeakMap<object, ModelGroupNode>;
4
+ export declare function makeFormModel(group: ModelGroupNode): any;
5
+ /**
6
+ * Value-фасад для ручки {@link modelOf}: массив → {@link ModelArray}, объект → под-модель
7
+ * {@link FormModel}.
8
+ *
9
+ * @group Model
10
+ */
11
+ export type ModelOf<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArray<U> : FormModel<NonNullable<V>>;
12
+ /**
13
+ * Value-фасад по ручке дерева `model.$`: группа → под-модель {@link FormModel}, массив →
14
+ * {@link ModelArray}. Обратная операция к `model.$`: `modelOf(model.$.address) === model.address`.
15
+ *
16
+ * Ручка находит фасад по ИДЕНТИЧНОСТИ, а не по пути. Путь для этого не годится: он абсолютный
17
+ * (`items.0.phones`) и меняется при перестановке строк, а области поведения и формы строк
18
+ * вложенные — поиск по пути от под-модели строки ничего не находит.
19
+ *
20
+ * Лист ручкой контейнера не является — функция бросает. В частности, группа или массив,
21
+ * созданные из начального `null`, в модели — лист: вид узла фиксируется при создании.
22
+ *
23
+ * @typeParam V - Значение, которое хранит ручка (выводится из `peek()`).
24
+ * @param handle - Узел-группа или узел-массив дерева `model.$`.
25
+ * @returns Под-модель группы либо фасад массива.
26
+ * @throws TypeError если `handle` — лист или не ручка модели.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const model = createModel({ address: { city: '' }, phones: [{ number: '' }] });
31
+ *
32
+ * modelOf(model.$.address).city = 'Казань'; // под-модель: чтение, запись, get/set/patch
33
+ * modelOf(model.$.phones).push({ number: '+7' }); // фасад массива: мутации и обход
34
+ * modelOf(model.$.phones[0]).number; // строка массива — тоже группа
35
+ * ```
36
+ *
37
+ * @group Model
38
+ */
39
+ export declare function modelOf<V extends object | null | undefined>(handle: {
40
+ peek(): V;
41
+ }): ModelOf<V>;
42
+ /**
43
+ * Ручка массива по привязке: сама ручка `model.$.<массив>` либо value-фасад `model.<массив>`.
44
+ * Для всего остального — `undefined`.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function arrayHandleOf(binding: unknown): object | undefined;
49
+ /**
50
+ * Ручка группы по привязке: `model.$.<группа>` либо под-модель `model.<группа>`. Для всего
51
+ * остального — `undefined`.
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function groupHandleOf(binding: unknown): object | undefined;
56
+ /**
57
+ * Запасной шаблон нового элемента массива — от того, кто строит модель извне: сборка формы
58
+ * передаёт сюда `initialValue` узла-массива. Действует, только если модель своего шаблона не
59
+ * объявила (`arrayOf`): так живут формы, чья модель строится из данных без кода. Что это узел
60
+ * схемы, модель не знает.
61
+ *
62
+ * @internal
63
+ */
64
+ export declare function provideArrayBlank(handle: unknown, initialValue: unknown): void;
65
+ /**
66
+ * Value-фасад модели (под-модель или фасад массива), а не обычный объект?
67
+ *
68
+ * Обходчикам чужих деревьев, в которых могут лежать фасады модели, в фасад спускаться нельзя:
69
+ * чтение его ключей — это реактивное чтение значений модели.
70
+ *
71
+ * @internal
72
+ */
73
+ /**
74
+ * Точка отсчёта модели по ручке, к которой привязана нода формы: сигналу листа или фасаду
75
+ * массива. К ней сброс формы (`form.reset()`) возвращает ноду — к тому же, к чему `model.reset()`
76
+ * возвращает модель: последний `captureInitial` либо значение создания модели.
77
+ *
78
+ * `undefined` — ручка не из модели (нода над собственным сигналом): сбрасывать её не к чему,
79
+ * кроме значения, с которым она создана. @internal
80
+ */
81
+ export declare function referenceOf(handle: unknown): {
82
+ readonly value: unknown;
83
+ } | undefined;
84
+ export declare function isModelFacade(value: unknown): boolean;
@@ -68,6 +68,38 @@ export declare function safeCallback<TArgs extends unknown[]>(callback: (...args
68
68
  * ```
69
69
  */
70
70
  export declare function runOutsideEffect(fn: () => void | Promise<void>): () => void;
71
+ /**
72
+ * Дождаться, пока отработают отложенные записи текущего тика — вместе с теми, что они породили
73
+ * (цепочка расчётов: каждая запись будит следующий расчёт, а тот снова пишет отложенно).
74
+ *
75
+ * Ждёт только микротаски: запись после таймера (debounce) или после `await` в async-callback сюда не
76
+ * входит — это уже не «тот же тик».
77
+ *
78
+ * Счётчик общий для рантайма, а не для одной модели: отложенные записи ставит {@link runOutsideEffect},
79
+ * а он моделей не различает. Нужен проверкам «по действию» — отправке и переходу шага: правила должны
80
+ * увидеть модель после записей поведения того же тика (колбэк `onChange`, сброс `resetOnDisable`).
81
+ *
82
+ * @returns Промис ожидания; `undefined` — отложенных записей сейчас нет, ждать нечего.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * model.loanType = 'mortgage'; // поведение допишет зависимые поля на микротаске
87
+ * await deferredWritesSettled();
88
+ * const values = model.get(); // уже с записями поведения
89
+ * ```
90
+ */
91
+ export declare function deferredWritesSettled(): Promise<void> | undefined;
92
+ /**
93
+ * Отложенные записи одной операции: задание выполняется на микротаске ({@link runOutsideEffect}),
94
+ * а `cancelAll` отменяет все ещё не выполненные — при снятии операции (запись после cleanup не
95
+ * случится) или когда новое задание делает ожидающие устаревшими (побеждает последнее).
96
+ *
97
+ * @internal
98
+ */
99
+ export declare function deferredWrites(): {
100
+ schedule(job: () => void): void;
101
+ cancelAll(): void;
102
+ };
71
103
  /**
72
104
  * Создает версию callback с поддержкой debounce, безопасную для effect
73
105
  *
@@ -76,7 +108,7 @@ export declare function runOutsideEffect(fn: () => void | Promise<void>): () =>
76
108
  * async-rejection) маршрутизируются dev-логгером (logDeferError).
77
109
  *
78
110
  * @param callback - Функция для выполнения
79
- * @param withDebounce - Функция debounce обёртки из BehaviorRegistry
111
+ * @param withDebounce - Обёртка, откладывающая вызов (debounce)
80
112
  * @returns Обёрнутая функция
81
113
  *
82
114
  * @example
@@ -14,6 +14,33 @@ export type PathAwareSignal<T> = Signal<T> & {
14
14
  * @internal
15
15
  */
16
16
  type Opaque = Date | File | Blob;
17
+ /**
18
+ * Значение одного ключа в {@link DeepPartial}: вложенная группа — частично, массив и спец-объект
19
+ * (Date/File/Blob) — целиком.
20
+ * @internal
21
+ */
22
+ type DeepPartialValue<V> = NonNullable<V> extends ReadonlyArray<unknown> | Opaque ? V : NonNullable<V> extends object ? DeepPartial<NonNullable<V>> | Extract<V, null | undefined> : V;
23
+ /**
24
+ * Частичное значение модели для {@link ModelApi.patch}: любой ключ можно опустить на любой
25
+ * глубине вложенных групп. Массив передаётся целиком — он заменяется, а не сливается.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * type Profile = { name: string; address: { city: string; street: string }; tags: string[] };
30
+ * const update: DeepPartial<Profile> = { address: { city: 'Казань' } };
31
+ * ```
32
+ *
33
+ * @group Model
34
+ */
35
+ export type DeepPartial<T> = {
36
+ [K in keyof T]?: DeepPartialValue<T[K]>;
37
+ };
38
+ /**
39
+ * `null` входит в тип `V`. Без `strictNullChecks` у потребителя `null` подходит любому типу —
40
+ * тогда признак ложен, и тип контейнера остаётся фасадом, как без `null`.
41
+ * @internal
42
+ */
43
+ type IncludesNull<V> = null extends string ? false : null extends V ? true : false;
17
44
  /**
18
45
  * Фасад элемента массива при обходе (`at`/`map`/`forEach`): массив → {@link ModelArray},
19
46
  * Opaque (Date/File/Blob) и примитив → значение, объект → под-модель {@link FormModel}.
@@ -21,7 +48,7 @@ type Opaque = Date | File | Blob;
21
48
  * чтобы `files: File[]`/`dates: Date[]`/вложенные массивы не мис-типизировались как `FormModel`.
22
49
  * @internal
23
50
  */
24
- type ModelArrayItem<U> = NonNullable<U> extends ReadonlyArray<infer E> ? ModelArray<E> : NonNullable<U> extends Opaque ? U : NonNullable<U> extends object ? FormModel<NonNullable<U>> : U;
51
+ type ModelArrayItem<U> = IncludesNull<U> extends true ? U : NonNullable<U> extends ReadonlyArray<infer E> ? ModelArray<E> : NonNullable<U> extends Opaque ? U : NonNullable<U> extends object ? FormModel<NonNullable<U>> : U;
25
52
  /**
26
53
  * Значение поля в value-доступе модели:
27
54
  * - массив → {@link ModelArray}
@@ -29,10 +56,13 @@ type ModelArrayItem<U> = NonNullable<U> extends ReadonlyArray<infer E> ? ModelAr
29
56
  * - объект → под-модель {@link FormModel} (value-доступ + `.$`-сигналы + API get/set/patch/…);
30
57
  * промоутится рантаймом (`makeFormModel`); сигналы идентичны `model.$.<path>`
31
58
  * - примитив → значение
59
+ * - тип с `null` (`File[] | null`, `Address | null`) → значение как есть, без фасада: поле,
60
+ * которое бывает `null`, начинают с `null`, а такое поле в рантайме — лист (значение целиком).
61
+ * Контейнер, созданный из `[]` или `{}`, стал бы фасадом — nullable-контейнер начинают с `null`.
32
62
  *
33
63
  * @group Model
34
64
  */
35
- export type ModelValue<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArray<U> : NonNullable<V> extends Opaque ? V : NonNullable<V> extends object ? FormModel<NonNullable<V>> : V;
65
+ export type ModelValue<V> = IncludesNull<V> extends true ? V : NonNullable<V> extends ReadonlyArray<infer U> ? ModelArray<U> : NonNullable<V> extends Opaque ? V : NonNullable<V> extends object ? FormModel<NonNullable<V>> : V;
36
66
  /**
37
67
  * Карта value-полей объекта (value-половина {@link FormModel}): поля доступны как обычные свойства
38
68
  * (чтение реактивно внутри `effect`/`computed`, запись — присваиванием). Вложенные объекты-поля
@@ -57,10 +87,13 @@ export interface ModelArray<U> {
57
87
  readonly __path: string;
58
88
  /** Реактивная длина. */
59
89
  readonly length: number;
60
- /** Добавить элемент в конец (значение элемента целиком). */
61
- push(item: U): void;
62
- /** Вставить элемент по индексу. */
63
- insertAt(index: number, item: U): void;
90
+ /**
91
+ * Добавить элемент в конец (значение элемента целиком). Без значения — элемент по шаблону
92
+ * массива (`arrayOf(blank)` в модели); шаблона нет — ошибка.
93
+ */
94
+ push(item?: U): void;
95
+ /** Вставить элемент по индексу. Без значения — элемент по шаблону, как у {@link push}. */
96
+ insertAt(index: number, item?: U): void;
64
97
  /** Удалить элемент по индексу. */
65
98
  removeAt(index: number): void;
66
99
  /** Переместить элемент. */
@@ -77,6 +110,8 @@ export interface ModelArray<U> {
77
110
  forEach(fn: (item: ModelArrayItem<U>, index: number) => void): void;
78
111
  /** Снимок массива значений (без подписки). */
79
112
  toArray(): U[];
113
+ /** Обход `for…of` / spread — те же элементы, что у {@link forEach}. */
114
+ [Symbol.iterator](): IterableIterator<ModelArrayItem<U>>;
80
115
  /** Индексный value-доступ. */
81
116
  [index: number]: ModelValue<U>;
82
117
  }
@@ -112,13 +147,13 @@ export type ModelGroupSignals<T> = ModelSignals<T> & ContainerSignal<T, keyof T>
112
147
  * push/removeAt/move).
113
148
  *
114
149
  * В отличие от группы, узел ЗАПИСЫВАЕМ: `model.$.tags.value = ['a']` заменяет массив целиком. Это
115
- * делает его {@link PathAwareSignal} — к нему привязывают поле (`{ value: model.$.tags, component }`),
150
+ * делает его {@link PathAwareSignal} — к нему привязывают поле (`{ model: model.$.tags, component }`),
116
151
  * правило (`validate(model.$.tags, …)`) и поведение, как к листу. Так массив служит ОДНИМ значением
117
152
  * поля: мультивыбор, теги, список файлов.
118
153
  *
119
- * `V` — объявленный тип поля, включая `null`/`undefined`: поле `string[] | null` с начальным `null`
120
- * в рантайме — лист с тем же `.value`/`peek`/`subscribe`, поэтому общая часть честна для обоих
121
- * случаев. `length` и индексы есть только у настоящего узла-массива (начальное значение — массив).
154
+ * `V` — объявленный тип поля, включая `undefined`. Поле с `null` в типе (`string[] | null`) — не
155
+ * узел-массив, а лист {@link PathAwareSignal}: его начинают с `null`, и `length` с индексами у него
156
+ * в рантайме нет (см. {@link ModelValue}).
122
157
  *
123
158
  * @group Model
124
159
  */
@@ -132,10 +167,11 @@ export type ModelArraySignals<U, V> = Signal<V> & {
132
167
  * Узел дерева сигналов `model.$`: массив → {@link ModelArraySignals}, Opaque (Date/File/Blob)
133
168
  * и примитив → {@link PathAwareSignal}, объект → {@link ModelGroupSignals}. Для примитивных
134
169
  * и Opaque массивов элемент — сам сигнал листа (а не под-дерево), поэтому `model.$.tags[0]` — это
135
- * `PathAwareSignal<string>`, а не `ModelGroupSignals<never>`.
170
+ * `PathAwareSignal<string>`, а не `ModelGroupSignals<never>`. Тип с `null` — лист
171
+ * {@link PathAwareSignal}: такое поле начинают с `null`, и под-сигналов у него нет.
136
172
  * @internal
137
173
  */
138
- type ModelSignalNode<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArraySignals<U, V> : NonNullable<V> extends Opaque ? PathAwareSignal<V> : NonNullable<V> extends object ? ModelGroupSignals<NonNullable<V>> : PathAwareSignal<V>;
174
+ type ModelSignalNode<V> = IncludesNull<V> extends true ? PathAwareSignal<V> : NonNullable<V> extends ReadonlyArray<infer U> ? ModelArraySignals<U, V> : NonNullable<V> extends Opaque ? PathAwareSignal<V> : NonNullable<V> extends object ? ModelGroupSignals<NonNullable<V>> : PathAwareSignal<V>;
139
175
  /**
140
176
  * API уровня модели (доступно на корне, под-моделях вложенных объектов-групп и элементов массива).
141
177
  *
@@ -156,18 +192,35 @@ export interface ModelApi<T> {
156
192
  * Полная установка значений: принимает объект целиком (все ключи `T`) и записывает их в модель.
157
193
  * Производными полями (цели `compute`) владеет compute — их значения из payload игнорируются.
158
194
  * Не меняет initial-снимок. Для частичного обновления (только переданные ключи) — {@link ModelApi.patch}.
195
+ *
196
+ * Если в объекте не хватает ключей (значение пришло без типа — например, с сервера), эти поля
197
+ * остаются прежними, а в dev выводится предупреждение со списком.
159
198
  */
160
199
  set(value: T): void;
161
200
  /**
162
201
  * Частичное слияние значений (load/patch с сервера): обновляет только переданные ключи,
163
- * отсутствующие ключи НЕ трогаются. Не меняет initial-снимок.
202
+ * отсутствующие ключи НЕ трогаются. Вложенные группы сливаются так же, массив заменяется
203
+ * целиком. Не меняет initial-снимок.
204
+ */
205
+ patch(value: DeepPartial<T>): void;
206
+ /**
207
+ * Отличаются ли текущие значения от initial-снимка (value-diff). Производные поля — цели
208
+ * `compute` / `computeFrom` и поля с `markDerived` — не в счёт: их пишет поведение, а не
209
+ * пользователь, и форма без единой правки изменённой не считается.
164
210
  */
165
- patch(value: Partial<T>): void;
166
- /** Отличаются ли текущие значения от initial-снимка (value-diff). */
167
211
  isDirty(): boolean;
168
- /** Сбросить значения к initial-снимку. */
212
+ /**
213
+ * Сбросить значения к initial-снимку. Производные поля сброс не пишет, как `set` и `patch`:
214
+ * цель `compute` пересчитает поведение, когда вернутся её источники.
215
+ *
216
+ * Ноды формы не трогает: замечания погаснут при следующей проверке. Сброс для пользователя —
217
+ * `form.reset()`: та же точка отсчёта и чистые ноды.
218
+ */
169
219
  reset(): void;
170
- /** Зафиксировать текущие значения как новый initial-снимок («точка отсчёта»). */
220
+ /**
221
+ * Зафиксировать текущие значения как новый initial-снимок («точка отсчёта»). К нему возвращают
222
+ * и `model.reset()`, и `form.reset()`.
223
+ */
171
224
  captureInitial(): void;
172
225
  /**
173
226
  * Резолв строкового пути в сигнал (для error-routing/мостов): лист либо массив целиком
package/dist/model.js CHANGED
@@ -1,23 +1,27 @@
1
- import { c as s, e as r, a as i } from "./create-model-B8U-COv-.js";
2
- import { b as o, i as c, a as n, m as t, u as d } from "./model-signals-proxy-2vqS243J.js";
3
- import { c as m, a as u, r as b, b as h, d as p, e as g, f as k, s as v, t as D, w as F } from "./behaviors-value-C7dSEhCc.js";
1
+ import { c as s, e as r, a as l } from "./create-model-BvcbNPgL.js";
2
+ import { a as d, g as o, f as t, d as c, i as f, b as n, c as m, m as u, r as p, s as b, e as g, u as h } from "./safe-effect-BbtX3KuN.js";
3
+ import { c as k, a as v, r as D, b as F, s as x, t as y, w as C } from "./behaviors-value-HA_Q03hc.js";
4
4
  export {
5
- m as computeFrom,
6
- u as copyFrom,
5
+ d as arrayOf,
6
+ k as computeFrom,
7
+ v as copyFrom,
7
8
  s as createModel,
9
+ o as deepEqual,
10
+ t as deferredWritesSettled,
8
11
  r as eachLeafSignal,
9
- i as eachValueSignal,
10
- o as isDerived,
11
- c as isModelContainerSignal,
12
+ l as eachValueSignal,
13
+ c as isDerived,
14
+ f as isModelContainerSignal,
12
15
  n as isValueSignal,
13
- t as markDerived,
14
- b as resetWhen,
15
- h as revalidateWhen,
16
+ m as markDerived,
17
+ u as modelOf,
18
+ D as resetWhen,
19
+ F as revalidateWhen,
16
20
  p as runOutsideEffect,
17
- g as safeCallback,
18
- k as safeDebouncedCallback,
19
- v as syncFields,
20
- D as transformValue,
21
- d as unmarkDerived,
22
- F as watchField
21
+ b as safeCallback,
22
+ g as safeDebouncedCallback,
23
+ x as syncFields,
24
+ y as transformValue,
25
+ h as unmarkDerived,
26
+ C as watchField
23
27
  };
@@ -0,0 +1,15 @@
1
+ const n = Symbol.for("@reformer/core/form-node-kind"), e = Symbol.for("@reformer/core/form-node-raw");
2
+ function t(r) {
3
+ if (typeof r != "object" || r === null) return;
4
+ const o = r[n];
5
+ return o === "control" || o === "group" || o === "array" ? o : void 0;
6
+ }
7
+ function f(r) {
8
+ return r[e] ?? r;
9
+ }
10
+ export {
11
+ n as N,
12
+ e as R,
13
+ t as k,
14
+ f as r
15
+ };
@@ -41,7 +41,7 @@ export interface FieldAdapter {
41
41
  strip?: string[];
42
42
  /**
43
43
  * Передавать ли контролу ноду формы пропом `control` (только рендерер). По умолчанию `false`:
44
- * errors/touched обслуживает обёртка поля. Ставь `true`, если контрол сам вызывает
44
+ * issues/touched обслуживает обёртка поля. Ставь `true`, если контрол сам вызывает
45
45
  * `useFormControl(control)`.
46
46
  */
47
47
  passControl?: boolean;
@@ -43,8 +43,11 @@ export declare function makeElementFieldHandle(el: RefObject<HTMLElement | null>
43
43
  export declare function useFieldHandleRef<H = FieldHandle>(outerRef: Ref<H> | undefined): RefObject<unknown>;
44
44
  /**
45
45
  * Что публиковать потребителю по ref контрола: handle композита как есть, иначе (DOM-узел или
46
- * ещё пусто) — базовый {@link FieldHandle}, читающий узел лениво из `inner`.
46
+ * ещё пусто) — базовый {@link FieldHandle}, читающий узел лениво из `inner`. С `cache` базовый
47
+ * handle создаётся один раз и дальше переиспользуется.
47
48
  *
48
49
  * @internal Экспортируется для юнит-тестов.
49
50
  */
50
- export declare function resolveFieldHandle(inner: RefObject<unknown>): unknown;
51
+ export declare function resolveFieldHandle(inner: RefObject<unknown>, cache?: {
52
+ current: FieldHandle | null;
53
+ }): unknown;
@@ -0,0 +1,27 @@
1
+ import { FormBundle } from '../../form/form-bundle';
2
+ /**
3
+ * Контекст сборки. Значение — бандл `createForm`; вне рендерера — `null`.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * <FormBundleContext.Provider value={bundle}>
8
+ * <MyWizard />
9
+ * </FormBundleContext.Provider>
10
+ * ```
11
+ */
12
+ export declare const FormBundleContext: import('react').Context<FormBundle<any> | null>;
13
+ /**
14
+ * Бандл сборки, внутри которой отрисован компонент; `null` — компонент стоит вне рендерера
15
+ * (например, визард в разметке React-руками получает форму и поток шагов пропсами).
16
+ *
17
+ * @typeParam T - Форма данных модели.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * function StepFooter() {
22
+ * const bundle = useFormBundleContext<CreditForm>();
23
+ * return <button onClick={() => void bundle?.validate()}>Проверить</button>;
24
+ * }
25
+ * ```
26
+ */
27
+ export declare function useFormBundleContext<T = unknown>(): FormBundle<T> | null;