schema-components 0.0.0 → 1.1.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +21 -0
  3. package/README.md +526 -0
  4. package/dist/core/adapter.d.mts +19 -0
  5. package/dist/core/adapter.mjs +140 -0
  6. package/dist/core/errors.d.mts +2 -0
  7. package/dist/core/errors.mjs +74 -0
  8. package/dist/core/guards.d.mts +44 -0
  9. package/dist/core/guards.mjs +58 -0
  10. package/dist/core/renderer.d.mts +2 -0
  11. package/dist/core/renderer.mjs +71 -0
  12. package/dist/core/types.d.mts +3 -0
  13. package/dist/core/types.mjs +40 -0
  14. package/dist/core/walker.d.mts +14 -0
  15. package/dist/core/walker.mjs +366 -0
  16. package/dist/errors-DIKI2C78.d.mts +57 -0
  17. package/dist/html/a11y.d.mts +47 -0
  18. package/dist/html/a11y.mjs +81 -0
  19. package/dist/html/html.d.mts +135 -0
  20. package/dist/html/html.mjs +168 -0
  21. package/dist/html/renderToHtml.d.mts +32 -0
  22. package/dist/html/renderToHtml.mjs +352 -0
  23. package/dist/html/renderToHtmlStream.d.mts +58 -0
  24. package/dist/html/renderToHtmlStream.mjs +285 -0
  25. package/dist/html/styles.css +151 -0
  26. package/dist/openapi/components.d.mts +76 -0
  27. package/dist/openapi/components.mjs +223 -0
  28. package/dist/openapi/parser.d.mts +45 -0
  29. package/dist/openapi/parser.mjs +159 -0
  30. package/dist/react/SchemaComponent.d.mts +96 -0
  31. package/dist/react/SchemaComponent.mjs +283 -0
  32. package/dist/react/SchemaErrorBoundary.d.mts +26 -0
  33. package/dist/react/SchemaErrorBoundary.mjs +47 -0
  34. package/dist/react/headless.d.mts +13 -0
  35. package/dist/react/headless.mjs +163 -0
  36. package/dist/themes/shadcn.d.mts +6 -0
  37. package/dist/themes/shadcn.mjs +166 -0
  38. package/dist/types-BU0ETFHk.d.mts +326 -0
  39. package/package.json +113 -3
