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,166 @@
1
+ import { toRecord } from "../core/guards.mjs";
2
+ import { headlessResolver, toReactNode } from "../react/headless.mjs";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/themes/shadcn.tsx
5
+ function isString(value) {
6
+ return typeof value === "string";
7
+ }
8
+ function buildClassNames(...classes) {
9
+ return classes.filter(isString).join(" ");
10
+ }
11
+ function renderStringInput(props) {
12
+ const strValue = typeof props.value === "string" ? props.value : "";
13
+ const className = buildClassNames("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors", "file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
14
+ if (props.readOnly) return /* @__PURE__ */ jsx("span", {
15
+ className: "text-sm",
16
+ children: strValue || "—"
17
+ });
18
+ if (props.writeOnly) return /* @__PURE__ */ jsx("input", {
19
+ type: props.constraints.format === "email" ? "email" : "text",
20
+ className,
21
+ placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
22
+ value: "",
23
+ onChange: (e) => {
24
+ props.onChange(e.target.value);
25
+ }
26
+ });
27
+ return /* @__PURE__ */ jsx("input", {
28
+ type: props.constraints.format === "email" ? "email" : "text",
29
+ className,
30
+ value: strValue,
31
+ onChange: (e) => {
32
+ props.onChange(e.target.value);
33
+ },
34
+ placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
35
+ minLength: props.constraints.minLength,
36
+ maxLength: props.constraints.maxLength
37
+ });
38
+ }
39
+ function renderNumberInput(props) {
40
+ const className = buildClassNames("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
41
+ if (props.readOnly) {
42
+ if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", {
43
+ className: "text-sm",
44
+ children: "—"
45
+ });
46
+ return /* @__PURE__ */ jsx("span", {
47
+ className: "text-sm",
48
+ children: props.value.toLocaleString()
49
+ });
50
+ }
51
+ return /* @__PURE__ */ jsx("input", {
52
+ type: "number",
53
+ className,
54
+ value: typeof props.value === "number" ? props.value : "",
55
+ onChange: (e) => {
56
+ props.onChange(Number(e.target.value));
57
+ },
58
+ min: props.constraints.minimum,
59
+ max: props.constraints.maximum
60
+ });
61
+ }
62
+ function renderBooleanInput(props) {
63
+ const className = buildClassNames("h-4 w-4 rounded border border-primary shadow", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
64
+ if (props.readOnly) {
65
+ if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", {
66
+ className: "text-sm",
67
+ children: "—"
68
+ });
69
+ return /* @__PURE__ */ jsx("span", {
70
+ className: "text-sm",
71
+ children: props.value ? "Yes" : "No"
72
+ });
73
+ }
74
+ return /* @__PURE__ */ jsx("input", {
75
+ type: "checkbox",
76
+ className,
77
+ checked: props.value === true,
78
+ onChange: (e) => {
79
+ props.onChange(e.target.checked);
80
+ }
81
+ });
82
+ }
83
+ function renderObjectContainer(props) {
84
+ const fields = props.fields;
85
+ if (fields === void 0) return null;
86
+ const obj = typeof props.value === "object" && props.value !== null && !Array.isArray(props.value) ? props.value : {};
87
+ return /* @__PURE__ */ jsxs("div", {
88
+ className: "space-y-4",
89
+ children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx("h3", {
90
+ className: "text-lg font-medium",
91
+ children: props.meta.description
92
+ }), Object.entries(fields).map(([key, field]) => {
93
+ const childValue = toRecord(obj)[key];
94
+ const childOnChange = (v) => {
95
+ const updated = {};
96
+ for (const [k, val] of Object.entries(obj)) updated[k] = val;
97
+ updated[key] = v;
98
+ props.onChange(updated);
99
+ };
100
+ return /* @__PURE__ */ jsxs("div", {
101
+ className: "space-y-1",
102
+ children: [/* @__PURE__ */ jsx("label", {
103
+ className: "text-sm font-medium leading-none",
104
+ children: field.meta.description ?? key
105
+ }), toReactNode(props.renderChild(field, childValue, childOnChange))]
106
+ }, key);
107
+ })]
108
+ });
109
+ }
110
+ function renderArrayContainer(props) {
111
+ const arr = Array.isArray(props.value) ? props.value : [];
112
+ const element = props.element;
113
+ if (element === void 0) return null;
114
+ return /* @__PURE__ */ jsx("div", {
115
+ className: "space-y-2",
116
+ children: arr.map((item, i) => {
117
+ const childOnChange = (v) => {
118
+ const next = arr.slice();
119
+ next[i] = v;
120
+ props.onChange(next);
121
+ };
122
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
123
+ })
124
+ });
125
+ }
126
+ function renderEnumInput(props) {
127
+ const className = buildClassNames("flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm", "focus:outline-none focus:ring-1 focus:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
128
+ const enumValue = typeof props.value === "string" ? props.value : "";
129
+ if (props.readOnly) return /* @__PURE__ */ jsx("span", {
130
+ className: "text-sm",
131
+ children: enumValue || "—"
132
+ });
133
+ return /* @__PURE__ */ jsxs("select", {
134
+ className,
135
+ value: props.writeOnly ? "" : enumValue,
136
+ onChange: (e) => {
137
+ props.onChange(e.target.value);
138
+ },
139
+ children: [/* @__PURE__ */ jsx("option", {
140
+ value: "",
141
+ children: "Select…"
142
+ }), props.enumValues?.map((v) => /* @__PURE__ */ jsx("option", {
143
+ value: v,
144
+ children: v
145
+ }, v))]
146
+ });
147
+ }
148
+ function buildResolver() {
149
+ const resolver = {
150
+ string: renderStringInput,
151
+ number: renderNumberInput,
152
+ boolean: renderBooleanInput,
153
+ enum: renderEnumInput,
154
+ object: renderObjectContainer,
155
+ array: renderArrayContainer
156
+ };
157
+ if (headlessResolver.literal !== void 0) resolver.literal = headlessResolver.literal;
158
+ if (headlessResolver.union !== void 0) resolver.union = headlessResolver.union;
159
+ if (headlessResolver.record !== void 0) resolver.record = headlessResolver.record;
160
+ if (headlessResolver.file !== void 0) resolver.file = headlessResolver.file;
161
+ if (headlessResolver.unknown !== void 0) resolver.unknown = headlessResolver.unknown;
162
+ return resolver;
163
+ }
164
+ const shadcnResolver = buildResolver();
165
+ //#endregion
166
+ export { shadcnResolver };
@@ -0,0 +1,326 @@
1
+ //#region src/core/renderer.d.ts
2
+ /**
3
+ * Properties available on every schema field, regardless of rendering target.
4
+ * Both React and HTML renderers receive these.
5
+ */
6
+ interface BaseFieldProps {
7
+ /** Current field value. */
8
+ value: unknown;
9
+ /** Whether to render as read-only display. */
10
+ readOnly: boolean;
11
+ /** Whether to render as an empty input. */
12
+ writeOnly: boolean;
13
+ /** Schema metadata for this field. */
14
+ meta: SchemaMeta;
15
+ /** Constraints from schema checks. */
16
+ constraints: FieldConstraints;
17
+ /** Dot-separated path from root (e.g. "address.city"). */
18
+ path: string;
19
+ /** For enums: the allowed values. */
20
+ enumValues?: string[];
21
+ /** For arrays: the element schema. */
22
+ element?: WalkedField;
23
+ /** For objects: map of field name → WalkedField. */
24
+ fields?: Record<string, WalkedField>;
25
+ /** For unions: the option schemas. */
26
+ options?: WalkedField[];
27
+ /** For discriminated unions: the discriminator key. */
28
+ discriminator?: string;
29
+ /** For records: key and value schemas. */
30
+ keyType?: WalkedField;
31
+ valueType?: WalkedField;
32
+ /** Walked field tree for recursive rendering. */
33
+ tree: WalkedField;
34
+ }
35
+ /**
36
+ * Props for React render functions. Extends BaseFieldProps with:
37
+ * - `onChange` — callback to propagate value changes back to state
38
+ * - `renderChild` — recursively renders a child field, threading onChange
39
+ */
40
+ interface RenderProps extends BaseFieldProps {
41
+ /** Callback to update the field value. */
42
+ onChange: (value: unknown) => void;
43
+ /**
44
+ * Render a child field. Theme adapters call this to recursively render
45
+ * nested structures (object fields, array elements, union options).
46
+ * The resolver and rendering context are already wired in.
47
+ */
48
+ renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => unknown;
49
+ }
50
+ /**
51
+ * Props for HTML render functions. Extends BaseFieldProps with:
52
+ * - `renderChild` — recursively renders a child field to HTML string
53
+ *
54
+ * No `onChange` — HTML rendering is pure output with no event handling.
55
+ */
56
+ interface HtmlRenderProps extends BaseFieldProps {
57
+ /**
58
+ * Render a child field to an HTML string. Theme adapters call this
59
+ * to recursively render nested structures.
60
+ *
61
+ * @param tree - The walked field tree for the child
62
+ * @param value - The child's current value
63
+ * @param pathSuffix - Path segment from the parent (e.g. "city",
64
+ * "[0]"). When omitted, the child's description is used as fallback.
65
+ */
66
+ renderChild: (tree: WalkedField, value: unknown, pathSuffix?: string) => string;
67
+ }
68
+ type RenderFunction = (props: RenderProps) => unknown;
69
+ interface ComponentResolver {
70
+ string?: RenderFunction;
71
+ number?: RenderFunction;
72
+ boolean?: RenderFunction;
73
+ enum?: RenderFunction;
74
+ object?: RenderFunction;
75
+ array?: RenderFunction;
76
+ record?: RenderFunction;
77
+ union?: RenderFunction;
78
+ literal?: RenderFunction;
79
+ file?: RenderFunction;
80
+ unknown?: RenderFunction;
81
+ }
82
+ /** An HTML render function returns a string. */
83
+ type HtmlRenderFunction = (props: HtmlRenderProps) => string;
84
+ /**
85
+ * HTML resolver — maps schema types to HTML string renderers.
86
+ * Structurally mirrors ComponentResolver but produces strings.
87
+ */
88
+ interface HtmlResolver {
89
+ string?: HtmlRenderFunction;
90
+ number?: HtmlRenderFunction;
91
+ boolean?: HtmlRenderFunction;
92
+ enum?: HtmlRenderFunction;
93
+ object?: HtmlRenderFunction;
94
+ array?: HtmlRenderFunction;
95
+ record?: HtmlRenderFunction;
96
+ union?: HtmlRenderFunction;
97
+ literal?: HtmlRenderFunction;
98
+ file?: HtmlRenderFunction;
99
+ unknown?: HtmlRenderFunction;
100
+ }
101
+ declare const RESOLVER_KEYS: readonly ["string", "number", "boolean", "enum", "object", "array", "record", "union", "literal", "file", "unknown"];
102
+ type ResolverKey = (typeof RESOLVER_KEYS)[number];
103
+ /**
104
+ * Map a schema type to the resolver key that handles it.
105
+ * `discriminatedUnion` → `union`. Unknown types → `unknown`.
106
+ */
107
+ declare function typeToKey(type: WalkedField["type"]): ResolverKey;
108
+ /**
109
+ * Look up the render function for a schema type in a ComponentResolver.
110
+ */
111
+ declare function getRenderFunction(type: WalkedField["type"], resolver: ComponentResolver): RenderFunction | undefined;
112
+ /**
113
+ * Look up the render function for a schema type in an HtmlResolver.
114
+ */
115
+ declare function getHtmlRenderFn(type: WalkedField["type"], resolver: HtmlResolver): HtmlRenderFunction | undefined;
116
+ /**
117
+ * Merge two ComponentResolvers — user values take priority, fallback fills gaps.
118
+ */
119
+ declare function mergeResolvers(user: ComponentResolver, fallback: ComponentResolver): ComponentResolver;
120
+ /**
121
+ * Merge two HtmlResolvers — user values take priority, fallback fills gaps.
122
+ */
123
+ declare function mergeHtmlResolvers(user: HtmlResolver, fallback: HtmlResolver): HtmlResolver;
124
+ //#endregion
125
+ //#region src/core/types.d.ts
126
+ /**
127
+ * Core types for schema-components.
128
+ *
129
+ * These types define the vocabulary shared between the schema walker,
130
+ * component resolver, and React components.
131
+ */
132
+ /** A raw JSON object (JSON Schema or OpenAPI document). */
133
+ type JsonObject = Record<string, unknown>;
134
+ /**
135
+ * Metadata attached to schemas via `.meta()` or passed as props to
136
+ * `<SchemaComponent>`. Every field is also available as a top-level
137
+ * prop on `<SchemaComponent>` (with TypeScript-enforced exclusivity
138
+ * between prop and `meta`).
139
+ */
140
+ interface SchemaMeta {
141
+ readOnly?: boolean;
142
+ writeOnly?: boolean;
143
+ description?: string;
144
+ title?: string;
145
+ deprecated?: boolean;
146
+ /** Component hint — resolved before theme adapter. */
147
+ component?: string;
148
+ /** Arbitrary UI hints passed through to theme adapters. */
149
+ [key: string]: unknown;
150
+ }
151
+ type Editability = "presentation" | "input" | "editable";
152
+ /**
153
+ * Resolved editability state for a single field.
154
+ *
155
+ * Priority (highest wins):
156
+ * 1. Property-level readOnly → presentation
157
+ * 2. Property-level writeOnly → input
158
+ * 3. Component-level readOnly → presentation
159
+ * 4. Component-level writeOnly → input
160
+ * 5. Schema root readOnly → presentation
161
+ * 6. Schema root writeOnly → input
162
+ * 7. Neither → editable
163
+ */
164
+ declare function resolveEditability(propertyMeta: SchemaMeta | undefined, componentMeta: SchemaMeta | undefined, rootMeta: SchemaMeta | undefined): Editability;
165
+ /**
166
+ * Recursive mapped type that mirrors a schema's shape for per-field
167
+ * meta overrides. Each leaf is `Partial<SchemaMeta>`, objects recurse
168
+ * and also accept their own `SchemaMeta`.
169
+ */
170
+ type FieldOverrides<T> = { [K in keyof T]?: T[K] extends object ? FieldOverrides<T[K]> & Partial<SchemaMeta> : Partial<SchemaMeta> };
171
+ /**
172
+ * Fallback type for runtime schemas (no compile-time shape).
173
+ */
174
+ type FieldOverride = Partial<SchemaMeta>;
175
+ type SchemaType = "string" | "number" | "boolean" | "null" | "enum" | "literal" | "object" | "array" | "record" | "union" | "discriminatedUnion" | "optional" | "nullable" | "default" | "readonly" | "pipe" | "lazy" | "file" | "unknown";
176
+ interface WalkedField {
177
+ type: SchemaType;
178
+ editability: Editability;
179
+ meta: SchemaMeta;
180
+ /** For objects: map of field name → WalkedField. */
181
+ fields?: Record<string, WalkedField>;
182
+ /** For arrays: the element schema. */
183
+ element?: WalkedField;
184
+ /** For enums: the allowed values. */
185
+ enumValues?: string[];
186
+ /** For unions/discriminated unions: the options. */
187
+ options?: WalkedField[];
188
+ discriminator?: string;
189
+ /** For records: key and value schemas. */
190
+ keyType?: WalkedField;
191
+ valueType?: WalkedField;
192
+ /** For literals: the literal value(s). */
193
+ literalValues?: (string | number | boolean | null)[];
194
+ /** Whether the field is optional. */
195
+ isOptional?: boolean;
196
+ /** Whether the field is nullable. */
197
+ isNullable?: boolean;
198
+ /** Default value if present on the schema. */
199
+ defaultValue?: unknown;
200
+ /** Constraints from Zod checks (min, max, pattern, etc.). */
201
+ constraints: FieldConstraints;
202
+ }
203
+ interface FieldConstraints {
204
+ minLength?: number;
205
+ maxLength?: number;
206
+ minimum?: number;
207
+ maximum?: number;
208
+ pattern?: string;
209
+ format?: string;
210
+ mimeTypes?: string[];
211
+ minItems?: number;
212
+ maxItems?: number;
213
+ }
214
+ /**
215
+ * Maps a JSON Schema structure to a TypeScript type.
216
+ * Works with `as const` literals — provides full autocomplete for `fields`.
217
+ */
218
+ type FromJSONSchema<S> = S extends {
219
+ type: "string";
220
+ } ? string : S extends {
221
+ type: "number" | "integer";
222
+ } ? number : S extends {
223
+ type: "boolean";
224
+ } ? boolean : S extends {
225
+ type: "null";
226
+ } ? null : S extends {
227
+ type: "array";
228
+ items: infer I;
229
+ } ? FromJSONSchema<I>[] : S extends {
230
+ type: "object";
231
+ properties: infer P;
232
+ required?: infer R;
233
+ } ? { [K in keyof P]: K extends R ? FromJSONSchema<P[K]> : FromJSONSchema<P[K]> | undefined } : unknown;
234
+ /**
235
+ * Resolves an OpenAPI `ref` string to its JSON Schema, then parses it.
236
+ */
237
+ type ResolveOpenAPIRef<Spec extends Record<string, unknown>, Ref extends string> = Ref extends `#/components/schemas/${infer Name}` ? Spec["components"] extends Record<string, unknown> ? Spec["components"]["schemas"] extends Record<string, unknown> ? Name extends keyof Spec["components"]["schemas"] ? FromJSONSchema<Spec["components"]["schemas"][Name]> : unknown : unknown : unknown : Ref extends `${string}/${string}` ? unknown : unknown;
238
+ /** Navigate to a path item in an OpenAPI document. */
239
+ type PathItemOf<Doc, Path extends string> = Doc extends {
240
+ paths: Record<string, unknown>;
241
+ } ? Path extends keyof Doc["paths"] ? Doc["paths"][Path] : unknown : unknown;
242
+ /** Navigate to an operation within a path item. */
243
+ type OperationOf<PathItem, Method extends string> = PathItem extends Record<string, unknown> ? Method extends keyof PathItem ? PathItem[Method] : unknown : unknown;
244
+ /** Extract the schema from request body content. */
245
+ type RequestBodySchemaOf<Op> = Op extends {
246
+ requestBody: {
247
+ content: {
248
+ "application/json": {
249
+ schema: infer S;
250
+ };
251
+ };
252
+ };
253
+ } ? S : Op extends {
254
+ requestBody: {
255
+ content: Record<string, {
256
+ schema: infer S;
257
+ }>;
258
+ };
259
+ } ? S : unknown;
260
+ /** Extract the schema from response content. */
261
+ type ResponseSchemaOf<Op, Status extends string> = Op extends {
262
+ responses: Record<string, unknown>;
263
+ } ? Status extends keyof Op["responses"] ? Op["responses"][Status] extends {
264
+ content: {
265
+ "application/json": {
266
+ schema: infer S;
267
+ };
268
+ };
269
+ } ? S : Op["responses"][Status] extends {
270
+ content: Record<string, {
271
+ schema: infer S;
272
+ }>;
273
+ } ? S : unknown : unknown : unknown;
274
+ /** Resolve a schema that may be a $ref pointer. */
275
+ type ResolveMaybeRef<Doc, S> = S extends {
276
+ $ref: infer R extends string;
277
+ } ? ResolveOpenAPIRef<Doc & Record<string, unknown>, R> : S extends Record<string, unknown> ? FromJSONSchema<S> : unknown;
278
+ /** Extract parameter names from an operation. */
279
+ type ParameterNamesOf<Doc, Path extends string, Method extends string> = OperationOf<PathItemOf<Doc, Path>, Method> extends {
280
+ parameters: readonly unknown[];
281
+ } ? OperationOf<PathItemOf<Doc, Path>, Method>["parameters"][number] extends {
282
+ name: infer N;
283
+ } ? N extends string ? N : never : never : never;
284
+ /**
285
+ * Infer the TypeScript type of an OpenAPI operation's request body.
286
+ */
287
+ type OpenAPIRequestBodyType<Doc, Path extends string, Method extends string> = ResolveMaybeRef<Doc, RequestBodySchemaOf<OperationOf<PathItemOf<Doc, Path>, Method>>>;
288
+ /**
289
+ * Infer the TypeScript type of an OpenAPI operation's response.
290
+ */
291
+ type OpenAPIResponseType<Doc, Path extends string, Method extends string, Status extends string> = ResolveMaybeRef<Doc, ResponseSchemaOf<OperationOf<PathItemOf<Doc, Path>, Method>, Status>>;
292
+ /**
293
+ * Infer the fields prop type for ApiRequestBody.
294
+ * Falls back to Record<string, FieldOverride> for runtime documents.
295
+ */
296
+ type InferRequestBodyFields<Doc, Path extends string, Method extends string> = unknown extends OpenAPIRequestBodyType<Doc, Path, Method> ? Record<string, FieldOverride> : FieldOverrides<OpenAPIRequestBodyType<Doc, Path, Method>>;
297
+ /**
298
+ * Infer the fields prop type for ApiResponse.
299
+ * Falls back to Record<string, FieldOverride> for runtime documents.
300
+ */
301
+ type InferResponseFields<Doc, Path extends string, Method extends string, Status extends string> = unknown extends OpenAPIResponseType<Doc, Path, Method, Status> ? Record<string, FieldOverride> : FieldOverrides<OpenAPIResponseType<Doc, Path, Method, Status>>;
302
+ /**
303
+ * Infer the overrides prop type for ApiParameters.
304
+ * Falls back to Record<string, FieldOverride> for runtime documents.
305
+ */
306
+ type InferParameterOverrides<Doc, Path extends string, Method extends string> = string extends ParameterNamesOf<Doc, Path, Method> ? Record<string, FieldOverride> : Partial<Record<ParameterNamesOf<Doc, Path, Method>, FieldOverride>>;
307
+ /**
308
+ * Check if T is a "narrow" type (not wide like object, Record, or unknown).
309
+ * Used to determine if we can enumerate keys for path inference.
310
+ */
311
+ type IsNarrowObject<T> = T extends string | number | boolean | null | undefined | unknown[] ? false : T extends object ? Record<string, never> extends T ? false : true : false;
312
+ /**
313
+ * Extract all valid dot-separated paths from an object type.
314
+ * Produces paths like "name" | "address.city" | "address.postcode".
315
+ * Stops at leaf types (string, number, boolean, null) and arrays.
316
+ * Returns `string` for wide types (object, Record, unknown).
317
+ * Handles optional/nullable fields by unwrapping T | undefined.
318
+ */
319
+ type PathOfType<T, Prefix extends string = ""> = IsNarrowObject<T> extends true ? { [K in keyof T & string]: T[K] extends string | number | boolean | null | undefined ? `${Prefix}${K}` : T[K] extends unknown[] ? `${Prefix}${K}` : T[K] extends object | undefined ? PathOfType<Exclude<T[K], undefined>, `${Prefix}${K}.`> | `${Prefix}${K}` : `${Prefix}${K}` }[keyof T & string] : string;
320
+ /**
321
+ * Extract the type at a given dot-separated path.
322
+ * PathOfType<T> produces valid paths; TypeAtPath resolves the leaf type.
323
+ */
324
+ type TypeAtPath<T, P extends string> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? TypeAtPath<T[Key], Rest> : unknown : P extends keyof T ? T[P] : unknown;
325
+ //#endregion
326
+ export { mergeResolvers as A, HtmlResolver as C, getHtmlRenderFn as D, RenderProps as E, getRenderFunction as O, HtmlRenderProps as S, RenderFunction as T, WalkedField as _, FromJSONSchema as a, ComponentResolver as b, InferResponseFields as c, OpenAPIResponseType as d, PathOfType as f, TypeAtPath as g, SchemaType as h, FieldOverrides as i, typeToKey as j, mergeHtmlResolvers as k, JsonObject as l, SchemaMeta as m, FieldConstraints as n, InferParameterOverrides as o, ResolveOpenAPIRef as p, FieldOverride as r, InferRequestBodyFields as s, Editability as t, OpenAPIRequestBodyType as u, resolveEditability as v, RESOLVER_KEYS as w, HtmlRenderFunction as x, BaseFieldProps as y };
package/package.json CHANGED
@@ -1,5 +1,115 @@
1
1
  {
2
- "name": "schema-components",
3
- "version": "0.0.0",
4
- "private": false
2
+ "name": "schema-components",
3
+ "version": "1.1.0",
4
+ "description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
5
+ "type": "module",
6
+ "main": "./dist/index.mjs",
7
+ "types": "./dist/index.d.mts",
8
+ "exports": {
9
+ "./styles.css": "./dist/html/styles.css",
10
+ "./core/*": {
11
+ "types": "./dist/core/*.d.mts",
12
+ "import": "./dist/core/*.mjs"
13
+ },
14
+ "./react/*": {
15
+ "types": "./dist/react/*.d.mts",
16
+ "import": "./dist/react/*.mjs"
17
+ },
18
+ "./openapi/*": {
19
+ "types": "./dist/openapi/*.d.mts",
20
+ "import": "./dist/openapi/*.mjs"
21
+ },
22
+ "./html/*": {
23
+ "types": "./dist/html/*.d.mts",
24
+ "import": "./dist/html/*.mjs"
25
+ },
26
+ "./themes/*": {
27
+ "types": "./dist/themes/*.d.mts",
28
+ "import": "./dist/themes/*.mjs"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "LICENSE",
34
+ "README.md",
35
+ "CHANGELOG.md"
36
+ ],
37
+ "scripts": {
38
+ "build": "turbo run _build",
39
+ "_typecheck": "tsc --noEmit",
40
+ "_lint": "eslint --cache 'src/**/*.{ts,tsx}'",
41
+ "_lint:fix": "eslint --cache --fix 'src/**/*.{ts,tsx}'",
42
+ "_test": "node --test 'tests/**/*.unit.test.ts' 'tests/**/*.integration.test.ts'",
43
+ "_test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=80 --test-coverage-branches=80 --test-coverage-functions=80 --test-coverage-include='src/**/*.ts' --test-coverage-include='src/**/*.tsx' 'tests/**/*.unit.test.ts' 'tests/**/*.integration.test.ts'",
44
+ "_build": "tsdown && cp src/html/styles.css dist/html/styles.css",
45
+ "typecheck": "turbo run _typecheck",
46
+ "lint": "turbo run _lint",
47
+ "lint:fix": "turbo run _lint:fix",
48
+ "test": "turbo run _test",
49
+ "test:coverage": "turbo run _test:coverage",
50
+ "check": "turbo run _check",
51
+ "validate": "turbo run _validate",
52
+ "storybook": "storybook dev --port 6006",
53
+ "build-storybook": "storybook build --output-dir storybook-static",
54
+ "release": "semantic-release",
55
+ "release:dry": "semantic-release --dry-run",
56
+ "prepare": "husky",
57
+ "prepublishOnly": "pnpm validate"
58
+ },
59
+ "keywords": [
60
+ "react",
61
+ "zod",
62
+ "json-schema",
63
+ "openapi",
64
+ "schema",
65
+ "form",
66
+ "components",
67
+ "headless",
68
+ "ui"
69
+ ],
70
+ "author": "Joseph Mearman",
71
+ "license": "MIT",
72
+ "repository": {
73
+ "type": "git",
74
+ "url": "git+https://github.com/Mearman/schema-components.git"
75
+ },
76
+ "publishConfig": {
77
+ "access": "public",
78
+ "provenance": true
79
+ },
80
+ "peerDependencies": {
81
+ "react": "^18.0.0 || ^19.0.0",
82
+ "zod": "^4.0.0"
83
+ },
84
+ "devDependencies": {
85
+ "@commitlint/cli": "20.5.3",
86
+ "@commitlint/config-conventional": "20.5.3",
87
+ "@eslint/js": "10.0.1",
88
+ "@semantic-release/changelog": "6.0.3",
89
+ "@semantic-release/git": "10.0.1",
90
+ "@semantic-release/github": "12.0.6",
91
+ "@storybook/react": "10.4.0",
92
+ "@storybook/react-vite": "10.4.0",
93
+ "@types/node": "25.6.0",
94
+ "@types/react": "19.2.14",
95
+ "conventional-changelog-conventionalcommits": "9.3.1",
96
+ "eslint": "10.3.0",
97
+ "eslint-config-prettier": "10.1.8",
98
+ "eslint-plugin-prettier": "5.5.5",
99
+ "husky": "9.1.7",
100
+ "lint-staged": "17.0.2",
101
+ "prettier": "3.8.3",
102
+ "react": "19.2.6",
103
+ "react-dom": "19.2.6",
104
+ "semantic-release": "25.0.3",
105
+ "storybook": "10.4.0",
106
+ "tsdown": "0.21.10",
107
+ "tslib": "2.8.1",
108
+ "turbo": "2.9.9",
109
+ "typescript": "6.0.3",
110
+ "typescript-eslint": "8.59.2",
111
+ "vite": "^8.0.12",
112
+ "zod": "4.4.3"
113
+ },
114
+ "packageManager": "pnpm@10.33.1"
5
115
  }