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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +91 -73
  2. package/dist/behaviors-value-HA_Q03hc.js +108 -0
  3. package/dist/behaviors.js +370 -242
  4. package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
  5. package/dist/date-utils-90-qZbse.js +43 -0
  6. package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
  7. package/dist/form/aggregate-signals.d.ts +21 -63
  8. package/dist/form/behaviors/collections.d.ts +28 -7
  9. package/dist/form/behaviors/context.d.ts +19 -2
  10. package/dist/form/behaviors/index.d.ts +3 -3
  11. package/dist/form/behaviors/internals.d.ts +9 -2
  12. package/dist/form/behaviors/node.d.ts +25 -2
  13. package/dist/form/behaviors/operators.d.ts +17 -9
  14. package/dist/form/behaviors/schema-operators.d.ts +6 -4
  15. package/dist/form/behaviors/types.d.ts +12 -4
  16. package/dist/form/create-form.d.ts +47 -35
  17. package/dist/form/flow/flow.d.ts +87 -0
  18. package/dist/form/form-bundle.d.ts +45 -28
  19. package/dist/form/form-config-base.d.ts +46 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +16 -21
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +30 -14
  37. package/dist/form/signal-node-registry.d.ts +6 -7
  38. package/dist/form/submission.d.ts +93 -0
  39. package/dist/form/type-guards.d.ts +14 -73
  40. package/dist/form/types/contracts.d.ts +19 -92
  41. package/dist/form/types/field-config.d.ts +27 -0
  42. package/dist/form/types/index.d.ts +5 -57
  43. package/dist/form/types/nodes.d.ts +159 -0
  44. package/dist/form/types/schema-node.d.ts +139 -63
  45. package/dist/form/types/validation-schema.d.ts +23 -27
  46. package/dist/form/validation/context.d.ts +42 -7
  47. package/dist/form/validation/index.d.ts +9 -8
  48. package/dist/form/validation/operators.d.ts +22 -30
  49. package/dist/form/validation/run.d.ts +93 -18
  50. package/dist/form/validation/strategy.d.ts +41 -13
  51. package/dist/form/validation/types.d.ts +74 -20
  52. package/dist/form/validators/date-utils.d.ts +13 -1
  53. package/dist/form/validators/email.d.ts +8 -7
  54. package/dist/form/validators/file-type.d.ts +8 -7
  55. package/dist/form/validators/file-utils.d.ts +2 -1
  56. package/dist/form/validators/future-date.d.ts +8 -7
  57. package/dist/form/validators/index.d.ts +1 -1
  58. package/dist/form/validators/integer.d.ts +8 -7
  59. package/dist/form/validators/is-date.d.ts +11 -9
  60. package/dist/form/validators/is-number.d.ts +11 -9
  61. package/dist/form/validators/max-age.d.ts +13 -9
  62. package/dist/form/validators/max-date.d.ts +8 -7
  63. package/dist/form/validators/max-file-size.d.ts +8 -7
  64. package/dist/form/validators/max-files.d.ts +8 -7
  65. package/dist/form/validators/max-length.d.ts +14 -7
  66. package/dist/form/validators/max-total-file-size.d.ts +8 -7
  67. package/dist/form/validators/max.d.ts +10 -8
  68. package/dist/form/validators/min-age.d.ts +12 -9
  69. package/dist/form/validators/min-date.d.ts +8 -7
  70. package/dist/form/validators/min-file-size.d.ts +8 -7
  71. package/dist/form/validators/min-files.d.ts +11 -9
  72. package/dist/form/validators/min-length.d.ts +14 -7
  73. package/dist/form/validators/min.d.ts +10 -8
  74. package/dist/form/validators/multiple-of.d.ts +10 -8
  75. package/dist/form/validators/non-negative.d.ts +8 -7
  76. package/dist/form/validators/non-zero.d.ts +8 -7
  77. package/dist/form/validators/past-date.d.ts +8 -7
  78. package/dist/form/validators/pattern.d.ts +9 -8
  79. package/dist/form/validators/phone.d.ts +8 -7
  80. package/dist/form/validators/required.d.ts +11 -9
  81. package/dist/form/validators/url.d.ts +20 -13
  82. package/dist/form/validators/validation-issue.d.ts +8 -0
  83. package/dist/i18n/descriptor.d.ts +1 -1
  84. package/dist/i18n/en.json.d.ts +5 -1
  85. package/dist/i18n/locale-store.d.ts +7 -0
  86. package/dist/i18n/message-format.d.ts +24 -4
  87. package/dist/i18n/ru.json.d.ts +5 -1
  88. package/dist/i18n/translator.d.ts +11 -0
  89. package/dist/i18n/validation-message.d.ts +42 -7
  90. package/dist/i18n.d.ts +7 -12
  91. package/dist/i18n.js +66 -283
  92. package/dist/index.d.ts +16 -7
  93. package/dist/index.js +924 -2855
  94. package/dist/loader-BWkigpVe.js +52 -0
  95. package/dist/locale/en.js +1 -1
  96. package/dist/locale/en.json +4 -1
  97. package/dist/locale/ru.js +4 -1
  98. package/dist/locale/ru.json +4 -1
  99. package/dist/locale-CG54AU2Q.js +219 -0
  100. package/dist/locale.js +1 -1
  101. package/dist/model/behaviors-value.d.ts +24 -6
  102. package/dist/model/create-model.d.ts +2 -2
  103. package/dist/model/derived-registry.d.ts +1 -1
  104. package/dist/model/index.d.ts +11 -5
  105. package/dist/model/model-nodes.d.ts +79 -12
  106. package/dist/model/model-signals-proxy.d.ts +5 -6
  107. package/dist/model/model-value-proxy.d.ts +20 -15
  108. package/dist/model/safe-effect.d.ts +33 -1
  109. package/dist/model/types.d.ts +63 -13
  110. package/dist/model.js +20 -18
  111. package/dist/node-kind-C-r6ZMdo.js +15 -0
  112. package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
  113. package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
  114. package/dist/platforms/react/form-bundle-context.d.ts +2 -2
  115. package/dist/platforms/react/hooks/types.d.ts +57 -38
  116. package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
  117. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  118. package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
  119. package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
  120. package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
  121. package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
  122. package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
  123. package/dist/platforms/react/i18n/context.d.ts +6 -1
  124. package/dist/platforms/react/i18n/hooks.d.ts +16 -10
  125. package/dist/platforms/react/i18n/provider.d.ts +9 -2
  126. package/dist/platforms/react/index.d.ts +6 -4
  127. package/dist/react.d.ts +22 -0
  128. package/dist/react.js +505 -0
  129. package/dist/runtime-token.d.ts +1 -2
  130. package/dist/safe-effect-BbtX3KuN.js +643 -0
  131. package/dist/schema-controller-CpiO2JoK.js +143 -0
  132. package/dist/signals.d.ts +15 -9
  133. package/dist/signals.js +11 -6
  134. package/dist/strategy-CoFSemuP.js +375 -0
  135. package/dist/type-guards-D-N_5Ddh.js +19 -0
  136. package/dist/validation-issue-B9mjI8jz.js +7 -0
  137. package/dist/validation-message-Ced9MGxs.js +194 -0
  138. package/dist/validation.js +9 -9
  139. package/dist/validators/email.js +5 -5
  140. package/dist/validators/future-date.js +8 -8
  141. package/dist/validators/integer.js +3 -3
  142. package/dist/validators/is-date.js +4 -4
  143. package/dist/validators/is-number.js +4 -4
  144. package/dist/validators/max-age.js +8 -9
  145. package/dist/validators/max-date.js +8 -8
  146. package/dist/validators/max-length.js +7 -7
  147. package/dist/validators/max.js +4 -4
  148. package/dist/validators/min-age.js +10 -8
  149. package/dist/validators/min-date.js +3 -3
  150. package/dist/validators/min-length.js +5 -5
  151. package/dist/validators/min.js +4 -4
  152. package/dist/validators/multiple-of.js +11 -7
  153. package/dist/validators/non-negative.js +3 -3
  154. package/dist/validators/non-zero.js +3 -3
  155. package/dist/validators/past-date.js +8 -8
  156. package/dist/validators/pattern.js +5 -5
  157. package/dist/validators/phone.js +11 -9
  158. package/dist/validators/required.js +2 -2
  159. package/dist/validators/url.js +48 -12
  160. package/dist/validators.js +17 -17
  161. package/docs/llms/01-api-reference.md +60 -34
  162. package/docs/llms/02-quick-start.md +111 -80
  163. package/docs/llms/03-api-signatures.md +368 -153
  164. package/docs/llms/04-common-patterns.md +58 -56
  165. package/docs/llms/05-common-mistakes.md +69 -44
  166. package/docs/llms/06-troubleshooting.md +14 -9
  167. package/docs/llms/07-complete-import.md +74 -43
  168. package/docs/llms/08-form-types.md +11 -4
  169. package/docs/llms/09-formschema.md +129 -81
  170. package/docs/llms/10-arrays.md +106 -75
  171. package/docs/llms/11-async-watchfield.md +2 -2
  172. package/docs/llms/13-multi-step.md +134 -63
  173. package/docs/llms/14-extended-mistakes.md +20 -17
  174. package/docs/llms/15-project-structure.md +221 -74
  175. package/docs/llms/16-ui-components.md +55 -42
  176. package/docs/llms/17-nonexistent-api.md +199 -32
  177. package/docs/llms/18-conditional-fields.md +66 -32
  178. package/docs/llms/19-reading-values.md +15 -3
  179. package/docs/llms/20-compute-vs-watch.md +16 -10
  180. package/docs/llms/21-array-operations.md +51 -27
  181. package/docs/llms/22-cycle-detection.md +1 -1
  182. package/docs/llms/23-copy-from.md +2 -2
  183. package/docs/llms/24-sync-fields.md +6 -5
  184. package/docs/llms/25-reset-when.md +14 -7
  185. package/docs/llms/26-transform-value.md +4 -4
  186. package/docs/llms/27-revalidate-when.md +16 -15
  187. package/docs/llms/28-submit-and-reset.md +276 -128
  188. package/docs/llms/29-async-preload.md +39 -29
  189. package/docs/llms/30-type-safety-recipes.md +105 -51
  190. package/docs/llms/31-async-validator-debounce.md +96 -53
  191. package/docs/llms/33-validation-strategy.md +177 -65
  192. package/docs/llms/34-i18n.md +69 -43
  193. package/docs/llms/35-unified-contract.md +100 -39
  194. package/docs/llms/36-issue-messages.md +284 -0
  195. package/docs/llms/37-compiled-schema.md +332 -0
  196. package/docs/llms/38-form-flow.md +397 -0
  197. package/llms-index.json +1 -1
  198. package/llms.txt +6084 -3420
  199. package/package.json +23 -4
  200. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  201. package/dist/date-utils-dTT_x_Be.js +0 -31
  202. package/dist/form/create-core-form.d.ts +0 -72
  203. package/dist/form/factories/index.d.ts +0 -6
  204. package/dist/form/factories/node-factory.d.ts +0 -189
  205. package/dist/form/form-submitter.d.ts +0 -121
  206. package/dist/form/nodes/array-node.d.ts +0 -276
  207. package/dist/form/nodes/field-node.d.ts +0 -297
  208. package/dist/form/nodes/form-node.d.ts +0 -326
  209. package/dist/form/nodes/group-node.d.ts +0 -284
  210. package/dist/form/nodes/model-array-node.d.ts +0 -126
  211. package/dist/form/nodes/subscription-manager.d.ts +0 -180
  212. package/dist/form/status-machine.d.ts +0 -115
  213. package/dist/form/types/deep-schema.d.ts +0 -107
  214. package/dist/form/types/form-proxy.d.ts +0 -141
  215. package/dist/form/unique-id.d.ts +0 -45
  216. package/dist/form/validation/config.d.ts +0 -73
  217. package/dist/form/validation/error-handler.d.ts +0 -166
  218. package/dist/form/validators/validation-error.d.ts +0 -8
  219. package/dist/index-BrNkQj1y.js +0 -105
  220. package/dist/loader-Dv_Hl7JC.js +0 -216
  221. package/dist/model-value-proxy-caTmAiUd.js +0 -419
  222. package/dist/node-BeV_lV5i.js +0 -100
  223. package/dist/strategy-DgzELWno.js +0 -204
  224. package/dist/validation-error-B-U3RsV0.js +0 -11
