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

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 (56) hide show
  1. package/README.md +7 -3
  2. package/dist/behaviors.js +211 -174
  3. package/dist/create-model-BBRHhHwK.js +30 -0
  4. package/dist/form/behaviors/collections.d.ts +14 -1
  5. package/dist/form/behaviors/context.d.ts +18 -2
  6. package/dist/form/behaviors/index.d.ts +3 -1
  7. package/dist/form/behaviors/internals.d.ts +0 -1
  8. package/dist/form/behaviors/schema-operators.d.ts +76 -0
  9. package/dist/form/behaviors/types.d.ts +15 -3
  10. package/dist/form/create-core-form.d.ts +5 -3
  11. package/dist/form/create-form.d.ts +28 -53
  12. package/dist/form/form-bundle.d.ts +136 -0
  13. package/dist/form/index.d.ts +6 -1
  14. package/dist/form/nodes/field-node.d.ts +2 -2
  15. package/dist/form/nodes/group-node.d.ts +1 -1
  16. package/dist/form/nodes/model-array-node.d.ts +10 -5
  17. package/dist/form/schema-controller.d.ts +137 -0
  18. package/dist/form/schema-subtree.d.ts +28 -0
  19. package/dist/form/signal-node-registry.d.ts +22 -16
  20. package/dist/form/types/deep-schema.d.ts +2 -2
  21. package/dist/form/types/form-proxy.d.ts +1 -1
  22. package/dist/form/types/schema-node.d.ts +37 -12
  23. package/dist/form/validation/context.d.ts +6 -1
  24. package/dist/form/validation/index.d.ts +1 -1
  25. package/dist/form/validation/operators.d.ts +70 -8
  26. package/dist/form/validation/types.d.ts +2 -1
  27. package/dist/index.js +345 -254
  28. package/dist/model/index.d.ts +5 -0
  29. package/dist/model/model-nodes.d.ts +67 -4
  30. package/dist/model/model-signals-proxy.d.ts +8 -1
  31. package/dist/model/model-value-proxy.d.ts +76 -0
  32. package/dist/model/types.d.ts +7 -4
  33. package/dist/model-value-proxy-caTmAiUd.js +419 -0
  34. package/dist/model.js +19 -17
  35. package/dist/node-BeV_lV5i.js +100 -0
  36. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  37. package/dist/platforms/react/index.d.ts +1 -0
  38. package/dist/strategy-DgzELWno.js +204 -0
  39. package/dist/validation.js +7 -6
  40. package/docs/llms/03-api-signatures.md +4 -4
  41. package/docs/llms/09-formschema.md +5 -5
  42. package/docs/llms/10-arrays.md +23 -4
  43. package/docs/llms/16-ui-components.md +2 -2
  44. package/docs/llms/18-conditional-fields.md +1 -1
  45. package/docs/llms/21-array-operations.md +26 -0
  46. package/docs/llms/28-submit-and-reset.md +3 -3
  47. package/docs/llms/29-async-preload.md +4 -4
  48. package/docs/llms/33-validation-strategy.md +2 -2
  49. package/docs/llms/35-unified-contract.md +246 -0
  50. package/llms-index.json +1 -1
  51. package/llms.txt +1148 -122
  52. package/package.json +1 -1
  53. package/dist/create-model-B8U-COv-.js +0 -103
  54. package/dist/model-signals-proxy-2vqS243J.js +0 -250
  55. package/dist/node-DGEFMTQu.js +0 -18
  56. package/dist/strategy-BXS_gBfz.js +0 -170
