schema-components 1.12.11 → 1.14.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 (83) hide show
  1. package/README.md +35 -0
  2. package/dist/core/adapter.d.mts +8 -3
  3. package/dist/core/adapter.mjs +58 -14
  4. package/dist/core/constraints.d.mts +17 -0
  5. package/dist/core/constraints.mjs +150 -0
  6. package/dist/core/diagnostics.d.mts +2 -0
  7. package/dist/core/diagnostics.mjs +33 -0
  8. package/dist/core/errors.d.mts +1 -1
  9. package/dist/core/formats.d.mts +33 -0
  10. package/dist/core/formats.mjs +67 -0
  11. package/dist/core/merge.d.mts +48 -0
  12. package/dist/core/merge.mjs +125 -0
  13. package/dist/core/normalise.d.mts +41 -0
  14. package/dist/core/normalise.mjs +2 -0
  15. package/dist/core/openapi30.d.mts +41 -0
  16. package/dist/core/openapi30.mjs +2 -0
  17. package/dist/core/ref.d.mts +2 -0
  18. package/dist/core/ref.mjs +165 -0
  19. package/dist/core/renderer.d.mts +2 -2
  20. package/dist/core/renderer.mjs +8 -0
  21. package/dist/core/swagger2.d.mts +11 -0
  22. package/dist/core/swagger2.mjs +2 -0
  23. package/dist/core/typeInference.d.mts +2 -0
  24. package/dist/core/typeInference.mjs +1 -0
  25. package/dist/core/types.d.mts +2 -3
  26. package/dist/core/types.mjs +58 -2
  27. package/dist/core/version.d.mts +2 -0
  28. package/dist/core/version.mjs +151 -0
  29. package/dist/core/walkBuilders.d.mts +66 -0
  30. package/dist/core/walkBuilders.mjs +152 -0
  31. package/dist/core/walker.d.mts +3 -10
  32. package/dist/core/walker.mjs +245 -233
  33. package/dist/diagnostics-DzbZmcLI.d.mts +64 -0
  34. package/dist/html/a11y.d.mts +5 -4
  35. package/dist/html/renderToHtml.d.mts +3 -3
  36. package/dist/html/renderToHtml.mjs +23 -379
  37. package/dist/html/renderToHtmlStream.d.mts +29 -47
  38. package/dist/html/renderToHtmlStream.mjs +33 -305
  39. package/dist/html/renderers.d.mts +14 -0
  40. package/dist/html/renderers.mjs +406 -0
  41. package/dist/html/streamRenderers.d.mts +13 -0
  42. package/dist/html/streamRenderers.mjs +243 -0
  43. package/dist/normalise-tL9FckAk.mjs +748 -0
  44. package/dist/openapi/ApiCallbacks.d.mts +16 -0
  45. package/dist/openapi/ApiCallbacks.mjs +34 -0
  46. package/dist/openapi/ApiLinks.d.mts +16 -0
  47. package/dist/openapi/ApiLinks.mjs +42 -0
  48. package/dist/openapi/ApiResponseHeaders.d.mts +16 -0
  49. package/dist/openapi/ApiResponseHeaders.mjs +35 -0
  50. package/dist/openapi/ApiSecurity.d.mts +19 -0
  51. package/dist/openapi/ApiSecurity.mjs +33 -0
  52. package/dist/openapi/bundle.d.mts +47 -0
  53. package/dist/openapi/bundle.mjs +95 -0
  54. package/dist/openapi/components.d.mts +7 -1
  55. package/dist/openapi/components.mjs +30 -6
  56. package/dist/openapi/parser.d.mts +59 -2
  57. package/dist/openapi/parser.mjs +189 -8
  58. package/dist/react/SchemaComponent.d.mts +13 -4
  59. package/dist/react/SchemaComponent.mjs +51 -91
  60. package/dist/react/SchemaView.d.mts +10 -2
  61. package/dist/react/SchemaView.mjs +33 -15
  62. package/dist/react/fieldPath.d.mts +20 -0
  63. package/dist/react/fieldPath.mjs +81 -0
  64. package/dist/react/headless.d.mts +2 -4
  65. package/dist/react/headless.mjs +3 -492
  66. package/dist/react/headlessRenderers.d.mts +23 -0
  67. package/dist/react/headlessRenderers.mjs +507 -0
  68. package/dist/ref-DvWoULcy.d.mts +44 -0
  69. package/dist/renderer-BdSqllx5.d.mts +160 -0
  70. package/dist/themes/mantine.d.mts +1 -1
  71. package/dist/themes/mantine.mjs +2 -1
  72. package/dist/themes/mui.d.mts +1 -1
  73. package/dist/themes/mui.mjs +3 -2
  74. package/dist/themes/radix.d.mts +1 -1
  75. package/dist/themes/radix.mjs +2 -1
  76. package/dist/themes/shadcn.d.mts +1 -1
  77. package/dist/themes/shadcn.mjs +10 -6
  78. package/dist/typeInference-k7FXfTVO.d.mts +335 -0
  79. package/dist/types-D_5ST7SS.d.mts +269 -0
  80. package/dist/version-B5NV-35j.d.mts +69 -0
  81. package/package.json +1 -1
  82. package/dist/types-BJzEgJdX.d.mts +0 -335
  83. /package/dist/{errors-DIKI2C78.d.mts → errors-C5zRC2PU.d.mts} +0 -0