@@ -0,0 +1,100 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { ValidationIssue } from '../types/contracts';
3
+ import { FormArray, FormGroup } from '../types/nodes';
4
+ import { FormModel } from '../../model/types';
5
+ import { NODE_KIND, InternalFormNode } from './node-kind';
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(first: number, second: number): void;
15
+ clear(): void;
16
+ toArray(): TItem[];
17
+ }
18
+ export declare class FormArrayNode<T extends object> implements FormArray<T>, InternalFormNode<T[]> {
19
+ private readonly control;
20
+ readonly [NODE_KIND]: "array";
21
+ readonly value: ReadonlySignal<T[]>;
22
+ readonly valid: ReadonlySignal<boolean>;
23
+ readonly invalid: ReadonlySignal<boolean>;
24
+ readonly pending: ReadonlySignal<boolean>;
25
+ readonly disabled: ReadonlySignal<boolean>;
26
+ readonly touched: ReadonlySignal<boolean>;
27
+ readonly dirty: ReadonlySignal<boolean>;
28
+ readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
29
+ readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
30
+ private readonly rows;
31
+ /**
32
+ * Состав массива меняли методами ноды после создания или сброса. Как `dirty` поля: ставит запись
33
+ * через форму (`push`, `removeAt`, `move`, `setValue`…), а не запись в модель — загрузка данных
34
+ * массив изменённым не делает. Операция, которая состав не поменяла (`clear` пустого массива,
35
+ * `move(i, i)`), — тоже: поведение «очистить список при снятом флаге» после сброса формы её не
36
+ * испачкает. Снимает `reset`.
37
+ */
38
+ private readonly reshaped;
39
+ /** Форма строки по фасаду под-модели элемента. */
40
+ private readonly cache;
41
+ /** Под-модели строк, для которых сейчас есть формы, — «прежний» набор для сверки. */
42
+ private mounted;
43
+ private readonly initial;
44
+ private readonly own;
45
+ private readonly availability;
46
+ /** Ожидание async-правила на ручке самого массива. */
47
+ private readonly ownPending;
48
+ private readonly stopSync;
49
+ /**
50
+ * @param control - Массив модели (`model.<путь>`).
51
+ * @param buildRow - Форма строки по её под-модели; инъекция снимает цикл импорта со сборкой.
52
+ */
53
+ constructor(control: ModelArrayControl<T>, buildRow: (rowModel: FormModel<T>) => FormGroup<T>);
54
+ get length(): number;
55
+ at(index: number): FormGroup<T> | undefined;
56
+ map<R>(callback: (row: FormGroup<T>, index: number) => R): R[];
57
+ forEach(callback: (row: FormGroup<T>, index: number) => void): void;
58
+ keyAt(index: number): string;
59
+ push(item?: T): void;
60
+ insert(index: number, item?: T): void;
61
+ removeAt(index: number): void;
62
+ move(from: number, to: number): void;
63
+ swap(first: number, second: number): void;
64
+ clear(): void;
65
+ getValue(): T[];
66
+ setValue(values: T[]): void;
67
+ /**
68
+ * Сбросить массив: строки, замечания и `dirty`. Без аргумента — к точке отсчёта модели, той же, к
69
+ * которой возвращает `model.reset()`: элементам создания модели либо последнего
70
+ * `captureInitial()`.
71
+ */
72
+ reset(values?: T[]): void;
73
+ /** Строки пересобираются из значений создания — их поведение считает заново. @internal */
74
+ resetToCreation(): void;
75
+ /** Точка отсчёта модели за массивом; без модели — элементы создания. */
76
+ private referenceItems;
77
+ markAsTouched(): void;
78
+ markAsUntouched(): void;
79
+ disable(): void;
80
+ /** Включить массив и строки; поля с блокировкой из схемы (`disabled: true`) остаются выключенными. */
81
+ enable(): void;
82
+ /** Каскад от контейнера — то же, что `enable()`: своей блокировки из схемы у массива нет. @internal */
83
+ enableFromParent(): void;
84
+ setIssues(issues: readonly ValidationIssue[]): void;
85
+ clearIssues(): void;
86
+ /** Своё ожидание async-правила на ручке массива; ожидание строк агрегат добавит сам. @internal */
87
+ setPending(pending: boolean): void;
88
+ /** Выполнить мутацию массива модели и отметить `dirty`, если состав строк изменился. */
89
+ private reshape;
90
+ /**
91
+ * Индекс модели для индекса строки формы. Без `null`-элементов они совпадают; с ними строка `i` —
92
+ * `i`-й непустой элемент, а индекс за последней строкой — конец массива модели. Читает модель, а не
93
+ * `mounted`: внутри `batch` синхронизация ещё не прошла.
94
+ */
95
+ private modelIndex;
96
+ /** Под-модели строк по порядку — без подписки: мутацию могут вызвать из effect'а поведения. */
97
+ private composition;
98
+ /** Остановить синхронизацию и диспозить формы всех строк. @internal */
99
+ dispose(): void;
100
+ }
@@ -0,0 +1,73 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { FieldConfig } from '../types/field-config';
3
+ import { ValidationIssue } from '../types/contracts';
4
+ import { ComponentRef } from '../types/schema-node';
5
+ import { FormControl } from '../types/nodes';
6
+ import { NODE_KIND, InternalFormNode } from './node-kind';
7
+ export declare class FormControlNode<T> implements FormControl<T>, InternalFormNode<T> {
8
+ readonly [NODE_KIND]: "control";
9
+ readonly value: ReadonlySignal<T>;
10
+ readonly valid: ReadonlySignal<boolean>;
11
+ readonly invalid: ReadonlySignal<boolean>;
12
+ readonly pending: ReadonlySignal<boolean>;
13
+ readonly disabled: ReadonlySignal<boolean>;
14
+ readonly touched: ReadonlySignal<boolean>;
15
+ /** Уходы из поля, включая повторные, — триггер стратегии `blur`. @internal */
16
+ readonly touches: ReadonlySignal<number>;
17
+ readonly dirty: ReadonlySignal<boolean>;
18
+ readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
19
+ readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
20
+ readonly component: ComponentRef | undefined;
21
+ readonly componentProps: ReadonlySignal<Readonly<Record<string, unknown>>>;
22
+ readonly shouldShowError: ReadonlySignal<boolean>;
23
+ readonly shouldShowIssues: ReadonlySignal<boolean>;
24
+ private readonly source;
25
+ /**
26
+ * Значение на момент построения. К нему возвращает `resetToCreation` (`resetOnDisable`), а
27
+ * {@link reset} — только поле над собственным сигналом, без модели за ним.
28
+ */
29
+ private readonly initialValue;
30
+ private readonly interaction;
31
+ private readonly issueState;
32
+ private readonly availability;
33
+ /**
34
+ * Блокировка из схемы (`disabled: true` узла — вычисляемые `total`, `fullName`). Постоянная:
35
+ * `enable()` контейнера её не снимает, иначе «выключить форму на время отправки → включить»
36
+ * разблокировало бы такие поля. Снимает её только явный `enable()` этого поля.
37
+ */
38
+ private locked;
39
+ private readonly pendingState;
40
+ private readonly componentState;
41
+ constructor(config: FieldConfig<T>);
42
+ getValue(): T;
43
+ setValue(value: T): void;
44
+ /**
45
+ * Сбросить поле: значение, замечания, `touched` и `dirty`. Без аргумента — к точке отсчёта модели,
46
+ * той же, к которой возвращает `model.reset()`: значению создания модели либо последнему
47
+ * `captureInitial()` (загруженные данные). Поле над собственным сигналом, без модели, — к значению,
48
+ * с которым нода создана.
49
+ *
50
+ * У производного поля (цель `compute`) без аргумента сбрасывается только состояние: значением
51
+ * владеет поведение, а значение создания ноды снято до первого расчёта — запись вернула бы
52
+ * «сырое» значение, которое `compute` пересчитает лишь при смене источников.
53
+ */
54
+ reset(value?: T): void;
55
+ /** Точка отсчёта модели за полем; у поля без модели — значение создания. */
56
+ private referenceValue;
57
+ /** Значение создания и чистое состояние, у цели `compute` тоже — для `resetOnDisable`. @internal */
58
+ resetToCreation(): void;
59
+ private resetState;
60
+ markAsTouched(): void;
61
+ markAsUntouched(): void;
62
+ disable(): void;
63
+ enable(): void;
64
+ /** Каскад от контейнера: блокировку из схемы не снимает. @internal */
65
+ enableFromParent(): void;
66
+ setIssues(issues: readonly ValidationIssue[]): void;
67
+ clearIssues(): void;
68
+ updateComponentProps(patch: Record<string, unknown>): void;
69
+ /** Раннер валидации: начало (`true`) и конец (`false`) async-правил поля. @internal */
70
+ setPending(pending: boolean): void;
71
+ /** У поля подписок нет. @internal */
72
+ dispose(): void;
73
+ }
@@ -0,0 +1,66 @@
1
+ import { ReadonlySignal, Signal } from '@preact/signals-core';
2
+ import { ValidationIssue } from '../types/contracts';
3
+ import { FormGroup } from '../types/nodes';
4
+ import { NODE_KIND, InternalFormNode } from './node-kind';
5
+ export declare class FormGroupNode<T> implements InternalFormNode<T> {
6
+ readonly [NODE_KIND]: "group";
7
+ readonly value: ReadonlySignal<T>;
8
+ readonly valid: ReadonlySignal<boolean>;
9
+ readonly invalid: ReadonlySignal<boolean>;
10
+ readonly pending: ReadonlySignal<boolean>;
11
+ readonly disabled: ReadonlySignal<boolean>;
12
+ readonly touched: ReadonlySignal<boolean>;
13
+ readonly dirty: ReadonlySignal<boolean>;
14
+ readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
15
+ readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
16
+ /** Сырые ноды детей по именам полей данных. */
17
+ private readonly children;
18
+ /** Замечания самой группы (ответ сервера на форму целиком). */
19
+ private readonly own;
20
+ private readonly availability;
21
+ /** Ожидание async-правила на ручке самой группы. */
22
+ private readonly ownPending;
23
+ /**
24
+ * Лист → его записываемый сигнал модели: по нему `setValue` узнаёт цели `compute` (их
25
+ * помечает `markDerived`), а не по computed-обёртке `value` ноды.
26
+ */
27
+ private readonly leafSignals;
28
+ private behaviorCleanup;
29
+ private face;
30
+ /**
31
+ * @param children - Готовые ноды детей по именам полей данных. Вид ребёнка задан нодой: по
32
+ * именам и значениям группа ничего не угадывает.
33
+ */
34
+ constructor(children: Iterable<readonly [string, InternalFormNode]>);
35
+ /** Лицо группы: прокси с нодами полей. Строится один раз. */
36
+ get proxy(): FormGroup<T>;
37
+ getValue(): T;
38
+ /** Записать значения полей; цели `compute` пропускаются — ими владеет поведение. */
39
+ setValue(value: T): void;
40
+ /**
41
+ * Сбросить группу: замечания группы и всех полей, `touched`, `dirty` и значения. Без аргумента —
42
+ * к точке отсчёта модели, той же, к которой возвращает `model.reset()` (значения создания модели
43
+ * либо последнего `captureInitial()`), — и форма после сброса не изменена.
44
+ * Цели `compute` значения не получают, как и в `setValue`: ими владеет поведение.
45
+ */
46
+ reset(value?: T): void;
47
+ /** Сброс всех полей группы к значениям создания, у целей `compute` тоже. @internal */
48
+ resetToCreation(): void;
49
+ markAsTouched(): void;
50
+ markAsUntouched(): void;
51
+ disable(): void;
52
+ /** Включить группу и детей; поля с блокировкой из схемы (`disabled: true`) остаются выключенными. */
53
+ enable(): void;
54
+ /** Каскад от контейнера — то же, что `enable()`: своей блокировки из схемы у группы нет. @internal */
55
+ enableFromParent(): void;
56
+ setIssues(issues: readonly ValidationIssue[]): void;
57
+ clearIssues(): void;
58
+ /** Своё ожидание async-правила на ручке группы; ожидание детей агрегат добавит сам. @internal */
59
+ setPending(pending: boolean): void;
60
+ /** Прикрепить cleanup поведения формы (`createForm({ behavior })`). @internal */
61
+ attachBehaviorCleanup(cleanup: () => void): void;
62
+ /** Связать лист с его сигналом модели для `setValue`. @internal */
63
+ registerLeafSignal(node: InternalFormNode, source: Signal<unknown>): void;
64
+ /** Снять поведение и подписки всех детей. @internal */
65
+ dispose(): void;
66
+ }
@@ -0,0 +1,29 @@
1
+ import { FormNode } from '../types/nodes';
2
+ export declare const NODE_KIND: unique symbol;
3
+ /** Сырой узел за прокси группы: внутренним вызовам нужен он, а не лицо с полями. */
4
+ export declare const RAW_NODE: unique symbol;
5
+ export type NodeKind = 'control' | 'group' | 'array';
6
+ /** Внутренние члены ноды: их зовут сборка, раннер валидации и массив строк. */
7
+ export interface InternalFormNode<T = unknown> extends FormNode<T> {
8
+ readonly [NODE_KIND]: NodeKind;
9
+ /** Раннер валидации: начало и конец async-правил; у группы и массива — ничего. */
10
+ setPending(pending: boolean): void;
11
+ /** Снять подписки ноды и её детей. */
12
+ dispose(): void;
13
+ /**
14
+ * Сброс для `resetOnDisable`: вернуть значения создания и очистить состояние — в том числе у
15
+ * целей `compute`. Выключенное поле не должно нести прежний расчёт (его `compute` обязан нести то
16
+ * же условие `when`); публичный `reset()` без аргумента производное поле не пишет.
17
+ */
18
+ resetToCreation(): void;
19
+ /**
20
+ * Включение каскадом от контейнера (`enable()` группы или массива). Отличается от `enable()` одним:
21
+ * блокировку из схемы (`disabled: true` узла) не снимает — её снимает только явный `enable()`
22
+ * самого поля.
23
+ */
24
+ enableFromParent(): void;
25
+ }
26
+ /** Вид ноды либо `undefined`, если значение — не нода формы. */
27
+ export declare function kindOf(value: unknown): NodeKind | undefined;
28
+ /** Сырой узел: за прокси группы — её класс, у остальных — сама нода. */
29
+ export declare function rawNode<N extends object>(node: N): InternalFormNode;
@@ -0,0 +1,6 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ export interface AvailabilityState {
3
+ readonly disabled: ReadonlySignal<boolean>;
4
+ setDisabled(disabled: boolean): void;
5
+ }
6
+ export declare function createAvailability(initiallyDisabled?: boolean): AvailabilityState;
@@ -0,0 +1,6 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ export interface ComponentState {
3
+ readonly props: ReadonlySignal<Readonly<Record<string, unknown>>>;
4
+ update(patch: Record<string, unknown>): void;
5
+ }
6
+ export declare function createComponentState(initial?: Record<string, unknown>): ComponentState;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Модули состояния ноды формы. Каждый держит одну сторону состояния и ничего не знает о других;
3
+ * производные (`valid`, `invalid`, `pending`) складывает нода.
4
+ *
5
+ * @internal
6
+ * @module form/nodes/state
7
+ */
8
+ export { createInteraction } from './interaction';
9
+ export { createIssues } from './issues';
10
+ export { createAvailability } from './availability';
11
+ export { createPending } from './pending';
12
+ export { createComponentState } from './component';
@@ -0,0 +1,13 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ export interface InteractionState {
3
+ readonly touched: ReadonlySignal<boolean>;
4
+ readonly dirty: ReadonlySignal<boolean>;
5
+ /**
6
+ * Сколько раз поле помечали тронутым — каждый уход из поля, и повторный тоже. У тронутого поля
7
+ * `touched` при новом уходе не меняется; по счётчику стратегия `blur` видит и его.
8
+ */
9
+ readonly touches: ReadonlySignal<number>;
10
+ setTouched(touched: boolean): void;
11
+ setDirty(dirty: boolean): void;
12
+ }
13
+ export declare function createInteraction(): InteractionState;
@@ -0,0 +1,9 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ import { ValidationIssue } from '../../types/contracts';
3
+ export interface IssuesState {
4
+ readonly list: ReadonlySignal<readonly ValidationIssue[]>;
5
+ readonly blocking: ReadonlySignal<boolean>;
6
+ set(issues: readonly ValidationIssue[]): void;
7
+ clear(): void;
8
+ }
9
+ export declare function createIssues(): IssuesState;
@@ -0,0 +1,6 @@
1
+ import { ReadonlySignal } from '@preact/signals-core';
2
+ export interface PendingState {
3
+ readonly active: ReadonlySignal<boolean>;
4
+ mark(pending: boolean): void;
5
+ }
6
+ export declare function createPending(): PendingState;
@@ -0,0 +1,59 @@
1
+ import { FormSchemaNode } from '../types/schema-node';
2
+ import { CompiledNode, CompiledSchema, SchemaScopeId } from './compiled';
3
+ /**
4
+ * Узел скомпилированной схемы, а не авторский узел?
5
+ *
6
+ * @param value - Что угодно.
7
+ * @returns `true` для узла, выданного {@link compileSchema}.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * isCompiledNode(compileSchema(tree, model).root); // true
12
+ * isCompiledNode(tree); // false
13
+ * ```
14
+ *
15
+ * @group Schema
16
+ */
17
+ export declare function isCompiledNode(value: unknown): value is CompiledNode;
18
+ /** Опции {@link compileSchema}. */
19
+ export interface CompileSchemaOptions {
20
+ /** Идентификатор области дерева; по умолчанию корень `#`. */
21
+ readonly scope?: SchemaScopeId;
22
+ }
23
+ /**
24
+ * Скомпилировать дерево схемы: вид каждого узла, нормализованные привязки, идентификаторы и
25
+ * индексы области.
26
+ *
27
+ * Повтор `selector` в одной области — исключение с путями обоих узлов. Неизвестный ключ узла и
28
+ * нераспознанная привязка — предупреждения в dev; такой узел становится контейнером. Вторая
29
+ * привязка той же ручки — тоже предупреждение: нода формы у ручки одна.
30
+ *
31
+ * @param tree - Корень авторского дерева ({@link FormSchemaNode}).
32
+ * @param model - Модель области (корень сборки, под-модель строки либо группы): пути вложенных
33
+ * областей считаются от неё; без неё — абсолютные пути ручек (`#/phones.1.address`).
34
+ * @param options - {@link CompileSchemaOptions}.
35
+ * @returns {@link CompiledSchema} области.
36
+ * @throws Error при повторе `selector` в области, при селекторе, совпавшем с позиционным путём
37
+ * узла (`#/1`), и при зацикленном дереве (в том числе через подформу).
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const model = createModel({ email: '', phones: [{ number: '' }] });
42
+ * const compiled = compileSchema(
43
+ * {
44
+ * children: [
45
+ * { selector: 'email', model: model.$.email, component: Input },
46
+ * { model: model.$.phones, item: (row) => ({ model: row.$.number, component: Input }) },
47
+ * ],
48
+ * },
49
+ * model
50
+ * );
51
+ *
52
+ * compiled.nodes.get('email' as SchemaNodeId)?.kind; // 'field'
53
+ * compiled.fields.get(model.$.email)?.component; // Input
54
+ * compiled.arrays.get(model.$.phones)?.row(model.phones.at(0)!).scope; // '#/phones[0]'
55
+ * ```
56
+ *
57
+ * @group Schema
58
+ */
59
+ export declare function compileSchema(tree: FormSchemaNode, model?: object, options?: CompileSchemaOptions): CompiledSchema;
@@ -0,0 +1,143 @@
1
+ import { Signal } from '@preact/signals-core';
2
+ import { ComponentRef, FormSchemaNode, SchemaArrayControl, SchemaArrayHandle, SchemaGroupHandle, SchemaValueHandle } from '../types/schema-node';
3
+ /**
4
+ * Идентификатор узла: `selector` автора, а у узла без него — позиционный путь от корня области
5
+ * (`#/0/2`). Уникален в пределах области.
6
+ *
7
+ * @group Schema
8
+ */
9
+ export type SchemaNodeId = string & {
10
+ readonly __nodeId: true;
11
+ };
12
+ /**
13
+ * Идентификатор области схемы: `#` — корень, `#/address` — подформа, `#/phones[3]` — строка
14
+ * массива (в скобках — стабильный ключ строки, который не меняется при перестановке).
15
+ *
16
+ * @group Schema
17
+ */
18
+ export type SchemaScopeId = string & {
19
+ readonly __scopeId: true;
20
+ };
21
+ /** Ключи, общие для узлов всех видов, кроме текста. */
22
+ interface CompiledNodeBase {
23
+ /** Идентификатор в области: `selector`, иначе позиционный путь. */
24
+ readonly id: SchemaNodeId;
25
+ /** Позиционный путь узла в области (`#/0/2`) — для сообщений об ошибках. */
26
+ readonly path: string;
27
+ /** `selector` автора: по нему узел адресуют поведение и шаги. */
28
+ readonly selector: string | undefined;
29
+ /** Область, которой принадлежит узел. */
30
+ readonly scope: SchemaScopeId;
31
+ /** Компонент узла как есть: ядро его не интерпретирует. */
32
+ readonly component: ComponentRef | undefined;
33
+ /** `componentProps` автора; без них — пустой объект. */
34
+ readonly props: Readonly<Record<string, unknown>>;
35
+ /** Авторский узел, из которого скомпилирован этот. */
36
+ readonly source: FormSchemaNode;
37
+ }
38
+ /**
39
+ * Поле: узел, привязанный к ручке значения — листу или массиву целиком.
40
+ *
41
+ * @group Schema
42
+ */
43
+ export interface CompiledFieldNode extends CompiledNodeBase {
44
+ readonly kind: 'field';
45
+ /** Ручка значения `model.$.<лист | массив>`. */
46
+ readonly handle: SchemaValueHandle;
47
+ /** Поле создаётся отключённым. */
48
+ readonly disabled: boolean;
49
+ }
50
+ /**
51
+ * Массив под-форм: у каждой строки своя форма и своя область схемы.
52
+ *
53
+ * @group Schema
54
+ */
55
+ export interface CompiledArrayNode extends CompiledNodeBase {
56
+ readonly kind: 'array';
57
+ /** Ручка массива `model.$.<массив>` — привязка фасадом нормализуется к ней. */
58
+ readonly handle: SchemaArrayHandle;
59
+ /** Фасад массива модели: длина, строки и мутации. */
60
+ readonly control: SchemaArrayControl;
61
+ /** Запасной шаблон нового элемента из узла; шаблон модели (`arrayOf`) главнее. */
62
+ readonly initialValue: unknown;
63
+ /**
64
+ * Скомпилированная схема строки — одна на под-модель строки: сборка формы строки и рендерер
65
+ * получают один и тот же объект.
66
+ */
67
+ row(rowModel: object): CompiledSchema;
68
+ }
69
+ /**
70
+ * Подформа: часть схемы, подключённая к группе модели. Содержимое компилируется сразу, в своей
71
+ * области.
72
+ *
73
+ * @group Schema
74
+ */
75
+ export interface CompiledPartNode extends CompiledNodeBase {
76
+ readonly kind: 'part';
77
+ /** Ручка группы `model.$.<группа>`. */
78
+ readonly handle: SchemaGroupHandle;
79
+ /** Под-модель группы — ключ области подформы. */
80
+ readonly model: object;
81
+ /** Скомпилированное содержимое части. */
82
+ readonly content: CompiledSchema;
83
+ }
84
+ /**
85
+ * Контейнер: узел с детьми либо без привязки — секция, шаг визарда, html-тег. Без `component`
86
+ * рендерер рисует только детей.
87
+ *
88
+ * @group Schema
89
+ */
90
+ export interface CompiledContainerNode extends CompiledNodeBase {
91
+ readonly kind: 'container';
92
+ /** Дети в порядке следования: узлы и текст. */
93
+ readonly children: readonly CompiledChild[];
94
+ }
95
+ /**
96
+ * Часть текста: литерал, число или сигнал (его значение выводится и обновляется на месте).
97
+ *
98
+ * @group Schema
99
+ */
100
+ export type SchemaTextPart = string | number | Signal<any>;
101
+ /**
102
+ * Текст контейнера: подряд идущие текстовые дети автора, склеенные в один узел.
103
+ *
104
+ * @group Schema
105
+ */
106
+ export interface CompiledTextNode {
107
+ readonly kind: 'text';
108
+ /** Позиционный путь первой части. */
109
+ readonly id: SchemaNodeId;
110
+ readonly parts: readonly SchemaTextPart[];
111
+ }
112
+ /**
113
+ * Узел скомпилированной схемы: поле, массив под-форм, подформа или контейнер.
114
+ *
115
+ * @group Schema
116
+ */
117
+ export type CompiledNode = CompiledFieldNode | CompiledArrayNode | CompiledPartNode | CompiledContainerNode;
118
+ /**
119
+ * Ребёнок контейнера: узел либо текст.
120
+ *
121
+ * @group Schema
122
+ */
123
+ export type CompiledChild = CompiledNode | CompiledTextNode;
124
+ /**
125
+ * Скомпилированная схема одной области: корня сборки, строки массива или подформы.
126
+ *
127
+ * @group Schema
128
+ */
129
+ export interface CompiledSchema {
130
+ /** Идентификатор области. */
131
+ readonly scope: SchemaScopeId;
132
+ /** Модель области: корень сборки, под-модель строки или группы; без модели — `undefined`. */
133
+ readonly model: object | undefined;
134
+ /** Корневой узел области. */
135
+ readonly root: CompiledNode;
136
+ /** Узлы этой области по идентификатору; узлы строк и подформ лежат в их схемах. */
137
+ readonly nodes: ReadonlyMap<SchemaNodeId, CompiledNode>;
138
+ /** Поля по ручке модели — этой области и её подформ (строки массивов — в их схемах). */
139
+ readonly fields: ReadonlyMap<object, CompiledFieldNode>;
140
+ /** Массивы под-форм по ручке модели — этой области и её подформ. */
141
+ readonly arrays: ReadonlyMap<object, CompiledArrayNode>;
142
+ }
143
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Стабильный ключ строки массива под-форм — по идентичности под-модели строки.
3
+ *
4
+ * Один источник для области строки в скомпилированной схеме (`#/phones[<ключ>]`) и для
5
+ * `FormArray.keyAt(index)`: ключ держится за элемент модели и не меняется при `move` и `swap`.
6
+ *
7
+ * @internal
8
+ * @module form/schema/row-key
9
+ */
10
+ /** Ключ строки по её под-модели; первый запрос выдаёт новый ключ. */
11
+ export declare function rowKeyOf(rowModel: object): number;
@@ -1,4 +1,5 @@
1
1
  import { Signal } from '@preact/signals-core';
