@reformer/renderer-json 1.0.0-beta.1

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.
@@ -0,0 +1,80 @@
1
+ import { ReactNode } from 'react';
2
+ import { RenderBehaviorFn, RenderSchemaProxy } from '@reformer/renderer-react';
3
+ import { JsonFormSchema } from '../types/json-schema';
4
+ /**
5
+ * Props of {@link JsonFormRenderer}.
6
+ *
7
+ * @typeParam T - Тип формы (`getReformerForm<T>()`).
8
+ */
9
+ export interface JsonFormRendererProps<T> {
10
+ /** JSON-схема формы. См. {@link JsonFormSchema}. */
11
+ schema: JsonFormSchema;
12
+ /** Опциональный behavior: hideWhen/patchProps/onComponentEvent поверх готовой схемы. */
13
+ renderBehavior?: RenderBehaviorFn<T>;
14
+ /** Колбэк, получающий построенный `RenderSchemaProxy` для внешних манипуляций. */
15
+ onSchemaReady?: (schema: RenderSchemaProxy<T>) => void;
16
+ }
17
+ /**
18
+ * Главный компонент пакета. Рендерит форму, описанную JSON-схемой.
19
+ *
20
+ * Должен использоваться внутри {@link JsonRendererProvider}, который снабжает рендерер
21
+ * реестром компонентов. Без реестра компонент бросит исключение при попытке резолва.
22
+ *
23
+ * @typeParam T - Тип формы.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * import { useMemo } from 'react';
28
+ * import { createForm } from '@reformer/core';
29
+ * import { Input, FormField } from '@reformer/ui-kit';
30
+ * import {
31
+ * JsonFormRenderer,
32
+ * JsonRendererProvider,
33
+ * defineRegistry,
34
+ * FIELD_WRAPPER,
35
+ * type JsonFormSchema,
36
+ * } from '@reformer/renderer-json';
37
+ *
38
+ * const schema: JsonFormSchema = {
39
+ * version: '1.0',
40
+ * root: {
41
+ * component: 'Box',
42
+ * children: [
43
+ * { selector: 'email', model: 'email', component: 'Input' },
44
+ * ],
45
+ * },
46
+ * };
47
+ *
48
+ * type MyForm = { email: string };
49
+ *
50
+ * function MyFormPage() {
51
+ * // form живёт в page-state и передаётся в registry через closure (см. ниже)
52
+ * const form = useMemo(() => createForm<MyForm>({
53
+ * form: { email: { value: '', component: Input, componentProps: { label: 'Email' } } },
54
+ * }), []);
55
+ *
56
+ * // registry с FormRoot-closure: компоненты, использующие форму, получают её
57
+ * // через componentProps closure (JsonFormRenderer НЕ имеет form-prop'а — это by-design,
58
+ * // т.к. JSON-схема статична, а форма runtime).
59
+ * const registry = useMemo(() => defineRegistry((reg) => {
60
+ * reg.field('Input', Input);
61
+ * reg.container('Box', ({ children }) => <div>{children}</div>);
62
+ * reg.container(FIELD_WRAPPER, FormField);
63
+ * }), []);
64
+ *
65
+ * return (
66
+ * <JsonRendererProvider settings={{ registry, form }}>
67
+ * <JsonFormRenderer schema={schema} />
68
+ * </JsonRendererProvider>
69
+ * );
70
+ * }
71
+ * ```
72
+ *
73
+ * **Note**: `JsonFormRenderer` принимает ТОЛЬКО `{ schema, renderBehavior?, onSchemaReady? }`.
74
+ * Форма передаётся в render-pipeline через {@link JsonRendererProvider} settings или через
75
+ * closure-pattern с {@link createRenderSchemaFromJson} + `FormRoot.__selfManagedChildren = true`
76
+ * (см. recipe `multi-step-from-json` в `docs/llms/`).
77
+ *
78
+ * @see [docs/llms/01-overview.md](../../docs/llms/01-overview.md)
79
+ */
80
+ export declare function JsonFormRenderer<T>({ schema, renderBehavior, onSchemaReady, }: JsonFormRendererProps<T>): ReactNode;
@@ -0,0 +1,51 @@
1
+ import { ReactNode } from 'react';
2
+ import { RendererSettings } from '@reformer/renderer-react';
3
+ import { ComponentRegistry } from '../registry/types';
4
+ /**
5
+ * Расширенные настройки рендерера: всё из `RendererSettings` плюс реестр.
6
+ */
7
+ export interface JsonRendererSettings extends RendererSettings {
8
+ /** Реестр компонентов и source-значений. См. {@link defineRegistry}. */
9
+ registry?: ComponentRegistry;
10
+ }
11
+ /**
12
+ * Props {@link JsonRendererProvider}.
13
+ */
14
+ export interface JsonRendererProviderProps {
15
+ /** Настройки рендерера, как минимум содержащие `registry`. */
16
+ settings: JsonRendererSettings;
17
+ /** Дочернее поддерево, в котором доступен `JsonFormRenderer` и `useJsonRendererSettings`. */
18
+ children: ReactNode;
19
+ }
20
+ /**
21
+ * Провайдер настроек для {@link JsonFormRenderer}. Прокидывает реестр и
22
+ * fieldWrapper во вложенные компоненты через React Context.
23
+ *
24
+ * Поддерживает вложенность: внутренний провайдер сливается с внешним
25
+ * (внешний имеет приоритет в случае дублей имён в реестре).
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <JsonRendererProvider settings={{ registry, fieldWrapper: FormField }}>
30
+ * <JsonFormRenderer schema={schema} form={form} />
31
+ * </JsonRendererProvider>
32
+ * ```
33
+ */
34
+ export declare function JsonRendererProvider({ settings, children }: JsonRendererProviderProps): ReactNode;
35
+ /**
36
+ * Хук для чтения текущих настроек {@link JsonRendererProvider}.
37
+ *
38
+ * В режиме разработки бросает исключение, если вызван вне провайдера или
39
+ * если в провайдере не передан `registry`.
40
+ *
41
+ * @returns Текущие {@link JsonRendererSettings}.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * function MyControl() {
46
+ * const { registry } = useJsonRendererSettings();
47
+ * return <span>{registry?.has('Input') ? 'ready' : 'not registered'}</span>;
48
+ * }
49
+ * ```
50
+ */
51
+ export declare function useJsonRendererSettings(): JsonRendererSettings;
@@ -0,0 +1,31 @@
1
+ import { RenderSchemaFn } from '@reformer/renderer-react';
2
+ import { JsonFormSchema } from '../types/json-schema';
3
+ import { ComponentRegistry } from '../registry/types';
4
+ /**
5
+ * Создаёт RenderSchemaFn из JSON-схемы
6
+ *
7
+ * Конвертирует JSON-схему в функцию RenderSchemaFn, которую можно
8
+ * передать в FormRenderer из @reformer/renderer-react.
9
+ *
10
+ * @param schema - JSON-схема формы
11
+ * @param registry - Реестр компонентов для резолва имён
12
+ * @returns RenderSchemaFn для FormRenderer
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const jsonSchema = {
17
+ * root: {
18
+ * component: 'Box',
19
+ * children: [
20
+ * { model: 'email' },
21
+ * { model: 'password', component: 'InputPassword' }
22
+ * ]
23
+ * }
24
+ * };
25
+ *
26
+ * const renderSchema = createRenderSchemaFromJson(jsonSchema, registry);
27
+ *
28
+ * <FormRenderer render={renderSchema} settings={{ fieldWrapper: FormField }} />
29
+ * ```
30
+ */
31
+ export declare function createRenderSchemaFromJson<T>(schema: JsonFormSchema, registry: ComponentRegistry): RenderSchemaFn<T>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @reformer/renderer-json
3
+ *
4
+ * JSON-based form renderer for @reformer ecosystem
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ export { JsonFormRenderer } from './components/json-form-renderer';
9
+ export type { JsonFormRendererProps } from './components/json-form-renderer';
10
+ export type { JsonFormSchema, JsonNode } from './types/json-schema';
11
+ export { isFieldNode, isContainerNode } from './types/json-schema';
12
+ export { defineRegistry } from './registry/component-registry';
13
+ export { FIELD_WRAPPER } from './registry/constants';
14
+ export type { ComponentRegistry, ComponentMetadata, RegistryBuilder } from './registry/types';
15
+ export { JsonRendererProvider, useJsonRendererSettings } from './context/json-renderer-context';
16
+ export type { JsonRendererSettings, JsonRendererProviderProps, } from './context/json-renderer-context';
17
+ export { createRenderSchemaFromJson } from './converter/json-to-render-schema';
package/dist/index.js ADDED
@@ -0,0 +1,212 @@
1
+ import { jsx as y } from "react/jsx-runtime";
2
+ import { useContext as g, createContext as w, useMemo as m, createElement as P } from "react";
3
+ import { RenderNodeComponent as R, createRenderSchema as C, FormRenderer as F } from "@reformer/renderer-react";
4
+ import { createFieldPath as $ } from "@reformer/core";
5
+ class u {
6
+ own = /* @__PURE__ */ new Map();
7
+ parent = null;
8
+ get(e) {
9
+ return this.own.get(e) ?? this.parent?.get(e);
10
+ }
11
+ getSource(e) {
12
+ const r = this.get(e);
13
+ if (!(!r || r.type !== "source"))
14
+ return r.component;
15
+ }
16
+ has(e) {
17
+ return this.own.has(e) || (this.parent?.has(e) ?? !1);
18
+ }
19
+ names() {
20
+ const e = this.parent?.names() ?? [], r = Array.from(this.own.keys());
21
+ return [.../* @__PURE__ */ new Set([...e, ...r])];
22
+ }
23
+ _set(e, r) {
24
+ this.own.has(e) && console.warn(`[ComponentRegistry] Overwriting entry: ${e}`), this.own.set(e, r);
25
+ }
26
+ static withParent(e, r) {
27
+ const n = new u();
28
+ return n.parent = e, r.own.forEach((o, i) => {
29
+ n.own.set(i, o);
30
+ }), n;
31
+ }
32
+ }
33
+ function _(t) {
34
+ const e = new u();
35
+ return t({
36
+ field(n, o, i) {
37
+ e._set(n, { component: o, type: "field", description: i });
38
+ },
39
+ container(n, o, i) {
40
+ e._set(n, { component: o, type: "container", description: i });
41
+ },
42
+ source(n, o, i) {
43
+ e._set(n, { component: o, type: "source", description: i });
44
+ }
45
+ }), e;
46
+ }
47
+ const b = "$fieldWrapper", l = w({});
48
+ function M({ settings: t, children: e }) {
49
+ const r = g(l), n = m(() => r.registry && t.registry ? u.withParent(r.registry, t.registry) : t.registry ?? r.registry, [r.registry, t.registry]), o = m(() => {
50
+ const i = n?.get(b)?.component;
51
+ return {
52
+ ...r,
53
+ ...t,
54
+ registry: n,
55
+ fieldWrapper: t.fieldWrapper ?? i ?? r.fieldWrapper
56
+ };
57
+ }, [r, t, n]);
58
+ return /* @__PURE__ */ y(l.Provider, { value: o, children: e });
59
+ }
60
+ function E() {
61
+ return g(l);
62
+ }
63
+ function h(t, e) {
64
+ const r = e.split(/\.|\[|\]/).filter(Boolean);
65
+ let n = t;
66
+ for (const o of r) {
67
+ if (n == null)
68
+ throw new Error(`Invalid field path: "${e}" - segment "${o}" not found`);
69
+ const i = parseInt(o, 10);
70
+ isNaN(i) ? n = n[o] : n = n[i];
71
+ }
72
+ if (n == null)
73
+ throw new Error(`Invalid field path: "${e}" - path resolved to null/undefined`);
74
+ return n;
75
+ }
76
+ function p(t) {
77
+ if (t === null || typeof t != "object") return !1;
78
+ const e = t;
79
+ return typeof e.model == "string" || typeof e.component == "string";
80
+ }
81
+ function S(t) {
82
+ return t !== null && typeof t == "object" && p(t.$template);
83
+ }
84
+ function x(t, e) {
85
+ const r = $(), n = s(t, r, e), o = ({ control: i }) => P(R, { node: n, form: i });
86
+ return o.displayName = "TemplateItemFC", o;
87
+ }
88
+ function a(t, e, r) {
89
+ if (typeof t == "string") {
90
+ const n = r.get(t);
91
+ if (n && n.type === "source") {
92
+ const o = n.component;
93
+ return typeof o == "function" ? (...i) => {
94
+ const c = o(...i);
95
+ return p(c) ? s(c, i[0], r) : c;
96
+ } : o;
97
+ }
98
+ return t;
99
+ }
100
+ if (S(t))
101
+ return x(t.$template, r);
102
+ if (p(t))
103
+ return s(t, e, r);
104
+ if (Array.isArray(t))
105
+ return t.map((n) => a(n, e, r));
106
+ if (typeof t == "function")
107
+ return (...n) => {
108
+ const o = t(...n);
109
+ return p(o) ? s(o, n[0], r) : o;
110
+ };
111
+ if (t !== null && typeof t == "object") {
112
+ const n = t, o = {};
113
+ let i = !1;
114
+ for (const c of Object.keys(n)) {
115
+ const f = a(n[c], e, r);
116
+ f !== n[c] && (i = !0), o[c] = f;
117
+ }
118
+ return i ? o : t;
119
+ }
120
+ return t;
121
+ }
122
+ function d(t, e, r) {
123
+ const n = {};
124
+ for (const o of Object.keys(t)) {
125
+ const i = t[o];
126
+ if ((o === "control" || o.endsWith("Control")) && typeof i == "string") {
127
+ n[o] = h(e, i);
128
+ continue;
129
+ }
130
+ if ((o === "component" || o.endsWith("Component")) && typeof i == "string") {
131
+ const c = r.get(i);
132
+ if (c) {
133
+ n[o] = c.component;
134
+ continue;
135
+ }
136
+ }
137
+ n[o] = a(i, e, r);
138
+ }
139
+ return n;
140
+ }
141
+ function I(t, e) {
142
+ return typeof t.model == "string" && t.model.length > 0 ? t.model : typeof t.selector == "string" && typeof t.component == "string" && !t.children && e.get(t.component)?.type === "field" ? t.selector : null;
143
+ }
144
+ function s(t, e, r) {
145
+ const n = I(t, r);
146
+ if (n !== null) {
147
+ const i = {
148
+ component: h(e, n)
149
+ };
150
+ return t.selector && (i.selector = t.selector), t.componentProps && (i.componentProps = d(
151
+ t.componentProps,
152
+ e,
153
+ r
154
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
+ )), i;
156
+ }
157
+ if (t.component) {
158
+ const o = r.get(t.component);
159
+ if (!o)
160
+ throw new Error(
161
+ `Component "${t.component}" not found in registry. Available components: ${r.names().join(", ")}`
162
+ );
163
+ if (o.type === "source")
164
+ throw new Error(
165
+ `Entry "${t.component}" is a 'source' and cannot be used as component`
166
+ );
167
+ const i = {
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
+ component: o.component
170
+ };
171
+ return t.selector && (i.selector = t.selector), t.componentProps && (i.componentProps = d(
172
+ t.componentProps,
173
+ e,
174
+ r
175
+ )), t.children && t.children.length > 0 && (i.children = t.children.map((c) => s(c, e, r))), i;
176
+ }
177
+ throw new Error(
178
+ `Invalid JSON node: must have either "model" (for fields) or "component" (for containers). Got: ${JSON.stringify(t)}`
179
+ );
180
+ }
181
+ function J(t, e) {
182
+ return (r) => s(t.root, r, e);
183
+ }
184
+ function L({
185
+ schema: t,
186
+ renderBehavior: e,
187
+ onSchemaReady: r
188
+ }) {
189
+ const { registry: n, ...o } = E(), i = m(() => {
190
+ const c = J(t, n), f = C(c);
191
+ return e && e(f), f;
192
+ }, [t, n, e]);
193
+ return m(() => {
194
+ r && r(i);
195
+ }, [i]), /* @__PURE__ */ y(F, { render: i, settings: o });
196
+ }
197
+ function W(t) {
198
+ return typeof t.model == "string" && t.model.length > 0;
199
+ }
200
+ function V(t) {
201
+ return typeof t.component == "string" && !W(t);
202
+ }
203
+ export {
204
+ b as FIELD_WRAPPER,
205
+ L as JsonFormRenderer,
206
+ M as JsonRendererProvider,
207
+ J as createRenderSchemaFromJson,
208
+ _ as defineRegistry,
209
+ V as isContainerNode,
210
+ W as isFieldNode,
211
+ E as useJsonRendererSettings
212
+ };
@@ -0,0 +1,39 @@
1
+ import { ComponentRegistry, ComponentMetadata, RegistryBuilder } from './types';
2
+ export declare class ComponentRegistryImpl implements ComponentRegistry {
3
+ private own;
4
+ private parent;
5
+ get(name: string): ComponentMetadata | undefined;
6
+ getSource<T = unknown>(name: string): T | undefined;
7
+ has(name: string): boolean;
8
+ names(): string[];
9
+ _set(name: string, metadata: ComponentMetadata): void;
10
+ static withParent(parent: ComponentRegistry, child: ComponentRegistry): ComponentRegistry;
11
+ }
12
+ /**
13
+ * Создаёт реестр компонентов через builder-callback.
14
+ *
15
+ * Реестр обязателен для работы {@link JsonFormRenderer} — иначе компоненты,
16
+ * упомянутые в JSON-схеме, не отрезолвятся.
17
+ *
18
+ * @param fn - Builder-callback. Получает {@link RegistryBuilder} с методами `field`, `container`, `source`.
19
+ * @returns Готовый {@link ComponentRegistry}, который кладётся в `JsonRendererProvider`.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { defineRegistry, FIELD_WRAPPER } from '@reformer/renderer-json';
24
+ * import { Input, Select, FormField } from '@reformer/ui-kit';
25
+ *
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', [
31
+ * { value: 'consumer', label: 'Потребительский' },
32
+ * { value: 'mortgage', label: 'Ипотека' },
33
+ * ]);
34
+ * });
35
+ * ```
36
+ *
37
+ * @see [docs/llms/03-registry.md](../../docs/llms/03-registry.md)
38
+ */
39
+ export declare function defineRegistry(fn: (reg: RegistryBuilder) => void): ComponentRegistry;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Зарезервированный ключ реестра для контейнера-обёртки полей.
3
+ *
4
+ * Зарегистрируй компонент под этим именем (обычно `FormField` из `@reformer/ui-kit`),
5
+ * чтобы каждое поле получало label, error и hint автоматически.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { defineRegistry, FIELD_WRAPPER } from '@reformer/renderer-json';
10
+ * import { FormField } from '@reformer/ui-kit';
11
+ *
12
+ * const registry = defineRegistry((reg) => {
13
+ * reg.container(FIELD_WRAPPER, FormField);
14
+ * });
15
+ * ```
16
+ */
17
+ export declare const FIELD_WRAPPER = "$fieldWrapper";
@@ -0,0 +1,54 @@
1
+ import { ComponentType } from 'react';
2
+ /**
3
+ * Запись реестра. Хранит сам компонент (или source-значение) и его роль.
4
+ *
5
+ * @typeParam P - Пропсы компонента (для `field`/`container`).
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const meta: ComponentMetadata = registry.get('Input')!;
10
+ * meta.type; // 'field'
11
+ * ```
12
+ */
13
+ export interface ComponentMetadata<P = any> {
14
+ component: ComponentType<P> | unknown;
15
+ type: 'field' | 'container' | 'source';
16
+ description?: string;
17
+ }
18
+ /**
19
+ * Read-only API реестра, доступное в рантайме.
20
+ *
21
+ * Создаётся через {@link defineRegistry}. Передаётся в `JsonRendererProvider`
22
+ * через `settings.registry`.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * if (registry.has('Input')) {
27
+ * registry.get('Input'); // ComponentMetadata
28
+ * }
29
+ * registry.names(); // ['Input', 'Box', 'LOAN_TYPES', ...]
30
+ * ```
31
+ */
32
+ export interface ComponentRegistry {
33
+ get(name: string): ComponentMetadata | undefined;
34
+ getSource<T = unknown>(name: string): T | undefined;
35
+ has(name: string): boolean;
36
+ names(): string[];
37
+ }
38
+ /**
39
+ * Builder, который попадает в callback {@link defineRegistry}.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * defineRegistry((reg: RegistryBuilder) => {
44
+ * reg.field('Input', Input);
45
+ * reg.container('Box', Box);
46
+ * reg.source('LOAN_TYPES', LOAN_TYPES);
47
+ * });
48
+ * ```
49
+ */
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;
54
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Типы для JSON-схемы формы
3
+ *
4
+ * @module reformer/renderer-json/types
5
+ */
6
+ /**
7
+ * Унифицированный узел JSON-схемы
8
+ *
9
+ * Может представлять:
10
+ * - Поле формы (если указан `model`)
11
+ * - Контейнер/компонент (если указан только `component`)
12
+ * - Поле с кастомным компонентом (если указаны оба)
13
+ *
14
+ * @example
15
+ * ```json
16
+ * // Поле формы с дефолтным компонентом
17
+ * { "model": "email" }
18
+ *
19
+ * // Поле с кастомным компонентом
20
+ * { "model": "password", "component": "InputPassword" }
21
+ *
22
+ * // Контейнер
23
+ * { "component": "Box", "children": [...] }
24
+ * ```
25
+ */
26
+ export interface JsonNode {
27
+ /**
28
+ * Идентификатор узла для renderBehavior (hideWhen, patchProps).
29
+ * Необязателен — нужен только если к узлу привязано поведение.
30
+ */
31
+ selector?: string;
32
+ /**
33
+ * Имя компонента из реестра: "Input", "Box", "Section", "Select"
34
+ *
35
+ * - Для полей: компонент для рендеринга значения (опционально, по умолчанию Input)
36
+ * - Для контейнеров: React-компонент контейнера (обязательно)
37
+ */
38
+ component?: string;
39
+ /**
40
+ * Путь к полю формы (модели): "email", "personalData.firstName", "addresses[0].city"
41
+ *
42
+ * Если указан — узел представляет поле формы.
43
+ */
44
+ model?: string;
45
+ /**
46
+ * Props для компонента (className, title и т.д.)
47
+ */
48
+ componentProps?: Record<string, unknown>;
49
+ /**
50
+ * Дочерние узлы (для контейнеров)
51
+ */
52
+ children?: JsonNode[];
53
+ /**
54
+ * Обёртка для узла (например, fieldWrapper)
55
+ *
56
+ * @example
57
+ * ```json
58
+ * {
59
+ * "model": "email",
60
+ * "wrapper": {
61
+ * "component": "FormField",
62
+ * "componentProps": { "className": "col-span-2" }
63
+ * }
64
+ * }
65
+ * ```
66
+ */
67
+ wrapper?: JsonNode;
68
+ }
69
+ /**
70
+ * Корневая JSON-схема формы
71
+ *
72
+ * @example
73
+ * ```json
74
+ * {
75
+ * "version": "1.0",
76
+ * "root": {
77
+ * "component": "Box",
78
+ * "componentProps": { "className": "space-y-4" },
79
+ * "children": [
80
+ * { "model": "email" },
81
+ * { "model": "password", "component": "InputPassword" }
82
+ * ]
83
+ * }
84
+ * }
85
+ * ```
86
+ */
87
+ export interface JsonFormSchema {
88
+ /**
89
+ * Версия схемы (для миграций)
90
+ */
91
+ version?: string;
92
+ /**
93
+ * Корневой узел схемы
94
+ */
95
+ root: JsonNode;
96
+ }
97
+ /**
98
+ * Type guard: проверяет, является ли узел полем формы (имеет `model`).
99
+ *
100
+ * @param node - Узел JSON-схемы.
101
+ * @returns `true`, если у узла есть непустое `model`.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * if (isFieldNode(node)) {
106
+ * // node.model гарантированно строка
107
+ * readField(node.model);
108
+ * }
109
+ * ```
110
+ */
111
+ export declare function isFieldNode(node: JsonNode): boolean;
112
+ /**
113
+ * Type guard: проверяет, является ли узел контейнером (только `component`, без `model`).
114
+ *
115
+ * @param node - Узел JSON-схемы.
116
+ * @returns `true`, если у узла есть `component`, но нет `model`.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * if (isContainerNode(node)) {
121
+ * node.children?.forEach(walk);
122
+ * }
123
+ * ```
124
+ */
125
+ export declare function isContainerNode(node: JsonNode): boolean;
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@reformer/renderer-json",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "JSON-based form renderer for @reformer ecosystem",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "sideEffects": false,
16
+ "scripts": {
17
+ "generate:llms": "node ../../scripts/generate-llms-txt .",
18
+ "build": "npm run generate:llms && vite build",
19
+ "build:stackblitz": "vite build",
20
+ "dev": "vite",
21
+ "test": "node ../../scripts/run-vitest.mjs",
22
+ "test:watch": "vitest watch"
23
+ },
24
+ "keywords": [
25
+ "react",
26
+ "forms",
27
+ "reformer",
28
+ "renderer",
29
+ "json",
30
+ "typescript"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "author": "Alexandr Bukhtatyy",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/AlexandrBukhtatyy/ReFormer.git",
40
+ "directory": "packages/reformer-renderer-json"
41
+ },
42
+ "homepage": "https://alexandrbukhtatyy.github.io/ReFormer/",
43
+ "bugs": {
44
+ "url": "https://github.com/AlexandrBukhtatyy/ReFormer/issues"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "files": [
50
+ "dist",
51
+ "README.md",
52
+ "LICENSE"
53
+ ],
54
+ "peerDependencies": {
55
+ "@reformer/core": ">=1.1.0",
56
+ "@reformer/renderer-react": ">=1.0.0",
57
+ "@reformer/ui-kit": ">=1.0.0",
58
+ "react": "^18.0.0 || ^19.0.0",
59
+ "react-dom": "^18.0.0 || ^19.0.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "@reformer/ui-kit": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "devDependencies": {
67
+ "@reformer/core": "*",
68
+ "@reformer/renderer-react": "*",
69
+ "@reformer/ui-kit": "*",
70
+ "@types/node": "^24.10.1",
71
+ "@types/react": "^19.2.7",
72
+ "@types/react-dom": "^19.2.3",
73
+ "@vitejs/plugin-react": "^5.1.0",
74
+ "@vitest/utils": "^4.0.8",
75
+ "react": "^19.2.1",
76
+ "react-dom": "^19.2.1",
77
+ "typescript": "^5.9.3",
78
+ "vite": "^7.2.2",
79
+ "vite-plugin-dts": "^4.5.4",
80
+ "vitest": "^4.0.8"
81
+ }
82
+ }