@@ -0,0 +1,76 @@
1
+ import { SchemaNodeControl, SchemaScope } from '../schema-controller';
2
+ /**
3
+ * Скрывать узел, пока условие истинно.
4
+ *
5
+ * Условие реактивно: пересчитывается при изменении любого сигнала, прочитанного внутри него.
6
+ * Принудительное `schema.node(selector).setHidden(...)` главнее условия.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * hideWhen(schema.node('mortgage'), () => model.loanType !== 'mortgage');
11
+ * ```
12
+ */
13
+ export declare function hideWhen(node: SchemaNodeControl, condition: () => boolean): void;
14
+ /**
15
+ * Повесить обработчик на проп-событие компонента узла (`onSubmit`, `onChange`, …).
16
+ *
17
+ * Обработчик получает те же аргументы, что и исходный проп компонента, и перекрывает одноимённый
18
+ * проп из `componentProps`.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * onComponentEvent(schema.node('wizard'), 'onSubmit', async () => {
23
+ * await submitApplication(model.get());
24
+ * });
25
+ * ```
26
+ */
27
+ export declare function onComponentEvent(node: SchemaNodeControl, event: string, handler: (...args: any[]) => any): void;
28
+ /**
29
+ * Реактивный эффект, живущий, пока схема области смонтирована рендерером.
30
+ *
31
+ * В отличие от `effect` поведения, запускается после монтирования — внутри доступны ref узлов
32
+ * (`schema.node(selector).getRef().current`). Может вернуть функцию очистки.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const wizard = schema.node('wizard').getRef<FormWizardHandle>();
37
+ * renderEffect(schema, () => {
38
+ * if (model.loanType === 'mortgage') wizard.current?.goToStep(1);
39
+ * });
40
+ * ```
41
+ */
42
+ export declare function renderEffect(schema: SchemaScope, effectFn: () => void | (() => void)): void;
43
+ /**
44
+ * Синхронный хук времени сборки: `fn` вызывается сразу, до первого рендера узла. Единственный
45
+ * хук, способный повлиять на первый рендер, — внутри можно править пропсы через
46
+ * `schema.node(selector).patchProps({ … })`.
47
+ *
48
+ * К монтированию узла не привязан; аргумент `node` принимается ради симметрии с
49
+ * {@link onMount} / {@link onUnmount}.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * onInit(schema.node('summary'), () => {
54
+ * schema.node('summary').patchProps({ currency: detectCurrency() });
55
+ * });
56
+ * ```
57
+ */
58
+ export declare function onInit(_node: SchemaNodeControl, fn: () => void): void;
59
+ /**
60
+ * Хук после первого монтирования узла. Может вернуть очистку — она выполнится при размонтировании.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * onMount(schema.node('data-boundary'), () => void loadApplication());
65
+ * ```
66
+ */
67
+ export declare function onMount(node: SchemaNodeControl, fn: () => void | (() => void)): void;
68
+ /**
69
+ * Хук перед размонтированием узла.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * onUnmount(schema.node('wizard'), () => saveDraft(model.get()));
74
+ * ```
75
+ */
76
+ export declare function onUnmount(node: SchemaNodeControl, fn: () => void): void;
@@ -1,14 +1,26 @@
1
1
  import { Signal, ReadonlySignal } from '@preact/signals-core';
2
2
  import { BehaviorCleanup, FormModel, FormProxy } from '../../index';
3
- /** Контекст схемы поведения: модель (значения/сигналы) + форма (ноды). */
3
+ import { SchemaController, SchemaScope } from '../schema-controller';
4
+ /**
5
+ * Область схемы поведения: модель (значения и сигналы), форма (ноды) и схема (узлы разметки).
6
+ *
7
+ * `schema` адресует узлы СВОЕЙ области: в корневом поведении — корневое дерево, в поведении
8
+ * подформы (`apply`) и строки массива (`applyEach`) — поддерево этой части. Правила узлов
9
+ * (`hideWhen`, `onComponentEvent`, `onMount`) исполняет рендерер; без него они ничего не делают.
10
+ */
4
11
  export type BehaviorScope<T> = {
5
12
  model: FormModel<T>;
6
13
  form: FormProxy<T>;
14
+ schema: SchemaScope;
7
15
  };
8
16
  /** Результат {@link defineFormBehavior}; передаётся в `createForm({ behavior })`. */
9
17
  export interface FormBehavior<T> {
10
- /** @internal Запускается `createForm` после построения нод и заполнения реестра сигнал→нода. */
11
- __run(model: FormModel<T>, form: FormProxy<T>): BehaviorCleanup;
18
+ /**
19
+ * @internal Запускается сборкой формы после построения нод и заполнения реестра сигнал→нода.
20
+ * `controller` — схема-контроллер сборки; без него поведение получает собственный, который
21
+ * никто не читает (модель без рендерера).
22
+ */
23
+ __run(model: FormModel<T>, form: FormProxy<T>, controller?: SchemaController): BehaviorCleanup;
12
24
  }
13
25
  export type { Signal, ReadonlySignal, BehaviorCleanup };
14
26
  /** Контекст async-реакции: `signal` аннулируется, когда поле меняется снова до завершения колбэка. */