2
+ import { CompiledSchema } from './schema/compiled';
2
3
  /**
3
4
  * Хуки жизненного цикла узла схемы. Каждый хук опционален; повторная регистрация перезаписывает
4
5
  * предыдущее значение.
@@ -12,7 +13,8 @@ export interface NodeLifecycleHooks {
12
13
  onUnmount?: () => void;
13
14
  }
14
15
  /**
15
- * Хранилище переопределений одной области схемы. Ключ каждой карты — `selector` узла.
16
+ * Хранилище переопределений одной области схемы. Ключ каждой карты — `selector` узла: он же
17
+ * идентификатор узла в скомпилированной схеме области ({@link SchemaNodeId}).
16
18
  *
17
19
  * @group Schema
18
20
  */
@@ -59,9 +61,24 @@ export interface SchemaNodeControl {
59
61
  getRef<H>(): {
60
62
  current: H | null;
61
63
  };
64
+ /**
65
+ * @internal Записать условие скрытия узла (`hideWhen`). Возвращает снятие: оно убирает условие,
66
+ * только если его не сменило более позднее.
67
+ */
68
+ addHideCondition(condition: () => boolean): () => void;
69
+ /**
70
+ * @internal Записать обработчик проп-события компонента узла (`onComponentEvent`). Возвращает
71
+ * снятие: оно убирает обработчик, только если его не сменил более поздний.
72
+ */
73
+ addEventHandler(event: string, handler: (...args: any[]) => any): () => void;
74
+ /**
75
+ * @internal Записать хук жизненного цикла узла (`onMount`, `onUnmount`). Возвращает снятие: оно
76
+ * убирает хук, только если его не сменил более поздний.
77
+ */
78
+ addLifecycleHook<K extends keyof NodeLifecycleHooks>(hook: K, value: NonNullable<NodeLifecycleHooks[K]>): () => void;
62
79
  /** @internal Селектор узла. */
63
80
  __selector: string;
64
- /** @internal Хранилище области. */
81
+ /** @internal Хранилище области — его читает рендерер. */
65
82
  __overrideMaps: SchemaOverrideMaps;
66
83
  }