@@ -0,0 +1,283 @@
1
+ import { isObject, toRecord } from "../core/guards.mjs";
2
+ import { normaliseSchema } from "../core/adapter.mjs";
3
+ import { SchemaFieldError, SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
4
+ import { getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
5
+ import { walk } from "../core/walker.mjs";
6
+ import { headlessResolver } from "./headless.mjs";
7
+ import { z } from "zod";
8
+ import { createContext, isValidElement, useCallback, useContext, useMemo } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
+ //#region src/react/SchemaComponent.tsx
11
+ /**
12
+ * <SchemaComponent> — renders UI from Zod, JSON Schema, or OpenAPI schemas.
13
+ *
14
+ * Auto-detects the input format, normalises to JSON Schema via the adapter,
15
+ * walks the JSON Schema tree, and delegates rendering to the
16
+ * ComponentResolver (theme adapter). Falls back to headless HTML.
17
+ *
18
+ * The `fields` prop type is inferred from the `schema` prop:
19
+ * - Zod schemas → FieldOverrides<z.infer<T>> (full autocomplete)
20
+ * - JSON Schema `as const` → FieldOverrides<FromJSONSchema<T>> (full autocomplete)
21
+ * - OpenAPI `as const` + `ref` → FieldOverrides<ResolveOpenAPIRef<T, Ref>>
22
+ * - Runtime schemas → Record<string, FieldOverride> (no autocomplete)
23
+ */
24
+ const UserResolverContext = createContext(void 0);
25
+ function SchemaProvider({ resolver, children }) {
26
+ return /* @__PURE__ */ jsx(UserResolverContext.Provider, {
27
+ value: resolver,
28
+ children
29
+ });
30
+ }
31
+ const widgetRegistry = /* @__PURE__ */ new Map();
32
+ function registerWidget(name, render) {
33
+ widgetRegistry.set(name, render);
34
+ }
35
+ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange, validate, onValidationError, onError, fields, meta: componentMeta, readOnly, writeOnly, description }) {
36
+ const userResolver = useContext(UserResolverContext);
37
+ const mergedMeta = useMemo(() => {
38
+ const merged = { ...componentMeta };
39
+ if (readOnly === true) merged.readOnly = true;
40
+ if (writeOnly === true) merged.writeOnly = true;
41
+ if (description !== void 0) merged.description = description;
42
+ return merged;
43
+ }, [
44
+ componentMeta,
45
+ readOnly,
46
+ writeOnly,
47
+ description
48
+ ]);
49
+ let jsonSchema;
50
+ let zodSchema;
51
+ let rootMeta;
52
+ let rootDocument;
53
+ try {
54
+ const normalised = normaliseSchema(schemaInput, refInput);
55
+ jsonSchema = normalised.jsonSchema;
56
+ zodSchema = normalised.zodSchema;
57
+ rootMeta = normalised.rootMeta;
58
+ rootDocument = normalised.rootDocument;
59
+ } catch (err) {
60
+ const error = new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", schemaInput, detectNormalisationKind(err));
61
+ if (onError !== void 0) {
62
+ onError(error);
63
+ return null;
64
+ }
65
+ throw error;
66
+ }
67
+ const handleChange = useCallback((nextValue) => {
68
+ if (validate) runValidation(zodSchema, jsonSchema, nextValue, onValidationError);
69
+ onChange?.(nextValue);
70
+ }, [
71
+ validate,
72
+ zodSchema,
73
+ jsonSchema,
74
+ onChange,
75
+ onValidationError
76
+ ]);
77
+ const tree = walk(jsonSchema, {
78
+ componentMeta: mergedMeta,
79
+ rootMeta,
80
+ fieldOverrides: fields,
81
+ rootDocument
82
+ });
83
+ const renderChild = (childTree, childValue, childOnChange) => {
84
+ return renderField(childTree, childValue, childOnChange, userResolver, renderChild);
85
+ };
86
+ return renderField(tree, value, handleChange, userResolver, renderChild);
87
+ }
88
+ function runValidation(zodSchema, jsonSchema, value, onError) {
89
+ if (zodSchema !== void 0 && isObject(zodSchema)) {
90
+ const safeParseFn = zodSchema.safeParse;
91
+ if (isCallable(safeParseFn)) {
92
+ const result = safeParseFn(value);
93
+ if (isObject(result) && "success" in result && result.success !== true) {
94
+ onError?.(result.error);
95
+ return;
96
+ }
97
+ return;
98
+ }
99
+ }
100
+ const parsed = z.fromJSONSchema(jsonSchema);
101
+ if (isObject(parsed)) {
102
+ const safeParseFn = parsed.safeParse;
103
+ if (isCallable(safeParseFn)) {
104
+ const result = safeParseFn(value);
105
+ if (isObject(result) && "success" in result && result.success !== true) onError?.(result.error);
106
+ }
107
+ }
108
+ }
109
+ function renderField(tree, value, onChange, userResolver, renderChild) {
110
+ const componentHint = tree.meta.component;
111
+ if (typeof componentHint === "string") {
112
+ const widget = widgetRegistry.get(componentHint);
113
+ if (widget !== void 0) {
114
+ const result = widget(buildRenderProps(tree, value, onChange, renderChild));
115
+ if (result !== void 0 && result !== null) {
116
+ if (isValidElement(result)) return result;
117
+ if (typeof result === "string" || typeof result === "number") return result;
118
+ return null;
119
+ }
120
+ }
121
+ }
122
+ const resolver = userResolver !== void 0 ? mergeResolvers(userResolver, headlessResolver) : headlessResolver;
123
+ const renderFn = getRenderFunction(tree.type, resolver);
124
+ if (renderFn !== void 0) {
125
+ let result;
126
+ try {
127
+ result = renderFn(buildRenderProps(tree, value, onChange, renderChild));
128
+ } catch (err) {
129
+ throw new SchemaRenderError(err instanceof Error ? err.message : `Render function threw for type "${tree.type}"`, tree, tree.type, err);
130
+ }
131
+ if (result !== void 0 && result !== null) {
132
+ if (isValidElement(result)) return result;
133
+ if (typeof result === "string" || typeof result === "number") return result;
134
+ }
135
+ }
136
+ if (value === void 0 || value === null) return /* @__PURE__ */ jsx("span", { children: "—" });
137
+ return /* @__PURE__ */ jsx("span", { children: typeof value === "string" ? value : JSON.stringify(value) });
138
+ }
139
+ function buildRenderProps(tree, value, onChange, renderChild) {
140
+ const props = {
141
+ value,
142
+ onChange,
143
+ readOnly: tree.editability === "presentation",
144
+ writeOnly: tree.editability === "input",
145
+ meta: tree.meta,
146
+ constraints: tree.constraints,
147
+ path: "",
148
+ tree,
149
+ renderChild
150
+ };
151
+ if (tree.enumValues !== void 0) props.enumValues = tree.enumValues;
152
+ if (tree.element !== void 0) props.element = tree.element;
153
+ if (tree.fields !== void 0) props.fields = tree.fields;
154
+ if (tree.options !== void 0) props.options = tree.options;
155
+ if (tree.discriminator !== void 0) props.discriminator = tree.discriminator;
156
+ if (tree.keyType !== void 0) props.keyType = tree.keyType;
157
+ if (tree.valueType !== void 0) props.valueType = tree.valueType;
158
+ return props;
159
+ }
160
+ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange, meta: fieldMeta, validate, onValidationError }) {
161
+ const userResolver = useContext(UserResolverContext);
162
+ let jsonSchema;
163
+ let zodSchema;
164
+ let rootMeta;
165
+ let rootDocument;
166
+ try {
167
+ const normalised = normaliseSchema(schemaInput, refInput);
168
+ jsonSchema = normalised.jsonSchema;
169
+ zodSchema = normalised.zodSchema;
170
+ rootMeta = normalised.rootMeta;
171
+ rootDocument = normalised.rootDocument;
172
+ } catch (err) {
173
+ throw new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", schemaInput, detectNormalisationKind(err));
174
+ }
175
+ const fieldTree = resolvePath(walk(jsonSchema, {
176
+ componentMeta: fieldMeta,
177
+ rootMeta,
178
+ rootDocument
179
+ }), path);
180
+ if (fieldTree === void 0) throw new SchemaFieldError(`Field not found: ${path}`, schemaInput, path);
181
+ const fieldValue = resolveValue(value, path);
182
+ const handleChange = useCallback((nextFieldValue) => {
183
+ if (validate) {
184
+ const newRootValue = setNestedValue(value, path, nextFieldValue);
185
+ runValidation(zodSchema, jsonSchema, newRootValue, onValidationError);
186
+ }
187
+ const newRootValue = setNestedValue(value, path, nextFieldValue);
188
+ onChange?.(newRootValue);
189
+ }, [
190
+ validate,
191
+ zodSchema,
192
+ jsonSchema,
193
+ value,
194
+ path,
195
+ onChange,
196
+ onValidationError
197
+ ]);
198
+ const renderChild = (childTree, childValue, childOnChange) => {
199
+ return renderField(childTree, childValue, childOnChange, userResolver, renderChild);
200
+ };
201
+ return renderField(fieldTree, fieldValue, handleChange, userResolver, renderChild);
202
+ }
203
+ function resolvePath(tree, path) {
204
+ if (path.length === 0) return tree;
205
+ const parts = path.split(".");
206
+ let current = tree;
207
+ for (const part of parts) {
208
+ if (current === void 0) return void 0;
209
+ const bracketMatch = /^(.+)\[(\d+)\]$/.exec(part);
210
+ if (bracketMatch?.[1] !== void 0 && bracketMatch[2] !== void 0) {
211
+ const arrayField = bracketMatch[1];
212
+ if (current.fields !== void 0) current = current.fields[arrayField];
213
+ if (current?.element !== void 0) current = current.element;
214
+ continue;
215
+ }
216
+ if (current.fields !== void 0) current = current.fields[part];
217
+ else if (current.element !== void 0) current = current.element;
218
+ else return;
219
+ }
220
+ return current;
221
+ }
222
+ function resolveValue(root, path) {
223
+ if (path.length === 0) return root;
224
+ const parts = path.split(".");
225
+ let current = root;
226
+ for (const part of parts) {
227
+ if (typeof current !== "object" || current === null) return void 0;
228
+ const bracketMatch = /^(.+)\[(\d+)\]$/.exec(part);
229
+ if (bracketMatch?.[1] !== void 0 && bracketMatch[2] !== void 0) {
230
+ const key = bracketMatch[1];
231
+ const index = Number(bracketMatch[2]);
232
+ const arr = toRecord(current)[key];
233
+ if (Array.isArray(arr)) current = arr[index];
234
+ else return;
235
+ } else current = toRecord(current)[part];
236
+ }
237
+ return current;
238
+ }
239
+ function setNestedValue(root, path, leafValue) {
240
+ if (path.length === 0) return leafValue;
241
+ const parts = path.split(".");
242
+ const result = isObject(root) ? { ...toRecord(root) } : {};
243
+ let current = result;
244
+ for (let i = 0; i < parts.length; i++) {
245
+ const part = parts[i];
246
+ if (part === void 0) break;
247
+ const isLast = i === parts.length - 1;
248
+ const bracketMatch = /^(.+)\[(\d+)\]$/.exec(part);
249
+ if (bracketMatch?.[1] !== void 0 && bracketMatch[2] !== void 0) {
250
+ const key = bracketMatch[1];
251
+ const index = Number(bracketMatch[2]);
252
+ const existing = current[key];
253
+ const arr = Array.isArray(existing) ? existing.slice() : [];
254
+ if (isLast) arr[index] = leafValue;
255
+ current[key] = arr;
256
+ const nextCurrent = arr[index];
257
+ if (nextCurrent !== void 0 && isObject(nextCurrent)) current = toRecord(nextCurrent);
258
+ } else if (isLast) current[part] = leafValue;
259
+ else {
260
+ const existing = current[part];
261
+ const next = isObject(existing) ? { ...toRecord(existing) } : {};
262
+ current[part] = next;
263
+ current = next;
264
+ }
265
+ }
266
+ return result;
267
+ }
268
+ function isCallable(value) {
269
+ return typeof value === "function";
270
+ }
271
+ function detectNormalisationKind(err) {
272
+ if (err instanceof Error) {
273
+ const msg = err.message;
274
+ if (msg.includes("Zod 3")) return "zod3-unsupported";
275
+ if (msg.includes("Invalid Zod 4")) return "invalid-zod";
276
+ if (msg.includes("OpenAPI ref not found")) return "openapi-missing-ref";
277
+ if (msg.includes("OpenAPI")) return "openapi-invalid";
278
+ if (msg.includes("JSON Schema")) return "invalid-json-schema";
279
+ }
280
+ return "unknown";
281
+ }
282
+ //#endregion
283
+ export { SchemaComponent, SchemaField, SchemaProvider, registerWidget, renderField };
@@ -0,0 +1,26 @@
1
+ import { Component, ReactNode } from "react";
2
+
3
+ //#region src/react/SchemaErrorBoundary.d.ts
4
+ interface SchemaErrorBoundaryProps {
5
+ /** Called with the caught error. Returns fallback ReactNode to render. */
6
+ fallback: (error: Error, reset: () => void) => ReactNode;
7
+ children: ReactNode;
8
+ }
9
+ interface ErrorBoundaryState {
10
+ error: Error | undefined;
11
+ }
12
+ /**
13
+ * React error boundary that catches schema rendering errors.
14
+ *
15
+ * Provides a `reset` callback that clears the error state, allowing
16
+ * the children to re-render (e.g. after fixing a bad schema prop).
17
+ */
18
+ declare class SchemaErrorBoundary extends Component<SchemaErrorBoundaryProps, ErrorBoundaryState> {
19
+ state: ErrorBoundaryState;
20
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
21
+ componentDidCatch(error: Error): void;
22
+ reset: () => void;
23
+ render(): ReactNode;
24
+ }
25
+ //#endregion
26
+ export { SchemaErrorBoundary, SchemaErrorBoundaryProps };
@@ -0,0 +1,47 @@
1
+ import { SchemaError } from "../core/errors.mjs";
2
+ import { Component } from "react";
3
+ //#region src/react/SchemaErrorBoundary.tsx
4
+ /**
5
+ * React error boundary for schema-components.
6
+ *
7
+ * Catches render errors from `<SchemaComponent>`, theme adapters, and
8
+ * any child components. Without this boundary, a throwing render function
9
+ * crashes the entire React tree.
10
+ *
11
+ * Usage:
12
+ * import { SchemaErrorBoundary } from "schema-components/react/SchemaErrorBoundary";
13
+ *
14
+ * <SchemaErrorBoundary fallback={(error) => <p>{error.message}</p>}>
15
+ * <SchemaComponent schema={userSchema} value={user} />
16
+ * </SchemaErrorBoundary>
17
+ *
18
+ * The boundary catches `SchemaRenderError` from theme adapters and any
19
+ * other errors thrown during rendering. It does NOT catch:
20
+ * - Event handler errors (onChange, etc.)
21
+ * - Async errors
22
+ * - Errors in server-side rendering
23
+ */
24
+ /**
25
+ * React error boundary that catches schema rendering errors.
26
+ *
27
+ * Provides a `reset` callback that clears the error state, allowing
28
+ * the children to re-render (e.g. after fixing a bad schema prop).
29
+ */
30
+ var SchemaErrorBoundary = class extends Component {
31
+ state = { error: void 0 };
32
+ static getDerivedStateFromError(error) {
33
+ return { error };
34
+ }
35
+ componentDidCatch(error) {
36
+ if (!(error instanceof SchemaError)) console.error("[schema-components] Unhandled render error:", error);
37
+ }
38
+ reset = () => {
39
+ this.setState({ error: void 0 });
40
+ };
41
+ render() {
42
+ if (this.state.error !== void 0) return this.props.fallback(this.state.error, this.reset);
43
+ return this.props.children;
44
+ }
45
+ };
46
+ //#endregion
47
+ export { SchemaErrorBoundary };
@@ -0,0 +1,13 @@
1
+ import { b as ComponentResolver } from "../types-BU0ETFHk.mjs";
2
+ import { ReactNode } from "react";
3
+
4
+ //#region src/react/headless.d.ts
5
+ declare function toReactNode(value: unknown): ReactNode;
6
+ /**
7
+ * The headless resolver uses props.renderChild for recursive rendering.
8
+ * No factory function needed — the renderChild is always available
9
+ * on RenderProps.
10
+ */
11
+ declare const headlessResolver: ComponentResolver;
12
+ //#endregion
13
+ export { headlessResolver, toReactNode };
@@ -0,0 +1,163 @@
1
+ import { isObject } from "../core/guards.mjs";
2
+ import { isValidElement } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/react/headless.tsx
5
+ /**
6
+ * React headless renderer — the default ComponentResolver implementation.
7
+ *
8
+ * Produces plain HTML elements for every schema type. Theme adapters
9
+ * replace this by implementing ComponentResolver with their own components.
10
+ *
11
+ * This module imports React and lives in the react layer, not core,
12
+ * because it produces ReactNode values.
13
+ */
14
+ function toReactNode(value) {
15
+ if (value === null || value === void 0) return null;
16
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
17
+ if (isValidElement(value)) return value;
18
+ return null;
19
+ }
20
+ function renderString(props) {
21
+ if (props.readOnly) {
22
+ const strValue = typeof props.value === "string" ? props.value : void 0;
23
+ if (strValue === void 0 || strValue.length === 0) return /* @__PURE__ */ jsx("span", { children: "—" });
24
+ const format = props.constraints.format;
25
+ if (format === "email") return /* @__PURE__ */ jsx("a", {
26
+ href: `mailto:${strValue}`,
27
+ children: strValue
28
+ });
29
+ if (format === "uri" || format === "url") return /* @__PURE__ */ jsx("a", {
30
+ href: strValue,
31
+ children: strValue
32
+ });
33
+ return /* @__PURE__ */ jsx("span", { children: strValue });
34
+ }
35
+ const strValue = typeof props.value === "string" ? props.value : "";
36
+ if (props.enumValues !== void 0 && props.enumValues.length > 0) return /* @__PURE__ */ jsxs("select", {
37
+ value: strValue,
38
+ onChange: (e) => {
39
+ props.onChange(e.target.value);
40
+ },
41
+ children: [/* @__PURE__ */ jsx("option", {
42
+ value: "",
43
+ children: "Select…"
44
+ }), props.enumValues.map((v) => /* @__PURE__ */ jsx("option", {
45
+ value: v,
46
+ children: v
47
+ }, v))]
48
+ });
49
+ return /* @__PURE__ */ jsx("input", {
50
+ type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
51
+ value: props.writeOnly ? "" : strValue,
52
+ onChange: (e) => {
53
+ props.onChange(e.target.value);
54
+ },
55
+ placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
56
+ minLength: props.constraints.minLength,
57
+ maxLength: props.constraints.maxLength
58
+ });
59
+ }
60
+ function renderNumber(props) {
61
+ if (props.readOnly) {
62
+ if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", { children: "—" });
63
+ return /* @__PURE__ */ jsx("span", { children: props.value.toLocaleString() });
64
+ }
65
+ const numValue = typeof props.value === "number" ? props.value : "";
66
+ return /* @__PURE__ */ jsx("input", {
67
+ type: "number",
68
+ value: props.writeOnly ? "" : numValue,
69
+ onChange: (e) => {
70
+ props.onChange(Number(e.target.value));
71
+ },
72
+ min: props.constraints.minimum,
73
+ max: props.constraints.maximum
74
+ });
75
+ }
76
+ function renderBoolean(props) {
77
+ if (props.readOnly) {
78
+ if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", { children: "—" });
79
+ return /* @__PURE__ */ jsx("span", { children: props.value ? "Yes" : "No" });
80
+ }
81
+ return /* @__PURE__ */ jsx("input", {
82
+ type: "checkbox",
83
+ checked: props.value === true,
84
+ onChange: (e) => {
85
+ props.onChange(e.target.checked);
86
+ }
87
+ });
88
+ }
89
+ function renderEnum(props) {
90
+ const enumValue = typeof props.value === "string" ? props.value : "";
91
+ if (props.readOnly) return /* @__PURE__ */ jsx("span", { children: enumValue || "—" });
92
+ return /* @__PURE__ */ jsxs("select", {
93
+ value: props.writeOnly ? "" : enumValue,
94
+ onChange: (e) => {
95
+ props.onChange(e.target.value);
96
+ },
97
+ children: [/* @__PURE__ */ jsx("option", {
98
+ value: "",
99
+ children: "Select…"
100
+ }), props.enumValues?.map((v) => /* @__PURE__ */ jsx("option", {
101
+ value: v,
102
+ children: v
103
+ }, v))]
104
+ });
105
+ }
106
+ function renderObject(props) {
107
+ const obj = isObject(props.value) ? props.value : {};
108
+ const fields = props.fields;
109
+ if (fields === void 0) return null;
110
+ return /* @__PURE__ */ jsxs("fieldset", { children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx("legend", { children: props.meta.description }), Object.entries(fields).map(([key, field]) => {
111
+ const childValue = obj[key];
112
+ const childOnChange = (v) => {
113
+ const updated = {};
114
+ for (const [k, val] of Object.entries(obj)) updated[k] = val;
115
+ updated[key] = v;
116
+ props.onChange(updated);
117
+ };
118
+ return /* @__PURE__ */ jsxs("div", { children: [typeof field.meta.description === "string" && /* @__PURE__ */ jsx("label", { children: field.meta.description }), toReactNode(props.renderChild(field, childValue, childOnChange))] }, key);
119
+ })] });
120
+ }
121
+ function renderArray(props) {
122
+ const arr = Array.isArray(props.value) ? props.value : [];
123
+ const element = props.element;
124
+ if (element === void 0) return null;
125
+ return /* @__PURE__ */ jsx("div", { children: arr.map((item, i) => {
126
+ const childOnChange = (v) => {
127
+ const next = arr.slice();
128
+ next[i] = v;
129
+ props.onChange(next);
130
+ };
131
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
132
+ }) });
133
+ }
134
+ function renderUnknown(props) {
135
+ if (props.readOnly) {
136
+ if (props.value === void 0 || props.value === null) return /* @__PURE__ */ jsx("span", { children: "—" });
137
+ return /* @__PURE__ */ jsx("span", { children: typeof props.value === "string" ? props.value : JSON.stringify(props.value) });
138
+ }
139
+ const strValue = typeof props.value === "string" ? props.value : "";
140
+ return /* @__PURE__ */ jsx("input", {
141
+ type: "text",
142
+ value: props.writeOnly ? "" : strValue,
143
+ onChange: (e) => {
144
+ props.onChange(e.target.value);
145
+ }
146
+ });
147
+ }
148
+ /**
149
+ * The headless resolver uses props.renderChild for recursive rendering.
150
+ * No factory function needed — the renderChild is always available
151
+ * on RenderProps.
152
+ */
153
+ const headlessResolver = {
154
+ string: renderString,
155
+ number: renderNumber,
156
+ boolean: renderBoolean,
157
+ enum: renderEnum,
158
+ object: renderObject,
159
+ array: renderArray,
160
+ unknown: renderUnknown
161
+ };
162
+ //#endregion
163
+ export { headlessResolver, toReactNode };
@@ -0,0 +1,6 @@
1
+ import { b as ComponentResolver } from "../types-BU0ETFHk.mjs";
2
+
3
+ //#region src/themes/shadcn.d.ts
4
+ declare const shadcnResolver: ComponentResolver;
5
+ //#endregion
6
+ export { shadcnResolver };