@@ -0,0 +1,269 @@
1
+ //#region src/core/types.d.ts
2
+ /**
3
+ * Core types for schema-components.
4
+ *
5
+ * These types define the vocabulary shared between the schema walker,
6
+ * component resolver, and React components.
7
+ */
8
+ /** A raw JSON object (JSON Schema or OpenAPI document). */
9
+ type JsonObject = Record<string, unknown>;
10
+ /**
11
+ * Metadata attached to schemas via `.meta()` or passed as props to
12
+ * `<SchemaComponent>`. Every field is also available as a top-level
13
+ * prop on `<SchemaComponent>` (with TypeScript-enforced exclusivity
14
+ * between prop and `meta`).
15
+ */
16
+ interface SchemaMeta {
17
+ readOnly?: boolean;
18
+ writeOnly?: boolean;
19
+ description?: string;
20
+ title?: string;
21
+ deprecated?: boolean;
22
+ /** Component hint — resolved before theme adapter. */
23
+ component?: string;
24
+ /** Sort order for object fields. Lower values render first. */
25
+ order?: number;
26
+ /** Arbitrary UI hints passed through to theme adapters. */
27
+ [key: string]: unknown;
28
+ }
29
+ type Editability = "presentation" | "input" | "editable";
30
+ /**
31
+ * Resolved editability state for a single field.
32
+ *
33
+ * Priority (highest wins):
34
+ * 1. Property-level readOnly → presentation
35
+ * 2. Property-level writeOnly → input
36
+ * 3. Component-level readOnly → presentation
37
+ * 4. Component-level writeOnly → input
38
+ * 5. Schema root readOnly → presentation
39
+ * 6. Schema root writeOnly → input
40
+ * 7. Neither → editable
41
+ */
42
+ declare function resolveEditability(propertyMeta: SchemaMeta | undefined, componentMeta: SchemaMeta | undefined, rootMeta: SchemaMeta | undefined): Editability;
43
+ /**
44
+ * Recursive mapped type that mirrors a schema's shape for per-field
45
+ * overrides. Each leaf accepts schema meta overrides and an optional
46
+ * per-field validation error callback. Objects recurse and also accept
47
+ * their own overrides.
48
+ */
49
+ type FieldOverrides<T> = { [K in keyof T]?: T[K] extends object ? FieldOverrides<T[K]> & FieldOverride : FieldOverride };
50
+ /**
51
+ * Per-field override. Extends SchemaMeta with rendering controls
52
+ * and a per-field validation error callback.
53
+ */
54
+ type FieldOverride = Partial<SchemaMeta> & {
55
+ /** Called with the ZodError when this field fails validation. */onValidationError?: (error: unknown) => void; /** Hide this field when false. Defaults to true (visible). */
56
+ visible?: boolean;
57
+ };
58
+ /**
59
+ * All schema types the walker can produce.
60
+ * Used as the discriminant in the WalkedField tagged union.
61
+ */
62
+ type SchemaType = "string" | "number" | "boolean" | "null" | "enum" | "literal" | "object" | "array" | "tuple" | "record" | "union" | "discriminatedUnion" | "conditional" | "negation" | "recursive" | "file" | "never" | "unknown";
63
+ /** Constraints that apply to string schemas. */
64
+ interface StringConstraints {
65
+ minLength?: number;
66
+ maxLength?: number;
67
+ pattern?: string;
68
+ format?: string;
69
+ /** Derived RegExp from the format string, if the format is recognised. */
70
+ formatPattern?: RegExp;
71
+ contentEncoding?: string;
72
+ contentMediaType?: string;
73
+ }
74
+ /** Constraints that apply to number/integer schemas. */
75
+ interface NumberConstraints {
76
+ minimum?: number;
77
+ maximum?: number;
78
+ exclusiveMinimum?: number;
79
+ exclusiveMaximum?: number;
80
+ multipleOf?: number;
81
+ }
82
+ /** Constraints that apply to array schemas. */
83
+ interface ArrayConstraints {
84
+ minItems?: number;
85
+ maxItems?: number;
86
+ uniqueItems?: boolean;
87
+ /** Schema that at least one array item must match. */
88
+ contains?: Record<string, unknown>;
89
+ minContains?: number;
90
+ maxContains?: number;
91
+ /** Constraint schema for unevaluated items. */
92
+ unevaluatedItems?: Record<string, unknown>;
93
+ }
94
+ /** Constraints that apply to object schemas. */
95
+ interface ObjectConstraints {
96
+ minProperties?: number;
97
+ maxProperties?: number;
98
+ }
99
+ /** Constraints that apply to file schemas. */
100
+ interface FileConstraints {
101
+ mimeTypes?: string[];
102
+ }
103
+ /**
104
+ * Union of all constraint types. Renderers can narrow by checking
105
+ * the WalkedField's `type` discriminant.
106
+ */
107
+ type FieldConstraints = StringConstraints | NumberConstraints | ArrayConstraints | ObjectConstraints | FileConstraints | Record<string, never>;
108
+ /**
109
+ * Properties common to every WalkedField variant.
110
+ * The `type` field acts as the discriminant for the tagged union.
111
+ */
112
+ interface FieldBase {
113
+ editability: Editability;
114
+ meta: SchemaMeta;
115
+ /** Whether the field is optional (not in `required`). */
116
+ isOptional?: boolean;
117
+ /** Whether the field is nullable (`anyOf [T, null]` or `type: ["...", "null"]`). */
118
+ isNullable?: boolean;
119
+ /** Default value from the schema's `default` keyword. */
120
+ defaultValue?: unknown;
121
+ /** Example values from the schema's `examples` keyword. */
122
+ examples?: unknown[];
123
+ }
124
+ interface StringField extends FieldBase {
125
+ type: "string";
126
+ constraints: StringConstraints;
127
+ }
128
+ interface NumberField extends FieldBase {
129
+ type: "number";
130
+ constraints: NumberConstraints;
131
+ }
132
+ interface BooleanField extends FieldBase {
133
+ type: "boolean";
134
+ constraints: Record<string, never>;
135
+ }
136
+ interface NullField extends FieldBase {
137
+ type: "null";
138
+ constraints: Record<string, never>;
139
+ }
140
+ interface EnumField extends FieldBase {
141
+ type: "enum";
142
+ constraints: Record<string, never>;
143
+ enumValues: (string | number | boolean | null)[];
144
+ }
145
+ interface LiteralField extends FieldBase {
146
+ type: "literal";
147
+ constraints: Record<string, never>;
148
+ literalValues: (string | number | boolean | null)[];
149
+ }
150
+ interface ObjectField extends FieldBase {
151
+ type: "object";
152
+ constraints: ObjectConstraints;
153
+ /** Map of property name → walked sub-schema. */
154
+ fields: Record<string, WalkedField>;
155
+ /** Property names declared in `required`. */
156
+ requiredFields: string[];
157
+ /** Regex-keyed sub-schemas from `patternProperties`. */
158
+ patternProperties?: Record<string, WalkedField>;
159
+ /** Whether `additionalProperties` is explicitly `false` (closed). */
160
+ additionalPropertiesClosed?: boolean;
161
+ /** Schema for additional properties when not `false` and not a Record. */
162
+ additionalPropertiesSchema?: WalkedField;
163
+ /** Property-presence-activated sub-schemas from `dependentSchemas`. */
164
+ dependentSchemas?: Record<string, WalkedField>;
165
+ /** Property-presence-conditional required fields from `dependentRequired`. */
166
+ dependentRequired?: Record<string, string[]>;
167
+ /** Constraint schema for unevaluated properties. */
168
+ unevaluatedProperties?: WalkedField;
169
+ /** Whether unevaluatedProperties is explicitly `false`. */
170
+ unevaluatedPropertiesClosed?: boolean;
171
+ /** Schema constraining property names (from `propertyNames`). */
172
+ propertyNames?: WalkedField;
173
+ }
174
+ interface ArrayField extends FieldBase {
175
+ type: "array";
176
+ constraints: ArrayConstraints;
177
+ /** The element sub-schema. */
178
+ element?: WalkedField;
179
+ /** Walked schema for unevaluated items. */
180
+ unevaluatedItems?: WalkedField;
181
+ }
182
+ interface TupleField extends FieldBase {
183
+ type: "tuple";
184
+ constraints: ArrayConstraints;
185
+ /** Positional element schemas from `prefixItems`. */
186
+ prefixItems: WalkedField[];
187
+ }
188
+ interface RecordField extends FieldBase {
189
+ type: "record";
190
+ constraints: ObjectConstraints;
191
+ /** Key name validation schema (from `propertyNames`). */
192
+ keyType: WalkedField;
193
+ /** Value schema (from `additionalProperties`). */
194
+ valueType: WalkedField;
195
+ }
196
+ interface UnionField extends FieldBase {
197
+ type: "union";
198
+ constraints: Record<string, never>;
199
+ /** The union options. */
200
+ options: WalkedField[];
201
+ }
202
+ interface DiscriminatedUnionField extends FieldBase {
203
+ type: "discriminatedUnion";
204
+ constraints: Record<string, never>;
205
+ /** The union options. */
206
+ options: WalkedField[];
207
+ /** Property name that discriminates between options. */
208
+ discriminator: string;
209
+ }
210
+ interface ConditionalField extends FieldBase {
211
+ type: "conditional";
212
+ constraints: Record<string, never>;
213
+ /** The `if` sub-schema. */
214
+ ifClause: WalkedField;
215
+ /** The `then` sub-schema. */
216
+ thenClause?: WalkedField;
217
+ /** The `else` sub-schema. */
218
+ elseClause?: WalkedField;
219
+ }
220
+ interface NegationField extends FieldBase {
221
+ type: "negation";
222
+ constraints: Record<string, never>;
223
+ /** The negated sub-schema. */
224
+ negated: WalkedField;
225
+ }
226
+ interface FileField extends FieldBase {
227
+ type: "file";
228
+ constraints: FileConstraints;
229
+ }
230
+ interface RecursiveField extends FieldBase {
231
+ type: "recursive";
232
+ constraints: Record<string, never>;
233
+ /** The $ref string that would create the cycle (e.g. "#" or "#Node"). */
234
+ refTarget: string;
235
+ }
236
+ /** Schema position where `false` appears — the field cannot have any value. */
237
+ interface NeverField extends FieldBase {
238
+ type: "never";
239
+ constraints: Record<string, never>;
240
+ }
241
+ interface UnknownField extends FieldBase {
242
+ type: "unknown";
243
+ constraints: Record<string, never>;
244
+ }
245
+ /**
246
+ * Tagged union of all schema field types produced by the walker.
247
+ * Use `field.type` to narrow to a specific variant.
248
+ */
249
+ type WalkedField = StringField | NumberField | BooleanField | NullField | EnumField | LiteralField | ObjectField | ArrayField | TupleField | RecordField | UnionField | DiscriminatedUnionField | ConditionalField | NegationField | RecursiveField | NeverField | FileField | UnknownField;
250
+ declare function isStringField(field: WalkedField): field is StringField;
251
+ declare function isNumberField(field: WalkedField): field is NumberField;
252
+ declare function isBooleanField(field: WalkedField): field is BooleanField;
253
+ declare function isNullField(field: WalkedField): field is NullField;
254
+ declare function isEnumField(field: WalkedField): field is EnumField;
255
+ declare function isLiteralField(field: WalkedField): field is LiteralField;
256
+ declare function isObjectField(field: WalkedField): field is ObjectField;
257
+ declare function isArrayField(field: WalkedField): field is ArrayField;
258
+ declare function isTupleField(field: WalkedField): field is TupleField;
259
+ declare function isRecordField(field: WalkedField): field is RecordField;
260
+ declare function isUnionField(field: WalkedField): field is UnionField;
261
+ declare function isDiscriminatedUnionField(field: WalkedField): field is DiscriminatedUnionField;
262
+ declare function isConditionalField(field: WalkedField): field is ConditionalField;
263
+ declare function isNegationField(field: WalkedField): field is NegationField;
264
+ declare function isFileField(field: WalkedField): field is FileField;
265
+ declare function isRecursiveField(field: WalkedField): field is RecursiveField;
266
+ declare function isNeverField(field: WalkedField): field is NeverField;
267
+ declare function isUnknownField(field: WalkedField): field is UnknownField;
268
+ //#endregion
269
+ export { UnionField as A, isNegationField as B, RecordField as C, StringConstraints as D, SchemaType as E, isConditionalField as F, isRecordField as G, isNullField as H, isDiscriminatedUnionField as I, isTupleField as J, isRecursiveField as K, isEnumField as L, WalkedField as M, isArrayField as N, StringField as O, isBooleanField as P, isFileField as R, ObjectField as S, SchemaMeta as T, isNumberField as U, isNeverField as V, isObjectField as W, isUnknownField as X, isUnionField as Y, resolveEditability as Z, NeverField as _, DiscriminatedUnionField as a, NumberField as b, FieldBase as c, FieldOverrides as d, FileConstraints as f, NegationField as g, LiteralField as h, ConditionalField as i, UnknownField as j, TupleField as k, FieldConstraints as l, JsonObject as m, ArrayField as n, Editability as o, FileField as p, isStringField as q, BooleanField as r, EnumField as s, ArrayConstraints as t, FieldOverride as u, NullField as v, RecursiveField as w, ObjectConstraints as x, NumberConstraints as y, isLiteralField as z };
@@ -0,0 +1,69 @@
1
+ //#region src/core/version.d.ts
2
+ /**
3
+ * JSON Schema draft and OpenAPI version detection.
4
+ *
5
+ * Detects the version from `$schema` URIs and OpenAPI `openapi`/`swagger`
6
+ * fields. Used by the normaliser to apply version-specific transformations
7
+ * before the walker processes the schema.
8
+ */
9
+ type JsonSchemaDraft = "draft-04" | "draft-06" | "draft-07" | "draft-2019-09" | "draft-2020-12";
10
+ /**
11
+ * Detect the JSON Schema draft version from a schema's `$schema` URI.
12
+ * When `$schema` is absent, uses heuristic keyword detection via
13
+ * `inferJsonSchemaDraft` to guess the draft version.
14
+ * Returns `"draft-2020-12"` as the final fallback when no heuristic
15
+ * matches either.
16
+ */
17
+ declare function detectJsonSchemaDraft(schema: Record<string, unknown>): JsonSchemaDraft;
18
+ /**
19
+ * Inference result carrying the detected draft and the heuristic
20
+ * that triggered it.
21
+ */
22
+ interface InferredDraft {
23
+ draft: JsonSchemaDraft;
24
+ inferredFrom: string;
25
+ }
26
+ /**
27
+ * Infer the JSON Schema draft from keyword presence when `$schema`
28
+ * is absent. Examined from highest-confidence to lowest.
29
+ *
30
+ * Heuristics:
31
+ * 1. `$dynamicRef` / `$dynamicAnchor` / `prefixItems` → Draft 2020-12
32
+ * 2. `$recursiveRef` / `$recursiveAnchor` / `unevaluatedProperties` /
33
+ * `dependentSchemas` → Draft 2019-09
34
+ * 3. `if` / `then` / `else`, `contentEncoding` / `contentMediaType` → Draft 07
35
+ * 4. `const`, `examples` (array), `propertyNames` → Draft 06
36
+ * 5. Boolean `exclusiveMinimum`, `id` (no `$`), `definitions` only → Draft 04
37
+ * 6. No signal → Draft 2020-12
38
+ */
39
+ declare function inferJsonSchemaDraft(schema: Record<string, unknown>): JsonSchemaDraft;
40
+ /**
41
+ * Like `inferJsonSchemaDraft` but also returns the heuristic that
42
+ * triggered the inference, for diagnostic emission.
43
+ */
44
+ declare function inferJsonSchemaDraftWithReason(schema: Record<string, unknown>): InferredDraft;
45
+ interface OpenApiVersionInfo {
46
+ major: number;
47
+ minor: number;
48
+ patch: number;
49
+ }
50
+ /**
51
+ * Detect the OpenAPI/Swagger version from a document.
52
+ * Returns `undefined` for documents that are not OpenAPI or Swagger.
53
+ */
54
+ declare function detectOpenApiVersion(doc: Record<string, unknown>): OpenApiVersionInfo | undefined;
55
+ /**
56
+ * Check if an OpenAPI version is 3.0.x (uses modified Draft 04 schemas
57
+ * with `nullable` instead of `anyOf [T, null]`).
58
+ */
59
+ declare function isOpenApi30(version: OpenApiVersionInfo): boolean;
60
+ /**
61
+ * Check if an OpenAPI version is 3.1.x (uses standard Draft 2020-12).
62
+ */
63
+ declare function isOpenApi31(version: OpenApiVersionInfo): boolean;
64
+ /**
65
+ * Check if a document is Swagger 2.0.
66
+ */
67
+ declare function isSwagger2(version: OpenApiVersionInfo): boolean;
68
+ //#endregion
69
+ export { detectOpenApiVersion as a, isOpenApi30 as c, detectJsonSchemaDraft as i, isOpenApi31 as l, JsonSchemaDraft as n, inferJsonSchemaDraft as o, OpenApiVersionInfo as r, inferJsonSchemaDraftWithReason as s, InferredDraft as t, isSwagger2 as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-components",
3
- "version": "1.12.11",
3
+ "version": "1.14.0",
4
4
  "description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -1,335 +0,0 @@
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
- discriminatedUnion?: RenderFunction;
79
- literal?: RenderFunction;
80
- file?: RenderFunction;
81
- unknown?: RenderFunction;
82
- }
83
- /** An HTML render function returns a string. */
84
- type HtmlRenderFunction = (props: HtmlRenderProps) => string;
85
- /**
86
- * HTML resolver — maps schema types to HTML string renderers.
87
- * Structurally mirrors ComponentResolver but produces strings.
88
- */
89
- interface HtmlResolver {
90
- string?: HtmlRenderFunction;
91
- number?: HtmlRenderFunction;
92
- boolean?: HtmlRenderFunction;
93
- enum?: HtmlRenderFunction;
94
- object?: HtmlRenderFunction;
95
- array?: HtmlRenderFunction;
96
- record?: HtmlRenderFunction;
97
- union?: HtmlRenderFunction;
98
- discriminatedUnion?: HtmlRenderFunction;
99
- literal?: HtmlRenderFunction;
100
- file?: HtmlRenderFunction;
101
- unknown?: HtmlRenderFunction;
102
- }
103
- declare const RESOLVER_KEYS: readonly ["string", "number", "boolean", "enum", "object", "array", "record", "union", "discriminatedUnion", "literal", "file", "unknown"];
104
- type ResolverKey = (typeof RESOLVER_KEYS)[number];
105
- /**
106
- * Map a schema type to the resolver key that handles it.
107
- * `discriminatedUnion` → `union`. Unknown types → `unknown`.
108
- */
109
- declare function typeToKey(type: WalkedField["type"]): ResolverKey;
110
- /**
111
- * Look up the render function for a schema type in a ComponentResolver.
112
- */
113
- declare function getRenderFunction(type: WalkedField["type"], resolver: ComponentResolver): RenderFunction | undefined;
114
- /**
115
- * Look up the render function for a schema type in an HtmlResolver.
116
- */
117
- declare function getHtmlRenderFn(type: WalkedField["type"], resolver: HtmlResolver): HtmlRenderFunction | undefined;
118
- /**
119
- * Merge two ComponentResolvers — user values take priority, fallback fills gaps.
120
- */
121
- declare function mergeResolvers(user: ComponentResolver, fallback: ComponentResolver): ComponentResolver;
122
- /**
123
- * Merge two HtmlResolvers — user values take priority, fallback fills gaps.
124
- */
125
- declare function mergeHtmlResolvers(user: HtmlResolver, fallback: HtmlResolver): HtmlResolver;
126
- //#endregion
127
- //#region src/core/types.d.ts
128
- /**
129
- * Core types for schema-components.
130
- *
131
- * These types define the vocabulary shared between the schema walker,
132
- * component resolver, and React components.
133
- */
134
- /** A raw JSON object (JSON Schema or OpenAPI document). */
135
- type JsonObject = Record<string, unknown>;
136
- /**
137
- * Metadata attached to schemas via `.meta()` or passed as props to
138
- * `<SchemaComponent>`. Every field is also available as a top-level
139
- * prop on `<SchemaComponent>` (with TypeScript-enforced exclusivity
140
- * between prop and `meta`).
141
- */
142
- interface SchemaMeta {
143
- readOnly?: boolean;
144
- writeOnly?: boolean;
145
- description?: string;
146
- title?: string;
147
- deprecated?: boolean;
148
- /** Component hint — resolved before theme adapter. */
149
- component?: string;
150
- /** Sort order for object fields. Lower values render first. */
151
- order?: number;
152
- /** Arbitrary UI hints passed through to theme adapters. */
153
- [key: string]: unknown;
154
- }
155
- type Editability = "presentation" | "input" | "editable";
156
- /**
157
- * Resolved editability state for a single field.
158
- *
159
- * Priority (highest wins):
160
- * 1. Property-level readOnly → presentation
161
- * 2. Property-level writeOnly → input
162
- * 3. Component-level readOnly → presentation
163
- * 4. Component-level writeOnly → input
164
- * 5. Schema root readOnly → presentation
165
- * 6. Schema root writeOnly → input
166
- * 7. Neither → editable
167
- */
168
- declare function resolveEditability(propertyMeta: SchemaMeta | undefined, componentMeta: SchemaMeta | undefined, rootMeta: SchemaMeta | undefined): Editability;
169
- /**
170
- * Recursive mapped type that mirrors a schema's shape for per-field
171
- * overrides. Each leaf accepts schema meta overrides and an optional
172
- * per-field validation error callback. Objects recurse and also accept
173
- * their own overrides.
174
- */
175
- type FieldOverrides<T> = { [K in keyof T]?: T[K] extends object ? FieldOverrides<T[K]> & FieldOverride : FieldOverride };
176
- /**
177
- * Per-field override. Extends SchemaMeta with rendering controls
178
- * and a per-field validation error callback.
179
- */
180
- type FieldOverride = Partial<SchemaMeta> & {
181
- /** Called with the ZodError when this field fails validation. */onValidationError?: (error: unknown) => void; /** Hide this field when false. Defaults to true (visible). */
182
- visible?: boolean;
183
- };
184
- type SchemaType = "string" | "number" | "boolean" | "null" | "enum" | "literal" | "object" | "array" | "record" | "union" | "discriminatedUnion" | "optional" | "nullable" | "default" | "readonly" | "pipe" | "lazy" | "file" | "unknown";
185
- interface WalkedField {
186
- type: SchemaType;
187
- editability: Editability;
188
- meta: SchemaMeta;
189
- /** For objects: map of field name → WalkedField. */
190
- fields?: Record<string, WalkedField>;
191
- /** For arrays: the element schema. */
192
- element?: WalkedField;
193
- /** For enums: the allowed values. */
194
- enumValues?: string[];
195
- /** For unions/discriminated unions: the options. */
196
- options?: WalkedField[];
197
- discriminator?: string;
198
- /** For records: key and value schemas. */
199
- keyType?: WalkedField;
200
- valueType?: WalkedField;
201
- /** For literals: the literal value(s). */
202
- literalValues?: (string | number | boolean | null)[];
203
- /** Whether the field is optional. */
204
- isOptional?: boolean;
205
- /** Whether the field is nullable. */
206
- isNullable?: boolean;
207
- /** Default value if present on the schema. */
208
- defaultValue?: unknown;
209
- /** Constraints from Zod checks (min, max, pattern, etc.). */
210
- constraints: FieldConstraints;
211
- }
212
- interface FieldConstraints {
213
- minLength?: number;
214
- maxLength?: number;
215
- minimum?: number;
216
- maximum?: number;
217
- pattern?: string;
218
- format?: string;
219
- mimeTypes?: string[];
220
- minItems?: number;
221
- maxItems?: number;
222
- }
223
- /**
224
- * Maps a JSON Schema structure to a TypeScript type.
225
- * Works with `as const` literals — provides full autocomplete for `fields`.
226
- */
227
- type FromJSONSchema<S> = S extends {
228
- type: "string";
229
- } ? string : S extends {
230
- type: "number" | "integer";
231
- } ? number : S extends {
232
- type: "boolean";
233
- } ? boolean : S extends {
234
- type: "null";
235
- } ? null : S extends {
236
- type: "array";
237
- items: infer I;
238
- } ? FromJSONSchema<I>[] : S extends {
239
- type: "object";
240
- properties: infer P;
241
- required?: infer R;
242
- } ? { [K in keyof P]: K extends R ? FromJSONSchema<P[K]> : FromJSONSchema<P[K]> | undefined } : unknown;
243
- /**
244
- * Resolves an OpenAPI `ref` string to its JSON Schema, then parses it.
245
- */
246
- 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;
247
- /** Navigate to a path item in an OpenAPI document. */
248
- type PathItemOf<Doc, Path extends string> = Doc extends {
249
- paths: Record<string, unknown>;
250
- } ? Path extends keyof Doc["paths"] ? Doc["paths"][Path] : unknown : unknown;
251
- /** Navigate to an operation within a path item. */
252
- type OperationOf<PathItem, Method extends string> = PathItem extends Record<string, unknown> ? Method extends keyof PathItem ? PathItem[Method] : unknown : unknown;
253
- /** Extract the schema from request body content. */
254
- type RequestBodySchemaOf<Op> = Op extends {
255
- requestBody: {
256
- content: {
257
- "application/json": {
258
- schema: infer S;
259
- };
260
- };
261
- };
262
- } ? S : Op extends {
263
- requestBody: {
264
- content: Record<string, {
265
- schema: infer S;
266
- }>;
267
- };
268
- } ? S : unknown;
269
- /** Extract the schema from response content. */
270
- type ResponseSchemaOf<Op, Status extends string> = Op extends {
271
- responses: Record<string, unknown>;
272
- } ? Status extends keyof Op["responses"] ? Op["responses"][Status] extends {
273
- content: {
274
- "application/json": {
275
- schema: infer S;
276
- };
277
- };
278
- } ? S : Op["responses"][Status] extends {
279
- content: Record<string, {
280
- schema: infer S;
281
- }>;
282
- } ? S : unknown : unknown : unknown;
283
- /** Resolve a schema that may be a $ref pointer. */
284
- type ResolveMaybeRef<Doc, S> = S extends {
285
- $ref: infer R extends string;
286
- } ? ResolveOpenAPIRef<Doc & Record<string, unknown>, R> : S extends Record<string, unknown> ? FromJSONSchema<S> : unknown;
287
- /** Extract parameter names from an operation. */
288
- type ParameterNamesOf<Doc, Path extends string, Method extends string> = OperationOf<PathItemOf<Doc, Path>, Method> extends {
289
- parameters: readonly unknown[];
290
- } ? OperationOf<PathItemOf<Doc, Path>, Method>["parameters"][number] extends {
291
- name: infer N;
292
- } ? N extends string ? N : never : never : never;
293
- /**
294
- * Infer the TypeScript type of an OpenAPI operation's request body.
295
- */
296
- type OpenAPIRequestBodyType<Doc, Path extends string, Method extends string> = ResolveMaybeRef<Doc, RequestBodySchemaOf<OperationOf<PathItemOf<Doc, Path>, Method>>>;
297
- /**
298
- * Infer the TypeScript type of an OpenAPI operation's response.
299
- */
300
- type OpenAPIResponseType<Doc, Path extends string, Method extends string, Status extends string> = ResolveMaybeRef<Doc, ResponseSchemaOf<OperationOf<PathItemOf<Doc, Path>, Method>, Status>>;
301
- /**
302
- * Infer the fields prop type for ApiRequestBody.
303
- * Falls back to Record<string, FieldOverride> for runtime documents.
304
- */
305
- type InferRequestBodyFields<Doc, Path extends string, Method extends string> = unknown extends OpenAPIRequestBodyType<Doc, Path, Method> ? Record<string, FieldOverride> : FieldOverrides<OpenAPIRequestBodyType<Doc, Path, Method>>;
306
- /**
307
- * Infer the fields prop type for ApiResponse.
308
- * Falls back to Record<string, FieldOverride> for runtime documents.
309
- */
310
- 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>>;
311
- /**
312
- * Infer the overrides prop type for ApiParameters.
313
- * Falls back to Record<string, FieldOverride> for runtime documents.
314
- */
315
- 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>>;
316
- /**
317
- * Check if T is a "narrow" type (not wide like object, Record, or unknown).
318
- * Used to determine if we can enumerate keys for path inference.
319
- */
320
- type IsNarrowObject<T> = T extends string | number | boolean | null | undefined | unknown[] ? false : T extends object ? Record<string, never> extends T ? false : true : false;
321
- /**
322
- * Extract all valid dot-separated paths from an object type.
323
- * Produces paths like "name" | "address.city" | "address.postcode".
324
- * Stops at leaf types (string, number, boolean, null) and arrays.
325
- * Returns `string` for wide types (object, Record, unknown).
326
- * Handles optional/nullable fields by unwrapping T | undefined.
327
- */
328
- 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;
329
- /**
330
- * Extract the type at a given dot-separated path.
331
- * PathOfType<T> produces valid paths; TypeAtPath resolves the leaf type.
332
- */
333
- 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;
334
- //#endregion
335
- 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 };