@reformer/renderer-json 6.1.0-beta.1 → 7.0.0

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.
@@ -32,11 +32,11 @@ export interface JsonFormRendererProps<T> {
32
32
  *
33
33
  * @typeParam T - Тип формы.
34
34
  *
35
- * @example
35
+ * @example Форма из JSON-схемы (M1)
36
36
  * ```tsx
37
37
  * import { useMemo } from 'react';
38
- * import { createForm } from '@reformer/core';
39
- * import { Input, FormField } from '@reformer/ui-kit';
38
+ * import { createModel } from '@reformer/core';
39
+ * import { Input, Box, FormField } from '@reformer/ui-kit';
40
40
  * import {
41
41
  * JsonFormRenderer,
42
42
  * JsonRendererProvider,
@@ -45,12 +45,17 @@ export interface JsonFormRendererProps<T> {
45
45
  * type JsonFormSchema,
46
46
  * } from '@reformer/renderer-json';
47
47
  *
48
+ * // Привязки — строки-операторы: '$model(...)', '$component(...)', '$dataSource(...)'.
48
49
  * const schema: JsonFormSchema = {
49
50
  * version: '1.0',
50
51
  * root: {
51
- * component: 'Box',
52
+ * component: '$component(Box)',
52
53
  * children: [
53
- * { selector: 'email', model: 'email', component: 'Input' },
54
+ * {
55
+ * value: '$model(email)',
56
+ * component: '$component(Input)',
57
+ * componentProps: { label: 'Email' },
58
+ * },
54
59
  * ],
55
60
  * },
56
61
  * };
@@ -58,31 +63,26 @@ export interface JsonFormRendererProps<T> {
58
63
  * type MyForm = { email: string };
59
64
  *
60
65
  * function MyFormPage() {
61
- * // form живёт в page-state и передаётся в registry через closure (см. ниже)
62
- * const form = useMemo(() => createForm<MyForm>({
63
- * form: { email: { value: '', component: Input, componentProps: { label: 'Email' } } },
64
- * }), []);
65
- *
66
- * // registry с FormRoot-closure: компоненты, использующие форму, получают её
67
- * // через componentProps closure (JsonFormRenderer НЕ имеет form-prop'а — это by-design,
68
- * // т.к. JSON-схема статична, а форма runtime).
66
+ * // M1: модель источник истины значений; листья схемы биндятся к её сигналам.
67
+ * const model = useMemo(() => createModel<MyForm>({ email: '' }), []);
69
68
  * const registry = useMemo(() => defineRegistry((reg) => {
70
- * reg.field('Input', Input);
71
- * reg.container('Box', ({ children }) => <div>{children}</div>);
72
- * reg.container(FIELD_WRAPPER, FormField);
69
+ * reg.component('Input', Input);
70
+ * reg.component('Box', Box);
71
+ * reg.component(FIELD_WRAPPER, FormField); // системная обёртка полей
73
72
  * }), []);
74
73
  *
74
+ * // Модель передаётся через провайдер (settings.model), НЕ пропом рендерера.
75
75
  * return (
76
- * <JsonRendererProvider settings={{ registry, form }}>
77
- * <JsonFormRenderer schema={schema} />
76
+ * <JsonRendererProvider settings={{ registry, model }}>
77
+ * <JsonFormRenderer<MyForm> schema={schema} validate={import.meta.env.DEV} />
78
78
  * </JsonRendererProvider>
79
79
  * );
80
80
  * }
81
81
  * ```
82
82
  *
83
- * **Note**: `JsonFormRenderer` принимает ТОЛЬКО `{ schema, renderBehavior?, onSchemaReady? }`.
83
+ * **Note**: `JsonFormRenderer` принимает ТОЛЬКО `{ schema, renderBehavior?, onSchemaReady?, validate? }`.
84
84
  * Под M1 модель (`FormModel`) передаётся через {@link JsonRendererProvider} settings (`model`);
85
- * листья JSON-схемы биндятся к её сигналам конвертером `convertJsonToM1Tree`.
85
+ * листья JSON-схемы биндятся к её сигналам конвертером {@link createRenderSchemaFromJsonM1}.
86
86
  *
87
87
  * @see [docs/llms/01-overview.md](../../docs/llms/01-overview.md)
88
88
  */
@@ -4,5 +4,22 @@ export interface SchemaErrorPanelProps {
4
4
  /** Человекочитаемые ошибки (`validateFormSchema().errors`). */
5
5
  errors: string[];
6
6
  }
7
- /** Список ошибок схемы (path + message), с пометкой источника. Без внешних UI-зависимостей. */
7
+ /**
8
+ * Список ошибок схемы (path + message), с пометкой источника. Без внешних UI-зависимостей
9
+ * (инлайн-стили), помечен `role="alert"` и `data-testid="schema-error-panel"`.
10
+ *
11
+ * Обычно рендерится автоматически внутри {@link JsonFormRenderer} (при `validate` и невалидной
12
+ * схеме). Можно использовать напрямую для показа результата `validateFormSchema().errors`.
13
+ *
14
+ * @param props - {@link SchemaErrorPanelProps} (массив `errors`).
15
+ * @returns Панель со списком ошибок.
16
+ *
17
+ * @example Показать ошибки валидации схемы вручную
18
+ * ```tsx
19
+ * import { validateFormSchema } from '@reformer/renderer-json/validate';
20
+ *
21
+ * const { valid, errors } = validateFormSchema(schema, { registry });
22
+ * if (!valid) return <SchemaErrorPanel errors={errors} />;
23
+ * ```
24
+ */
8
25
  export declare function SchemaErrorPanel({ errors }: SchemaErrorPanelProps): ReactNode;
@@ -30,10 +30,17 @@ export interface JsonRendererProviderProps {
30
30
  * Поддерживает вложенность: внутренний провайдер сливается с внешним
31
31
  * (внешний имеет приоритет в случае дублей имён в реестре).
32
32
  *
33
- * @example
33
+ * @example M1: реестр + модель через settings
34
34
  * ```tsx
35
- * <JsonRendererProvider settings={{ registry, fieldWrapper: FormField }}>
36
- * <JsonFormRenderer schema={schema} form={form} />
35
+ * // Модель источник истины (M1); листья схемы биндятся к её сигналам.
36
+ * const model = useMemo(() => createModel<MyForm>({ email: '' }), []);
37
+ * const registry = useMemo(() => defineRegistry((reg) => {
38
+ * reg.component('Input', Input);
39
+ * reg.component(FIELD_WRAPPER, FormField);
40
+ * }), []);
41
+ *
42
+ * <JsonRendererProvider settings={{ registry, model }}>
43
+ * <JsonFormRenderer<MyForm> schema={schema} />
37
44
  * </JsonRendererProvider>
38
45
  * ```
39
46
  */
@@ -3,13 +3,52 @@ import { FormModel } from '@reformer/core';
3
3
  import { JsonFormSchema } from '../types/json-schema';
4
4
  import { ComponentRegistry } from '../registry/types';
5
5
  /**
6
- * Сырое дерево RenderNode из JSON (M1) — для `createForm({ model, schema })`.
6
+ * Сырое дерево RenderNode из JSON (M1) — для `createForm({ model, schema })`. Листья привязываются
7
+ * к сигналам модели (`'$model(path)'` → `model.signalAt`), компоненты/источники — из реестра.
8
+ *
9
+ * @typeParam T - Тип формы (форма данных модели).
10
+ * @param schema - JSON-схема формы ({@link JsonFormSchema}).
11
+ * @param registry - Реестр компонентов/источников (см. {@link defineRegistry}).
12
+ * @param model - Модель данных — источник значений (`FormModel`).
13
+ * @returns Корневой {@link RenderNode} — кладётся в `createForm({ schema })`.
14
+ *
15
+ * @example Собрать форму из JSON-схемы (M1)
16
+ * ```ts
17
+ * import { createForm } from '@reformer/core';
18
+ *
19
+ * const form = createForm<MyForm>({
20
+ * model,
21
+ * schema: convertJsonToM1Tree(jsonSchema, registry, model),
22
+ * behavior,
23
+ * });
24
+ * ```
25
+ *
7
26
  * @group Converter
8
27
  */
9
28
  export declare function convertJsonToM1Tree<T>(schema: JsonFormSchema, registry: ComponentRegistry, model: FormModel<T>): RenderNode<T>;
10
29
  /**
11
30
  * `RenderSchemaFn` из JSON (M1) — для `FormRenderer`/`JsonFormRenderer`. Листья привязываются к
12
31
  * сигналам модели (`'$model(path)'` → `model.signalAt`), компоненты/источники — из реестра.
32
+ *
33
+ * В отличие от {@link convertJsonToM1Tree} (который возвращает готовое дерево), здесь результат —
34
+ * ленивая функция-фабрика дерева, как её ждёт `createRenderSchema`. Обычно вызывается внутри
35
+ * {@link JsonFormRenderer}; напрямую нужен для интеграции с `FormRenderer` без JSON-обёртки.
36
+ *
37
+ * @typeParam T - Тип формы.
38
+ * @param schema - JSON-схема формы ({@link JsonFormSchema}).
39
+ * @param registry - Реестр компонентов/источников (см. {@link defineRegistry}).
40
+ * @param model - Модель данных — источник значений (`FormModel`).
41
+ * @returns `RenderSchemaFn<T>` — фабрика {@link RenderNode}-дерева для `createRenderSchema`.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { createRenderSchema, FormRenderer } from '@reformer/renderer-react';
46
+ *
47
+ * const fn = createRenderSchemaFromJsonM1<MyForm>(jsonSchema, registry, model);
48
+ * const proxy = createRenderSchema<MyForm>(fn);
49
+ * <FormRenderer render={proxy} />;
50
+ * ```
51
+ *
13
52
  * @group Converter
14
53
  */
15
54
  export declare function createRenderSchemaFromJsonM1<T>(schema: JsonFormSchema, registry: ComponentRegistry, model: FormModel<T>): RenderSchemaFn<T>;
@@ -16,13 +16,10 @@ const y = (e) => n(e)?.op === "model", g = (e) => n(e)?.op === "component", S =
16
16
  definitions: $
17
17
  }, O = r;
18
18
  function b(e) {
19
- return e.names().filter((t) => {
20
- const o = e.get(t)?.type;
21
- return o === "field" || o === "container";
22
- });
19
+ return e.names().filter((t) => e.get(t)?.type === "component");
23
20
  }
24
21
  function N(e) {
25
- return e.names().filter((t) => e.get(t)?.type === "source");
22
+ return e.names().filter((t) => e.get(t)?.type === "dataSource");
26
23
  }
27
24
  const h = (e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
25
  function j(e) {
package/dist/index.d.ts CHANGED
@@ -19,4 +19,4 @@ export type { ComponentRegistry, ComponentMetadata, RegistryBuilder } from './re
19
19
  export { JsonRendererProvider, useJsonRendererSettings } from './context/json-renderer-context';
20
20
  export type { JsonRendererSettings, JsonRendererProviderProps, } from './context/json-renderer-context';
21
21
  export { createRenderSchemaFromJsonM1, convertJsonToM1Tree, } from './converter/json-to-render-schema';
22
- export { formSchemaMetaSchema, buildFormSchemaMetaSchema, getComponentNames, getSourceNames, } from './schema';
22
+ export { formSchemaMetaSchema, buildFormSchemaMetaSchema, getComponentNames, getDataSourceNames, } from './schema';
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import { jsx as u, jsxs as M } from "react/jsx-runtime";
2
2
  import { useContext as N, createContext as O, useMemo as g, useState as C, useEffect as F } from "react";
3
3
  import { createRenderSchema as j, FormRenderer as k } from "@reformer/renderer-react";
4
- import { i as f, a as R, p as m, b as A } from "./index-CI7YxPq2.js";
5
- import { c as rr, f as er, g as tr, d as nr } from "./index-CI7YxPq2.js";
4
+ import { i as f, a as R, p as m, b as A } from "./index-BZLcW0SX.js";
5
+ import { c as rr, f as er, g as tr, d as nr } from "./index-BZLcW0SX.js";
6
6
  class h {
7
7
  own = /* @__PURE__ */ new Map();
8
8
  parent = null;
9
9
  get(e) {
10
10
  return this.own.get(e) ?? this.parent?.get(e);
11
11
  }
12
- getSource(e) {
12
+ getDataSource(e) {
13
13
  const t = this.get(e);
14
- if (!(!t || t.type !== "source"))
14
+ if (!(!t || t.type !== "dataSource"))
15
15
  return t.component;
16
16
  }
17
17
  has(e) {
@@ -34,20 +34,17 @@ class h {
34
34
  function K(r) {
35
35
  const e = new h();
36
36
  return r({
37
- field(n, o, i) {
38
- e._set(n, { component: o, type: "field", description: i });
37
+ component(n, o, i) {
38
+ e._set(n, { component: o, type: "component", description: i });
39
39
  },
40
- container(n, o, i) {
41
- e._set(n, { component: o, type: "container", description: i });
42
- },
43
- source(n, o, i) {
44
- e._set(n, { component: o, type: "source", description: i });
40
+ dataSource(n, o, i) {
41
+ e._set(n, { component: o, type: "dataSource", description: i });
45
42
  }
46
43
  }), e;
47
44
  }
48
- const v = "$fieldWrapper", S = O({});
45
+ const v = "$fieldWrapper", w = O({});
49
46
  function Q({ settings: r, children: e }) {
50
- const t = N(S), n = g(() => t.registry && r.registry ? h.withParent(t.registry, r.registry) : r.registry ?? t.registry, [t.registry, r.registry]), o = g(() => {
47
+ const t = N(w), n = g(() => t.registry && r.registry ? h.withParent(t.registry, r.registry) : r.registry ?? t.registry, [t.registry, r.registry]), o = g(() => {
51
48
  const i = n?.get(v)?.component;
52
49
  return {
53
50
  ...t,
@@ -56,10 +53,10 @@ function Q({ settings: r, children: e }) {
56
53
  fieldWrapper: r.fieldWrapper ?? i ?? t.fieldWrapper
57
54
  };
58
55
  }, [t, r, n]);
59
- return /* @__PURE__ */ u(S.Provider, { value: o, children: e });
56
+ return /* @__PURE__ */ u(w.Provider, { value: o, children: e });
60
57
  }
61
- function W() {
62
- return N(S);
58
+ function D() {
59
+ return N(w);
63
60
  }
64
61
  function $(r) {
65
62
  const e = r;
@@ -68,7 +65,7 @@ function $(r) {
68
65
  function E(r) {
69
66
  return f(r.value);
70
67
  }
71
- function D(r) {
68
+ function W(r) {
72
69
  return R(r.component) && !E(r) && !$(r);
73
70
  }
74
71
  function P(r, e) {
@@ -80,8 +77,8 @@ function P(r, e) {
80
77
  throw new Error(
81
78
  `Component "${t}" not found in registry. Available: ${e.names().join(", ")}`
82
79
  );
83
- if (n.type === "source")
84
- throw new Error(`Entry "${t}" is a 'source' and cannot be used as $component(...)`);
80
+ if (n.type === "dataSource")
81
+ throw new Error(`Entry "${t}" is a 'dataSource' and cannot be used as $component(...)`);
85
82
  return n.component;
86
83
  }
87
84
  function L(r, e) {
@@ -92,22 +89,22 @@ function L(r, e) {
92
89
  );
93
90
  return t.component;
94
91
  }
95
- function _(r, e) {
92
+ function I(r, e) {
96
93
  return e.split(".").reduce((t, n) => t == null ? t : t[n], r);
97
94
  }
98
- function I(r) {
95
+ function V(r) {
99
96
  if (r === null || typeof r != "object") return !1;
100
97
  const e = r;
101
98
  return f(e.value) || f(e.array) || R(e.component);
102
99
  }
103
- function V(r) {
100
+ function _(r) {
104
101
  return r == null ? r : JSON.parse(JSON.stringify(r));
105
102
  }
106
103
  function b(r, e, t) {
107
104
  if (A(r)) return L(m(r).arg, t);
108
105
  if (R(r)) return P(r, t);
109
106
  if (f(r)) return e.signalAt(m(r).arg);
110
- if (I(r)) return p(r, e, t);
107
+ if (V(r)) return p(r, e, t);
111
108
  if (Array.isArray(r)) return r.map((n) => b(n, e, t));
112
109
  if (r !== null && typeof r == "object") {
113
110
  const n = r, o = {};
@@ -116,7 +113,7 @@ function b(r, e, t) {
116
113
  }
117
114
  return r;
118
115
  }
119
- function w(r, e, t) {
116
+ function S(r, e, t) {
120
117
  if (!r) return;
121
118
  const n = {};
122
119
  for (const o of Object.keys(r)) n[o] = b(r[o], e, t);
@@ -124,13 +121,13 @@ function w(r, e, t) {
124
121
  }
125
122
  function p(r, e, t) {
126
123
  if ($(r)) {
127
- const n = _(e, m(r.array).arg), o = r.item.$template, i = r.initialValue;
124
+ const n = I(e, m(r.array).arg), o = r.item.$template, i = r.initialValue;
128
125
  return {
129
126
  ...r.selector ? { selector: r.selector } : {},
130
127
  array: n,
131
- initialValue: () => i ? V(i) : {},
128
+ initialValue: () => i ? _(i) : {},
132
129
  item: (y) => p(o, y, t),
133
- componentProps: w(r.componentProps, e, t)
130
+ componentProps: S(r.componentProps, e, t)
134
131
  };
135
132
  }
136
133
  if (E(r)) {
@@ -139,14 +136,14 @@ function p(r, e, t) {
139
136
  ...r.selector ? { selector: r.selector } : {},
140
137
  value: o,
141
138
  component: P(r.component, t),
142
- componentProps: w(r.componentProps, e, t)
139
+ componentProps: S(r.componentProps, e, t)
143
140
  };
144
141
  }
145
- if (D(r))
142
+ if (W(r))
146
143
  return {
147
144
  ...r.selector ? { selector: r.selector } : {},
148
145
  component: P(r.component, t),
149
- componentProps: w(r.componentProps, e, t),
146
+ componentProps: S(r.componentProps, e, t),
150
147
  children: r.children?.map((n) => p(n, e, t))
151
148
  };
152
149
  throw new Error(`Invalid JSON node (M1): ${JSON.stringify(r)}`);
@@ -188,7 +185,7 @@ function X({
188
185
  onSchemaReady: t,
189
186
  validate: n = !1
190
187
  }) {
191
- const { registry: o, model: i, ...y } = W(), [a, d] = C(
188
+ const { registry: o, model: i, ...y } = D(), [a, d] = C(
192
189
  n ? void 0 : null
193
190
  );
194
191
  F(() => {
@@ -231,13 +228,13 @@ export {
231
228
  K as defineRegistry,
232
229
  er as formSchemaMetaSchema,
233
230
  tr as getComponentNames,
234
- nr as getSourceNames,
231
+ nr as getDataSourceNames,
235
232
  $ as isArrayNode,
236
233
  R as isComponentOp,
237
- D as isContainerNode,
234
+ W as isContainerNode,
238
235
  A as isDataSourceOp,
239
236
  E as isFieldNode,
240
237
  f as isModelOp,
241
238
  m as parseOperator,
242
- W as useJsonRendererSettings
239
+ D as useJsonRendererSettings
243
240
  };
@@ -4,8 +4,8 @@
4
4
  * JSON нельзя «вызвать функцию», поэтому привязки кодируются СТРОКАМИ функц-стиля с дискриминатором:
5
5
  *
6
6
  * - `"$model(path)"` — путь к полю/массиву модели: лист → `model.signalAt(path)`, массив → `model.<path>`.
7
- * - `"$component(Name)"` — имя компонента в реестре (`reg.field`/`reg.container`).
8
- * - `"$dataSource(NAME)"` — имя registry-source (`reg.source`): options/itemLabel/константы/loading-компоненты.
7
+ * - `"$component(Name)"` — имя компонента в реестре (`reg.component`).
8
+ * - `"$dataSource(NAME)"` — имя registry-source (`reg.dataSource`): options/itemLabel/константы/loading-компоненты.
9
9
  *
10
10
  * Схема остаётся чистым JSON (копируется в `.json`, приходит строкой с сервера). Типобезопасность
11
11
  * на этапе компиляции даётся template-literal типами ({@link ModelOp} и т.д.) — литерал
@@ -30,6 +30,9 @@ export interface ParsedOperator {
30
30
  * Разбор строки-оператора `"$op(arg)"`. Возвращает `null` для не-операторов (обычных строк),
31
31
  * чтобы вызывающий оставил значение как есть.
32
32
  *
33
+ * @param value - Проверяемое значение (не-строки сразу дают `null`).
34
+ * @returns {@link ParsedOperator} (`{ op, arg }`) либо `null`, если строка — не оператор.
35
+ *
33
36
  * @example
34
37
  * ```ts
35
38
  * parseOperator('$model(loanType)'); // { op: 'model', arg: 'loanType' }
@@ -38,9 +41,45 @@ export interface ParsedOperator {
38
41
  * ```
39
42
  */
40
43
  export declare function parseOperator(value: unknown): ParsedOperator | null;
41
- /** Type-guard: строка — `"$model(...)"`. */
44
+ /**
45
+ * Type-guard: строка — оператор `"$model(...)"` (привязка к полю/массиву модели).
46
+ *
47
+ * @param v - Проверяемое значение.
48
+ * @returns `true`, если `v` — {@link ModelOp}.
49
+ *
50
+ * @example Сузить тип значения prop до ModelOp
51
+ * ```ts
52
+ * if (isModelOp(value)) {
53
+ * const path = parseOperator(value)!.arg; // 'loanType'
54
+ * }
55
+ * ```
56
+ */
42
57
  export declare const isModelOp: (v: unknown) => v is ModelOp;
43
- /** Type-guard: строка — `"$component(...)"`. */
58
+ /**
59
+ * Type-guard: строка — оператор `"$component(...)"` (ссылка на компонент реестра).
60
+ *
61
+ * @param v - Проверяемое значение.
62
+ * @returns `true`, если `v` — {@link ComponentOp}.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * if (isComponentOp(node.component)) {
67
+ * const name = parseOperator(node.component)!.arg; // 'Select'
68
+ * }
69
+ * ```
70
+ */
44
71
  export declare const isComponentOp: (v: unknown) => v is ComponentOp;
45
- /** Type-guard: строка — `"$dataSource(...)"`. */
72
+ /**
73
+ * Type-guard: строка — оператор `"$dataSource(...)"` (ссылка на registry-source).
74
+ *
75
+ * @param v - Проверяемое значение.
76
+ * @returns `true`, если `v` — {@link DataSourceOp}.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * if (isDataSourceOp(props.options)) {
81
+ * const name = parseOperator(props.options)!.arg; // 'LOAN_TYPES'
82
+ * }
83
+ * ```
84
+ */
46
85
  export declare const isDataSourceOp: (v: unknown) => v is DataSourceOp;
@@ -3,7 +3,7 @@ export declare class ComponentRegistryImpl implements ComponentRegistry {
3
3
  private own;
4
4
  private parent;
5
5
  get(name: string): ComponentMetadata | undefined;
6
- getSource<T = unknown>(name: string): T | undefined;
6
+ getDataSource<T = unknown>(name: string): T | undefined;
7
7
  has(name: string): boolean;
8
8
  names(): string[];
9
9
  _set(name: string, metadata: ComponentMetadata): void;
@@ -15,7 +15,7 @@ export declare class ComponentRegistryImpl implements ComponentRegistry {
15
15
  * Реестр обязателен для работы {@link JsonFormRenderer} — иначе компоненты,
16
16
  * упомянутые в JSON-схеме, не отрезолвятся.
17
17
  *
18
- * @param fn - Builder-callback. Получает {@link RegistryBuilder} с методами `field`, `container`, `source`.
18
+ * @param fn - Builder-callback. Получает {@link RegistryBuilder} с методами `component`, `dataSource`.
19
19
  * @returns Готовый {@link ComponentRegistry}, который кладётся в `JsonRendererProvider`.
20
20
  *
21
21
  * @example
@@ -24,10 +24,10 @@ export declare class ComponentRegistryImpl implements ComponentRegistry {
24
24
  * import { Input, Select, FormField } from '@reformer/ui-kit';
25
25
  *
26
26
  * const registry = defineRegistry((reg) => {
27
- * reg.field('Input', Input);
28
- * reg.field('Select', Select);
29
- * reg.container(FIELD_WRAPPER, FormField);
30
- * reg.source('LOAN_TYPES', [
27
+ * reg.component('Input', Input);
28
+ * reg.component('Select', Select);
29
+ * reg.component(FIELD_WRAPPER, FormField);
30
+ * reg.dataSource('LOAN_TYPES', [
31
31
  * { value: 'consumer', label: 'Потребительский' },
32
32
  * { value: 'mortgage', label: 'Ипотека' },
33
33
  * ]);
@@ -10,7 +10,7 @@
10
10
  * import { FormField } from '@reformer/ui-kit';
11
11
  *
12
12
  * const registry = defineRegistry((reg) => {
13
- * reg.container(FIELD_WRAPPER, FormField);
13
+ * reg.component(FIELD_WRAPPER, FormField);
14
14
  * });
15
15
  * ```
16
16
  */
@@ -1,18 +1,18 @@
1
1
  import { ComponentType } from 'react';
2
2
  /**
3
- * Запись реестра. Хранит сам компонент (или source-значение) и его роль.
3
+ * Запись реестра. Хранит сам компонент (или dataSource-значение) и его роль.
4
4
  *
5
5
  * @typeParam P - Пропсы компонента (для `field`/`container`).
6
6
  *
7
7
  * @example
8
8
  * ```typescript
9
9
  * const meta: ComponentMetadata = registry.get('Input')!;
10
- * meta.type; // 'field'
10
+ * meta.type; // 'component'
11
11
  * ```
12
12
  */
13
13
  export interface ComponentMetadata<P = any> {
14
14
  component: ComponentType<P> | unknown;
15
- type: 'field' | 'container' | 'source';
15
+ type: 'component' | 'dataSource';
16
16
  description?: string;
17
17
  }
18
18
  /**
@@ -31,7 +31,7 @@ export interface ComponentMetadata<P = any> {
31
31
  */
32
32
  export interface ComponentRegistry {
33
33
  get(name: string): ComponentMetadata | undefined;
34
- getSource<T = unknown>(name: string): T | undefined;
34
+ getDataSource<T = unknown>(name: string): T | undefined;
35
35
  has(name: string): boolean;
36
36
  names(): string[];
37
37
  }
@@ -41,14 +41,20 @@ export interface ComponentRegistry {
41
41
  * @example
42
42
  * ```typescript
43
43
  * defineRegistry((reg: RegistryBuilder) => {
44
- * reg.field('Input', Input);
45
- * reg.container('Box', Box);
46
- * reg.source('LOAN_TYPES', LOAN_TYPES);
44
+ * reg.component('Input', Input);
45
+ * reg.component('Box', Box);
46
+ * reg.dataSource('LOAN_TYPES', LOAN_TYPES);
47
47
  * });
48
48
  * ```
49
49
  */
50
50
  export interface RegistryBuilder {
51
- field<P>(name: string, component: ComponentType<P>, description?: string): void;
52
- container<P>(name: string, component: ComponentType<P>, description?: string): void;
53
- source<T>(name: string, value: T, description?: string): void;
51
+ /**
52
+ * Зарегистрировать React-компонент под именем доступен в схеме как
53
+ * `$component(name)`. Одним методом регистрируются и компоненты-листья
54
+ * (Input/Select), и контейнеры (Box/Section/FormField): роль узла (лист vs
55
+ * контейнер) определяется структурой узла схемы (`value` vs `children`),
56
+ * а не регистрацией.
57
+ */
58
+ component<P>(name: string, component: ComponentType<P>, description?: string): void;
59
+ dataSource<T>(name: string, value: T, description?: string): void;
54
60
  }
@@ -1,10 +1,42 @@
1
1
  import { ComponentRegistry } from '../registry/types';
2
- /** Базовая мета-схема form-DSL (draft-07): структура + синтаксис операторов, имена не ограничены. */
2
+ /**
3
+ * Базовая мета-схема form-DSL (draft-07): структура узлов + синтаксис операторов, имена компонентов
4
+ * НЕ ограничены (паттерн `$component(...)` открыт). Для сужения до конкретного реестра —
5
+ * {@link buildFormSchemaMetaSchema}. Используется как `$schema` в IDE и как база валидатора.
6
+ *
7
+ * @example Подключить как `$schema` в JSON-файле схемы
8
+ * ```ts
9
+ * // record можно записать в файл и сослаться на него из "$schema" JSON-схемы.
10
+ * formSchemaMetaSchema.$schema; // 'http://json-schema.org/draft-07/schema#'
11
+ * ```
12
+ */
3
13
  export declare const formSchemaMetaSchema: Record<string, unknown>;
4
- /** Имена компонентов реестра (`reg.field`/`reg.container`). */
14
+ /**
15
+ * Имена компонентов реестра (тип `component`) — то, что валидно в `$component(...)`.
16
+ *
17
+ * @param registry - Реестр (см. {@link defineRegistry}).
18
+ * @returns Массив имён компонентов.
19
+ *
20
+ * @example Сузить мета-схему до компонентов реестра
21
+ * ```ts
22
+ * const names = getComponentNames(registry); // ['Input', 'Select', 'Box', ...]
23
+ * const schema = buildFormSchemaMetaSchema({ componentNames: names });
24
+ * ```
25
+ */
5
26
  export declare function getComponentNames(registry: ComponentRegistry): string[];
6
- /** Имена registry-source (`reg.source`): options/itemLabel/константы/loading-компоненты. */
7
- export declare function getSourceNames(registry: ComponentRegistry): string[];
27
+ /**
28
+ * Имена registry-dataSource (`reg.dataSource`) — то, что валидно в `$dataSource(...)`:
29
+ * options/itemLabel/константы/loading-компоненты.
30
+ *
31
+ * @param registry - Реестр (см. {@link defineRegistry}).
32
+ * @returns Массив имён источников.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * getDataSourceNames(registry); // ['LOAN_TYPES', 'GENDERS', 'LoadingState', ...]
37
+ * ```
38
+ */
39
+ export declare function getDataSourceNames(registry: ComponentRegistry): string[];
8
40
  /**
9
41
  * Конкретная мета-схема: базовая + (если заданы `componentNames`) сужение паттерна `$component(...)`
10
42
  * до enum имён (`^\$component\((Input|Select|…)\)$`). `$dataSource`-имена JSON Schema'й не покрыть
@@ -68,9 +68,49 @@ export interface JsonFormSchema {
68
68
  /** Корневой узел. */
69
69
  root: JsonNode;
70
70
  }
71
- /** Type-guard: узел — массив (`array: '$model(...)'` + `item.$template`). Проверять ПЕРВЫМ. */
71
+ /**
72
+ * Type-guard: узел — массив (`array: '$model(...)'` + `item.$template`). Проверять ПЕРВЫМ
73
+ * (лист/контейнер отсеиваются после, т.к. массив тоже несёт `$model`).
74
+ *
75
+ * @param node - Узел JSON-схемы.
76
+ * @returns `true`, если узел — {@link JsonArrayNode}.
77
+ *
78
+ * @example Сузить тип узла перед доступом к `item.$template`
79
+ * ```ts
80
+ * if (isArrayNode(node)) {
81
+ * node.array; // ModelOp
82
+ * node.item.$template; // JsonNode
83
+ * }
84
+ * ```
85
+ */
72
86
  export declare function isArrayNode(node: JsonNode): node is JsonArrayNode;
73
- /** Type-guard: узел — лист (`value: '$model(...)'`). */
87
+ /**
88
+ * Type-guard: узел — лист (`value: '$model(...)'`).
89
+ *
90
+ * @param node - Узел JSON-схемы.
91
+ * @returns `true`, если узел — {@link JsonFieldNode}.
92
+ *
93
+ * @example Сузить тип узла перед доступом к `value`/`component`
94
+ * ```ts
95
+ * if (isFieldNode(node)) {
96
+ * node.value; // ModelOp
97
+ * node.component; // ComponentOp | undefined
98
+ * }
99
+ * ```
100
+ */
74
101
  export declare function isFieldNode(node: JsonNode): node is JsonFieldNode;
75
- /** Type-guard: узел — контейнер (`component: '$component(...)'`, без `value`/`array`). */
102
+ /**
103
+ * Type-guard: узел — контейнер (`component: '$component(...)'`, без `value`/`array`).
104
+ *
105
+ * @param node - Узел JSON-схемы.
106
+ * @returns `true`, если узел — {@link JsonContainerNode}.
107
+ *
108
+ * @example Сузить тип узла перед обходом `children`
109
+ * ```ts
110
+ * if (isContainerNode(node)) {
111
+ * node.component; // ComponentOp
112
+ * node.children?.forEach(walk);
113
+ * }
114
+ * ```
115
+ */
76
116
  export declare function isContainerNode(node: JsonNode): node is JsonContainerNode;
@@ -9,10 +9,16 @@ export interface FormSchemaValidationResult {
9
9
  export interface ValidateFormSchemaOptions {
10
10
  registry?: ComponentRegistry;
11
11
  componentNames?: string[];
12
- sourceNames?: string[];
12
+ dataSourceNames?: string[];
13
13
  }
14
14
  /**
15
- * Валидирует form-DSL JSON-схему. Если передан `registry`, имена компонентов/source берутся из него.
15
+ * Валидирует form-DSL JSON-схему. Если передан `registry`, имена компонентов/source берутся из него
16
+ * (иначе можно задать `componentNames`/`dataSourceNames` явно). Тянет `ajv` — живёт в subpath
17
+ * `@reformer/renderer-json/validate`, чтобы не попадать в основной render-бандл.
18
+ *
19
+ * @param schema - Проверяемая JSON-схема (обычно {@link JsonFormSchema}, но принимает `unknown`).
20
+ * @param opts - {@link ValidateFormSchemaOptions}: `registry` либо явные списки имён.
21
+ * @returns {@link FormSchemaValidationResult} — `{ valid, errors }` (ошибки — путь + сообщение).
16
22
  *
17
23
  * @example
18
24
  * ```ts
package/dist/validate.js CHANGED
@@ -1,4 +1,4 @@
1
- import { g as an, d as on, f as un, p as cn } from "./index-CI7YxPq2.js";
1
+ import { g as an, d as on, f as un, p as cn } from "./index-BZLcW0SX.js";
2
2
  function ln(e) {
3
3
  return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
4
4
  }
@@ -4456,7 +4456,7 @@ function qt(e, r, S, d, g) {
4456
4456
  qt(f, r ? `${r}.${s}` : s, S, d, g);
4457
4457
  }
4458
4458
  function gs(e, r = {}) {
4459
- const S = r.componentNames ?? (r.registry ? an(r.registry) : void 0), d = r.sourceNames ?? (r.registry ? on(r.registry) : void 0), g = [], f = new vs({ allErrors: !0 }).compile(un);
4459
+ const S = r.componentNames ?? (r.registry ? an(r.registry) : void 0), d = r.dataSourceNames ?? (r.registry ? on(r.registry) : void 0), g = [], f = new vs({ allErrors: !0 }).compile(un);
4460
4460
  if (!f(e))
4461
4461
  for (const c of f.errors ?? [])
4462
4462
  g.push(`${c.instancePath || "/"} ${c.message ?? "invalid"}`.trim());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reformer/renderer-json",
3
- "version": "6.1.0-beta.1",
3
+ "version": "7.0.0",
4
4
  "description": "JSON-based form renderer for @reformer ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",