67
84
  /**
@@ -72,6 +89,11 @@ export interface SchemaNodeControl {
72
89
  export interface SchemaScope {
73
90
  /** Управление узлом области по его `selector`. */
74
91
  node(selector: string): SchemaNodeControl;
92
+ /**
93
+ * @internal Записать эффект, живущий, пока область смонтирована (`renderEffect`). Возвращает
94
+ * снятие.
95
+ */
96
+ addRenderEffect(effect: () => void | (() => void)): () => void;
75
97
  /** @internal Хранилище области — его читает рендерер. */
76
98
  __overrideMaps: SchemaOverrideMaps;
77
99
  }
@@ -87,12 +109,6 @@ export interface SchemaController {
87
109
  */
88
110
  scopeOf(subModel: object): SchemaScope;
89
111
  }
90
- /**
91
- * Пустое хранилище переопределений области.
92
- *
93
- * @group Schema
94
- */
95
- export declare function createSchemaOverrideMaps(): SchemaOverrideMaps;
96
112
  /**
97
113
  * Схема области поверх хранилища: `node(selector)` → управление узлом.
98
114
  *
@@ -116,22 +132,22 @@ export declare function createSchemaScope(maps?: SchemaOverrideMaps): SchemaScop
116
132
  */
117
133
  export declare function createSchemaController(): SchemaController;
