@reformer/renderer-json 4.0.1 → 6.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.
@@ -13,6 +13,16 @@ export interface JsonFormRendererProps<T> {
13
13
  renderBehavior?: RenderBehaviorFn<T>;
14
14
  /** Колбэк, получающий построенный `RenderSchemaProxy` для внешних манипуляций. */
15
15
  onSchemaReady?: (schema: RenderSchemaProxy<T>) => void;
16
+ /**
17
+ * Валидировать JSON-схему против мета-схемы перед рендером. При ошибках рисует
18
+ * {@link SchemaErrorPanel} вместо формы. ajv грузится **динамически** (`import('../validate')`) —
19
+ * в prod-бандл не попадает, пока `validate` не включён.
20
+ *
21
+ * По умолчанию `false`. Чтобы валидировать только в dev, приложение передаёт значение из
22
+ * СВОЕГО окружения: `validate={import.meta.env.DEV}` — детекцию dev нельзя «запечь» в пакет,
23
+ * т.к. `import.meta.env.DEV` инлайнится в `false` при production-сборке самого пакета.
24
+ */
25
+ validate?: boolean;
16
26
  }
17
27
  /**
18
28
  * Главный компонент пакета. Рендерит форму, описанную JSON-схемой.
@@ -71,10 +81,9 @@ export interface JsonFormRendererProps<T> {
71
81
  * ```
72
82
  *
73
83
  * **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/`).
84
+ * Под M1 модель (`FormModel`) передаётся через {@link JsonRendererProvider} settings (`model`);
85
+ * листья JSON-схемы биндятся к её сигналам конвертером `convertJsonToM1Tree`.
77
86
  *
78
87
  * @see [docs/llms/01-overview.md](../../docs/llms/01-overview.md)
79
88
  */
80
- export declare function JsonFormRenderer<T>({ schema, renderBehavior, onSchemaReady, }: JsonFormRendererProps<T>): ReactNode;
89
+ export declare function JsonFormRenderer<T>({ schema, renderBehavior, onSchemaReady, validate, }: JsonFormRendererProps<T>): ReactNode;
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from 'react';
2
+ /** Props of {@link SchemaErrorPanel}. */
3
+ export interface SchemaErrorPanelProps {
4
+ /** Человекочитаемые ошибки (`validateFormSchema().errors`). */
5
+ errors: string[];
6
+ }
7
+ /** Список ошибок схемы (path + message), с пометкой источника. Без внешних UI-зависимостей. */
8
+ export declare function SchemaErrorPanel({ errors }: SchemaErrorPanelProps): ReactNode;
@@ -1,5 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { RendererSettings } from '@reformer/renderer-react';
3
+ import { FormModel } from '@reformer/core';
3
4
  import { ComponentRegistry } from '../registry/types';
4
5
  /**
5
6
  * Расширенные настройки рендерера: всё из `RendererSettings` плюс реестр.
@@ -7,6 +8,11 @@ import { ComponentRegistry } from '../registry/types';
7
8
  export interface JsonRendererSettings extends RendererSettings {
8
9
  /** Реестр компонентов и source-значений. См. {@link defineRegistry}. */
9
10
  registry?: ComponentRegistry;
11
+ /**
12
+ * Модель данных (M1, единая схема). Если задана — JSON-листья биндятся к сигналам модели
13
+ * (`model.signalAt(selector)`), а форма строится из той же JSON-схемы (без отдельной схемы формы).
14
+ */
15
+ model?: FormModel<any>;
10
16
  }
11
17
  /**
12
18
  * Props {@link JsonRendererProvider}.
@@ -1,31 +1,15 @@
1
- import { RenderSchemaFn } from '@reformer/renderer-react';
1
+ import { RenderSchemaFn, RenderNode } from '@reformer/renderer-react';
2
+ import { FormModel } from '@reformer/core';
2
3
  import { JsonFormSchema } from '../types/json-schema';
3
4
  import { ComponentRegistry } from '../registry/types';
4
5
  /**
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
- * ```
6
+ * Сырое дерево RenderNode из JSON (M1) — для `createForm({ model, schema })`.
7
+ * @group Converter
30
8
  */