@@ -27,7 +27,7 @@ export interface CreateFormConfigBase<T, B> {
27
27
  seed?: (model: FormModel<T>) => void;
28
28
  /**
29
29
  * Донастройка ПОСЛЕ сборки. Единственное место для правок, которые обязаны идти следом за
30
- * `createForm`: поле, чьё значение уже массив, ноды не получает (см. `buildModelConfig`),
30
+ * сборкой формы: поле, чьё значение уже массив, ноды не получает (см. `buildModelConfig`),
31
31
  * поэтому такой префилл выполняется здесь.
32
32
  */
33
33
  setup?: (bundle: B) => void;
@@ -41,14 +41,16 @@ export interface CoreForm<T> {
41
41
  /** Конфиг {@link createCoreForm}. */
42
42
  export interface CreateCoreFormConfig<T> extends CreateFormConfigBase<T, CoreForm<T>> {
43
43
  /**
44
- * Билдер схемы формы. Именно функция, а не готовое дерево: листья схемы держат сами сигналы
45
- * модели (`value: model.$.email`), поэтому построить дерево до модели нечем.
44
+ * Билдер схемы формы. Именно функция, а не готовое дерево: узлы схемы держат ручки модели
45
+ * (`model: model.$.email`), поэтому построить дерево до модели нечем.
46
46
  */
47
47
  schema?: (model: FormModel<T>) => FormSchemaNode;
48
48
  }
49
49
  /**
50
50
  * Собрать модель, форму и валидацию за один проход.
51
51
  *
52
+ * @deprecated Пишите `createForm` — та же сборка, в бандле вдобавок дерево для рендерера.
53
+ *
52
54
  * @typeParam T - Форма данных модели.
53
55
  * @param config - {@link CreateCoreFormConfig}: (`initial` | `model`) + опц. `schema`, `behavior`,
54
56
  * `validation`, `seed`, `setup`.
@@ -2,6 +2,7 @@ import { FormProxy, GroupNodeConfig, FormSchema } from './types/index';
2
2
  import { FormSchemaNode } from './types/schema-node';
3
3
  import { FormModel } from '../model/types';
4
4
  import { FormBehavior } from './behaviors';
5
+ import { SchemaController } from './schema-controller';
5
6
  /**
6
7
  * Аргументы createForm под архитектуру M1: данные приходят из {@link FormModel},
7
8
  * конфиг полей (component/componentProps) — из единой схемы.
@@ -21,24 +22,32 @@ export interface CreateFormFromModelArgs<T> {
21
22
  * заполнения реестра сигнал→нода; cleanup живёт на форме и вызывается в `form.dispose()`.
22
23
  */
23
24
  behavior?: FormBehavior<T>;
25
+ /**
26
+ * Схема-контроллер сборки: через него поведение получает `schema` своей области. Его передаёт
27
+ * сборка одним вызовом, чтобы правила узлов (`hideWhen`, `onComponentEvent`) дошли до рендерера.
28
+ * Без него поведение пишет правила в собственный контроллер, который никто не читает.
29
+ */
30
+ schemaController?: SchemaController;
24
31
  }
25
32
  /**
26
33
  * Собрать форму из {@link FormModel} и единой схемы (низкоуровневая фабрика архитектуры M1).
27
34
  *
28
35
  * Значения принадлежат модели (источник истины), ноды формы держат UI/валидационное состояние и
29
36
  * ссылаются на сигналы модели по идентичности (`node.value === model.$.path`). Обходит структуру
30
- * модели, привязывает конфиг поля (component/componentProps) из схемы, материализует
31
- * top-level массивы как {@link ModelArrayNode}, заполняет реестр сигнал→нода (для `enableWhen`/
32
- * роутинга ошибок) и, при наличии, запускает декларативное поведение (cleanup живёт на форме).
37
+ * модели, привязывает конфиг поля (component/componentProps) из схемы, материализует массивы
38
+ * под-форм как {@link ModelArrayNode} (на любой глубине), заполняет реестр ручка→нода (для
39
+ * `enableWhen`, `apply`/`applyEach` и роутинга ошибок) и, при наличии, запускает декларативное
40
+ * поведение (cleanup живёт на форме).
33
41
  *
34
- * Обычно вызывается неявно через {@link createForm} с аргументом `{ model, schema }` — прямой вызов
35
- * нужен редко (например, для построения формы элемента массива).
42
+ * Обычно вызывается сборкой одним вызовом (`createForm`): она строит дерево схемы, зовёт эту
43
+ * фабрику и собирает валидацию. Прямой вызов нужен там, где бандл не нужен, — форма элемента
44
+ * массива, тесты, свой слой сборки.
36
45
  *
37
46
  * @typeParam T - Тип модели данных формы
38
47
  * @param args - Модель, единая схема и (опционально) декларативное поведение {@link CreateFormFromModelArgs}
39
48
  * @returns Типизированная форма с Proxy-доступом к полям {@link FormProxy}
40
49
  *
41
- * @example Форма из модели + схемы (эквивалент `createForm({ model, schema })`)
50
+ * @example Форма из модели + схемы (эквивалент `createFormFromModel({ model, schema })`)
42
51
  * ```typescript
43
52
  * import { createModel, createFormFromModel } from '@reformer/core';
44
53
  *
@@ -65,69 +74,35 @@ export interface CreateFormFromModelArgs<T> {
65
74
  * console.log(model.email); // 'user@mail.com'
66
75
  * ```
67
76
  *
68
- * @see {@link createForm} - основная фабрика (диспетчеризует сюда при аргументе `{ model, schema }`)
77
+ * @see `createForm` — сборка одним вызовом: модель, форма, валидация и дерево для рендера
69
78
  * @group Utilities
70
79
  */
71
80
  export declare function createFormFromModel<T>(args: CreateFormFromModelArgs<T>): FormProxy<T>;
72
81
  /**
73
- * Создать форму из {@link FormModel} + единой схемы (архитектура M1, рекомендуемый путь).
74
- *
75
- * @group Utilities
82
+ * Создать форму из готового конфига группы.
76
83
  *
77
- * @param args - Модель данных, layout-схема (component/componentProps) и (опционально) поведение
84
+ * @param config - Конфиг {@link GroupNode}
78
85
  * @returns Типизированная форма с Proxy-доступом к полям
79
- *
80
- * @example Архитектура M1: `createModel` + layout-схема + `createForm({ model, schema })`
81
- * ```typescript
82
- * import { createModel, createForm } from '@reformer/core';
83
- * import { defineValidationSchema, validate, validateModel } from '@reformer/core/validation';
84
- * import { required, email, minLength } from '@reformer/core/validators';
85
- *
86
- * interface UserForm {
87
- * email: string;
88
- * password: string;
89
- * }
90
- *
91
- * const model = createModel<UserForm>({ email: '', password: '' });
92
- *
93
- * // Layout-схема НЕ несёт validators — только component/componentProps.
94
- * const schema = {
95
- * children: [
96
- * { value: model.$.email, component: Input },
97
- * { value: model.$.password, component: Input },
98
- * ],
99
- * };
100
- *
101
- * const form = createForm<UserForm>({ model, schema });
102
- * form.email.setValue('test@mail.com');
103
- *
104
- * // Валидация — ОТДЕЛЬНАЯ схема, прогон внешним раннером (ошибки сами доезжают до нод):
105
- * const validation = defineValidationSchema<UserForm>(({ model }) => {
106
- * validate(model.$.email, [required(), email()]);
107
- * validate(model.$.password, [required(), minLength(8)]);
108
- * });
109
- * const ok = await validateModel(model, validation);
110
- * ```
86
+ * @group Utilities
111
87
  */
112
- export declare function createForm<T>(args: CreateFormFromModelArgs<T>): FormProxy<T>;
113
- export declare function createForm<T>(config: GroupNodeConfig<T>): FormProxy<T>;
88
+ export declare function createLegacyForm<T>(config: GroupNodeConfig<T>): FormProxy<T>;
114
89
  /**
115
- * Создать форму только со схемой полей.
90
+ * Создать форму из плоской схемы полей с инлайн-значениями (`value: ''`) — путь ДО архитектуры M1.
116
91
  *
117
- * @deprecated Legacy / back-compat: плоская `FormSchema` с инлайн-значениями (`value: ''`) — путь
118
- * ДО архитектуры M1. Для нового кода используйте перегрузку `createForm({ model, schema })`
119
- * (значения принадлежат {@link FormModel}, а правила — отдельной схеме `defineValidationSchema`,
120
- * которую прогоняет `validateModel`; поля `validators` у узла layout-схемы нет).
92
+ * Значения живут в нодах, модели нет: поведение (`defineFormBehavior`) и внешняя валидация
93
+ * (`validateModel`) к такой форме не подключаются. Для нового кода — {@link createFormFromModel}
94
+ * либо сборка одним вызовом.
121
95
  *
122
96
  * @param schema - Схема полей формы
123
97
  * @returns Типизированная форма с Proxy-доступом к полям
98
+ * @group Utilities
124
99
  *
125
- * @example Legacy (back-compat) — плоская схема без модели
100
+ * @example
126
101
  * ```typescript
127
- * const form = createForm<UserForm>({
102
+ * const form = createLegacyForm<UserForm>({
128
103
  * email: { value: '', component: Input },
129
104
  * password: { value: '', component: Input },
130
105
  * });
131
106
  * ```
132
107
  */
133
- export declare function createForm<T>(schema: FormSchema<T>): FormProxy<T>;
108
+ export declare function createLegacyForm<T>(schema: FormSchema<T>): FormProxy<T>;
@@ -0,0 +1,136 @@
1
+ import { FormModel } from '../model/types';
2
+ import { FormBehavior } from './behaviors';
3
+ import { SchemaController, SchemaNodeControl } from './schema-controller';
4
+ import { FormSchemaNode } from './types/schema-node';
5
+ import { FormProxy } from './types/index';
6
+ import { FormValidation, FormValidationBundle } from './validation/config';
7
+ import { ValidationSchema } from './validation';
8
+ /**
9
+ * Дерево, собранное реестром из документа схемы (JSON), и то, что реестр хочет донести до
10
+ * рендерера вместе с ним.
11
+ *
12
+ * @group Utilities
13
+ */
14
+ export interface ResolvedSchema {
15
+ /** Корневой узел дерева. */
16
+ tree: FormSchemaNode;
17
+ /** Обёртка поля — компонент, которым рендерер оборачивает каждое поле. */
18
+ fieldWrapper?: unknown;
19
+ /** Граница ошибок — компонент, которым рендерер оборачивает дерево. */
20
+ errorBoundary?: unknown;
21
+ }
22
+ /**
23
+ * Реестр, умеющий превратить документ схемы в дерево. Метод необязателен: у интерфейса реестра
24
+ * есть сторонние реализации, и ядро документ само не разбирает.
25
+ *
26
+ * @group Utilities
27
+ */
28
+ export interface SchemaResolver {
29
+ resolveSchema?(document: unknown, model: FormModel<never>): ResolvedSchema;
30
+ }
31
+ /**
32
+ * Часть бандла для рендерера: готовое дерево и схема-контроллер сборки.
33
+ *
34
+ * @group Utilities
35
+ */
36
+ export interface FormRender {
37
+ /** Корневой узел дерева схемы; `undefined`, если схема не задана. */
38
+ readonly tree: FormSchemaNode | undefined;
39
+ /** Схема-контроллер сборки: области корня, строк массивов и подформ. */
40
+ readonly controller: SchemaController;
41
+ /** Управление узлом КОРНЕВОЙ области по его `selector`. */
42
+ node(selector: string): SchemaNodeControl;
43
+ /** Обёртка поля из реестра (JSON-вариант). */
44
+ readonly fieldWrapper?: unknown;
45
+ /** Граница ошибок из реестра (JSON-вариант). */
46
+ readonly errorBoundary?: unknown;
47
+ }
48
+ /**
49
+ * Результат {@link createForm}.
50
+ *
51
+ * @typeParam T - Форма данных модели.
52
+ * @group Utilities
53
+ */
54
+ export interface FormBundle<T> {
55
+ model: FormModel<T>;
56
+ form: FormProxy<T>;
57
+ /** Собранная валидация; есть, если в конфиге заданы правила. */
58
+ validation?: FormValidationBundle<T>;
59
+ render: FormRender;
60
+ }
61
+ /**
62
+ * Конфиг {@link createForm}.
63
+ *
64
+ * @typeParam T - Форма данных модели.
65
+ * @group Utilities
66
+ */
67
+ export interface CreateFormConfig<T> {
68
+ /** Начальные значения — из них создаётся модель. Взаимоисключимо с `model`. */
69
+ initial?: T;
70
+ /** Готовая модель (если создана отдельной фабрикой). Приоритетнее `initial`. */
71
+ model?: FormModel<T>;
72
+ /**
73
+ * Схема формы: билдер дерева `(model) => узел` либо документ (JSON) — тогда нужен `registry`.
74
+ * Билдер, а не готовое дерево: узлы держат ручки модели (`model: model.$.email`), и построить
75
+ * дерево до модели нечем.
76
+ */
77
+ schema?: ((model: FormModel<T>) => FormSchemaNode) | object;
78
+ /** Реестр JSON-варианта: превращает документ схемы в дерево. */
79
+ registry?: SchemaResolver;
80
+ /** Поведение формы: значения, состояние нод и правила узлов схемы. */
81
+ behavior?: FormBehavior<T>;
82
+ /** Правила валидации: готовая схема либо {@link FormValidation} со стратегией и шагами. */
83
+ validation?: FormValidation<T> | ValidationSchema<T>;
84
+ /**
85
+ * Правка модели ДО сборки формы. Нужна там, где значение обязано существовать к моменту
86
+ * построения нод — например, массив, наполняемый из вычислений (реактивные `onChange` на
87
+ * инициализации не срабатывают).
88
+ */
89
+ seed?: (model: FormModel<T>) => void;
90
+ /** Донастройка ПОСЛЕ сборки: получает готовый бандл. */
91
+ setup?: (bundle: FormBundle<T>) => void;
92
+ }
93
+ /** Бандл формы, у которой заданы правила: `validation` в нём есть всегда. */
94
+ type ValidatedFormBundle<T> = FormBundle<T> & {
95
+ validation: FormValidationBundle<T>;
96
+ };
97
+ /**
98
+ * Собрать модель, форму, валидацию и дерево для рендера за один проход.
99
+ *
100
+ * @typeParam T - Форма данных модели.
101
+ * @param config - {@link CreateFormConfig}: (`initial` | `model`) + опц. `schema`, `registry`,
102
+ * `behavior`, `validation`, `seed`, `setup`.
103
+ * @returns {@link FormBundle} — `{ model, form, validation?, render }`.
104
+ *
105
+ * @example Рендерер по TS-схеме
106
+ * ```tsx
107
+ * const credit = useFormBundle(() =>
108
+ * createForm<CreditForm>({
109
+ * model: createCreditModel(),
110
+ * schema: creditSchema, // (model) => узел
111
+ * behavior: creditBehavior,
112
+ * validation: { steps: { loan: loanRules }, extras: crossRules },
113
+ * })
114
+ * );
115
+ * return <FormRenderer form={credit} settings={{ fieldWrapper: FormField }} />;
116
+ * ```
117
+ *
118
+ * @example React-руками — та же сборка, разметка в JSX
119
+ * ```tsx
120
+ * const credit = useFormBundle(() => createForm<CreditForm>({ initial, schema: creditSchema }));
121
+ * return <FormField control={credit.form.loanType} />;
122
+ * ```
123
+ *
124
+ * @example JSON — документ схемы и реестр
125
+ * ```tsx
126
+ * const credit = useFormBundle(() =>
127
+ * createForm<CreditForm>({ model: createCreditModel(), schema: creditJson, registry })
128
+ * );
129
+ * return <FormRenderer form={credit} />;
130
+ * ```
131
+ */
132
+ export declare function createForm<T extends object>(config: CreateFormConfig<T> & {
133
+ validation: FormValidation<T> | ValidationSchema<T>;
134
+ }): ValidatedFormBundle<T>;
135
+ export declare function createForm<T extends object>(config: CreateFormConfig<T>): FormBundle<T>;
136
+ export {};
@@ -18,8 +18,13 @@ export { GroupNode } from './nodes/group-node';
18
18
  export { ArrayNode } from './nodes/array-node';
19
19
  export { ModelArrayNode } from './nodes/model-array-node';
20
20
  export type { ModelArrayControl } from './nodes/model-array-node';
21
- export { createForm, createFormFromModel } from './create-form';
21
+ export { createForm } from './form-bundle';
22
+ export type { CreateFormConfig, FormBundle, FormRender, ResolvedSchema, SchemaResolver, } from './form-bundle';
23
+ export { createFormFromModel, createLegacyForm } from './create-form';
22
24
  export type { CreateFormFromModelArgs } from './create-form';
25
+ export { schemaSubtree } from './schema-subtree';
26
+ export { createSchemaController, createSchemaScope, createSchemaOverrideMaps, unknownSchemaSelectors, } from './schema-controller';
27
+ export type { SchemaController, SchemaScope, SchemaNodeControl, SchemaOverrideMaps, NodeLifecycleHooks, } from './schema-controller';
23
28
  export { createCoreForm } from './create-core-form';
24
29
  export type { CoreForm, CreateCoreFormConfig, CreateFormConfigBase } from './create-core-form';
25
30
  export { buildValidation } from './validation/config';
@@ -196,10 +196,10 @@ export declare class FieldNode<T> extends FormNode<T> {
196
196
  *
197
197
  * @example
198
198
  * Пример node-level: плоская схема с инлайн-значениями (back-compat путь до M1). В layout-схеме
199
- * `createForm({ model, schema })` поля `validators` нет — правила живут в `defineValidationSchema`.
199
+ * `createFormFromModel({ model, schema })` поля `validators` нет — правила живут в `defineValidationSchema`.
200
200
  * ```typescript
201
201
  * // Сценарий 1: Instant feedback после submit
202
- * const form = createForm({
202
+ * const form = createLegacyForm({
203
203
  * email: {
204
204
  * value: '',
205
205
  * component: Input,
@@ -7,7 +7,7 @@ import { SubmitOptions, SubmitResult } from '../form-submitter';
7
7
  * GroupNode - узел для группы полей
8
8
  *
9
9
  * Создаётся из {@link FormSchema} (дерево field-конфигов). Обычно строится через `createForm`
10
- * (M1: `createForm({ model, schema })`); schema-валидация/behavior живут на слое модели
10
+ * (M1: `createFormFromModel({ model, schema })`); schema-валидация/behavior живут на слое модели
11
11
  * (`validateModel` из `@reformer/core/validation`, `computeFrom`/`enableWhen`/…), а не на ноде.
12
12
  *
13
13
  * @group Nodes
@@ -7,8 +7,8 @@ import { FormProxy } from '../types/form-proxy';
7
7
  export interface ModelArrayControl<TItem extends object> {
8
8
  readonly length: number;
9
9
  at(index: number): FormModel<TItem> | undefined;
10
- push(item: TItem): void;
11
- insertAt(index: number, item: TItem): void;
10
+ push(item?: TItem): void;
11
+ insertAt(index: number, item?: TItem): void;
12
12
  removeAt(index: number): void;
13
13
  move(from: number, to: number): void;
14
14
  swap(a: number, b: number): void;
@@ -25,7 +25,7 @@ export interface ModelArrayControl<TItem extends object> {
25
25
  * reorder/повторном рендере не пересоздаются (состояние и валидация сохраняются). Реализует тот же
26
26
  * контракт, что ждут секции массива и `useFormControl` (`length`/`value`/`valid`/`errors`/`at`/`push`/…).
27
27
  *
28
- * Обычно создаётся не напрямую, а `createForm({ model, schema })`: когда в схеме встречается узел
28
+ * Обычно создаётся не напрямую, а `createFormFromModel({ model, schema })`: когда в схеме встречается узел
29
29
  * массива `{ array: model.<field>, item: (item) => itemSchema }`, форма материализует его как
30
30
  * `ModelArrayNode` и кладёт под `form.<field>` (совместим с `FormArraySection`).
31
31
  *
@@ -40,7 +40,7 @@ export interface ModelArrayControl<TItem extends object> {
40
40
  * qty: { value: item.$.qty, component: Input },
41
41
  * });
42
42
  *
43
- * const form = createForm({
43
+ * const form = createFormFromModel({
44
44
  * model,
45
45
  * schema: {
46
46
  * children: [{ array: model.rows, item: rowItem }],
@@ -79,13 +79,18 @@ export declare class ModelArrayNode<T extends object> extends FormNode<T[]> {
79
79
  /**
80
80
  * @param control Реактивный массив модели (`model.<path>`).
81
81
  * @param buildItem Строитель формы элемента по его под-модели (инъекция, чтобы избежать цикла
82
- * импорта с `create-form`). Обычно `(item) => createForm({ model: item, schema: itemSchema(item) })`.
82
+ * импорта с `create-form`). Обычно `(item) => createFormFromModel({ model: item, schema: itemSchema(item) })`.
83
83
  */
84
84
  constructor(control: ModelArrayControl<T>, buildItem: (item: FormModel<T>) => FormProxy<T>);
85
85
  at(index: number): FormProxy<T> | undefined;
86
86
  map<R>(fn: (item: FormProxy<T>, index: number) => R): R[];
87
87
  forEach(fn: (item: FormProxy<T>, index: number) => void): void;
88
+ /**
89
+ * Добавить элемент. Без значения — элемент по шаблону массива модели (`arrayOf(blank)` либо
90
+ * `initialValue` узла-массива схемы); шаблона нет — ошибка модели.
91
+ */
88
92
  push(item?: Partial<T>): void;
93
+ /** Вставить элемент по индексу; без значения — по шаблону, как у {@link push}. */
89
94
  insert(index: number, item?: Partial<T>): void;
90
95
  removeAt(index: number): void;
91
96
  /**
@@ -0,0 +1,137 @@
1
+ import { Signal } from '@preact/signals-core';
2
+ /**
3
+ * Хуки жизненного цикла узла схемы. Каждый хук опционален; повторная регистрация перезаписывает
4
+ * предыдущее значение.
5
+ *
6
+ * @group Schema
7
+ */
8
+ export interface NodeLifecycleHooks {
9
+ /** Срабатывает один раз при mount узла. Может вернуть cleanup-функцию. */
10
+ onMount?: () => void | (() => void);
11
+ /** Срабатывает один раз при unmount узла. */
12
+ onUnmount?: () => void;
13
+ }
14
+ /**
15
+ * Хранилище переопределений одной области схемы. Ключ каждой карты — `selector` узла.
16
+ *
17
+ * @group Schema
18
+ */
19
+ export interface SchemaOverrideMaps {
20
+ hiddenOverrides: Map<string, boolean | null>;
21
+ propsOverrides: Map<string, Record<string, unknown> | null>;
22
+ refRegistry: Map<string, {
23
+ current: any;
24
+ }>;
25
+ /** Условия скрытия узлов: selector → реактивное условие. */
26
+ conditionRegistry: Map<string, () => boolean>;
27
+ /** Реактивные эффекты, живущие, пока область смонтирована (`renderEffect`). */
28
+ effectRegistry: Array<() => void | (() => void)>;
29
+ /** Обработчики событий компонентов: selector → { имя пропа → обработчик }. */
30
+ callbackRegistry: Map<string, Map<string, (...args: any[]) => any>>;
31
+ /** Хуки жизненного цикла узла: selector → { onMount, onUnmount }. */
32
+ lifecycleRegistry: Map<string, NodeLifecycleHooks>;
33
+ /** Общий счётчик изменений области. Узлы на него не подписываются — см. `versionFor`. */
34
+ version: Signal<number>;
35
+ /**
36
+ * Версия-сигнал одного селектора (создаётся лениво). Узел подписывается только на сигнал СВОЕГО
37
+ * селектора, поэтому `setHidden` / `patchProps` уведомляют один узел, а не всё дерево.
38
+ */
39
+ versionFor(selector: string): Signal<number>;
40
+ }
41
+ /**
42
+ * Управление одним узлом схемы. Получается через `schema.node(selector)`.
43
+ *
44
+ * @group Schema
45
+ */
46
+ export interface SchemaNodeControl {
47
+ /** Принудительно скрыть или показать узел, минуя условие `hideWhen`. */
48
+ setHidden(value: boolean): this;
49
+ /** Убрать принудительное значение — снова действует условие `hideWhen`. */
50
+ resetHidden(): this;
51
+ /** Подмешать объект в `componentProps` узла. */
52
+ patchProps(partial: Record<string, unknown>): this;
53
+ /** Убрать подмешанные пропсы. */
54
+ resetProps(): this;
55
+ /**
56
+ * Ref на компонент узла. Создаётся один раз на селектор; рендерер вешает его на компонент.
57
+ * Компонент обязан принимать `ref`.
58
+ */
59
+ getRef<H>(): {
60
+ current: H | null;
61
+ };
62
+ /** @internal Селектор узла. */
63
+ __selector: string;
64
+ /** @internal Хранилище области. */
65
+ __overrideMaps: SchemaOverrideMaps;
66
+ }
67
+ /**
68
+ * Схема одной области: адресует узлы по `selector`.
69
+ *
70
+ * @group Schema
71
+ */
72
+ export interface SchemaScope {
73
+ /** Управление узлом области по его `selector`. */
74
+ node(selector: string): SchemaNodeControl;
75
+ /** @internal Хранилище области — его читает рендерер. */
76
+ __overrideMaps: SchemaOverrideMaps;
77
+ }
78
+ /**
79
+ * Схема-контроллер сборки: области по под-моделям.
80
+ *
81
+ * @group Schema
82
+ */
83
+ export interface SchemaController {
84
+ /**
85
+ * Область под-модели: корня сборки, строки массива или группы подформы. Создаётся при первом
86
+ * обращении; повторное обращение отдаёт ту же область.
87
+ */
88
+ scopeOf(subModel: object): SchemaScope;
89
+ }
90
+ /**
91
+ * Пустое хранилище переопределений области.
92
+ *
93
+ * @group Schema
94
+ */
95
+ export declare function createSchemaOverrideMaps(): SchemaOverrideMaps;
96
+ /**
97
+ * Схема области поверх хранилища: `node(selector)` → управление узлом.
98
+ *
99
+ * @param maps - Хранилище области; по умолчанию создаётся новое.
100
+ * @group Schema
101
+ */
102
+ export declare function createSchemaScope(maps?: SchemaOverrideMaps): SchemaScope;
103
+ /**
104
+ * Схема-контроллер одной сборки формы.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const controller = createSchemaController();
109
+ * const root = controller.scopeOf(model);
110
+ * root.node('mortgage').setHidden(true);
111
+ *
112
+ * controller.scopeOf(model.items.at(0)); // область строки — свои селекторы
113
+ * ```
114
+ *
115
+ * @group Schema
116
+ */
117
+ export declare function createSchemaController(): SchemaController;
118
+ /**
119
+ * Селекторы, на которые в области записаны правила, но которых нет в её дереве. Такое правило
120
+ * ничего не делает — сборка и рендерер в dev предупреждают о нём.
121
+ *
122
+ * Ref в проверку не входит: его разрешено брать по абсолютному пути модели
123
+ * (`phones.0.number`), а такой строки при сборке может ещё не быть.
124
+ *
125
+ * @param maps - Хранилище области.
126
+ * @param tree - Дерево (поддерево) области.
127
+ * @returns Селекторы с правилами, не найденные в дереве.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const scope = controller.scopeOf(model);
132
+ * unknownSchemaSelectors(scope.__overrideMaps, tree); // ['mortgage'] — узла с таким selector нет
133
+ * ```
134
+ *
135
+ * @group Schema
136
+ */
137
+ export declare function unknownSchemaSelectors(maps: SchemaOverrideMaps, tree: unknown): string[];