118
134
  /**
119
- * Селекторы, на которые в области записаны правила, но которых нет в её дереве. Такое правило
120
- * ничего не делает — сборка и рендерер в dev предупреждают о нём.
135
+ * Селекторы, на которые в области записаны правила, но которых нет в её скомпилированной схеме.
136
+ * Такое правило ничего не делает — сборка и рендерер в dev предупреждают о нём.
121
137
  *
122
138
  * Ref в проверку не входит: его разрешено брать по абсолютному пути модели
123
139
  * (`phones.0.number`), а такой строки при сборке может ещё не быть.
124
140
  *
125
141
  * @param maps - Хранилище области.
126
- * @param tree - Дерево (поддерево) области.
127
- * @returns Селекторы с правилами, не найденные в дереве.
142
+ * @param compiled - Скомпилированная схема области ({@link CompiledSchema}).
143
+ * @returns Селекторы с правилами, не найденные в схеме области.
128
144
  *
129
145
  * @example
130
146
  * ```typescript
131
147
  * const scope = controller.scopeOf(model);
132
- * unknownSchemaSelectors(scope.__overrideMaps, tree); // ['mortgage'] — узла с таким selector нет
148
+ * unknownSchemaSelectors(scope.__overrideMaps, bundle.render.tree!); // ['mortgage'] — узла нет
133
149
  * ```
134
150
  *
135
151
  * @group Schema
136
152
  */
137
- export declare function unknownSchemaSelectors(maps: SchemaOverrideMaps, tree: unknown): string[];
153
+ export declare function unknownSchemaSelectors(maps: SchemaOverrideMaps, compiled: CompiledSchema): string[];