31
- export declare function createRenderSchemaFromJson<T>(schema: JsonFormSchema, registry: ComponentRegistry): RenderSchemaFn<T>;
9
+ export declare function convertJsonToM1Tree<T>(schema: JsonFormSchema, registry: ComponentRegistry, model: FormModel<T>): RenderNode<T>;
10
+ /**
11
+ * `RenderSchemaFn` из JSON (M1) — для `FormRenderer`/`JsonFormRenderer`. Листья привязываются к
12
+ * сигналам модели (`'$model(path)'` → `model.signalAt`), компоненты/источники — из реестра.
13
+ * @group Converter
14
+ */
15
+ export declare function createRenderSchemaFromJsonM1<T>(schema: JsonFormSchema, registry: ComponentRegistry, model: FormModel<T>): RenderSchemaFn<T>;
@@ -0,0 +1,45 @@
1
+ const a = /^\$(model|component|dataSource)\((.+)\)$/;
2
+ function n(e) {
3
+ if (typeof e != "string") return null;
4
+ const t = a.exec(e);
5
+ return t ? { op: t[1], arg: t[2] } : null;
6
+ }
7
+ const y = (e) => n(e)?.op === "model", g = (e) => n(e)?.op === "component", S = (e) => n(e)?.op === "dataSource", s = "http://json-schema.org/draft-07/schema#", p = "https://reformer.dev/schemas/form-schema.schema.json", c = "ReFormer JSON form schema (M1, string-operator DSL)", m = "Base meta-schema: validates node structure + operator string syntax ($model/$component/$dataSource). Component-name enum is tightened per-app via buildFormSchemaMetaSchema; $dataSource names and $model paths are checked outside this schema.", d = "object", f = ["root"], l = !1, u = { $schema: { type: "string" }, version: { type: "string" }, root: { $ref: "#/definitions/node" } }, $ = { modelOp: { type: "string", pattern: "^\\$model\\(.+\\)$", description: 'Model binding: "$model(path)" — path resolved at runtime (not validated here).' }, componentOp: { type: "string", pattern: "^\\$component\\(.+\\)$", description: 'Registry component: "$component(Name)". Name is tightened to a registry enum by buildFormSchemaMetaSchema.' }, node: { oneOf: [{ $ref: "#/definitions/fieldNode" }, { $ref: "#/definitions/arrayNode" }, { $ref: "#/definitions/containerNode" }] }, fieldNode: { type: "object", required: ["value"], additionalProperties: !1, properties: { selector: { type: "string" }, value: { $ref: "#/definitions/modelOp" }, component: { $ref: "#/definitions/componentOp" }, componentProps: { type: "object" }, wrapper: { $ref: "#/definitions/node" } } }, arrayNode: { type: "object", required: ["array", "item"], additionalProperties: !1, properties: { selector: { type: "string" }, array: { $ref: "#/definitions/modelOp" }, item: { type: "object", required: ["$template"], additionalProperties: !1, properties: { $template: { $ref: "#/definitions/node" } } }, initialValue: { type: "object" }, componentProps: { type: "object" } } }, containerNode: { type: "object", required: ["component"], additionalProperties: !1, properties: { selector: { type: "string" }, component: { $ref: "#/definitions/componentOp" }, componentProps: { type: "object" }, children: { type: "array", items: { $ref: "#/definitions/node" } } } } }, r = {
8
+ $schema: s,
9
+ $id: p,
10
+ title: c,
11
+ description: m,
12
+ type: d,
13
+ required: f,
14
+ additionalProperties: l,
15
+ properties: u,
16
+ definitions: $
17
+ }, O = r;
18
+ function b(e) {
19
+ return e.names().filter((t) => {
20
+ const o = e.get(t)?.type;
21
+ return o === "field" || o === "container";
22
+ });
23
+ }
24
+ function N(e) {
25
+ return e.names().filter((t) => e.get(t)?.type === "source");
26
+ }
27
+ const h = (e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ function j(e) {
29
+ const t = JSON.parse(JSON.stringify(r)), o = e?.componentNames;
30
+ if (o && o.length > 0) {
31
+ const i = o.map(h).join("|");
32
+ t.definitions.componentOp.pattern = `^\\$component\\((${i})\\)$`;
33
+ }
34
+ return t;
35
+ }
36
+ export {
37
+ g as a,
38
+ S as b,
39
+ j as c,
40
+ N as d,
41
+ O as f,
42
+ b as g,
43
+ y as i,
44
+ n as p
45
+ };
package/dist/index.d.ts CHANGED
@@ -7,11 +7,16 @@
7
7
  */
8
8
  export { JsonFormRenderer } from './components/json-form-renderer';
9
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';
10
+ export { SchemaErrorPanel } from './components/schema-error-panel';
11
+ export type { SchemaErrorPanelProps } from './components/schema-error-panel';
12
+ export type { JsonFormSchema, JsonNode, JsonFieldNode, JsonArrayNode, JsonContainerNode, } from './types/json-schema';
13
+ export { isFieldNode, isArrayNode, isContainerNode } from './types/json-schema';
14
+ export { parseOperator, isModelOp, isComponentOp, isDataSourceOp } from './operators';
15
+ export type { ModelOp, ComponentOp, DataSourceOp, JsonOperator, ParsedOperator } from './operators';
12
16
  export { defineRegistry } from './registry/component-registry';
13
17
  export { FIELD_WRAPPER } from './registry/constants';
14
18
  export type { ComponentRegistry, ComponentMetadata, RegistryBuilder } from './registry/types';
15
19
  export { JsonRendererProvider, useJsonRendererSettings } from './context/json-renderer-context';
16
20
  export type { JsonRendererSettings, JsonRendererProviderProps, } from './context/json-renderer-context';
17
- export { createRenderSchemaFromJson } from './converter/json-to-render-schema';
21
+ export { createRenderSchemaFromJsonM1, convertJsonToM1Tree, } from './converter/json-to-render-schema';
22
+ export { formSchemaMetaSchema, buildFormSchemaMetaSchema, getComponentNames, getSourceNames, } from './schema';
package/dist/index.js CHANGED
@@ -1,38 +1,39 @@
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 {
1
+ import { jsx as u, jsxs as M } from "react/jsx-runtime";
2
+ import { useContext as N, createContext as O, useMemo as g, useState as C, useEffect as F } from "react";
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";
6
+ class h {
6
7
  own = /* @__PURE__ */ new Map();
7
8
  parent = null;
8
9
  get(e) {
9
10
  return this.own.get(e) ?? this.parent?.get(e);
10
11
  }
11
12
  getSource(e) {
12
- const r = this.get(e);
13
- if (!(!r || r.type !== "source"))
14
- return r.component;
13
+ const t = this.get(e);
14
+ if (!(!t || t.type !== "source"))
15
+ return t.component;
15
16
  }
16
17
  has(e) {
17
18
  return this.own.has(e) || (this.parent?.has(e) ?? !1);
18
19
  }
19
20
  names() {
20
- const e = this.parent?.names() ?? [], r = Array.from(this.own.keys());
21
- return [.../* @__PURE__ */ new Set([...e, ...r])];
21
+ const e = this.parent?.names() ?? [], t = Array.from(this.own.keys());
22
+ return [.../* @__PURE__ */ new Set([...e, ...t])];
22
23
  }
23
- _set(e, r) {
24
- this.own.has(e) && console.warn(`[ComponentRegistry] Overwriting entry: ${e}`), this.own.set(e, r);
24
+ _set(e, t) {
25
+ this.own.has(e) && console.warn(`[ComponentRegistry] Overwriting entry: ${e}`), this.own.set(e, t);
25
26
  }
26
- static withParent(e, r) {
27
- const n = new u();
28
- return n.parent = e, r.own.forEach((o, i) => {
27
+ static withParent(e, t) {
28
+ const n = new h();
29
+ return n.parent = e, t.own.forEach((o, i) => {
29
30
  n.own.set(i, o);
30
31
  }), n;
31
32
  }
32
33
  }
33
- function _(t) {
34
- const e = new u();
35
- return t({
34
+ function K(r) {
35
+ const e = new h();
36
+ return r({
36
37
  field(n, o, i) {
37
38
  e._set(n, { component: o, type: "field", description: i });
38
39
  },
@@ -44,169 +45,199 @@ function _(t) {
44
45
  }
45
46
  }), e;
46
47
  }
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;
48
+ const v = "$fieldWrapper", S = O({});
49
+ 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(() => {
51
+ const i = n?.get(v)?.component;
51
52
  return {
52
- ...r,
53
53
  ...t,
54
+ ...r,
54
55
  registry: n,
55
- fieldWrapper: t.fieldWrapper ?? i ?? r.fieldWrapper
56
+ fieldWrapper: r.fieldWrapper ?? i ?? t.fieldWrapper
56
57
  };
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];
58
+ }, [t, r, n]);
59
+ return /* @__PURE__ */ u(S.Provider, { value: o, children: e });
60
+ }
61
+ function W() {
62
+ return N(S);
63
+ }
64
+ function $(r) {
65
+ const e = r;
66
+ return f(e.array) && typeof e.item == "object" && e.item !== null && "$template" in e.item;
67
+ }
68
+ function E(r) {
69
+ return f(r.value);
70
+ }
71
+ function D(r) {
72
+ return R(r.component) && !E(r) && !$(r);
73
+ }
74
+ function P(r, e) {
75
+ if (!r) return;
76
+ const t = m(r)?.arg;
77
+ if (!t) throw new Error(`Invalid $component operator: "${r}"`);
78
+ const n = e.get(t);
79
+ if (!n)
80
+ throw new Error(
81
+ `Component "${t}" not found in registry. Available: ${e.names().join(", ")}`
82
+ );
83
+ if (n.type === "source")
84
+ throw new Error(`Entry "${t}" is a 'source' and cannot be used as $component(...)`);
85
+ return n.component;
86
+ }
87
+ function L(r, e) {
88
+ const t = e.get(r);
89
+ if (!t)
90
+ throw new Error(
91
+ `Data source "${r}" not found in registry. Available: ${e.names().join(", ")}`
92
+ );
93
+ return t.component;
94
+ }
95
+ function _(r, e) {
96
+ return e.split(".").reduce((t, n) => t == null ? t : t[n], r);
97
+ }
98
+ function I(r) {
99
+ if (r === null || typeof r != "object") return !1;
100
+ const e = r;
101
+ return f(e.value) || f(e.array) || R(e.component);
102
+ }
103
+ function V(r) {
104
+ return r == null ? r : JSON.parse(JSON.stringify(r));
105
+ }
106
+ function b(r, e, t) {
107
+ if (A(r)) return L(m(r).arg, t);
108
+ if (R(r)) return P(r, t);
109
+ if (f(r)) return e.signalAt(m(r).arg);
110
+ if (I(r)) return p(r, e, t);
111
+ if (Array.isArray(r)) return r.map((n) => b(n, e, t));
112
+ if (r !== null && typeof r == "object") {
113
+ const n = r, o = {};
114
+ for (const i of Object.keys(n)) o[i] = b(n[i], e, t);
115
+ return o;
71
116
  }
72
- if (n == null)
73
- throw new Error(`Invalid field path: "${e}" - path resolved to null/undefined`);
74
- return n;
117
+ return r;
75
118
  }
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) {
119
+ function w(r, e, t) {
120
+ if (!r) return;
123
121
  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
- }
122
+ for (const o of Object.keys(r)) n[o] = b(r[o], e, t);
139
123
  return n;
140
124
  }
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)
125
+ function p(r, e, t) {
126
+ if ($(r)) {
127
+ const n = _(e, m(r.array).arg), o = r.item.$template, i = r.initialValue;
128
+ return {
129
+ ...r.selector ? { selector: r.selector } : {},
130
+ array: n,
131
+ initialValue: () => i ? V(i) : {},
132
+ item: (y) => p(o, y, t),
133
+ componentProps: w(r.componentProps, e, t)
149
134
  };
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
135
  }
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
136
+ if (E(r)) {
137
+ const n = m(r.value).arg, o = e.signalAt(n);
138
+ return !o && typeof console < "u" && console.warn(`[JsonRenderer/M1] No model signal for "${n}".`), {
139
+ ...r.selector ? { selector: r.selector } : {},
140
+ value: o,
141
+ component: P(r.component, t),
142
+ componentProps: w(r.componentProps, e, t)
170
143
  };
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
144
  }
177
- throw new Error(
178
- `Invalid JSON node: must have either "model" (for fields) or "component" (for containers). Got: ${JSON.stringify(t)}`
145
+ if (D(r))
146
+ return {
147
+ ...r.selector ? { selector: r.selector } : {},
148
+ component: P(r.component, t),
149
+ componentProps: w(r.componentProps, e, t),
150
+ children: r.children?.map((n) => p(n, e, t))
151
+ };
152
+ throw new Error(`Invalid JSON node (M1): ${JSON.stringify(r)}`);
153
+ }
154
+ function U(r, e, t) {
155
+ return p(r.root, t, e);
156
+ }
157
+ function T(r, e, t) {
158
+ return () => p(r.root, t, e);
159
+ }
160
+ function q({ errors: r }) {
161
+ return /* @__PURE__ */ M(
162
+ "div",
163
+ {
164
+ role: "alert",
165
+ "data-testid": "schema-error-panel",
166
+ style: {
167
+ border: "1px solid #f5a9a9",
168
+ background: "#fff5f5",
169
+ color: "#9b1c1c",
170
+ borderRadius: 8,
171
+ padding: "16px 20px",
172
+ font: "14px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace"
173
+ },
174
+ children: [
175
+ /* @__PURE__ */ M("strong", { style: { display: "block", marginBottom: 8, fontSize: 15 }, children: [
176
+ "Невалидная JSON-схема формы (",
177
+ r.length,
178
+ ")"
179
+ ] }),
180
+ /* @__PURE__ */ u("ul", { style: { margin: 0, paddingLeft: 20 }, children: r.map((e, t) => /* @__PURE__ */ u("li", { style: { marginBottom: 4 }, children: e }, t)) })
181
+ ]
182
+ }
179
183
  );
180
184
  }
181
- function J(t, e) {
182
- return (r) => s(t.root, r, e);
183
- }
184
- function L({
185
- schema: t,
185
+ function X({
186
+ schema: r,
186
187
  renderBehavior: e,
187
- onSchemaReady: r
188
+ onSchemaReady: t,
189
+ validate: n = !1
188
190
  }) {
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);
191
+ const { registry: o, model: i, ...y } = W(), [a, d] = C(
192
+ n ? void 0 : null
193
+ );
194
+ F(() => {
195
+ if (!n) {
196
+ d(null);
197
+ return;
198
+ }
199
+ let l = !1;
200
+ return d(void 0), import("./validate.js").then(({ validateFormSchema: s }) => {
201
+ if (l) return;
202
+ const { valid: J, errors: x } = s(r, { registry: o });
203
+ d(J ? null : x);
204
+ }).catch((s) => {
205
+ l || d([`Schema validator failed to load: ${String(s)}`]);
206
+ }), () => {
207
+ l = !0;
208
+ };
209
+ }, [n, r, o]);
210
+ const c = g(() => {
211
+ if (!i)
212
+ throw new Error(
213
+ "JsonFormRenderer: settings.model is required (M1). Pass the FormModel via JsonRendererProvider."
214
+ );
215
+ if (a !== null) return null;
216
+ const l = T(r, o, i), s = j(l);
217
+ return e && e(s), s;
218
+ }, [r, o, e, i, a]);
219
+ return g(() => {
220
+ c && t && t(c);
221
+ }, [c]), a && a.length > 0 ? /* @__PURE__ */ u(q, { errors: a }) : c ? /* @__PURE__ */ u(k, { render: c, settings: y }) : null;
202
222
  }
203
223
  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
224
+ v as FIELD_WRAPPER,
225
+ X as JsonFormRenderer,
226
+ Q as JsonRendererProvider,
227
+ q as SchemaErrorPanel,
228
+ rr as buildFormSchemaMetaSchema,
229
+ U as convertJsonToM1Tree,
230
+ T as createRenderSchemaFromJsonM1,
231
+ K as defineRegistry,
232
+ er as formSchemaMetaSchema,
233
+ tr as getComponentNames,
234
+ nr as getSourceNames,
235
+ $ as isArrayNode,
236
+ R as isComponentOp,
237
+ D as isContainerNode,
238
+ A as isDataSourceOp,
239
+ E as isFieldNode,
240
+ f as isModelOp,
241
+ m as parseOperator,
242
+ W as useJsonRendererSettings
212
243
  };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Операторы JSON-схемы (M1) — СТРОКОВЫЕ ссылки на модель/реестр.
3
+ *
4
+ * JSON нельзя «вызвать функцию», поэтому привязки кодируются СТРОКАМИ функц-стиля с дискриминатором:
5
+ *
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-компоненты.
9
+ *
10
+ * Схема остаётся чистым JSON (копируется в `.json`, приходит строкой с сервера). Типобезопасность
11
+ * на этапе компиляции даётся template-literal типами ({@link ModelOp} и т.д.) — литерал
12
+ * `'$model(loanType)'` проверяется без вызова функций. Голые строки (не-`$`, напр. `label`) не резолвятся.
13
+ *
14
+ * @module reformer/renderer-json/operators
15
+ */
16
+ /** Строка-оператор привязки к полю/массиву модели: `` `$model(${path})` ``. */
17
+ export type ModelOp = `$model(${string})`;
18
+ /** Строка-оператор ссылки на компонент реестра: `` `$component(${name})` ``. */
19
+ export type ComponentOp = `$component(${string})`;
20
+ /** Строка-оператор ссылки на registry-source: `` `$dataSource(${name})` ``. */
21
+ export type DataSourceOp = `$dataSource(${string})`;
22
+ /** Любой строковый оператор JSON-схемы. */
23
+ export type JsonOperator = ModelOp | ComponentOp | DataSourceOp;
24
+ /** Разобранный оператор: тип + аргумент (путь/имя). */
25
+ export interface ParsedOperator {
26
+ op: 'model' | 'component' | 'dataSource';
27
+ arg: string;
28
+ }
29
+ /**
30
+ * Разбор строки-оператора `"$op(arg)"`. Возвращает `null` для не-операторов (обычных строк),
31
+ * чтобы вызывающий оставил значение как есть.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * parseOperator('$model(loanType)'); // { op: 'model', arg: 'loanType' }
36
+ * parseOperator('$dataSource(LOAN_TYPES)'); // { op: 'dataSource', arg: 'LOAN_TYPES' }
37
+ * parseOperator('Введите сумму'); // null (обычная строка)
38
+ * ```
39
+ */
40
+ export declare function parseOperator(value: unknown): ParsedOperator | null;
41
+ /** Type-guard: строка — `"$model(...)"`. */
42
+ export declare const isModelOp: (v: unknown) => v is ModelOp;
43
+ /** Type-guard: строка — `"$component(...)"`. */
44
+ export declare const isComponentOp: (v: unknown) => v is ComponentOp;
45
+ /** Type-guard: строка — `"$dataSource(...)"`. */
46
+ export declare const isDataSourceOp: (v: unknown) => v is DataSourceOp;